From 17060bc5e373ca7d41348c1cb1b1522bdd4b0af1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Iv=C3=A1n=20Renison?=
<85908989+IvanRenison@users.noreply.github.com>
Date: Sat, 13 Jun 2026 01:31:30 +0000
Subject: [PATCH 0001/1300] feat(Combinatorics/SimpleGraph/Maps): add theorems
about composition (#37624)
---
Mathlib/Combinatorics/SimpleGraph/Maps.lean | 35 ++++++++++++++++++---
1 file changed, 31 insertions(+), 4 deletions(-)
diff --git a/Mathlib/Combinatorics/SimpleGraph/Maps.lean b/Mathlib/Combinatorics/SimpleGraph/Maps.lean
index a4d9325a549275..3fbde0b4f959eb 100644
--- a/Mathlib/Combinatorics/SimpleGraph/Maps.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/Maps.lean
@@ -45,7 +45,7 @@ open Function
namespace SimpleGraph
-variable {V W X : Type*} (G : SimpleGraph V) (G' : SimpleGraph W) {u v : V}
+variable {V W X Y : Type*} (G : SimpleGraph V) (G' : SimpleGraph W) {u v : V}
/-! ## Map and comap -/
@@ -420,7 +420,7 @@ theorem le_comap (f : H →g G) : H ≤ G.comap f :=
theorem nonempty_hom_iff_exists_le_comap : Nonempty (H →g G) ↔ ∃ f, H ≤ G.comap f :=
⟨fun ⟨f⟩ ↦ ⟨f, f.le_comap⟩, fun ⟨f, h⟩ ↦ ⟨f, (h ·)⟩⟩
-variable {G'' : SimpleGraph X}
+variable {G'' : SimpleGraph X} {G''' : SimpleGraph Y}
/-- Composition of graph homomorphisms. -/
abbrev comp (f' : G' →g G'') (f : G →g G') : G →g G'' :=
@@ -430,6 +430,15 @@ abbrev comp (f' : G' →g G'') (f : G →g G') : G →g G'' :=
theorem coe_comp (f' : G' →g G'') (f : G →g G') : ⇑(f'.comp f) = f' ∘ f :=
rfl
+theorem comp_assoc (f : G'' →g G''') (g : G' →g G'') (h : G →g G') :
+ f.comp (g.comp h) = (f.comp g).comp h := rfl
+
+@[simp]
+theorem comp_id (f : G →g G') : f.comp .id = f := rfl
+
+@[simp]
+theorem id_comp (f : G →g G') : .comp .id f = f := rfl
+
@[simp]
theorem comp_comap_ofLE (f : H →g G) : .comp (.comap f G) (.ofLE f.le_comap) = f :=
rfl
@@ -533,7 +542,7 @@ protected def completeGraph {α β : Type*} (f : α ↪ β) : completeGraph α
@[simp] lemma coe_completeGraph {α β : Type*} (f : α ↪ β) : ⇑(Embedding.completeGraph f) = f := rfl
-variable {G'' : SimpleGraph X}
+variable {G'' : SimpleGraph X} {G''' : SimpleGraph Y}
/-- Composition of graph embeddings. -/
abbrev comp (f' : G' ↪g G'') (f : G ↪g G') : G ↪g G'' :=
@@ -543,6 +552,15 @@ abbrev comp (f' : G' ↪g G'') (f : G ↪g G') : G ↪g G'' :=
theorem coe_comp (f' : G' ↪g G'') (f : G ↪g G') : ⇑(f'.comp f) = f' ∘ f :=
rfl
+theorem comp_assoc (f : G'' ↪g G''') (g : G' ↪g G'') (h : G ↪g G') :
+ f.comp (g.comp h) = (f.comp g).comp h := rfl
+
+@[simp]
+theorem comp_refl (f : G ↪g G') : f.comp .refl = f := rfl
+
+@[simp]
+theorem refl_comp (f : G ↪g G') : .comp .refl f = f := rfl
+
/-- Graph embeddings from `G` to `H` are the same thing as graph embeddings from `Gᶜ` to `Hᶜ`. -/
def complEquiv : G ↪g H ≃ Gᶜ ↪g Hᶜ where
toFun f := ⟨f.toEmbedding, by simp⟩
@@ -713,7 +731,7 @@ theorem toEmbedding_completeGraph {α β : Type*} (f : α ≃ β) :
(Iso.completeGraph f).toEmbedding = Embedding.completeGraph f.toEmbedding :=
rfl
-variable {G'' : SimpleGraph X}
+variable {G'' : SimpleGraph X} {G''' : SimpleGraph Y}
/-- Composition of graph isomorphisms. -/
abbrev comp (f' : G' ≃g G'') (f : G ≃g G') : G ≃g G'' :=
@@ -723,6 +741,15 @@ abbrev comp (f' : G' ≃g G'') (f : G ≃g G') : G ≃g G'' :=
theorem coe_comp (f' : G' ≃g G'') (f : G ≃g G') : ⇑(f'.comp f) = f' ∘ f :=
rfl
+theorem comp_assoc (f : G'' ≃g G''') (g : G' ≃g G'') (h : G ≃g G') :
+ f.comp (g.comp h) = (f.comp g).comp h := rfl
+
+@[simp]
+theorem comp_refl (f : G ≃g G') : f.comp .refl = f := rfl
+
+@[simp]
+theorem refl_comp (f : G ≃g G') : .comp .refl f = f := rfl
+
section induce
variable {s : Set V} {t : Set W} {r : Set X}
From 1680840431e25235216ab3384877c76f073e24a6 Mon Sep 17 00:00:00 2001
From: Bingyu Xia <71547343+BryceT233@users.noreply.github.com>
Date: Sat, 13 Jun 2026 09:53:21 +0000
Subject: [PATCH 0002/1300] feat(RingTheory/Extension):
`h1CotangentEquivCotangent` (#39520)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Given `Algebra R S` and an extension `P : Extension R S`, this PR adds `extendScalars` (viewing `P` as an extension of `S` over `P.Ring`), `defaultHom` (the canonical extension homomorphism from the universal extension `R[S] → S` to `P`) and some related linear equivalences on cotangent spaces or the first homology of the naive cotangent complexes. We show the commutativity of the following diagram:
Co-authored-by: @chrisflav
---
Mathlib.lean | 1 +
.../RingTheory/Extension/Cotangent/Basic.lean | 11 ++
.../RingTheory/Extension/ExtendScalars.lean | 167 ++++++++++++++++++
Mathlib/RingTheory/Extension/Generators.lean | 13 ++
Mathlib/RingTheory/Kaehler/JacobiZariski.lean | 5 +
5 files changed, 197 insertions(+)
create mode 100644 Mathlib/RingTheory/Extension/ExtendScalars.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 2ac87fdbac13e4..09b8c19e674791 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -6491,6 +6491,7 @@ public import Mathlib.RingTheory.Extension.Cotangent.Basic
public import Mathlib.RingTheory.Extension.Cotangent.Basis
public import Mathlib.RingTheory.Extension.Cotangent.Free
public import Mathlib.RingTheory.Extension.Cotangent.LocalizationAway
+public import Mathlib.RingTheory.Extension.ExtendScalars
public import Mathlib.RingTheory.Extension.Generators
public import Mathlib.RingTheory.Extension.Presentation.Basic
public import Mathlib.RingTheory.Extension.Presentation.Core
diff --git a/Mathlib/RingTheory/Extension/Cotangent/Basic.lean b/Mathlib/RingTheory/Extension/Cotangent/Basic.lean
index e389a24689ec0e..0f6e4172bbc0d5 100644
--- a/Mathlib/RingTheory/Extension/Cotangent/Basic.lean
+++ b/Mathlib/RingTheory/Extension/Cotangent/Basic.lean
@@ -68,6 +68,12 @@ def cotangentComplex : P.Cotangent →ₗ[S] P.CotangentSpace :=
lemma cotangentComplex_mk (x) : P.cotangentComplex (.mk x) = 1 ⊗ₜ .D _ _ x :=
rfl
+lemma Cotangent.mk_C_mem_ker_cotangentComplex {σ : Type*} (G : Generators R S σ)
+ {r : R} (hr : C r ∈ G.ker) :
+ Extension.Cotangent.mk ⟨C r, hr⟩ ∈ G.toExtension.cotangentComplex.ker := by
+ have : D R G.toExtension.Ring (C r) = 0 := Derivation.map_algebraMap ..
+ simp [this]
+
section baseChange
variable {A : Type*} [CommRing A] [Algebra S A] [Algebra P.Ring A] [IsScalarTower P.Ring S A]
@@ -435,6 +441,11 @@ def H1Cotangent.equiv {P₁ P₂ : Extension R S} (f₁ : P₁.Hom P₂) (f₂ :
rw [← Extension.H1Cotangent.map_id, eq_comm, map_eq _ (f₁.comp f₂),
Extension.H1Cotangent.map_comp]; rfl
+omit [IsScalarTower R S S'] in
+lemma Cotangent.map_comp_h1Cotangentι (f : P.Hom P') :
+ Cotangent.map f ∘ₗ P.h1Cotangentι =
+ P'.h1Cotangentι.restrictScalars S ∘ₗ H1Cotangent.map f := rfl
+
end Extension
namespace Generators
diff --git a/Mathlib/RingTheory/Extension/ExtendScalars.lean b/Mathlib/RingTheory/Extension/ExtendScalars.lean
new file mode 100644
index 00000000000000..0037ec8ed4ea75
--- /dev/null
+++ b/Mathlib/RingTheory/Extension/ExtendScalars.lean
@@ -0,0 +1,167 @@
+/-
+Copyright (c) 2024 Bingyu Xia. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Bingyu Xia
+-/
+module
+
+public import Mathlib.RingTheory.Kaehler.JacobiZariski
+
+/-!
+# Extension of Scalars for Algebra Extensions
+
+This file provides APIs for extending the base ring of an algebra extension `P : Extension R S`
+to its own extension ring `P.Ring`. We introduce canonical maps and isomorphisms between
+the cotangent spaces and the first homology of naive cotangent complex associated with
+`P.extendScalars` and `P`. We provide commutativity results of these maps and ismorphisms
+(See https://github.com/leanprover-community/mathlib4/pull/39520 for an image of the full diagram).
+In particular, we show the boundary map of the Jacobi-Zariski sequence of `R → P.Ring → S`
+coincides with `P.cotangentComplex` via a canonical isomorphism `P.h1CotangentEquivCotangent`.
+
+## Main definitions and results
+
+- `extendScalars`: Views `P : Extension R S` as `Extension P.Ring S`.
+- `toExtendScalars`: The canonical homomorphism from `P` to `P.extendScalars` induced by
+ the identity map on the underlying extension rings.
+- `cotangentExtendScalarsEquiv` : The linear equivalence between the cotangent spaces of
+ `P.extensScalars` and `P` induced by the identity map.
+- `h1CotangentExtendScalarsEquiv`: `P.extensScalars` can be used to compute the first homology of
+ the naive cotangent complex of `S` over `P.Ring`.
+- `h1CotangentEquivOfSurjective`: If `R → P.Ring` is surjective, this is the linear isomorphism
+ induced by `P.h1Cotangentι`.
+- `h1CotangentEquivCotangent`: This is the linear equivalence between `H1Cotangent P.Ring S` and
+ `P.Cotangent` defined by the composition of `h1CotangentExtendScalarsEquiv.symm`,
+ `h1CotangentEquivOfSurjective` and `cotangentExtendScalarsEquiv`.
+- `cotangentComplex_comp_h1CotangentEquivCotangent`,
+ `h1CotangentEquivCotangent_comp_map`: commutativity results.
+
+-/
+
+@[expose] public section
+
+open KaehlerDifferential
+
+namespace Algebra.Extension
+
+universe w v u
+
+variable {R : Type u} {S : Type v} [CommRing R] [CommRing S] [Algebra R S]
+
+/-- Given an extension `P` of `S` over `R`, `P.extendScalars` is the same extension
+but viewed as an extension of `S` over `P.Ring`. -/
+@[simps]
+def extendScalars {R : Type u} {S : Type v} [CommRing R] [CommRing S] [Algebra R S]
+ (P : Extension.{w} R S) : Extension P.Ring S where
+ Ring := P.Ring
+ σ := P.σ
+ algebraMap_σ := P.algebraMap_σ
+
+set_option backward.isDefEq.respectTransparency false in
+set_option backward.defeqAttrib.useBackward true in
+/-- The canonical homomorphism from `P` to `P.extendScalars` induced by the identity map
+on the underlying extension rings. -/
+@[simps!]
+noncomputable
+def toExtendScalars {R : Type u} {S : Type v} [CommRing R] [CommRing S] [Algebra R S]
+ (P : Extension.{w} R S) : P.Hom P.extendScalars :=
+ .ofAlgHom (IsScalarTower.toAlgHom R P.Ring P.extendScalars.Ring)
+ (by dsimp; ext; simp)
+
+/-- `Extension.extendScalars` does not change the cotangent space of an extension. -/
+noncomputable
+def cotangentExtendScalarsEquiv {R : Type u} {S : Type v} [CommRing R] [CommRing S]
+ [Algebra R S] (P : Extension.{w} R S) :
+ P.extendScalars.Cotangent ≃ₗ[S] P.Cotangent :=
+ LinearEquiv.refl _ _
+
+@[simp]
+lemma cotangentExtendScalarsEquiv_symm_toLinearMap (P : Extension.{w} R S) :
+ P.cotangentExtendScalarsEquiv.symm.toLinearMap = Cotangent.map P.toExtendScalars := by
+ ext x
+ obtain ⟨x, rfl⟩ := Cotangent.mk_surjective x
+ rfl
+
+set_option backward.isDefEq.respectTransparency false in
+theorem H1Cotangent.map_toExtendScalars_injective (P : Extension.{w} R S) :
+ Function.Injective (H1Cotangent.map P.toExtendScalars) := by
+ rw [← LinearMap.ker_eq_bot, H1Cotangent.map, LinearMap.ker_restrict,
+ ← cotangentExtendScalarsEquiv_symm_toLinearMap, LinearEquiv.ker,
+ Submodule.comap_bot, Submodule.ker_subtype]
+
+/-- The first homology of the naive cotangent complex of `P.extendScalars` is
+linearly equivalent to that of `S` over `P.Ring`. -/
+@[simps! toLinearMap]
+noncomputable
+def h1CotangentExtendScalarsEquiv {R : Type u} {S : Type v} [CommRing R] [CommRing S]
+ [Algebra R S] (P : Extension.{w} R S) :
+ P.extendScalars.H1Cotangent ≃ₗ[S] H1Cotangent P.Ring S :=
+ Extension.H1Cotangent.equiv
+ (.ofAlgHom (Algebra.ofId _ _) (by ext)) P.extendScalars.defaultHom
+
+@[simp]
+lemma h1CotangentExtendScalarsEquiv_symm_toLinearMap (P : Extension.{w} R S) :
+ P.h1CotangentExtendScalarsEquiv.symm = H1Cotangent.map P.extendScalars.defaultHom := rfl
+
+/-- Given an extension `P` of `S` over `R` such that `algebraMap R P.Ring` is surjective,
+this is the equivalence induced by `P.h1Cotangentι`. -/
+@[simps! toLinearMap]
+noncomputable
+def h1CotangentEquivOfSurjective {R : Type u} {S : Type v} [CommRing R] [CommRing S]
+ [Algebra R S] (P : Extension.{w} R S) (h : Function.Surjective (algebraMap R P.Ring)) :
+ P.H1Cotangent ≃ₗ[S] P.Cotangent where
+ __ := P.h1Cotangentι
+ invFun x := ⟨x, by
+ have : Subsingleton Ω[P.Ring⁄R] := subsingleton_of_surjective R P.Ring h
+ exact Subsingleton.elim _ _⟩
+
+/-- Given an extension `P : Extension R S`, this is the linear equivalence between
+the first homology of the naive cotangent complex of `S` over `P.Ring` and
+the cotangent space of `P`. -/
+noncomputable
+def h1CotangentEquivCotangent {R : Type u} {S : Type v} [CommRing R] [CommRing S]
+ [Algebra R S] (P : Extension.{w} R S) :
+ H1Cotangent P.Ring S ≃ₗ[S] P.Cotangent :=
+ P.h1CotangentExtendScalarsEquiv.symm ≪≫ₗ
+ P.extendScalars.h1CotangentEquivOfSurjective Function.surjective_id ≪≫ₗ
+ P.cotangentExtendScalarsEquiv
+
+theorem cotangentComplex_comp_h1CotangentEquivCotangent (P : Extension.{w} R S) :
+ P.cotangentComplex.comp P.h1CotangentEquivCotangent.toLinearMap =
+ H1Cotangent.δ R P.Ring S := by
+ rw [h1CotangentEquivCotangent, LinearEquiv.coe_trans, LinearEquiv.coe_trans,
+ h1CotangentEquivOfSurjective_toLinearMap, ← LinearMap.comp_assoc, ← LinearMap.comp_assoc,
+ LinearEquiv.comp_toLinearMap_symm_eq, LinearMap.comp_assoc,
+ h1CotangentExtendScalarsEquiv_toLinearMap]
+ ext ⟨x, _⟩
+ obtain ⟨⟨x : P.Ring, x_in : x ∈ P.ker⟩, rfl⟩ := Cotangent.mk_surjective x
+ trans 1 ⊗ₜ[P.Ring] D R P.Ring x; · exact cotangentComplex_mk P ⟨x, x_in⟩
+ let u : (Generators.self P.Ring S).toExtension.ker :=
+ ⟨algebraMap P.Ring (Generators.self P.Ring S).toExtension.Ring x, by
+ rwa [← Ideal.mem_comap, RingHom.comap_ker, ← IsScalarTower.algebraMap_eq]⟩
+ rw [← Generators.H1Cotangent.δ_C _ _ u.prop]
+ congr
+
+theorem h1CotangentEquivCotangent_comp_map (P : Extension.{w} R S) :
+ P.h1CotangentEquivCotangent.toLinearMap.comp (Algebra.H1Cotangent.map R P.Ring S S) =
+ h1Cotangentι.comp (H1Cotangent.map P.defaultHom) := by
+ rw [h1CotangentEquivCotangent, LinearEquiv.coe_trans, LinearEquiv.coe_trans,
+ h1CotangentExtendScalarsEquiv_symm_toLinearMap, h1CotangentEquivOfSurjective_toLinearMap,
+ LinearMap.comp_assoc, LinearMap.comp_assoc, Algebra.H1Cotangent.map,
+ ← (H1Cotangent.map P.extendScalars.defaultHom).restrictScalars_self, ← H1Cotangent.map_comp,
+ eq_comm, ← LinearEquiv.toLinearMap_symm_comp_eq, cotangentExtendScalarsEquiv_symm_toLinearMap,
+ ← LinearMap.comp_assoc, Cotangent.map_comp_h1Cotangentι, LinearMap.restrictScalars_self,
+ LinearMap.comp_assoc, ← (H1Cotangent.map P.toExtendScalars).restrictScalars_self,
+ ← H1Cotangent.map_comp, H1Cotangent.map_eq]
+
+theorem H1Cotangent.map_defaultHom_surjective (P : Extension.{w} R S) :
+ Function.Surjective (H1Cotangent.map P.defaultHom) := by
+ rw [← LinearMap.range_eq_top,
+ ← (Submodule.map_injective_of_injective h1Cotangentι_injective).eq_iff,
+ ← LinearMap.range_comp, ← P.h1CotangentEquivCotangent_comp_map, LinearMap.range_comp,
+ ← (Algebra.H1Cotangent.exact_map_δ R P.Ring S).linearMap_ker_eq, Submodule.map_top,
+ ← exact_hCotangentι_cotangentComplex.linearMap_ker_eq, Submodule.map_equiv_eq_comap_symm,
+ LinearMap.ker, LinearMap.ker, ← Submodule.comap_comp]
+ congr
+ rw [LinearEquiv.comp_toLinearMap_symm_eq, P.cotangentComplex_comp_h1CotangentEquivCotangent]
+
+end Algebra.Extension
diff --git a/Mathlib/RingTheory/Extension/Generators.lean b/Mathlib/RingTheory/Extension/Generators.lean
index a165eabc2a2ff7..1e3ede9f668cb7 100644
--- a/Mathlib/RingTheory/Extension/Generators.lean
+++ b/Mathlib/RingTheory/Extension/Generators.lean
@@ -798,3 +798,16 @@ lemma toAlgHom_ofComp_localizationAway (g : S) [IsLocalization.Away g T] :
end Hom
end Algebra.Generators
+
+namespace Algebra.Extension
+
+set_option backward.isDefEq.respectTransparency false in
+set_option backward.defeqAttrib.useBackward true in
+/-- The canonical homomorphism of extensions from the universal extension `R[S] → S`
+(given by `Generators.self R S`) to any extension `P` defined via the designated section `P.σ`. -/
+@[simps!]
+noncomputable
+def defaultHom (P : Extension.{w} R S) : (Generators.self R S).toExtension.Hom P :=
+ .ofAlgHom (MvPolynomial.aeval P.σ) (by dsimp; ext; simp)
+
+end Algebra.Extension
diff --git a/Mathlib/RingTheory/Kaehler/JacobiZariski.lean b/Mathlib/RingTheory/Kaehler/JacobiZariski.lean
index 0175dfdec63ab4..1220b5ccd0bf32 100644
--- a/Mathlib/RingTheory/Kaehler/JacobiZariski.lean
+++ b/Mathlib/RingTheory/Kaehler/JacobiZariski.lean
@@ -400,6 +400,11 @@ lemma δ_eq_δAux (x : Q.ker) (hx) :
((Q.comp P).toExtension.cotangentComplex y)
rw [CotangentSpace.fst_compEquiv, Extension.CotangentSpace.map_cotangentComplex, hy, hx]
+lemma δ_C {r : S} (hr : C r ∈ Q.ker) :
+ δ Q P ⟨Extension.Cotangent.mk ⟨C r, hr⟩, Extension.Cotangent.mk_C_mem_ker_cotangentComplex ..⟩
+ = 1 ⊗ₜ[S] D R S r := by
+ rw [δ_eq_δAux, δAux_C]
+
lemma δ_eq_δ : δ Q P = δ Q P' := by
ext ⟨x, hx⟩
obtain ⟨x, rfl⟩ := Extension.Cotangent.mk_surjective x
From 9d9fa2436c5971d37e55b6aa8171817dce7bac3c Mon Sep 17 00:00:00 2001
From: Jireh Loreaux
Date: Sat, 13 Jun 2026 10:48:58 +0000
Subject: [PATCH 0003/1300] feat: connections between order, `realPart` and
`imaginaryPart` in star ordered rings (#40565)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Mainly, this provides the characterization: `a ≤ b ↔ ℜ a ≤ ℜ b ∧ ℑ a = ℑ b`, but also several related lemmas of convenience.
---
Mathlib/LinearAlgebra/Complex/Module.lean | 38 +++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/Mathlib/LinearAlgebra/Complex/Module.lean b/Mathlib/LinearAlgebra/Complex/Module.lean
index 30edae0cc3b9f8..7db6282b5b709a 100644
--- a/Mathlib/LinearAlgebra/Complex/Module.lean
+++ b/Mathlib/LinearAlgebra/Complex/Module.lean
@@ -7,11 +7,13 @@ module
public import Mathlib.Algebra.Algebra.RestrictScalars
public import Mathlib.Algebra.CharP.Invertible
+public import Mathlib.Algebra.Order.Star.Basic
public import Mathlib.Algebra.Star.Unitary
public import Mathlib.Data.Complex.Basic
public import Mathlib.Data.Real.Star
public import Mathlib.LinearAlgebra.Matrix.ToLin
import Mathlib.Algebra.Module.Torsion.Field
+import Mathlib.Algebra.Order.Monoid.Submonoid
/-!
# Complex number as a vector space over `ℝ`
@@ -595,6 +597,42 @@ lemma star_mul_self_eq_realPart_sq_add_imaginaryPart_sq (x : A) [hx : IsStarNorm
end NonUnitalNonAssocRing
+section StarOrderedRing
+
+variable [NonUnitalRing A] [StarRing A] [PartialOrder A]
+ [StarOrderedRing A] [Module ℂ A] [StarModule ℂ A]
+
+lemma nonneg_iff_realPart_imaginaryPart {a : A} :
+ 0 ≤ a ↔ 0 ≤ ℜ a ∧ ℑ a = 0 := by
+ refine ⟨fun h ↦ ⟨?_, h.isSelfAdjoint.imaginaryPart⟩, fun h ↦ ?_⟩
+ · simpa +singlePass [← h.isSelfAdjoint.coe_realPart] using! h
+ · rw [← realPart_add_I_smul_imaginaryPart a, h.2]
+ simpa using! h.1
+
+lemma nonpos_iff_realPart_imaginaryPart {a : A} :
+ a ≤ 0 ↔ ℜ a ≤ 0 ∧ ℑ a = 0 := by
+ simpa using nonneg_iff_realPart_imaginaryPart (a := -a)
+
+lemma realPart_nonneg_of_nonneg {a : A} (ha : 0 ≤ a) : 0 ≤ ℜ a :=
+ nonneg_iff_realPart_imaginaryPart.mp ha |>.1
+
+lemma realPart_nonpos_of_nonpos {a : A} (ha : a ≤ 0) : ℜ a ≤ 0 :=
+ nonpos_iff_realPart_imaginaryPart.mp ha |>.1
+
+lemma le_iff_realPart_imaginaryPart {a b : A} :
+ a ≤ b ↔ ℜ a ≤ ℜ b ∧ ℑ a = ℑ b := by
+ simpa [sub_eq_zero, eq_comm (a := ℑ a)] using nonneg_iff_realPart_imaginaryPart (a := b - a)
+
+lemma imaginaryPart_eq_of_le {a b : A} (hab : a ≤ b) :
+ ℑ a = ℑ b :=
+ le_iff_realPart_imaginaryPart.mp hab |>.2
+
+lemma realPart_mono {a b : A} (hab : a ≤ b) :
+ ℜ a ≤ ℜ b :=
+ le_iff_realPart_imaginaryPart.mp hab |>.1
+
+end StarOrderedRing
+
@[simp]
lemma realPart_one [Ring A] [StarRing A] [Module ℂ A] [StarModule ℂ A] :
ℜ (1 : A) = 1 := by
From 36ac4e29ee787119d0ee5e1abba9a560ce7e65d9 Mon Sep 17 00:00:00 2001
From: Christian Merten <136261474+chrisflav@users.noreply.github.com>
Date: Sat, 13 Jun 2026 12:29:28 +0000
Subject: [PATCH 0004/1300] chore(CategoryTheory/Filtered): generalise criteria
for filteredness of `CostructuredArrow` (#40559)
From Proetale and subsequently cleaned up by Claude Fable 5.
---
Mathlib/CategoryTheory/Filtered/Final.lean | 37 ++++++++++------------
1 file changed, 17 insertions(+), 20 deletions(-)
diff --git a/Mathlib/CategoryTheory/Filtered/Final.lean b/Mathlib/CategoryTheory/Filtered/Final.lean
index bebaba5487036e..edd610db14229d 100644
--- a/Mathlib/CategoryTheory/Filtered/Final.lean
+++ b/Mathlib/CategoryTheory/Filtered/Final.lean
@@ -60,12 +60,12 @@ theorem Functor.initial_of_isCofiltered_costructuredArrow
[∀ d, IsCofiltered (CostructuredArrow F d)] : Initial F where
out _ := IsCofiltered.isConnected _
-theorem isFiltered_structuredArrow_of_isFiltered_of_exists [IsFilteredOrEmpty C]
- (h₁ : ∀ d, ∃ c, Nonempty (d ⟶ F.obj c)) (h₂ : ∀ {d : D} {c : C} (s s' : d ⟶ F.obj c),
- ∃ (c' : C) (t : c ⟶ c'), s ≫ F.map t = s' ≫ F.map t) (d : D) :
+theorem isFiltered_structuredArrow_of_isFiltered_of_exists [IsFilteredOrEmpty C] (d : D)
+ (h₁ : ∃ c, Nonempty (d ⟶ F.obj c)) (h₂ : ∀ {c : C} (s s' : d ⟶ F.obj c),
+ ∃ (c' : C) (t : c ⟶ c'), s ≫ F.map t = s' ≫ F.map t) :
IsFiltered (StructuredArrow d F) := by
have : Nonempty (StructuredArrow d F) := by
- obtain ⟨c, ⟨f⟩⟩ := h₁ d
+ obtain ⟨c, ⟨f⟩⟩ := h₁
exact ⟨.mk f⟩
suffices IsFilteredOrEmpty (StructuredArrow d F) from IsFiltered.mk
refine ⟨fun f g => ?_, fun f g η μ => ?_⟩
@@ -78,18 +78,17 @@ theorem isFiltered_structuredArrow_of_isFiltered_of_exists [IsFilteredOrEmpty C]
StructuredArrow.homMk (IsFiltered.coeqHom η.right μ.right) (by simp), ?_⟩
simpa using IsFiltered.coeq_condition _ _
-theorem isCofiltered_costructuredArrow_of_isCofiltered_of_exists [IsCofilteredOrEmpty C]
- (h₁ : ∀ d, ∃ c, Nonempty (F.obj c ⟶ d)) (h₂ : ∀ {d : D} {c : C} (s s' : F.obj c ⟶ d),
- ∃ (c' : C) (t : c' ⟶ c), F.map t ≫ s = F.map t ≫ s') (d : D) :
+theorem isCofiltered_costructuredArrow_of_isCofiltered_of_exists [IsCofilteredOrEmpty C] (d : D)
+ (h₁ : ∃ c, Nonempty (F.obj c ⟶ d)) (h₂ : ∀ {c : C} (s s' : F.obj c ⟶ d),
+ ∃ (c' : C) (t : c' ⟶ c), F.map t ≫ s = F.map t ≫ s') :
IsCofiltered (CostructuredArrow F d) := by
suffices IsFiltered (CostructuredArrow F d)ᵒᵖ from isCofiltered_of_isFiltered_op _
suffices IsFiltered (StructuredArrow (op d) F.op) from
IsFiltered.of_equivalence (costructuredArrowOpEquivalence _ _).symm
apply isFiltered_structuredArrow_of_isFiltered_of_exists
- · intro d
- obtain ⟨c, ⟨t⟩⟩ := h₁ d.unop
+ · obtain ⟨c, ⟨t⟩⟩ := h₁
exact ⟨op c, ⟨Quiver.Hom.op t⟩⟩
- · intro d c s s'
+ · intro c s s'
obtain ⟨c', t, ht⟩ := h₂ s.unop s'.unop
exact ⟨op c', Quiver.Hom.op t, Quiver.Hom.unop_inj ht⟩
@@ -99,7 +98,7 @@ theorem Functor.final_of_exists_of_isFiltered [IsFilteredOrEmpty C]
(h₁ : ∀ d, ∃ c, Nonempty (d ⟶ F.obj c)) (h₂ : ∀ {d : D} {c : C} (s s' : d ⟶ F.obj c),
∃ (c' : C) (t : c ⟶ c'), s ≫ F.map t = s' ≫ F.map t) : Functor.Final F := by
suffices ∀ d, IsFiltered (StructuredArrow d F) from final_of_isFiltered_structuredArrow F
- exact isFiltered_structuredArrow_of_isFiltered_of_exists F h₁ h₂
+ exact fun d => isFiltered_structuredArrow_of_isFiltered_of_exists F d (h₁ d) h₂
/-- The inclusion of a terminal object is final. -/
theorem Functor.final_const_of_isTerminal [IsFiltered C] {X : D} (hX : IsTerminal X) :
@@ -119,7 +118,7 @@ theorem Functor.initial_of_exists_of_isCofiltered [IsCofilteredOrEmpty C]
∃ (c' : C) (t : c' ⟶ c), F.map t ≫ s = F.map t ≫ s') : Functor.Initial F := by
suffices ∀ d, IsCofiltered (CostructuredArrow F d) from
initial_of_isCofiltered_costructuredArrow F
- exact isCofiltered_costructuredArrow_of_isCofiltered_of_exists F h₁ h₂
+ exact fun d => isCofiltered_costructuredArrow_of_isCofiltered_of_exists F d (h₁ d) h₂
/-- The inclusion of an initial object is initial. -/
theorem Functor.initial_const_of_isInitial [IsCofiltered C] {X : D} (hX : IsInitial X) :
@@ -196,16 +195,14 @@ theorem Functor.initial_of_exists_of_isCofiltered_of_fullyFaithful [IsCofiltered
/-- Any under category on a filtered or empty category is filtered.
(Note that under categories are always cofiltered since they have an initial object.) -/
instance IsFiltered.under [IsFilteredOrEmpty C] (c : C) : IsFiltered (Under c) :=
- isFiltered_structuredArrow_of_isFiltered_of_exists _
- (fun c' => ⟨c', ⟨𝟙 _⟩⟩)
- (fun s s' => IsFilteredOrEmpty.cocone_maps s s') c
+ isFiltered_structuredArrow_of_isFiltered_of_exists _ c ⟨c, ⟨𝟙 _⟩⟩
+ (fun s s' => IsFilteredOrEmpty.cocone_maps s s')
/-- Any over category on a cofiltered or empty category is cofiltered.
(Note that over categories are always filtered since they have a terminal object.) -/
instance IsCofiltered.over [IsCofilteredOrEmpty C] (c : C) : IsCofiltered (Over c) :=
- isCofiltered_costructuredArrow_of_isCofiltered_of_exists _
- (fun c' => ⟨c', ⟨𝟙 _⟩⟩)
- (fun s s' => IsCofilteredOrEmpty.cone_maps s s') c
+ isCofiltered_costructuredArrow_of_isCofiltered_of_exists _ c ⟨c, ⟨𝟙 _⟩⟩
+ (fun s s' => IsCofilteredOrEmpty.cone_maps s s')
set_option backward.defeqAttrib.useBackward true in
/-- The forgetful functor of the under category on any filtered or empty category is final. -/
@@ -296,7 +293,7 @@ theorem Functor.final_iff_isFiltered_structuredArrow [IsFilteredOrEmpty C] :
Final F ↔ ∀ d, IsFiltered (StructuredArrow d F) := by
refine ⟨?_, fun h => final_of_isFiltered_structuredArrow F⟩
rw [final_iff_of_isFiltered]
- exact fun h => isFiltered_structuredArrow_of_isFiltered_of_exists F h.1 h.2
+ exact fun h d => isFiltered_structuredArrow_of_isFiltered_of_exists F d (h.1 d) h.2
/-- If `C` is cofiltered, then `F : C ⥤ D` is initial if and only if `CostructuredArrow F d` is
cofiltered for all `d : D`. -/
@@ -304,7 +301,7 @@ theorem Functor.initial_iff_isCofiltered_costructuredArrow [IsCofilteredOrEmpty
Initial F ↔ ∀ d, IsCofiltered (CostructuredArrow F d) := by
refine ⟨?_, fun h => initial_of_isCofiltered_costructuredArrow F⟩
rw [initial_iff_of_isCofiltered]
- exact fun h => isCofiltered_costructuredArrow_of_isCofiltered_of_exists F h.1 h.2
+ exact fun h d => isCofiltered_costructuredArrow_of_isCofiltered_of_exists F d (h.1 d) h.2
/-- If `C` is filtered, then the structured arrow category on the diagonal functor `C ⥤ C × C`
is filtered as well. -/
From 6ca5e2d4f1ab325db1fe867e2da3144302f79904 Mon Sep 17 00:00:00 2001
From: Jireh Loreaux
Date: Sat, 13 Jun 2026 12:57:04 +0000
Subject: [PATCH 0005/1300] =?UTF-8?q?feat:=20the=20canonical=20approximate?=
=?UTF-8?q?=20unit=20in=20a=20C=E2=8B=86-algebra=20is=20not=20`=E2=8A=A5`?=
=?UTF-8?q?=20(#40566)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
We also add a convenience lemma. This is almost trivial from the definition, but it's useful to have the `closedBall` version.
---
Mathlib/Analysis/CStarAlgebra/ApproximateUnit.lean | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/Mathlib/Analysis/CStarAlgebra/ApproximateUnit.lean b/Mathlib/Analysis/CStarAlgebra/ApproximateUnit.lean
index ab32228a31290d..0e759020348256 100644
--- a/Mathlib/Analysis/CStarAlgebra/ApproximateUnit.lean
+++ b/Mathlib/Analysis/CStarAlgebra/ApproximateUnit.lean
@@ -158,6 +158,11 @@ lemma eventually_star_eq {l : Filter A} (hl : l.IsIncreasingApproximateUnit) :
∀ᶠ x in l, star x = x :=
hl.eventually_isSelfAdjoint.mp <| .of_forall fun _ ↦ IsSelfAdjoint.star_eq
+omit [StarOrderedRing A] in
+lemma closedBall_mem {l : Filter A} (hl : l.IsIncreasingApproximateUnit) :
+ Metric.closedBall 0 1 ∈ l := by
+ simpa [Metric.closedBall] using! hl.eventually_norm
+
lemma pure_one (A : Type*) [CStarAlgebra A] [PartialOrder A] [StarOrderedRing A] :
(pure 1 : Filter A).IsIncreasingApproximateUnit where
toIsApproximateUnit := .pure_one A
@@ -326,6 +331,8 @@ lemma increasingApproximateUnit :
neBot := hasBasis_approximateUnit A |>.neBot_iff.mpr
fun hx ↦ ⟨_, ⟨le_rfl, by simpa using hx.2.le⟩⟩
+instance : (approximateUnit A).NeBot := (increasingApproximateUnit A).neBot
+
end CStarAlgebra
end ApproximateUnit
From bf69091c35a4c18a1294f4fba3528046966fd3b0 Mon Sep 17 00:00:00 2001
From: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com>
Date: Sat, 13 Jun 2026 13:42:07 +0000
Subject: [PATCH 0006/1300] =?UTF-8?q?feat(Order/ConditionallyCompleteLatti?=
=?UTF-8?q?ce/Finset):=20`sSup=20s=20=E2=89=A0=20=E2=8A=A4`=20in=20a=20`Co?=
=?UTF-8?q?mpleteLinearOrder`=20for=20a=20finite=20set=20without=20`?=
=?UTF-8?q?=E2=8A=A4`=20(#38356)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
and more generally, `sSup s ≠ a` for a finite set `s` without `a`, when `a ≠ ⊥`.
Also adds the equivalent `sInf`/`iSup`/`iInf` lemmas.
---
.../ConditionallyCompleteLattice/Finset.lean | 36 +++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/Mathlib/Order/ConditionallyCompleteLattice/Finset.lean b/Mathlib/Order/ConditionallyCompleteLattice/Finset.lean
index c6f70a19799255..386e4e5f5ff243 100644
--- a/Mathlib/Order/ConditionallyCompleteLattice/Finset.lean
+++ b/Mathlib/Order/ConditionallyCompleteLattice/Finset.lean
@@ -179,6 +179,42 @@ end ListMultiset
end ConditionallyCompleteLinearOrder
+section CompleteLinearOrder
+
+variable {α : Type*} [CompleteLinearOrder α] {ι : Sort*}
+
+theorem sSup_ne_of_notMem {s : Set α} (hfin : s.Finite) {a : α} (hne : a ≠ ⊥) (hmem : a ∉ s) :
+ sSup s ≠ a := by
+ rcases s.eq_empty_or_nonempty with rfl | hnonempty
+ · simp [eq_comm, hne]
+ exact (hmem <| · ▸ hnonempty.csSup_mem hfin)
+
+theorem sInf_ne_of_notMem {s : Set α} (hfin : s.Finite) {a : α} (hne : a ≠ ⊤) (hmem : a ∉ s) :
+ sInf s ≠ a :=
+ sSup_ne_of_notMem (α := αᵒᵈ) hfin hne hmem
+
+theorem sSup_ne_top [Nontrivial α] {s : Set α} (hfin : s.Finite) (htop : ⊤ ∉ s) : sSup s ≠ ⊤ :=
+ sSup_ne_of_notMem hfin top_ne_bot htop
+
+theorem sInf_ne_bot [Nontrivial α] {s : Set α} (hfin : s.Finite) (hbot : ⊥ ∉ s) : sInf s ≠ ⊥ :=
+ sSup_ne_top (α := αᵒᵈ) hfin hbot
+
+theorem iSup_ne_of_notMem [Finite ι] {f : ι → α} {a : α} (hne : a ≠ ⊥) (h : ∀ x, f x ≠ a) :
+ iSup f ≠ a :=
+ sSup_ne_of_notMem (Set.finite_range f) hne <| by grind
+
+theorem iInf_ne_of_notMem [Finite ι] {f : ι → α} {a : α} (hne : a ≠ ⊤) (h : ∀ x, f x ≠ a) :
+ iInf f ≠ a :=
+ iSup_ne_of_notMem (α := αᵒᵈ) hne h
+
+theorem iSup_ne_top [Finite ι] [Nontrivial α] {f : ι → α} (h : ∀ x, f x ≠ ⊤) : iSup f ≠ ⊤ :=
+ iSup_ne_of_notMem top_ne_bot h
+
+theorem iInf_ne_bot [Finite ι] [Nontrivial α] {f : ι → α} (h : ∀ x, f x ≠ ⊥) : iInf f ≠ ⊥ :=
+ iSup_ne_top (α := αᵒᵈ) h
+
+end CompleteLinearOrder
+
/-!
### Relation between `sSup` / `sInf` and `Finset.sup'` / `Finset.inf'`
From e0cf79c4769ba1be20bb3e188bc59130676dda9d Mon Sep 17 00:00:00 2001
From: Hannah Scholz <70071345+scholzhannah@users.noreply.github.com>
Date: Sat, 13 Jun 2026 14:25:02 +0000
Subject: [PATCH 0007/1300] feat: use `alias_in` attribute for CW complexes
(#38785)
Using the `alias_in` attribute for classical CW complexes to get rid of the `export` sections.
---
.../Topology/CWComplex/Classical/Basic.lean | 127 ++++++++++++------
.../Topology/CWComplex/Classical/Finite.lean | 18 +--
.../CWComplex/Classical/Subcomplex.lean | 16 +--
3 files changed, 103 insertions(+), 58 deletions(-)
diff --git a/Mathlib/Topology/CWComplex/Classical/Basic.lean b/Mathlib/Topology/CWComplex/Classical/Basic.lean
index 9ddc89d00f237c..97b4720eaba62a 100644
--- a/Mathlib/Topology/CWComplex/Classical/Basic.lean
+++ b/Mathlib/Topology/CWComplex/Classical/Basic.lean
@@ -8,6 +8,7 @@ module
public import Mathlib.Analysis.Normed.Module.RCLike.Real
public import Mathlib.Data.ENat.Basic
public import Mathlib.Logic.Equiv.PartialEquiv
+public import Mathlib.Util.AliasIn
/-!
# CW complexes
@@ -62,6 +63,19 @@ together.
cells `cell C` of an absolute CW complex `C`, this actually refers to `RelCWComplex.cell C`
through this instance. Again, we want typeclass inference to first consider absolute CW
structures.
+* The namespaces `CWComplex` and `RelCWComplex` generally should not be opened at the same time
+ as they contain many declarations with identical names. Still, we want working with absolute
+ CW complexes to be as convenient as possible. Thus every declaration about relative CW complexes
+ that doesn't have a modified version for absolute CW complexes should receive an alias in the
+ `CWComplex` namespace. It is recommended to use the `alias_in` attribute for this here. See
+ below for a restriction on when we want to create aliases.
+* For types and definitions relevant to CW complexes like `cell`, `openCell`, `closedCell`,
+ `cellFrontier`, `skeletonLT` and similar, we want there to exist only one actually used version,
+ namely the version in the `RelCWComplex` namespace (and thus no seperate definition in the
+ `CWComplex` namespace.) This is to avoid unnecessary duplication of lemmas. To achieve this,
+ definitions from the `RelCWComplex` namespace should be added to the `CWComplex` namespace with
+ `export` intead of `alias_in`/`alias`. These will then apply to the absolute CW complex through
+ the instance `CWComplex.instRelCWComplex`.
* For statements, the auxiliary construction `skeletonLT` is preferred over `skeleton` as it makes
the base case of inductions easier. The statement about `skeleton` should then be derived from the
one about `skeletonLT`.
@@ -199,7 +213,7 @@ def RelCWComplex.cellFrontier [RelCWComplex C D] (n : ℕ) (i : cell C n) : Set
namespace CWComplex
-export RelCWComplex (cell map source_eq continuousOn continuousOn_symm mapsTo isClosedBase openCell
+export RelCWComplex (cell map source_eq continuousOn continuousOn_symm isClosedBase openCell
closedCell cellFrontier)
end CWComplex
@@ -210,14 +224,17 @@ lemma CWComplex.mapsTo [CWComplex C] (n : ℕ) (i : cell C n) : ∃ I : Π m, Fi
simp_rw [empty_union] at this
exact this
+@[alias_in CWComplex]
lemma RelCWComplex.pairwiseDisjoint [RelCWComplex C D] :
(univ : Set (Σ n, cell C n)).PairwiseDisjoint (fun ni ↦ openCell ni.1 ni.2) :=
RelCWComplex.pairwiseDisjoint'
+@[alias_in CWComplex]
lemma RelCWComplex.disjointBase [RelCWComplex C D] (n : ℕ) (i : cell C n) :
Disjoint (openCell n i) D :=
RelCWComplex.disjointBase' n i
+@[alias_in CWComplex]
lemma RelCWComplex.disjoint_openCell_of_ne [RelCWComplex C D] {n m : ℕ} {i : cell C n}
{j : cell C m} (ne : (⟨n, i⟩ : Σ n, cell C n) ≠ ⟨m, j⟩) :
Disjoint (openCell n i) (openCell m j) :=
@@ -246,23 +263,28 @@ lemma CWComplex.union [CWComplex C] : ⋃ (n : ℕ) (j : cell C n), closedCell n
rw [empty_union] at this
exact this
+@[alias_in CWComplex]
lemma RelCWComplex.openCell_subset_closedCell [RelCWComplex C D] (n : ℕ) (i : cell C n) :
openCell n i ⊆ closedCell n i := image_mono Metric.ball_subset_closedBall
+@[alias_in CWComplex]
lemma RelCWComplex.cellFrontier_subset_closedCell [RelCWComplex C D] (n : ℕ) (i : cell C n) :
cellFrontier n i ⊆ closedCell n i := image_mono Metric.sphere_subset_closedBall
+@[alias_in CWComplex]
lemma RelCWComplex.cellFrontier_union_openCell_eq_closedCell [RelCWComplex C D] (n : ℕ)
(i : cell C n) : cellFrontier n i ∪ openCell n i = closedCell n i := by
rw [cellFrontier, openCell, closedCell, ← image_union]
congrm map n i '' ?_
exact sphere_union_ball
+@[alias_in CWComplex]
lemma RelCWComplex.map_zero_mem_openCell [RelCWComplex C D] (n : ℕ) (i : cell C n) :
map n i 0 ∈ openCell n i := by
apply mem_image_of_mem
simp only [mem_ball, dist_self, zero_lt_one]
+@[alias_in CWComplex]
lemma RelCWComplex.map_zero_mem_closedCell [RelCWComplex C D] (n : ℕ) (i : cell C n) :
map n i 0 ∈ closedCell n i :=
openCell_subset_closedCell _ _ (map_zero_mem_openCell _ _)
@@ -298,21 +320,26 @@ lemma CWComplex.eq_of_eq_union_iUnion [CWComplex C] (I J : Π n, Set (cell C n))
apply RelCWComplex.eq_of_eq_union_iUnion
simp_rw [empty_union, hIJ]
+@[alias_in CWComplex]
lemma RelCWComplex.isCompact_closedCell [RelCWComplex C D] {n : ℕ} {i : cell C n} :
IsCompact (closedCell n i) :=
(isCompact_closedBall _ _).image_of_continuousOn (continuousOn n i)
+@[alias_in CWComplex]
lemma RelCWComplex.isClosed_closedCell [RelCWComplex C D] [T2Space X] {n : ℕ} {i : cell C n} :
IsClosed (closedCell n i) := isCompact_closedCell.isClosed
+@[alias_in CWComplex]
lemma RelCWComplex.isCompact_cellFrontier [RelCWComplex C D] {n : ℕ} {i : cell C n} :
IsCompact (cellFrontier n i) :=
(isCompact_sphere _ _).image_of_continuousOn ((continuousOn n i).mono sphere_subset_closedBall)
+@[alias_in CWComplex]
lemma RelCWComplex.isClosed_cellFrontier [RelCWComplex C D] [T2Space X] {n : ℕ} {i : cell C n} :
IsClosed (cellFrontier n i) :=
isCompact_cellFrontier.isClosed
+@[alias_in CWComplex]
lemma RelCWComplex.closure_openCell_eq_closedCell [RelCWComplex C D] [T2Space X] {n : ℕ}
{j : cell C n} : closure (openCell n j) = closedCell n j := by
apply subset_antisymm (isClosed_closedCell.closure_subset_iff.2 (openCell_subset_closedCell n j))
@@ -332,31 +359,38 @@ lemma CWComplex.closed (C : Set X) [CWComplex C] [T2Space X] (A : Set X) (asubc
have := RelCWComplex.closed C A asubc
simp_all
+@[alias_in CWComplex]
lemma RelCWComplex.closedCell_subset_complex [RelCWComplex C D] (n : ℕ) (j : cell C n) :
closedCell n j ⊆ C := by
simp_rw [← union]
exact subset_union_of_subset_right (subset_iUnion₂ _ _) _
+@[alias_in CWComplex]
lemma RelCWComplex.openCell_subset_complex [RelCWComplex C D] (n : ℕ) (j : cell C n) :
openCell n j ⊆ C :=
(openCell_subset_closedCell _ _).trans (closedCell_subset_complex _ _)
+@[alias_in CWComplex]
lemma RelCWComplex.cellFrontier_subset_complex [RelCWComplex C D] (n : ℕ) (j : cell C n) :
cellFrontier n j ⊆ C :=
(cellFrontier_subset_closedCell n j).trans (closedCell_subset_complex n j)
+@[alias_in CWComplex]
lemma RelCWComplex.closedCell_zero_eq_singleton [RelCWComplex C D] {j : cell C 0} :
closedCell 0 j = {map 0 j ![]} := by
simp [closedCell, Matrix.empty_eq]
+@[alias_in CWComplex]
lemma RelCWComplex.openCell_zero_eq_singleton [RelCWComplex C D] {j : cell C 0} :
openCell 0 j = {map 0 j ![]} := by
simp [openCell, Matrix.empty_eq]
+@[alias_in CWComplex]
lemma RelCWComplex.cellFrontier_zero_eq_empty [RelCWComplex C D] {j : cell C 0} :
cellFrontier 0 j = ∅ := by
simp [cellFrontier, sphere_eq_empty_of_subsingleton]
+@[alias_in CWComplex]
lemma RelCWComplex.nonempty_cellFrontier [CWComplex C] {n : ℕ} (hn : n ≠ 0) (j : cell C n) :
(cellFrontier n j).Nonempty := by
letI : NeZero n := ⟨hn⟩
@@ -365,6 +399,7 @@ lemma RelCWComplex.nonempty_cellFrontier [CWComplex C] {n : ℕ} (hn : n ≠ 0)
use Pi.single 0 1, by simp [Pi.norm_single]
/-- If two 0-cells have the same characteristic image point, they are equal. -/
+@[alias_in CWComplex]
lemma RelCWComplex.injective_map_zero (C : Set X) [RelCWComplex C D] :
Injective ((map 0 · ![]) : cell C 0 → X) := by
rintro x z h
@@ -372,23 +407,26 @@ lemma RelCWComplex.injective_map_zero (C : Set X) [RelCWComplex C D] :
exact not_disjoint_iff.mpr ⟨map 0 x ![], by simp [openCell_zero_eq_singleton, h]⟩
<| disjoint_openCell_of_ne (by grind : (⟨0, x⟩ : Σ n, cell C n) ≠ ⟨0, z⟩)
-@[simp]
+@[simp, alias_in CWComplex]
lemma RelCWComplex.map_zero_eq_self_iff (C : Set X) [RelCWComplex C D] {x z : cell C 0} :
map 0 x ![] = map 0 z ![] ↔ x = z :=
⟨fun h ↦ injective_map_zero C h, fun h ↦ h ▸ rfl⟩
+@[alias_in CWComplex]
lemma RelCWComplex.closedCell_zero_injective (C : Set X) [RelCWComplex C D] :
Injective (closedCell 0 : cell C 0 → _) := by
intro x y h
rw [closedCell_zero_eq_singleton, closedCell_zero_eq_singleton, singleton_eq_singleton_iff] at h
exact injective_map_zero C h
+@[alias_in CWComplex]
lemma RelCWComplex.openCell_zero_injective (C : Set X) [RelCWComplex C D] :
Injective (openCell 0 : cell C 0 → _) := by
intro x y h
rw [openCell_zero_eq_singleton, openCell_zero_eq_singleton, singleton_eq_singleton_iff] at h
exact injective_map_zero C h
+@[alias_in CWComplex]
lemma RelCWComplex.cellFrontier_one_eq [RelCWComplex C D] (e : cell C 1) :
cellFrontier 1 e = map 1 e '' {-1, 1} := by
rw [cellFrontier]
@@ -411,10 +449,12 @@ lemma CWComplex.exists_cellFrontier_one_eq [CWComplex C] (e : cell C 1) :
simp [RelCWComplex.cellFrontier_one_eq, image_pair, RelCWComplex.closedCell_zero_eq_singleton,
hun1, hv1, pair_comm]
+@[alias_in CWComplex]
lemma RelCWComplex.base_subset_complex [RelCWComplex C D] : D ⊆ C := by
simp_rw [← union]
exact subset_union_left
+@[alias_in CWComplex]
lemma RelCWComplex.isClosed [T2Space X] [RelCWComplex C D] : IsClosed C := by
rw [closed C C (by rfl)]
constructor
@@ -467,6 +507,7 @@ lemma CWComplex.iUnion_openCell_eq_complex [CWComplex C] :
simpa using RelCWComplex.union_iUnion_openCell_eq_complex (C := C)
/-- The contrapositive of `disjoint_openCell_of_ne`. -/
+@[alias_in CWComplex]
lemma RelCWComplex.eq_of_not_disjoint_openCell [RelCWComplex C D] {n : ℕ} {j : cell C n} {m : ℕ}
{i : cell C m} (h : ¬ Disjoint (openCell n j) (openCell m i)) :
(⟨n, j⟩ : (Σ n, cell C n)) = ⟨m, i⟩ := by
@@ -629,13 +670,17 @@ instance : PartialOrder (Subcomplex C) := .ofSetLike (Subcomplex C) X
initialize_simps_projections Subcomplex (carrier → coe, as_prefix coe)
+@[alias_in CWComplex.Subcomplex]
lemma mem_carrier {E : Subcomplex C} {x : X} : x ∈ E.carrier ↔ x ∈ (E : Set X) := Iff.rfl
+@[alias_in CWComplex.Subcomplex]
lemma coe_eq_carrier {E : Subcomplex C} : (E : Set X) = E.carrier := rfl
-@[ext] lemma ext {E F : Subcomplex C} (h : ∀ x, x ∈ E ↔ x ∈ F) : E = F :=
+@[ext, alias_in CWComplex.Subcomplex]
+lemma ext {E F : Subcomplex C} (h : ∀ x, x ∈ E ↔ x ∈ F) : E = F :=
SetLike.ext h
+@[alias_in CWComplex.Subcomplex]
lemma eq_iff (E F : Subcomplex C) : E = F ↔ (E : Set X) = F :=
SetLike.coe_injective.eq_iff.symm
@@ -648,10 +693,12 @@ protected def copy (E : Subcomplex C) (F : Set X) (hF : F = E) (J : (n : ℕ)
closed' := hF.symm ▸ E.closed'
union' := hF.symm ▸ hJ ▸ E.union' }
-@[simp] lemma coe_copy (E : Subcomplex C) (F : Set X) (hF : F = E) (J : (n : ℕ) → Set (cell C n))
+@[simp, alias_in CWComplex.Subcomplex]
+lemma coe_copy (E : Subcomplex C) (F : Set X) (hF : F = E) (J : (n : ℕ) → Set (cell C n))
(hJ : J = E.I) : (E.copy F hF J hJ : Set X) = F :=
rfl
+@[alias_in CWComplex.Subcomplex]
lemma copy_eq (E : Subcomplex C) (F : Set X) (hF : F = E) (J : (n : ℕ) → Set (cell C n))
(hJ : J = E.I) : E.copy F hF J hJ = E :=
SetLike.coe_injective hF
@@ -661,6 +708,7 @@ lemma union (E : Subcomplex C) :
rw [E.union']
rfl
+@[alias_in CWComplex.Subcomplex]
lemma closed (E : Subcomplex C) : IsClosed (E : Set X) := E.closed'
end Subcomplex
@@ -669,13 +717,9 @@ end RelCWComplex
namespace CWComplex
-export RelCWComplex (Subcomplex)
+export RelCWComplex (Subcomplex Subcomplex.I Subcomplex.copy)
-namespace Subcomplex
-
-export RelCWComplex.Subcomplex (I closed union mem_carrier coe_eq_carrier ext copy coe_copy copy_eq)
-
-end CWComplex.Subcomplex
+end CWComplex
lemma CWComplex.Subcomplex.union {C : Set X} [CWComplex C] {E : Subcomplex C} :
⋃ (n : ℕ) (j : E.I n), openCell (C := C) n j = E := by
@@ -749,22 +793,18 @@ def CWComplex.Subcomplex.mk'' [T2Space X] (C : Set X) [h : CWComplex C] (E : Set
rw [empty_union]
exact union
+@[alias_in CWComplex.Subcomplex]
lemma RelCWComplex.Subcomplex.subset_complex {C D : Set X} [RelCWComplex C D] (E : Subcomplex C) :
↑E ⊆ C := by
simp_rw [← union, ← RelCWComplex.union_iUnion_openCell_eq_complex]
exact union_subset_union_right _ (iUnion_mono fun _ ↦ iUnion_mono' fun j ↦ ⟨j, subset_rfl⟩)
+@[alias_in CWComplex.Subcomplex]
lemma RelCWComplex.Subcomplex.base_subset {C D : Set X} [RelCWComplex C D] (E : Subcomplex C) :
D ⊆ E := by
simp_rw [← union]
exact subset_union_left
-namespace CWComplex.Subcomplex
-
-export RelCWComplex.Subcomplex (subset_complex base_subset)
-
-end CWComplex.Subcomplex
-
end Subcomplex
section skeleton
@@ -778,7 +818,7 @@ This allows the base case of induction to be about the base instead of being abo
the base and some points.
The standard `skeleton` is defined in terms of `skeletonLT`. `skeletonLT` is preferred
in statements. You should then derive the statement about `skeleton`. -/
-@[simps! -isSimp, irreducible]
+@[simps! (attr := alias_in CWComplex) -isSimp, irreducible]
def skeletonLT (C : Set X) {D : Set X} [RelCWComplex C D] (n : ℕ∞) : Subcomplex C :=
Subcomplex.mk' _ (D ∪ ⋃ (m : ℕ) (_ : m < n) (j : cell C m), closedCell m j)
(fun l ↦ {x : cell C l | l < n})
@@ -803,7 +843,7 @@ end RelCWComplex
namespace CWComplex
-export RelCWComplex (skeletonLT coe_skeletonLT skeletonLT_I skeleton)
+export RelCWComplex (skeletonLT skeleton)
end CWComplex
@@ -813,11 +853,14 @@ lemma RelCWComplex.skeletonLT_zero_eq_base [RelCWComplex C D] : skeletonLT C 0 =
lemma CWComplex.skeletonLT_zero_eq_empty [CWComplex C] : (skeletonLT C 0 : Set X) = ∅ :=
RelCWComplex.skeletonLT_zero_eq_base
-@[simp] lemma RelCWComplex.skeletonLT_top [RelCWComplex C D] : skeletonLT C ⊤ = C := by
+@[simp, alias_in CWComplex] lemma RelCWComplex.skeletonLT_top [RelCWComplex C D] :
+ skeletonLT C ⊤ = C := by
simp [coe_skeletonLT, union]
-@[simp] lemma RelCWComplex.skeleton_top [RelCWComplex C D] : skeleton C ⊤ = C := skeletonLT_top
+@[simp, alias_in CWComplex] lemma RelCWComplex.skeleton_top [RelCWComplex C D] : skeleton C ⊤ = C :=
+ skeletonLT_top
+@[alias_in CWComplex]
lemma RelCWComplex.skeletonLT_mono [RelCWComplex C D] {n m : ℕ∞} (h : m ≤ n) :
(skeletonLT C m : Set X) ⊆ skeletonLT C n := by
simp_rw [coe_skeletonLT]
@@ -827,16 +870,20 @@ lemma RelCWComplex.skeletonLT_mono [RelCWComplex C D] {n m : ℕ∞} (h : m ≤
obtain ⟨l, lltm, xmeml⟩ := xmem
exact ⟨l, lt_of_lt_of_le lltm h, xmeml⟩
+@[alias_in CWComplex]
lemma RelCWComplex.skeletonLT_monotone [RelCWComplex C D] : Monotone (skeletonLT C) :=
fun _ _ h ↦ skeletonLT_mono h
+@[alias_in CWComplex]
lemma RelCWComplex.skeleton_mono [RelCWComplex C D] {n m : ℕ∞} (h : m ≤ n) :
(skeleton C m : Set X) ⊆ skeleton C n :=
skeletonLT_mono (by gcongr)
+@[alias_in CWComplex]
lemma RelCWComplex.skeleton_monotone [RelCWComplex C D] : Monotone (skeleton C) :=
fun _ _ h ↦ skeleton_mono h
+@[alias_in CWComplex]
lemma RelCWComplex.closedCell_subset_skeletonLT [RelCWComplex C D] (n : ℕ) (j : cell C n) :
closedCell n j ⊆ skeletonLT C (n + 1) := by
intro x xmem
@@ -845,18 +892,22 @@ lemma RelCWComplex.closedCell_subset_skeletonLT [RelCWComplex C D] (n : ℕ) (j
simp_rw [mem_iUnion, exists_prop]
refine ⟨n, (by norm_cast; exact lt_add_one n), ⟨j,xmem⟩⟩
+@[alias_in CWComplex]
lemma RelCWComplex.closedCell_subset_skeleton [RelCWComplex C D] (n : ℕ) (j : cell C n) :
closedCell n j ⊆ skeleton C n :=
closedCell_subset_skeletonLT n j
+@[alias_in CWComplex]
lemma RelCWComplex.openCell_subset_skeletonLT [RelCWComplex C D] (n : ℕ) (j : cell C n) :
openCell n j ⊆ skeletonLT C (n + 1) :=
(openCell_subset_closedCell _ _).trans (closedCell_subset_skeletonLT _ _)
+@[alias_in CWComplex]
lemma RelCWComplex.openCell_subset_skeleton [RelCWComplex C D] (n : ℕ) (j : cell C n) :
openCell n j ⊆ skeleton C n :=
(openCell_subset_closedCell _ _).trans (closedCell_subset_skeleton _ _)
+@[alias_in CWComplex]
lemma RelCWComplex.cellFrontier_subset_skeletonLT [RelCWComplex C D] (n : ℕ) (j : cell C n) :
cellFrontier n j ⊆ skeletonLT C n := by
obtain ⟨I, hI⟩ := cellFrontier_subset_base_union_finite_closedCell n j
@@ -868,18 +919,22 @@ lemma RelCWComplex.cellFrontier_subset_skeletonLT [RelCWComplex C D] (n : ℕ) (
obtain ⟨i, iltn, j, _, xmem⟩ := xmem
exact ⟨i, by norm_cast, j, xmem⟩
+@[alias_in CWComplex]
lemma RelCWComplex.cellFrontier_subset_skeleton [RelCWComplex C D] (n : ℕ) (j : cell C (n + 1)) :
cellFrontier (n + 1) j ⊆ skeleton C n :=
cellFrontier_subset_skeletonLT _ _
+@[alias_in CWComplex]
lemma RelCWComplex.iUnion_cellFrontier_subset_skeletonLT [RelCWComplex C D] (l : ℕ) :
⋃ (j : cell C l), cellFrontier l j ⊆ skeletonLT C l :=
iUnion_subset (fun _ ↦ cellFrontier_subset_skeletonLT _ _)
+@[alias_in CWComplex]
lemma RelCWComplex.iUnion_cellFrontier_subset_skeleton [RelCWComplex C D] (l : ℕ) :
⋃ (j : cell C l), cellFrontier l j ⊆ skeleton C l :=
(iUnion_cellFrontier_subset_skeletonLT l).trans (skeletonLT_mono le_self_add)
+@[alias_in CWComplex]
lemma RelCWComplex.skeletonLT_union_iUnion_closedCell_eq_skeletonLT_succ [RelCWComplex C D]
(n : ℕ) :
(skeletonLT C n : Set X) ∪ ⋃ (j : cell C n), closedCell n j = skeletonLT C (n + 1) := by
@@ -888,6 +943,7 @@ lemma RelCWComplex.skeletonLT_union_iUnion_closedCell_eq_skeletonLT_succ [RelCWC
norm_cast
exact (biUnion_lt_succ _ _).symm
+@[alias_in CWComplex]
lemma RelCWComplex.skeleton_union_iUnion_closedCell_eq_skeleton_succ [RelCWComplex C D] (n : ℕ) :
(skeleton C n : Set X) ∪ ⋃ (j : cell C (n + 1)), closedCell (n + 1) j = skeleton C (n + 1) :=
skeletonLT_union_iUnion_closedCell_eq_skeletonLT_succ _
@@ -909,6 +965,7 @@ lemma CWComplex.iUnion_openCell_eq_skeleton [CWComplex C] (n : ℕ∞) :
⋃ (m : ℕ) (_ : m < n + 1) (j : cell C m), openCell m j = skeleton C n :=
iUnion_openCell_eq_skeletonLT _
+@[alias_in CWComplex]
lemma RelCWComplex.iUnion_skeletonLT_eq_complex [RelCWComplex C D] :
⋃ (n : ℕ), skeletonLT C n = C := by
apply subset_antisymm (iUnion_subset_iff.2 fun _ ↦ (skeletonLT C _).subset_complex)
@@ -916,6 +973,7 @@ lemma RelCWComplex.iUnion_skeletonLT_eq_complex [RelCWComplex C D] :
exact ⟨subset_iUnion_of_subset 0 (skeletonLT C 0).base_subset,
fun n i ↦ subset_iUnion_of_subset _ (openCell_subset_skeletonLT n i)⟩
+@[alias_in CWComplex]
lemma RelCWComplex.iUnion_skeleton_eq_complex [RelCWComplex C D] :
⋃ (n : ℕ), skeleton C n = C := by
apply subset_antisymm (iUnion_subset_iff.2 fun _ ↦ (skeleton C _).subset_complex)
@@ -940,11 +998,15 @@ lemma RelCWComplex.mem_skeleton_iff [RelCWComplex C D] {n : ℕ∞} {x : X} :
· simp
· rw [← Nat.cast_one, ← Nat.cast_add, Nat.cast_lt, Nat.cast_le, Order.lt_add_one_iff]
-lemma CWComplex.exists_mem_openCell_of_mem_skeleton [CWComplex C] {n : ℕ∞} {x : X} :
+lemma CWComplex.mem_skeleton_iff [CWComplex C] {n : ℕ∞} {x : X} :
x ∈ skeleton C n ↔ ∃ (m : ℕ) (_ : m ≤ n) (j : cell C m), x ∈ openCell m j := by
rw [RelCWComplex.mem_skeleton_iff, mem_empty_iff_false, false_or]
+@[deprecated (since := "2026-04-30")] alias CWComplex.exists_mem_openCell_of_mem_skeleton :=
+ CWComplex.mem_skeleton_iff
+
/-- A skeleton and an open cell of a higher dimension are disjoint. -/
+@[alias_in CWComplex]
lemma RelCWComplex.disjoint_skeletonLT_openCell [RelCWComplex C D] {n : ℕ∞} {m : ℕ}
{j : cell C m} (hnm : n ≤ m) : Disjoint (skeletonLT C n : Set X) (openCell m j) := by
-- This is a consequence of `iUnion_openCell_eq_skeletonLT` and `disjoint_openCell_of_ne`
@@ -957,12 +1019,14 @@ lemma RelCWComplex.disjoint_skeletonLT_openCell [RelCWComplex C D] {n : ℕ∞}
exact (lt_self_iff_false m).mp (ENat.coe_lt_coe.1 (hln.trans_le hnm))
/-- A skeleton and an open cell of a higher dimension are disjoint. -/
+@[alias_in CWComplex]
lemma RelCWComplex.disjoint_skeleton_openCell [RelCWComplex C D] {n : ℕ∞} {m : ℕ}
{j : cell C m} (nlem : n < m) : Disjoint (skeleton C n : Set X) (openCell m j) :=
disjoint_skeletonLT_openCell (Order.add_one_le_of_lt nlem)
/-- A skeleton intersected with a closed cell of a higher dimension is the skeleton intersected with
the boundary of the cell. -/
+@[alias_in CWComplex]
lemma RelCWComplex.skeletonLT_inter_closedCell_eq_skeletonLT_inter_cellFrontier [RelCWComplex C D]
{n : ℕ∞} {m : ℕ} {j : cell C m} (hnm : n ≤ m) :
(skeletonLT C n : Set X) ∩ closedCell m j = (skeletonLT C n : Set X) ∩ cellFrontier m j := by
@@ -973,6 +1037,7 @@ lemma RelCWComplex.skeletonLT_inter_closedCell_eq_skeletonLT_inter_cellFrontier
exact empty_subset _
/-- Version of `skeletonLT_inter_closedCell_eq_skeletonLT_inter_cellFrontier` using `skeleton`. -/
+@[alias_in CWComplex]
lemma RelCWComplex.skeleton_inter_closedCell_eq_skeleton_inter_cellFrontier [RelCWComplex C D]
{n : ℕ∞} {m : ℕ} {j : cell C m} (hnm : n < m) :
(skeleton C n : Set X) ∩ closedCell m j = (skeleton C n : Set X) ∩ cellFrontier m j :=
@@ -996,24 +1061,4 @@ lemma RelCWComplex.disjoint_interior_base_iUnion_closedCell [T2Space X] [RelCWCo
simp_rw [disjoint_iff_inter_eq_empty, inter_iUnion, disjoint_interior_base_closedCell.inter_eq,
iUnion_empty]
-namespace CWComplex
-
-export RelCWComplex (pairwiseDisjoint disjoint_openCell_of_ne openCell_subset_closedCell
- cellFrontier_subset_closedCell cellFrontier_union_openCell_eq_closedCell map_zero_mem_openCell
- map_zero_mem_closedCell isCompact_closedCell isClosed_closedCell isCompact_cellFrontier
- isClosed_cellFrontier closure_openCell_eq_closedCell skeletonLT_top skeleton_top skeletonLT_mono
- skeleton_mono skeletonLT_monotone skeleton_monotone closedCell_subset_skeletonLT
- closedCell_subset_skeleton closedCell_subset_complex openCell_subset_skeletonLT
- openCell_subset_skeleton
- openCell_subset_complex cellFrontier_subset_skeletonLT cellFrontier_subset_skeleton
- cellFrontier_subset_complex iUnion_cellFrontier_subset_skeletonLT
- iUnion_cellFrontier_subset_skeleton closedCell_zero_eq_singleton openCell_zero_eq_singleton
- cellFrontier_zero_eq_empty isClosed skeletonLT_union_iUnion_closedCell_eq_skeletonLT_succ
- skeleton_union_iUnion_closedCell_eq_skeleton_succ iUnion_skeletonLT_eq_complex
- iUnion_skeleton_eq_complex eq_of_not_disjoint_openCell disjoint_skeletonLT_openCell
- disjoint_skeleton_openCell skeletonLT_inter_closedCell_eq_skeletonLT_inter_cellFrontier
- skeleton_inter_closedCell_eq_skeleton_inter_cellFrontier)
-
-end CWComplex
-
end Topology
diff --git a/Mathlib/Topology/CWComplex/Classical/Finite.lean b/Mathlib/Topology/CWComplex/Classical/Finite.lean
index f28ba68f9b5798..993561d306d25e 100644
--- a/Mathlib/Topology/CWComplex/Classical/Finite.lean
+++ b/Mathlib/Topology/CWComplex/Classical/Finite.lean
@@ -42,18 +42,24 @@ class RelCWComplex.FiniteDimensional.{u} {X : Type u} [TopologicalSpace X] (C :
/-- For some natural number `n`, the type `cell C m` is empty for all `m ≥ n`. -/
eventually_isEmpty_cell : ∀ᶠ n in Filter.atTop, IsEmpty (cell C n)
+alias CWComplex.FiniteDimensional.eventually_isEmpty_cell :=
+ RelCWComplex.FiniteDimensional.eventually_isEmpty_cell
+
/-- A CW complex is of finite type if `cell C n` is finite for every `n`. -/
class RelCWComplex.FiniteType.{u} {X : Type u} [TopologicalSpace X] (C : Set X) {D : Set X}
[RelCWComplex C D] : Prop where
/-- `cell C n` is finite for every `n`. -/
finite_cell (n : ℕ) : Finite (cell C n)
+alias CWComplex.FiniteType.finite_cell := RelCWComplex.FiniteType.finite_cell
+
/-- A CW complex is finite if it is finite dimensional and of finite type. -/
class RelCWComplex.Finite {X : Type*} [TopologicalSpace X] (C : Set X) {D : Set X}
[RelCWComplex C D] extends FiniteDimensional C, FiniteType C
variable {X : Type*} [TopologicalSpace X] (C : Set X) {D : Set X} [RelCWComplex C D]
+@[alias_in CWComplex]
lemma RelCWComplex.finite_of_finiteDimensional_finiteType [FiniteDimensional C]
[FiniteType C] : Finite C where
eventually_isEmpty_cell := FiniteDimensional.eventually_isEmpty_cell
@@ -61,8 +67,7 @@ lemma RelCWComplex.finite_of_finiteDimensional_finiteType [FiniteDimensional C]
namespace CWComplex
-export RelCWComplex (FiniteDimensional FiniteType Finite FiniteDimensional.eventually_isEmpty_cell
- FiniteType.finite_cell finite_of_finiteDimensional_finiteType)
+export RelCWComplex (FiniteDimensional FiniteType Finite)
end CWComplex
@@ -306,6 +311,7 @@ variable {X : Type*} [TopologicalSpace X] {C D : Set X} [RelCWComplex C D]
/-- If the collection of all cells (of any dimension) of a relative CW complex `C` is finite, then
`C` is finite as a CW complex. -/
+@[alias_in CWComplex]
lemma RelCWComplex.finite_of_finite_cells (finite : _root_.Finite (Σ n, cell C n)) : Finite C where
eventually_isEmpty_cell := by
simp only [Filter.eventually_atTop]
@@ -329,6 +335,7 @@ lemma RelCWComplex.finite_of_finite_cells (finite : _root_.Finite (Σ n, cell C
/-- If `C` is finite as a CW complex then the collection of all cells (of any dimension) is
finite. -/
+@[alias_in CWComplex]
lemma RelCWComplex.finite_cells_of_finite [finite : Finite C] : _root_.Finite (Σ n, cell C n) := by
-- We show that there is a bijection between `Σ n, cell C n` and
-- `Σ (m : {m : ℕ // m < n}), cell C m`.
@@ -348,13 +355,8 @@ lemma RelCWComplex.finite_cells_of_finite [finite : Finite C] : _root_.Finite (
exact Finite.instSigma
/-- A CW complex is finite iff the total number of its cells is finite. -/
+@[alias_in CWComplex]
lemma RelCWComplex.finite_iff_finite_cells : Finite C ↔ _root_.Finite (Σ n, cell C n) :=
⟨fun h ↦ finite_cells_of_finite (finite := h), finite_of_finite_cells⟩
-namespace CWComplex
-
-export RelCWComplex (finite_of_finite_cells finite_cells_of_finite finite_iff_finite_cells)
-
-end CWComplex
-
end Topology
diff --git a/Mathlib/Topology/CWComplex/Classical/Subcomplex.lean b/Mathlib/Topology/CWComplex/Classical/Subcomplex.lean
index 438323f8296faf..3c8c3994156c1d 100644
--- a/Mathlib/Topology/CWComplex/Classical/Subcomplex.lean
+++ b/Mathlib/Topology/CWComplex/Classical/Subcomplex.lean
@@ -32,6 +32,7 @@ namespace Topology
variable {X : Type*} [t : TopologicalSpace X] {C D : Set X}
+@[alias_in CWComplex.Subcomplex]
lemma RelCWComplex.Subcomplex.closedCell_subset_of_mem [T2Space X] [RelCWComplex C D]
(E : Subcomplex C) {n : ℕ} {i : cell C n} (hi : i ∈ E.I n) :
closedCell n i ⊆ E := by
@@ -40,11 +41,13 @@ lemma RelCWComplex.Subcomplex.closedCell_subset_of_mem [T2Space X] [RelCWComplex
exact subset_iUnion_of_subset n
(subset_iUnion (fun (j : ↑(E.I n)) ↦ openCell (C := C) n j) ⟨i, hi⟩)
+@[alias_in CWComplex.Subcomplex]
lemma RelCWComplex.Subcomplex.openCell_subset_of_mem [T2Space X] [RelCWComplex C D]
(E : Subcomplex C) {n : ℕ} {i : cell C n} (hi : i ∈ E.I n) :
openCell n i ⊆ E :=
(openCell_subset_closedCell n i).trans (closedCell_subset_of_mem E hi)
+@[alias_in CWComplex.Subcomplex]
lemma RelCWComplex.Subcomplex.cellFrontier_subset_of_mem [T2Space X] [RelCWComplex C D]
(E : Subcomplex C) {n : ℕ} {i : cell C n} (hi : i ∈ E.I n) :
cellFrontier n i ⊆ E :=
@@ -66,6 +69,7 @@ lemma CWComplex.Subcomplex.union_closedCell [T2Space X] [CWComplex C] (E : Subco
⋃ (n : ℕ) (j : E.I n), closedCell (C := C) n j = E :=
(empty_union _).symm.trans (RelCWComplex.Subcomplex.union_closedCell E)
+@[alias_in CWComplex.Subcomplex]
lemma RelCWComplex.Subcomplex.disjoint_openCell_subcomplex_of_not_mem [RelCWComplex C D]
(E : Subcomplex C) {n : ℕ} {i : cell C n} (h : i ∉ E.I n) : Disjoint (openCell n i) E := by
simp_rw [← union, disjoint_union_right, disjoint_iUnion_right]
@@ -142,12 +146,14 @@ lemma RelCWComplex.Subcomplex.cellFrontier_eq [T2Space X] [RelCWComplex C D] (E
(n : ℕ) (i : E.I n) : cellFrontier (C := E) n i = cellFrontier n (i : cell C n) := by
rfl
+@[alias_in CWComplex.Subcomplex]
instance RelCWComplex.Subcomplex.finiteType_subcomplex_of_finiteType [T2Space X]
[RelCWComplex C D] [FiniteType C] (E : Subcomplex C) : FiniteType (E : Set X) where
finite_cell n :=
let _ := FiniteType.finite_cell (C := C) (D := D) n
Subtype.finite
+@[alias_in CWComplex.Subcomplex]
instance RelCWComplex.Subcomplex.finiteDimensional_subcomplex_of_finiteDimensional
[T2Space X] [RelCWComplex C D] [FiniteDimensional C] (E : Subcomplex C) :
FiniteDimensional (E : Set X) where
@@ -156,17 +162,9 @@ instance RelCWComplex.Subcomplex.finiteDimensional_subcomplex_of_finiteDimension
simp [isEmpty_subtype]
/-- A subcomplex of a finite CW complex is again finite. -/
+@[alias_in CWComplex.Subcomplex]
instance RelCWComplex.Subcomplex.finite_subcomplex_of_finite [T2Space X] [RelCWComplex C D]
[Finite C] (E : Subcomplex C) : Finite (E : Set X) :=
finite_of_finiteDimensional_finiteType _
-namespace CWComplex.Subcomplex
-
-export RelCWComplex.Subcomplex (closedCell_subset_of_mem openCell_subset_of_mem
- cellFrontier_subset_of_mem disjoint_openCell_subcomplex_of_not_mem subset_complex
- finiteType_subcomplex_of_finiteType finiteDimensional_subcomplex_of_finiteDimensional
- finite_subcomplex_of_finite)
-
-end CWComplex.Subcomplex
-
end Topology
From c3c39061306554f18e141a8008b30778118c3285 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9my=20Degenne?=
<4094732+RemyDegenne@users.noreply.github.com>
Date: Sat, 13 Jun 2026 14:25:04 +0000
Subject: [PATCH 0008/1300] feat(Probability/Decision): Bayes estimators
(#39810)
This PR adds the concept of a Bayes estimator for an estimation problem: an estimator that attains the Bayes risk.
We can get such estimators by taking an argmin of an integral involving a posterior kernel, when a measurable version of that argmin exists.
Co-authored-by: Lorenzo Luccioli @LorenzoLuccioli
Co-authored-by: Remy Degenne
---
Mathlib.lean | 1 +
.../Probability/Decision/BayesEstimator.lean | 168 ++++++++++++++++++
2 files changed, 169 insertions(+)
create mode 100644 Mathlib/Probability/Decision/BayesEstimator.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 09b8c19e674791..2ba33f02117f1e 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -6202,6 +6202,7 @@ public import Mathlib.Probability.Combinatorics.BinomialRandomGraph.Defs
public import Mathlib.Probability.CondVar
public import Mathlib.Probability.ConditionalExpectation
public import Mathlib.Probability.ConditionalProbability
+public import Mathlib.Probability.Decision.BayesEstimator
public import Mathlib.Probability.Decision.Risk.Basic
public import Mathlib.Probability.Decision.Risk.Countable
public import Mathlib.Probability.Decision.Risk.Defs
diff --git a/Mathlib/Probability/Decision/BayesEstimator.lean b/Mathlib/Probability/Decision/BayesEstimator.lean
new file mode 100644
index 00000000000000..7f3991f3a74edc
--- /dev/null
+++ b/Mathlib/Probability/Decision/BayesEstimator.lean
@@ -0,0 +1,168 @@
+/-
+Copyright (c) 2025 Rémy Degenne. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Rémy Degenne, Lorenzo Luccioli
+-/
+module
+
+public import Mathlib.Probability.Decision.Risk.Defs
+public import Mathlib.Probability.Kernel.Posterior
+
+import Mathlib.Probability.Decision.Risk.Basic
+
+/-!
+# Bayes estimator
+
+Let `Θ` be a parameter space, `𝓧` a data space, `𝓨` a prediction space, `P : Kernel Θ 𝓧` a
+data generating kernel, `π` a prior on the parameter space, and `ℓ : Θ → 𝓨 → ℝ≥0∞` a loss function.
+
+An estimator (a `Kernel 𝓧 𝓨`) is said to be a Bayes estimator if it attains the Bayes risk for
+the estimation problem.
+It can be written as a measurable function `x ↦ argmin_y P†π(x)[θ ↦ ℓ θ y]`
+for `(P ∘ₘ π)`-almost every `x`, where `P†π` is the posterior kernel, whenever we can select
+the argmin in a measurable way.
+
+## Main definitions
+
+* `IsBayesEstimator`: an estimator is a Bayes estimator if it attains the Bayes risk for the prior.
+* `IsArgminEstimator`: a measurable function `f : 𝓧 → 𝓨` is an argmin estimator
+ if for `(P ∘ₘ π)`-almost every `x` the value `f x` belongs to `argmin_y P†π(x)[θ ↦ ℓ θ y]`.
+* `HasArgminEstimator`: the estimation problem admits an argmin estimator.
+ That is, we can choose the argmin of the posterior expected loss in a measurable way.
+
+## Main statements
+
+* `lintegral_iInf_posterior_le_bayesRisk`: the Bayes risk with respect to a prior is bounded
+ from below by the integral over the data (with distribution `P ∘ₘ π`) of the infimum over the
+ possible predictions `y` of the posterior loss `∫⁻ θ, ℓ θ y ∂((P†π) x)`:
+ `∫⁻ x, ⨅ y : 𝓨, ∫⁻ θ, ℓ θ y ∂((P†π) x) ∂(P ∘ₘ π) ≤ bayesRisk ℓ P π`
+* `IsArgminEstimator.isBayesEstimator`: an argmin Bayes estimator is a Bayes estimator.
+ That is, it minimizes the Bayesian risk.
+* `bayesRisk_eq_of_hasArgminEstimator`: if the estimation problem admits an argmin estimator,
+ then the Bayesian risk attains the risk lower bound `∫⁻ x, ⨅ y, ∫⁻ θ, ℓ θ y ∂(P†π) x ∂(P ∘ₘ π)`.
+
+## TODO
+
+Once Mathlib has measurable selection theorems, we will be able to prove `HasArgminEstimator` under
+general conditions on the measurable spaces `𝓧` and/or `𝓨`.
+
+-/
+
+@[expose] public section
+
+open MeasureTheory
+open scoped ENNReal NNReal
+
+namespace ProbabilityTheory
+
+variable {Θ 𝓧 𝓨 : Type*} {mΘ : MeasurableSpace Θ} {m𝓧 : MeasurableSpace 𝓧} {m𝓨 : MeasurableSpace 𝓨}
+ {ℓ : Θ → 𝓨 → ℝ≥0∞} {P : Kernel Θ 𝓧} {κ : Kernel 𝓧 𝓨} {π : Measure Θ}
+
+section Posterior
+
+variable [StandardBorelSpace Θ] [Nonempty Θ]
+
+/-- The average risk of an estimator `κ` with respect to a prior `π` can be expressed as
+an integral in the following way: `R_π(κ) = ((P†π × κ) ∘ P ∘ π)[(θ, y) ↦ ℓ θ y]`. -/
+lemma avgRisk_eq_lintegral_posterior_prod
+ (hl : Measurable (Function.uncurry ℓ)) (P : Kernel Θ 𝓧) [IsFiniteKernel P]
+ (κ : Kernel 𝓧 𝓨) [IsSFiniteKernel κ] (π : Measure Θ) [IsFiniteMeasure π] :
+ avgRisk ℓ P κ π = ∫⁻ θy, ℓ θy.1 θy.2 ∂(((P†π) ×ₖ κ) ∘ₘ (P ∘ₘ π)) := by
+ rw [avgRisk, ← Measure.lintegral_compProd (f := fun θy ↦ ℓ θy.1 θy.2) (by fun_prop)]
+ congr
+ calc π ⊗ₘ (κ ∘ₖ P) = (Kernel.id ∥ₖ κ) ∘ₘ (π ⊗ₘ P) := Measure.parallelComp_comp_compProd.symm
+ _ = (Kernel.id ∥ₖ κ) ∘ₘ ((P†π) ×ₖ Kernel.id) ∘ₘ P ∘ₘ π := by rw [posterior_prod_id_comp]
+ _ = ((P†π) ×ₖ κ) ∘ₘ P ∘ₘ π := by
+ rw [Measure.comp_assoc, Kernel.parallelComp_comp_prod, Kernel.id_comp, Kernel.comp_id]
+
+lemma avgRisk_eq_lintegral_lintegral_lintegral
+ (hl : Measurable (Function.uncurry ℓ)) (P : Kernel Θ 𝓧) [IsFiniteKernel P]
+ (κ : Kernel 𝓧 𝓨) [IsSFiniteKernel κ] (π : Measure Θ) [IsFiniteMeasure π] :
+ avgRisk ℓ P κ π = ∫⁻ x, ∫⁻ y, ∫⁻ θ, ℓ θ y ∂(P†π) x ∂κ x ∂(P ∘ₘ π) := by
+ rw [avgRisk_eq_lintegral_posterior_prod hl, Measure.lintegral_bind (by fun_prop) (by fun_prop)]
+ congr with x
+ rw [Kernel.prod_apply, lintegral_prod_symm' _ (by fun_prop)]
+
+lemma lintegral_iInf_posterior_le_avgRisk
+ (hl : Measurable (Function.uncurry ℓ)) (P : Kernel Θ 𝓧) [IsFiniteKernel P]
+ (κ : Kernel 𝓧 𝓨) [IsMarkovKernel κ] (π : Measure Θ) [IsFiniteMeasure π] :
+ ∫⁻ x, ⨅ y : 𝓨, ∫⁻ θ, ℓ θ y ∂((P†π) x) ∂(P ∘ₘ π) ≤ avgRisk ℓ P κ π := by
+ rw [avgRisk_eq_lintegral_lintegral_lintegral hl]
+ gcongr with x
+ exact iInf_le_lintegral _
+
+lemma lintegral_iInf_posterior_le_bayesRisk
+ (hl : Measurable (Function.uncurry ℓ)) (P : Kernel Θ 𝓧) [IsFiniteKernel P]
+ (π : Measure Θ) [IsFiniteMeasure π] :
+ ∫⁻ x, ⨅ y : 𝓨, ∫⁻ θ, ℓ θ y ∂((P†π) x) ∂(P ∘ₘ π) ≤ bayesRisk ℓ P π :=
+ le_iInf₂ fun κ _ ↦ lintegral_iInf_posterior_le_avgRisk hl P κ π
+
+end Posterior
+
+/-- An estimator is a Bayes estimator for a prior `π` if it attains the Bayes risk for `π`. -/
+def IsBayesEstimator (ℓ : Θ → 𝓨 → ℝ≥0∞) (P : Kernel Θ 𝓧) (κ : Kernel 𝓧 𝓨) (π : Measure Θ) : Prop :=
+ avgRisk ℓ P κ π = bayesRisk ℓ P π
+
+variable [StandardBorelSpace Θ] [Nonempty Θ] {f : 𝓧 → 𝓨} [IsFiniteKernel P] [IsFiniteMeasure π]
+
+/-- We say that a measurable function `f : 𝓧 → 𝓨` is an argmin estimator
+with respect to the prior `π` if for `(P ∘ₘ π)`-almost every `x` it is of
+the form `x ↦ argmin_y P†π(x)[θ ↦ ℓ θ y]`. -/
+structure IsArgminEstimator {𝓨 : Type*} [MeasurableSpace 𝓨]
+ (ℓ : Θ → 𝓨 → ℝ≥0∞) (P : Kernel Θ 𝓧) [IsFiniteKernel P]
+ (π : Measure Θ) [IsFiniteMeasure π] (f : 𝓧 → 𝓨) : Prop where
+ measurable : Measurable f
+ property : ∀ᵐ x ∂(P ∘ₘ π), ∫⁻ θ, ℓ θ (f x) ∂(P†π) x = ⨅ y, ∫⁻ θ, ℓ θ y ∂(P†π) x
+
+/-- Given an argmin estimator `f`, we can define a deterministic kernel. -/
+protected noncomputable
+abbrev IsArgminEstimator.kernel (h : IsArgminEstimator ℓ P π f) : Kernel 𝓧 𝓨 :=
+ Kernel.deterministic f h.measurable
+
+/-- The risk of an argmin estimator is the risk lower bound
+`∫⁻ x, ⨅ z, ∫⁻ θ, ℓ θ z ∂(P†π) x ∂(P ∘ₘ π)`. -/
+lemma IsArgminEstimator.avgRisk_eq_lintegral_iInf (hf : IsArgminEstimator ℓ P π f)
+ (hl : Measurable (Function.uncurry ℓ)) :
+ avgRisk ℓ P hf.kernel π = ∫⁻ x, ⨅ y, ∫⁻ θ, ℓ θ y ∂(P†π) x ∂(P ∘ₘ π) := by
+ rw [avgRisk_eq_lintegral_lintegral_lintegral hl]
+ refine lintegral_congr_ae ?_
+ filter_upwards [hf.property] with x hx
+ rwa [Kernel.lintegral_deterministic' _ (by fun_prop)]
+
+/-- An argmin estimator is a Bayes estimator: that is, it minimizes the Bayesian risk. -/
+lemma IsArgminEstimator.isBayesEstimator (hf : IsArgminEstimator ℓ P π f)
+ (hl : Measurable (Function.uncurry ℓ)) :
+ IsBayesEstimator ℓ P hf.kernel π := by
+ refine le_antisymm ?_ (bayesRisk_le_avgRisk _ _ _ _)
+ rw [hf.avgRisk_eq_lintegral_iInf hl]
+ exact lintegral_iInf_posterior_le_bayesRisk hl _ _
+
+/-- The estimation problem admits an argmin estimator with respect to the prior `π`.
+That is, we can choose the argmin of the posterior expected loss in a measurable way. -/
+structure HasArgminEstimator {𝓨 : Type*} [MeasurableSpace 𝓨]
+ (ℓ : Θ → 𝓨 → ℝ≥0∞) (P : Kernel Θ 𝓧) [IsFiniteKernel P] (π : Measure Θ) [IsFiniteMeasure π] :
+ Prop where
+ exists_isArgminEstimator : ∃ f : 𝓧 → 𝓨, IsArgminEstimator ℓ P π f
+
+namespace HasArgminEstimator
+
+/-- An estimator for an estimation problem that for `(P ∘ₘ π)`-almost every `x` is of
+the form `x ↦ argmin_y P†π(x)[θ ↦ ℓ θ y]`. -/
+noncomputable
+def argminEstimator (h : HasArgminEstimator ℓ P π) : 𝓧 → 𝓨 :=
+ h.exists_isArgminEstimator.choose
+
+lemma isArgminEstimator_argminEstimator (h : HasArgminEstimator ℓ P π) :
+ IsArgminEstimator ℓ P π h.argminEstimator :=
+ h.exists_isArgminEstimator.choose_spec
+
+/-- If the estimation problem admits an argmin estimator, then the Bayesian risk
+attains the risk lower bound `∫⁻ x, ⨅ y, ∫⁻ θ, ℓ θ y ∂((P†π) x) ∂(P ∘ₘ π)`. -/
+lemma bayesRisk_eq (hl : Measurable (Function.uncurry ℓ)) (h : HasArgminEstimator ℓ P π) :
+ bayesRisk ℓ P π = ∫⁻ x, ⨅ y, ∫⁻ θ, ℓ θ y ∂((P†π) x) ∂(P ∘ₘ π) := by
+ rw [← h.isArgminEstimator_argminEstimator.isBayesEstimator hl,
+ h.isArgminEstimator_argminEstimator.avgRisk_eq_lintegral_iInf hl]
+
+end HasArgminEstimator
+
+end ProbabilityTheory
From b55fea0a288e55a0ccc12c183d203073951e5861 Mon Sep 17 00:00:00 2001
From: "mathlib-splicebot[bot]"
<261196803+mathlib-splicebot[bot]@users.noreply.github.com>
Date: Sat, 13 Jun 2026 14:25:06 +0000
Subject: [PATCH 0009/1300] feat: variants of lemmas in CondJensen with a.e.
inequalities for the trimmed measure (#39819)
This PR was automatically created from PR #35349 by @RemyDegenne via a [review comment](https://github.com/leanprover-community/mathlib4/pull/35349#discussion_r3298163528) by @RemyDegenne.
Co-authored-by: RemyDegenne <4094732+RemyDegenne@users.noreply.github.com>
---
.../ConditionalExpectation/CondJensen.lean | 34 +++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/Mathlib/MeasureTheory/Function/ConditionalExpectation/CondJensen.lean b/Mathlib/MeasureTheory/Function/ConditionalExpectation/CondJensen.lean
index 2fe493d15670d3..7d25c166b20b66 100644
--- a/Mathlib/MeasureTheory/Function/ConditionalExpectation/CondJensen.lean
+++ b/Mathlib/MeasureTheory/Function/ConditionalExpectation/CondJensen.lean
@@ -181,6 +181,15 @@ theorem ConvexOn.map_condExp_le (hm : m ≤ mα) [SigmaFinite (μ.trim hm)]
filter_upwards [h1, h2, h3] with a ha hb hc
simpa [← ha, ← hb]
+theorem ConvexOn.map_condExp_le_trim {mE : MeasurableSpace E} [BorelSpace E]
+ (hm : m ≤ mα) [SigmaFinite (μ.trim hm)]
+ (hφ_cvx : ConvexOn ℝ s φ) (hφ_cont : LowerSemicontinuousOn φ s)
+ (hφ_meas : StronglyMeasurable φ) (hf : ∀ᵐ a ∂μ, f a ∈ s)
+ (hs : IsClosed s) (hf_int : Integrable f μ) (hφ_int : Integrable (φ ∘ f) μ) :
+ φ ∘ μ[f | m] ≤ᵐ[μ.trim hm] μ[φ ∘ f | m] := by
+ rw [StronglyMeasurable.ae_le_trim_iff hm (by fun_prop) (by fun_prop)]
+ exact hφ_cvx.map_condExp_le hm hφ_cont hf hs hf_int hφ_int
+
theorem ConcaveOn.condExp_map_le (hm : m ≤ mα) [SigmaFinite (μ.trim hm)]
(hφ_cvx : ConcaveOn ℝ s φ) (hφ_cont : UpperSemicontinuousOn φ s) (hf : ∀ᵐ a ∂μ, f a ∈ s)
(hs : IsClosed s) (hf_int : Integrable f μ) (hφ_int : Integrable (φ ∘ f) μ) :
@@ -189,6 +198,15 @@ theorem ConcaveOn.condExp_map_le (hm : m ≤ mα) [SigmaFinite (μ.trim hm)]
condExp_neg (φ ∘ f) m] with a h ha
simp_all [Pi.neg_comp]
+theorem ConcaveOn.condExp_map_le_trim {mE : MeasurableSpace E} [BorelSpace E]
+ (hm : m ≤ mα) [SigmaFinite (μ.trim hm)]
+ (hφ_cvx : ConcaveOn ℝ s φ) (hφ_cont : UpperSemicontinuousOn φ s)
+ (hφ_meas : StronglyMeasurable φ) (hf : ∀ᵐ a ∂μ, f a ∈ s)
+ (hs : IsClosed s) (hf_int : Integrable f μ) (hφ_int : Integrable (φ ∘ f) μ) :
+ μ[φ ∘ f | m] ≤ᵐ[μ.trim hm] φ ∘ μ[f | m] := by
+ rw [StronglyMeasurable.ae_le_trim_iff hm (by fun_prop) (by fun_prop)]
+ exact hφ_cvx.condExp_map_le hm hφ_cont hf hs hf_int hφ_int
+
/-- **Conditional Jensen's inequality**: in a Banach space `E` with a measure `μ` that is σ-finite
on a sub-σ-algebra `m`, if `φ : E → ℝ` is convex and lower-semicontinuous, then for any `f : α → E`
such that `f` and `φ ∘ f` are integrable, we have `φ (𝔼[f | m]) ≤ᵐ[μ] 𝔼[φ ∘ f | m]`. -/
@@ -199,6 +217,14 @@ theorem ConvexOn.map_condExp_le_univ (hm : m ≤ mα) [SigmaFinite (μ.trim hm)]
ConvexOn.map_condExp_le hm hφ_cvx (lowerSemicontinuousOn_univ_iff.2 hφ_cont) (by simp)
isClosed_univ hf_int hφ_int
+theorem ConvexOn.map_condExp_le_trim_univ {mE : MeasurableSpace E} [BorelSpace E]
+ (hm : m ≤ mα) [SigmaFinite (μ.trim hm)]
+ (hφ_cvx : ConvexOn ℝ univ φ) (hφ_cont : LowerSemicontinuous φ)
+ (hφ_meas : StronglyMeasurable φ) (hf_int : Integrable f μ) (hφ_int : Integrable (φ ∘ f) μ) :
+ φ ∘ μ[f | m] ≤ᵐ[μ.trim hm] μ[φ ∘ f | m] := by
+ rw [StronglyMeasurable.ae_le_trim_iff hm (by fun_prop) (by fun_prop)]
+ exact hφ_cvx.map_condExp_le_univ hm hφ_cont hf_int hφ_int
+
theorem ConcaveOn.condExp_map_le_univ (hm : m ≤ mα) [SigmaFinite (μ.trim hm)]
(hφ_cvx : ConcaveOn ℝ univ φ) (hφ_cont : UpperSemicontinuous φ)
(hf_int : Integrable f μ) (hφ_int : Integrable (φ ∘ f) μ) :
@@ -207,6 +233,14 @@ theorem ConcaveOn.condExp_map_le_univ (hm : m ≤ mα) [SigmaFinite (μ.trim hm)
condExp_neg (φ ∘ f) m] with a h ha
simp_all [Pi.neg_comp]
+theorem ConcaveOn.condExp_map_le_trim_univ {mE : MeasurableSpace E} [BorelSpace E]
+ (hm : m ≤ mα) [SigmaFinite (μ.trim hm)]
+ (hφ_cvx : ConcaveOn ℝ univ φ) (hφ_cont : UpperSemicontinuous φ)
+ (hφ_meas : StronglyMeasurable φ) (hf_int : Integrable f μ) (hφ_int : Integrable (φ ∘ f) μ) :
+ μ[φ ∘ f | m] ≤ᵐ[μ.trim hm] φ ∘ μ[f | m] := by
+ rw [StronglyMeasurable.ae_le_trim_iff hm (by fun_prop) (by fun_prop)]
+ exact hφ_cvx.condExp_map_le_univ hm hφ_cont hf_int hφ_int
+
/-- In a Banach space `E` with a measure `μ`, then for any `f : α → E`, we have
`‖𝔼[f | m]‖ ≤ᵐ[μ] 𝔼[‖f‖ | m]`. -/
theorem norm_condExp_le : (‖μ[f | m] ·‖) ≤ᵐ[μ] μ[(‖f ·‖) | m] := by
From 06b9dc81c6231b5b67ef260d241f1821e3a7b1ed Mon Sep 17 00:00:00 2001
From: Felix Pernegger <188575194+felixpernegger@users.noreply.github.com>
Date: Sat, 13 Jun 2026 14:25:07 +0000
Subject: [PATCH 0010/1300] chore: replace `(by rfl)` with `rfl` (#40371)
Replaces `(by rfl)` with `rfl` whenever possible.
Co-authored-by: Batixx
---
Archive/Imo/Imo1982Q1.lean | 2 +-
Counterexamples/CharPZeroNeCharZero.lean | 2 +-
Mathlib/Algebra/DirectSum/Internal.lean | 2 +-
Mathlib/Algebra/Order/CauSeq/Completion.lean | 4 ++--
Mathlib/Analysis/SpecialFunctions/Gamma/Beta.lean | 2 +-
Mathlib/CategoryTheory/Category/PartialFun.lean | 2 +-
Mathlib/CategoryTheory/Sites/Sheaf.lean | 2 +-
Mathlib/Data/Vector/Basic.lean | 2 +-
Mathlib/Dynamics/Flow.lean | 2 +-
Mathlib/FieldTheory/IntermediateField/Adjoin/Defs.lean | 2 +-
Mathlib/GroupTheory/FreeGroup/Basic.lean | 2 +-
Mathlib/GroupTheory/MonoidLocalization/Basic.lean | 2 +-
Mathlib/NumberTheory/BernoulliPolynomials.lean | 2 +-
Mathlib/Order/Filter/Germ/Basic.lean | 2 +-
Mathlib/RepresentationTheory/Intertwining.lean | 2 +-
Mathlib/RingTheory/AdicCompletion/Completeness.lean | 6 +++---
Mathlib/RingTheory/Adjoin/Singleton.lean | 2 +-
Mathlib/RingTheory/Algebraic/Basic.lean | 2 +-
18 files changed, 21 insertions(+), 21 deletions(-)
diff --git a/Archive/Imo/Imo1982Q1.lean b/Archive/Imo/Imo1982Q1.lean
index ef97cd79289882..544af84ecc0474 100644
--- a/Archive/Imo/Imo1982Q1.lean
+++ b/Archive/Imo/Imo1982Q1.lean
@@ -93,7 +93,7 @@ lemma part_1 : 660 ≤ f (1980) := by
lemma part_2 : f 1980 ≤ 660 := by
have h : 5 * f 1980 + 33 * f 3 ≤ 5 * 660 + 33 := by
calc (5 : ℕ+) * f 1980 + (33 : ℕ+) * f 3 ≤ f (5 * 1980 + 33 * 3) := by apply hf.superlinear
- _ = f 9999 := by rfl
+ _ = f 9999 := rfl
_ = 5 * 660 + 33 := by rw [hf.f_9999]
rw [hf.f₃, mul_one] at h
-- from 5 * f 1980 + 33 ≤ 5 * 660 + 33 we show f 1980 ≤ 660
diff --git a/Counterexamples/CharPZeroNeCharZero.lean b/Counterexamples/CharPZeroNeCharZero.lean
index 39e01fe8e56397..fcb0eb5e258ee4 100644
--- a/Counterexamples/CharPZeroNeCharZero.lean
+++ b/Counterexamples/CharPZeroNeCharZero.lean
@@ -24,7 +24,7 @@ namespace Counterexample
@[simp]
theorem add_one_eq_one (x : WithZero Unit) : x + 1 = 1 :=
- WithZero.cases_on x (by rfl) fun h => by rfl
+ WithZero.cases_on x rfl fun h => by rfl
theorem withZero_unit_charP_zero : CharP (WithZero Unit) 0 :=
⟨fun x => by cases x <;> simp⟩
diff --git a/Mathlib/Algebra/DirectSum/Internal.lean b/Mathlib/Algebra/DirectSum/Internal.lean
index 4aeb4fab711013..b0f553a0a9d362 100644
--- a/Mathlib/Algebra/DirectSum/Internal.lean
+++ b/Mathlib/Algebra/DirectSum/Internal.lean
@@ -320,7 +320,7 @@ theorem Submodule.iSup_eq_toSubmodule_range [AddMonoid ι] [CommSemiring S] [Sem
theorem DirectSum.coeAlgHom_of [AddMonoid ι] [CommSemiring S] [Semiring R] [Algebra S R]
(A : ι → Submodule S R) [SetLike.GradedMonoid A] (i : ι) (x : A i) :
DirectSum.coeAlgHom A (DirectSum.of (fun i => A i) i x) = x :=
- DirectSum.toSemiring_of _ (by rfl) (fun _ _ => (by rfl)) _ _
+ DirectSum.toSemiring_of _ rfl (fun _ _ => rfl) _ _
end DirectSum
diff --git a/Mathlib/Algebra/Order/CauSeq/Completion.lean b/Mathlib/Algebra/Order/CauSeq/Completion.lean
index b48635495389f0..9d8794438abc3f 100644
--- a/Mathlib/Algebra/Order/CauSeq/Completion.lean
+++ b/Mathlib/Algebra/Order/CauSeq/Completion.lean
@@ -142,7 +142,7 @@ theorem ofRat_injective : Function.Injective (ofRat : β → Cauchy abv) := fun
simpa [ofRat, mk_eq, ← const_sub, const_limZero, sub_eq_zero] using h
instance Cauchy.ring : Ring (Cauchy abv) := fast_instance%
- Function.Surjective.ring mk Quotient.mk'_surjective (by rfl) (by rfl)
+ Function.Surjective.ring mk Quotient.mk'_surjective rfl rfl
(fun _ _ => (mk_add _ _).symm) (fun _ _ => (mk_mul _ _).symm) (fun _ => (mk_neg _).symm)
(fun _ _ => (mk_sub _ _).symm) (fun _ _ => (mk_smul _ _).symm) (fun _ _ => (mk_smul _ _).symm)
(fun _ _ => (mk_pow _ _).symm) (fun _ => rfl) fun _ => rfl
@@ -170,7 +170,7 @@ variable {α : Type*} [Field α] [LinearOrder α] [IsStrictOrderedRing α]
variable {β : Type*} [CommRing β] {abv : β → α} [IsAbsoluteValue abv]
instance Cauchy.commRing : CommRing (Cauchy abv) := fast_instance%
- Function.Surjective.commRing mk Quotient.mk'_surjective (by rfl) (by rfl)
+ Function.Surjective.commRing mk Quotient.mk'_surjective rfl rfl
(fun _ _ => (mk_add _ _).symm) (fun _ _ => (mk_mul _ _).symm) (fun _ => (mk_neg _).symm)
(fun _ _ => (mk_sub _ _).symm) (fun _ _ => (mk_smul _ _).symm) (fun _ _ => (mk_smul _ _).symm)
(fun _ _ => (mk_pow _ _).symm) (fun _ => rfl) fun _ => rfl
diff --git a/Mathlib/Analysis/SpecialFunctions/Gamma/Beta.lean b/Mathlib/Analysis/SpecialFunctions/Gamma/Beta.lean
index 9fe6d6e66a330f..40edeb5bc6a42d 100644
--- a/Mathlib/Analysis/SpecialFunctions/Gamma/Beta.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Gamma/Beta.lean
@@ -242,7 +242,7 @@ theorem GammaSeq_add_one_left (s : ℂ) {n : ℕ} (hn : n ≠ 0) :
← mul_assoc, mul_comm _ (Finset.prod _ _)]
congr 3
· rw [cpow_add _ _ (Nat.cast_ne_zero.mpr hn), cpow_one, mul_comm]
- · refine Finset.prod_congr (by rfl) fun x _ => ?_
+ · refine Finset.prod_congr rfl fun x _ => ?_
push_cast; ring
· abel
diff --git a/Mathlib/CategoryTheory/Category/PartialFun.lean b/Mathlib/CategoryTheory/Category/PartialFun.lean
index b6a34630701d81..d544ff514b4e8e 100644
--- a/Mathlib/CategoryTheory/Category/PartialFun.lean
+++ b/Mathlib/CategoryTheory/Category/PartialFun.lean
@@ -146,7 +146,7 @@ noncomputable def partialFunEquivPointed : PartialFun.{u} ≌ Pointed where
exact hw.symm
counitIso :=
NatIso.ofComponents
- (fun X ↦ Pointed.Iso.mk (by classical exact Equiv.optionSubtypeNe X.point) (by rfl))
+ (fun X ↦ Pointed.Iso.mk (by classical exact Equiv.optionSubtypeNe X.point) rfl)
fun {X Y} f ↦ Pointed.Hom.ext <| funext fun a ↦ by
obtain _ | ⟨a, ha⟩ := a
· exact f.map_point.symm
diff --git a/Mathlib/CategoryTheory/Sites/Sheaf.lean b/Mathlib/CategoryTheory/Sites/Sheaf.lean
index 0483c6977df99f..1ee14826e07ea4 100644
--- a/Mathlib/CategoryTheory/Sites/Sheaf.lean
+++ b/Mathlib/CategoryTheory/Sites/Sheaf.lean
@@ -103,7 +103,7 @@ def conesEquivSieveCompatibleFamily :
toFun π :=
⟨fun _ f h => π.app (op ⟨Over.mk f, h⟩), fun X Y f g hf => by
let φ : S.arrows.categoryMk (g ≫ f) (S.downward_closed hf g) ⟶
- S.arrows.categoryMk f hf := ObjectProperty.homMk (Over.homMk _ (by rfl))
+ S.arrows.categoryMk f hf := ObjectProperty.homMk (Over.homMk _ rfl)
simpa using! π.naturality φ.op⟩
invFun x :=
{ app := fun f => x.1 f.unop.1.hom f.unop.2
diff --git a/Mathlib/Data/Vector/Basic.lean b/Mathlib/Data/Vector/Basic.lean
index 7ceba21415a26f..a056a01b21f9fc 100644
--- a/Mathlib/Data/Vector/Basic.lean
+++ b/Mathlib/Data/Vector/Basic.lean
@@ -522,7 +522,7 @@ def casesOn₃ {motive : ∀ {n}, Vector α n → Vector β n → Vector γ n
/-- Cast a vector to an array. -/
def toArray : Vector α n → Array α
- | ⟨xs, _⟩ => cast (by rfl) xs.toArray
+ | ⟨xs, _⟩ => xs.toArray
section InsertIdx
diff --git a/Mathlib/Dynamics/Flow.lean b/Mathlib/Dynamics/Flow.lean
index 6c6fe71cb8f661..bad8543dece3ee 100644
--- a/Mathlib/Dynamics/Flow.lean
+++ b/Mathlib/Dynamics/Flow.lean
@@ -247,7 +247,7 @@ theorem IsFactorOf.trans (h₁ : IsFactorOf ϕ ψ) (h₂ : IsFactorOf ψ χ) : I
h₁.elim fun π hπ => h₂.elim fun ρ hρ => ⟨π ∘ ρ, hρ.comp χ ψ ϕ hπ⟩
/-- Every flow is a factor of itself. -/
-theorem IsFactorOf.self : IsFactorOf ϕ ϕ := ⟨id, (isSemiconjugacy_id_iff_eq ϕ ϕ).mpr (by rfl)⟩
+theorem IsFactorOf.self : IsFactorOf ϕ ϕ := ⟨id, (isSemiconjugacy_id_iff_eq ϕ ϕ).mpr rfl⟩
end Flow
diff --git a/Mathlib/FieldTheory/IntermediateField/Adjoin/Defs.lean b/Mathlib/FieldTheory/IntermediateField/Adjoin/Defs.lean
index 12e8258353a615..3f596fe43485df 100644
--- a/Mathlib/FieldTheory/IntermediateField/Adjoin/Defs.lean
+++ b/Mathlib/FieldTheory/IntermediateField/Adjoin/Defs.lean
@@ -601,7 +601,7 @@ instance : Algebra A⟮b⟯ A⟮(algebraMap B C) b⟯ :=
RingHom.toAlgebra (RingHom.adjoinAlgebraMap _)
instance : IsScalarTower A⟮b⟯ A⟮(algebraMap B C) b⟯ C :=
- IsScalarTower.of_algebraMap_eq' (by rfl)
+ IsScalarTower.of_algebraMap_eq' rfl
end AdjoinSimple
diff --git a/Mathlib/GroupTheory/FreeGroup/Basic.lean b/Mathlib/GroupTheory/FreeGroup/Basic.lean
index 8a1e4753bdb254..3a4a00e0b12b57 100644
--- a/Mathlib/GroupTheory/FreeGroup/Basic.lean
+++ b/Mathlib/GroupTheory/FreeGroup/Basic.lean
@@ -931,7 +931,7 @@ def freeGroupUnitEquivInt : FreeGroup Unit ≃ ℤ where
rintro ⟨L⟩
simp only [quot_mk_eq_mk, map.mk, sum_mk, List.map_map]
exact List.recOn L
- (by rfl)
+ rfl
(fun ⟨⟨⟩, b⟩ tl ih => by
cases b <;> simp [zpow_add, ih] <;> rfl)
right_inv x :=
diff --git a/Mathlib/GroupTheory/MonoidLocalization/Basic.lean b/Mathlib/GroupTheory/MonoidLocalization/Basic.lean
index b8440d8c517a69..6b2cb8e36d0c07 100644
--- a/Mathlib/GroupTheory/MonoidLocalization/Basic.lean
+++ b/Mathlib/GroupTheory/MonoidLocalization/Basic.lean
@@ -235,7 +235,7 @@ then `f` is defined on the whole `AddLocalization S`. -/]
def rec {p : Localization S → Sort u} (f : ∀ (a : M) (b : S), p (mk a b))
(H : ∀ {a c : M} {b d : S} (h : r S (a, b) (c, d)),
(Eq.ndrec (f a b) (mk_eq_mk_iff.mpr h) : p (mk c d)) = f c d) (x) : p x :=
- Quot.rec (fun y ↦ Eq.ndrec (f y.1 y.2) (by rfl))
+ Quot.rec (fun y ↦ f y.1 y.2)
(fun y z h ↦ by cases y; cases z; exact H (r_iff_oreEqv_r.mpr h)) x
/-- Copy of `Quotient.recOnSubsingleton₂` for `Localization` -/
diff --git a/Mathlib/NumberTheory/BernoulliPolynomials.lean b/Mathlib/NumberTheory/BernoulliPolynomials.lean
index 6adffaddb78dfa..51e293fd0ad465 100644
--- a/Mathlib/NumberTheory/BernoulliPolynomials.lean
+++ b/Mathlib/NumberTheory/BernoulliPolynomials.lean
@@ -111,7 +111,7 @@ theorem derivative_bernoulli_add_one (k : ℕ) :
rw [range_add_one, sum_insert notMem_range_self, tsub_self, cast_zero, mul_zero,
map_zero, zero_add, mul_sum]
-- the rest of the sum is termwise equal:
- refine sum_congr (by rfl) fun m _ => ?_
+ refine sum_congr rfl fun m _ => ?_
conv_rhs => rw [← Nat.cast_one, ← Nat.cast_add, ← C_eq_natCast, C_mul_monomial, mul_comm]
rw [mul_assoc, mul_assoc, ← Nat.cast_mul, ← Nat.cast_mul]
congr 3
diff --git a/Mathlib/Order/Filter/Germ/Basic.lean b/Mathlib/Order/Filter/Germ/Basic.lean
index 7390a26c100c7e..dd0aa8723d3e62 100644
--- a/Mathlib/Order/Filter/Germ/Basic.lean
+++ b/Mathlib/Order/Filter/Germ/Basic.lean
@@ -406,7 +406,7 @@ theorem const_pow [Pow G M] (a : G) (n : M) : (↑(a ^ n) : Germ l G) = (↑a :
-- TODO: https://github.com/leanprover-community/mathlib4/pull/7432
@[to_additive]
instance instMonoid [Monoid M] : Monoid (Germ l M) :=
- { Function.Surjective.monoid ofFun Quot.mk_surjective (by rfl)
+ { Function.Surjective.monoid ofFun Quot.mk_surjective rfl
(fun _ _ => by rfl) fun _ _ => by rfl with
toSemigroup := instSemigroup
toOne := instOne
diff --git a/Mathlib/RepresentationTheory/Intertwining.lean b/Mathlib/RepresentationTheory/Intertwining.lean
index a4931239af1b82..ebcc0244e85ec0 100644
--- a/Mathlib/RepresentationTheory/Intertwining.lean
+++ b/Mathlib/RepresentationTheory/Intertwining.lean
@@ -498,7 +498,7 @@ noncomputable def equivAlgEnd :
IntertwiningMap ρ ρ ≃ₐ[A] Module.End A[G] ρ.asModule :=
AlgEquiv.ofLinearEquiv
(equivLinearMapAsModule ρ ρ)
- (by rfl)
+ rfl
(by intro f g; rfl)
theorem isIntertwiningMap_of_mem_center (g : G) (hg : g ∈ Submonoid.center G) :
diff --git a/Mathlib/RingTheory/AdicCompletion/Completeness.lean b/Mathlib/RingTheory/AdicCompletion/Completeness.lean
index ef7db25d3cb756..5f8b5b456c3b61 100644
--- a/Mathlib/RingTheory/AdicCompletion/Completeness.lean
+++ b/Mathlib/RingTheory/AdicCompletion/Completeness.lean
@@ -102,8 +102,8 @@ def ofValEqZero {n : ℕ} {x : AdicCompletion I M} (hxn : x.val n = 0) :
val i := ofValEqZeroAux I (Eq.refl (i + n)) hxn
property {i j} h := by
obtain ⟨k, rfl⟩ := Nat.exists_eq_add_of_le h
- rw [← (powSMulQuotInclusion_injective I (by rfl) ⊤).eq_iff, ofValEqZeroAux_prop,
- ← LinearMap.comp_apply, ← factorPow_comp_powSMulQuotInclusion I (by rfl)
+ rw [← (powSMulQuotInclusion_injective I rfl ⊤).eq_iff, ofValEqZeroAux_prop,
+ ← LinearMap.comp_apply, ← factorPow_comp_powSMulQuotInclusion I rfl
(show i + k + n = k + (i + n) by ring), LinearMap.comp_apply, ofValEqZeroAux_prop]
exact x.prop (by lia)
@@ -112,7 +112,7 @@ theorem ofPowSMul_ofValEqZero {n : ℕ} {x : AdicCompletion I M} (hxn : x.val n
ofPowSMul I M n (ofValEqZero I hxn) = x := by
ext i; by_cases! h : n ≤ i
· obtain ⟨k, rfl⟩ := Nat.exists_eq_add_of_le' h
- rw [ofPowSMul_val_apply _ (by rfl), ofValEqZero, ofValEqZeroAux_prop]
+ rw [ofPowSMul_val_apply _ rfl, ofValEqZero, ofValEqZeroAux_prop]
rw [ofPowSMul_val_apply_eq_zero _ h.le, ← x.prop h.le, hxn, _root_.map_zero]
theorem restrictScalars_range_ofPowSMul_eq_ker_eval {n : ℕ} :
diff --git a/Mathlib/RingTheory/Adjoin/Singleton.lean b/Mathlib/RingTheory/Adjoin/Singleton.lean
index 1bde86bb66cd0c..2b26c1ddf9c972 100644
--- a/Mathlib/RingTheory/Adjoin/Singleton.lean
+++ b/Mathlib/RingTheory/Adjoin/Singleton.lean
@@ -55,7 +55,7 @@ instance : Algebra A[b] A[(algebraMap B C) b] :=
RingHom.toAlgebra (RingHom.adjoinAlgebraMap b)
instance : IsScalarTower A[b] A[(algebraMap B C) b] C :=
- IsScalarTower.of_algebraMap_eq' (by rfl)
+ IsScalarTower.of_algebraMap_eq' rfl
/-- If the `algebraMap` injective then we have a Ring isomorphism between A[b] and A[↑b]. -/
noncomputable def RingHom.adjoinAlgebraMapEquiv [FaithfulSMul B C] :
diff --git a/Mathlib/RingTheory/Algebraic/Basic.lean b/Mathlib/RingTheory/Algebraic/Basic.lean
index d1fefdb9b42f5a..e4771691dbe162 100644
--- a/Mathlib/RingTheory/Algebraic/Basic.lean
+++ b/Mathlib/RingTheory/Algebraic/Basic.lean
@@ -343,7 +343,7 @@ theorem isAlgebraic_of_isAlgebraic_bot {x : S} (halg : IsAlgebraic (⊥ : Subalg
theorem isAlgebraic_bot_iff (h : Function.Injective (algebraMap R S)) {x : S} :
IsAlgebraic (⊥ : Subalgebra R S) x ↔ IsAlgebraic R x :=
isAlgebraic_ringHom_iff_of_comp_eq (Algebra.botEquivOfInjective h).symm (RingHom.id S)
- Function.injective_id (by rfl)
+ Function.injective_id rfl
variable (R S) in
theorem algebra_isAlgebraic_of_algebra_isAlgebraic_bot_left
From f1ceb733fa418759932c458de4f14076f8d15efe Mon Sep 17 00:00:00 2001
From: Jireh Loreaux
Date: Sat, 13 Jun 2026 14:25:09 +0000
Subject: [PATCH 0011/1300] feat: clean up `PositiveLinearMap` and add API
(#40492)
Among other things, this removes the coercion from the morphism class into the morphism type, and renames the underlying convenience constructor to `PositiveLinearMap.ofClass`.
---
.../Order/Module/PositiveLinearMap.lean | 46 ++++++++++++++-----
.../CStarAlgebra/PositiveLinearMap.lean | 2 +-
2 files changed, 36 insertions(+), 12 deletions(-)
diff --git a/Mathlib/Algebra/Order/Module/PositiveLinearMap.lean b/Mathlib/Algebra/Order/Module/PositiveLinearMap.lean
index b5141ae70a6e97..e32de62a976895 100644
--- a/Mathlib/Algebra/Order/Module/PositiveLinearMap.lean
+++ b/Mathlib/Algebra/Order/Module/PositiveLinearMap.lean
@@ -38,7 +38,7 @@ add_decl_doc PositiveLinearMap.toOrderHom
/-- Notation for a `PositiveLinearMap`. -/
notation:25 E " →ₚ[" R:25 "] " F:0 => PositiveLinearMap R E F
-namespace PositiveLinearMapClass
+section PositiveLinearMapClass
variable {F R E₁ E₂ : Type*} [Semiring R]
[AddCommMonoid E₁] [PartialOrder E₁] [AddCommMonoid E₂] [PartialOrder E₂]
@@ -46,16 +46,15 @@ variable {F R E₁ E₂ : Type*} [Semiring R]
[OrderHomClass F E₁ E₂]
/-- Reinterpret an element of a type of positive linear maps as a positive linear map. -/
-def toPositiveLinearMap (f : F) : E₁ →ₚ[R] E₂ :=
+def PositiveLinearMap.ofClass (f : F) : E₁ →ₚ[R] E₂ :=
{ (f : E₁ →ₗ[R] E₂), (f : E₁ →o E₂) with }
-/-- Reinterpret an element of a type of positive linear maps as a positive linear map. -/
-instance instCoeToLinearMap : CoeHead F (E₁ →ₚ[R] E₂) where
- coe f := toPositiveLinearMap f
+@[deprecated (since := "2026-06-10")]
+alias PositiveLinearMapClass.toPositiveLinearMap := PositiveLinearMap.ofClass
-/-- An additive group homomorphism that maps nonnegative elements to nonnegative elements
-is an order homomorphism. -/
-lemma _root_.OrderHomClass.of_addMonoidHom {F' E₁' E₂' : Type*} [FunLike F' E₁' E₂'] [AddGroup E₁']
+/-- A type of additive group homomorphisms that map nonnegative elements to nonnegative elements
+is also a type of order homomorphisms. -/
+lemma OrderHomClass.of_addMonoidHom {F' E₁' E₂' : Type*} [FunLike F' E₁' E₂'] [AddGroup E₁']
[LE E₁'] [AddRightMono E₁'] [AddGroup E₂'] [LE E₂'] [AddRightMono E₂']
[AddMonoidHomClass F' E₁' E₂']
(h : ∀ f : F', ∀ x, 0 ≤ x → 0 ≤ f x) : OrderHomClass F' E₁' E₂' where
@@ -67,9 +66,11 @@ namespace PositiveLinearMap
section general
-variable {R E₁ E₂ : Type*} [Semiring R]
- [AddCommMonoid E₁] [PartialOrder E₁] [AddCommMonoid E₂] [PartialOrder E₂]
- [Module R E₁] [Module R E₂]
+variable {R E₁ E₂ E₃ : Type*} [Semiring R]
+ [AddCommMonoid E₁] [PartialOrder E₁]
+ [AddCommMonoid E₂] [PartialOrder E₂]
+ [AddCommMonoid E₃] [PartialOrder E₃]
+ [Module R E₁] [Module R E₂] [Module R E₃]
instance : FunLike (E₁ →ₚ[R] E₂) E₁ E₂ where
coe f := f.toFun
@@ -80,10 +81,33 @@ instance : FunLike (E₁ →ₚ[R] E₂) E₁ E₂ where
apply DFunLike.coe_injective
exact h
+initialize_simps_projections PositiveLinearMap (toFun → apply, as_prefix toLinearMap)
+
@[ext]
lemma ext {f g : E₁ →ₚ[R] E₂} (h : ∀ x, f x = g x) : f = g :=
DFunLike.ext f g h
+variable (R E₁) in
+/-- The identity as a positive linear map. -/
+@[simps! apply toLinearMap] protected def id : E₁ →ₚ[R] E₁ where
+ __ := LinearMap.id
+ __ := OrderHom.id
+
+@[simp] lemma toOrderHom_id : (PositiveLinearMap.id R E₁).toOrderHom = .id := rfl
+
+/-- The composition of positive linear maps is again a positive linear map. -/
+@[simps! apply toLinearMap]
+def comp (g : E₂ →ₚ[R] E₃) (f : E₁ →ₚ[R] E₂) : E₁ →ₚ[R] E₃ where
+ toLinearMap := g.toLinearMap.comp f.toLinearMap
+ monotone' := g.monotone'.comp f.monotone'
+
+@[simp] lemma toOrderHom_comp (g : E₂ →ₚ[R] E₃) (f : E₁ →ₚ[R] E₂) :
+ (g.comp f).toOrderHom = g.toOrderHom.comp f.toOrderHom :=
+ rfl
+
+@[simp] lemma comp_id (f : E₁ →ₚ[R] E₂) : f.comp (.id R E₁) = f := rfl
+@[simp] lemma id_comp (f : E₁ →ₚ[R] E₂) : (PositiveLinearMap.id R E₂).comp f = f := rfl
+
instance : LinearMapClass (E₁ →ₚ[R] E₂) R E₁ E₂ where
map_add f := map_add f.toLinearMap
map_smulₛₗ f := f.toLinearMap.map_smul'
diff --git a/Mathlib/Analysis/CStarAlgebra/PositiveLinearMap.lean b/Mathlib/Analysis/CStarAlgebra/PositiveLinearMap.lean
index dc4dabe59f7fbd..a5d0adb3aab838 100644
--- a/Mathlib/Analysis/CStarAlgebra/PositiveLinearMap.lean
+++ b/Mathlib/Analysis/CStarAlgebra/PositiveLinearMap.lean
@@ -130,7 +130,7 @@ instance {F : Type*} [FunLike F A₁ A₂] [LinearMapClass F ℂ A₁ A₂] [Ord
ContinuousLinearMapClass F ℂ A₁ A₂ where
map_continuous f := by
have hbound : ∃ C : ℝ, ∀ a, ‖f a‖ ≤ C * ‖a‖ := by
- obtain ⟨C, h⟩ := exists_norm_apply_le (f : A₁ →ₚ[ℂ] A₂)
+ obtain ⟨C, h⟩ := exists_norm_apply_le (.ofClass f)
exact ⟨C, h⟩
exact (LinearMap.mkContinuousOfExistsBound (f : A₁ →ₗ[ℂ] A₂) hbound).continuous
From dab4b77c11870a1b54bd22fa185abdbf74bada85 Mon Sep 17 00:00:00 2001
From: Jireh Loreaux
Date: Sat, 13 Jun 2026 14:25:11 +0000
Subject: [PATCH 0012/1300] feat: selfadjointness for comparable elements in a
star ordered ring (#40513)
---
Mathlib/Algebra/Order/Star/Basic.lean | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/Mathlib/Algebra/Order/Star/Basic.lean b/Mathlib/Algebra/Order/Star/Basic.lean
index 5e2ab31abba409..99fa9e39b070f5 100644
--- a/Mathlib/Algebra/Order/Star/Basic.lean
+++ b/Mathlib/Algebra/Order/Star/Basic.lean
@@ -171,7 +171,7 @@ section NonUnitalSemiring
variable [NonUnitalSemiring R] [PartialOrder R] [StarRing R] [StarOrderedRing R]
-lemma IsSelfAdjoint.mono {x y : R} (h : x ≤ y) (hx : IsSelfAdjoint x) : IsSelfAdjoint y := by
+lemma IsSelfAdjoint.of_ge {x y : R} (h : x ≤ y) (hx : IsSelfAdjoint x) : IsSelfAdjoint y := by
rw [StarOrderedRing.le_iff] at h
obtain ⟨d, hd, rfl⟩ := h
rw [IsSelfAdjoint, star_add, hx.star_eq]
@@ -180,9 +180,11 @@ lemma IsSelfAdjoint.mono {x y : R} (h : x ≤ y) (hx : IsSelfAdjoint x) : IsSelf
rintro - ⟨s, rfl⟩
simp
+@[deprecated (since := "2026-06-12")] alias IsSelfAdjoint.mono := IsSelfAdjoint.of_ge
+
@[aesop 10% apply, grind ←]
lemma IsSelfAdjoint.of_nonneg {x : R} (hx : 0 ≤ x) : IsSelfAdjoint x :=
- .mono hx <| .zero R
+ .of_ge hx <| .zero R
/-- An alias of `IsSelfAdjoint.of_nonneg` for use with dot notation. -/
alias LE.le.isSelfAdjoint := IsSelfAdjoint.of_nonneg
@@ -204,6 +206,7 @@ protected theorem IsSelfAdjoint.mul_self_nonneg {a : R} (ha : IsSelfAdjoint a) :
simpa [ha.star_eq] using star_mul_self_nonneg a
/-- A star projection is non-negative in a star-ordered ring. -/
+@[grind →, aesop safe forward (rule_sets := [CStarAlgebra])]
theorem IsStarProjection.nonneg {p : R} (hp : IsStarProjection p) : 0 ≤ p :=
hp.isIdempotentElem ▸ hp.isSelfAdjoint.mul_self_nonneg
@@ -316,6 +319,19 @@ theorem mul_star_self_pos [Nontrivial R] {x : R} (hx : IsRegular x) : 0 < x * st
end NonUnitalSemiring
+section NonUnitalRing
+
+variable [NonUnitalRing R] [PartialOrder R] [StarRing R] [StarOrderedRing R]
+
+lemma IsSelfAdjoint.iff_of_le {a b : R} (hab : a ≤ b) :
+ IsSelfAdjoint a ↔ IsSelfAdjoint b := by
+ replace hab := (sub_nonneg.mpr hab).isSelfAdjoint
+ aesop (add simp IsSelfAdjoint)
+
+alias ⟨_, IsSelfAdjoint.of_le⟩ := IsSelfAdjoint.iff_of_le
+
+end NonUnitalRing
+
section Semiring
variable [Semiring R] [PartialOrder R] [StarRing R] [StarOrderedRing R]
From 9e7b1c1166169f7aa90a08054ce9b97948d44083 Mon Sep 17 00:00:00 2001
From: Bhavik Mehta <29959226+b-mehta@users.noreply.github.com>
Date: Sat, 13 Jun 2026 17:40:35 +0000
Subject: [PATCH 0013/1300] feat(Analysis/Complex/Exponential): add new bounds
on exponential (#39690)
We add new upper bounds on Real.exp in terms of 2x/(2+x), and use these to move some bounds on log higher in mathlib.
We also add a lemma for the common bound (1+1/n)^n <= e, though this is a special-case of `one_sub_div_pow_le_exp_neg` (immediately before), but is added for convenience and discoverability, as this is a "well-known" bound.
---
Mathlib/Analysis/Complex/Exponential.lean | 25 +++++++++++++++++++
.../Analysis/Complex/ExponentialBounds.lean | 2 +-
.../Analysis/SpecialFunctions/Log/Basic.lean | 10 ++++++++
.../Analysis/SpecialFunctions/Log/Deriv.lean | 10 --------
4 files changed, 36 insertions(+), 11 deletions(-)
diff --git a/Mathlib/Analysis/Complex/Exponential.lean b/Mathlib/Analysis/Complex/Exponential.lean
index d2ba768f73a164..4fbc28c76c4d3e 100644
--- a/Mathlib/Analysis/Complex/Exponential.lean
+++ b/Mathlib/Analysis/Complex/Exponential.lean
@@ -11,6 +11,8 @@ public import Mathlib.Algebra.Order.CauSeq.BigOperators
public import Mathlib.Algebra.Order.Star.Basic
public import Mathlib.Data.Complex.BigOperators
public import Mathlib.Data.Nat.Choose.Sum
+public import Mathlib.Tactic.NormNum.BigOperators
+public import Mathlib.Tactic.NormNum.NatFactorial
/-!
# Exponential Function
@@ -648,12 +650,35 @@ theorem one_sub_div_pow_le_exp_neg {n : ℕ} {t : ℝ} (ht' : t ≤ n) : (1 - t
· exact one_sub_le_exp_neg _
_ = rexp (-t) := by rw [← Real.exp_nat_mul, mul_neg, mul_comm, div_mul_cancel₀]; positivity
+lemma one_add_inv_pow_le_exp {n : ℕ} : (1 + (n : ℝ)⁻¹) ^ n ≤ exp 1 := by
+ convert one_sub_div_pow_le_exp_neg (n := n) (t := -1) (by grind) using 1
+ · field
+ · simp
+
lemma le_inv_mul_exp (x : ℝ) {c : ℝ} (hc : 0 < c) : x ≤ c⁻¹ * exp (c * x) := by
rw [le_inv_mul_iff₀ hc]
calc c * x
_ ≤ c * x + 1 := le_add_of_nonneg_right zero_le_one
_ ≤ _ := Real.add_one_le_exp (c * x)
+lemma exp_lt_two_add_div_two_sub {x : ℝ} (hx : 0 < x) (hx' : x < 2) :
+ exp x < (2 + x) / (2 - x) := by calc
+ _ = exp (x / 2) ^ 2 := by grind [Real.exp_nat_mul (x / 2) 2]
+ _ ≤ _ := by
+ grw [Real.exp_bound' (x := x / 2) (by grind) (by grind) (n := 3) (by simp)]
+ apply Real.exp_nonneg
+ _ < (2 + x) / (2 - x) := by
+ rw [lt_div_iff₀ (by linarith), ← sub_pos]
+ simp only [Finset.sum_range_succ]
+ ring_nf
+ positivity
+
+lemma exp_le_two_add_div_two_sub {x : ℝ} (hx : 0 ≤ x) (hx' : x < 2) :
+ exp x ≤ (2 + x) / (2 - x) := by
+ obtain rfl | hx₀ := hx.eq_or_lt
+ · simp
+ · exact (exp_lt_two_add_div_two_sub hx₀ hx').le
+
theorem prod_one_add_le_exp_sum {ι : Type*} (s : Finset ι) {f : ι → ℝ}
(hf : ∀ i, 0 ≤ f i) : ∏ i ∈ s, (1 + f i) ≤ exp (∑ i ∈ s, f i) :=
(Finset.prod_le_prod (fun i _ ↦ add_nonneg zero_le_one (hf i))
diff --git a/Mathlib/Analysis/Complex/ExponentialBounds.lean b/Mathlib/Analysis/Complex/ExponentialBounds.lean
index 56d1b556a6c820..190f489b95125a 100644
--- a/Mathlib/Analysis/Complex/ExponentialBounds.lean
+++ b/Mathlib/Analysis/Complex/ExponentialBounds.lean
@@ -77,7 +77,7 @@ theorem log_two_near_10 : |log 2 - 287209 / 414355| ≤ 1 / 10 ^ 10 := by
norm_num1 at z
rw [one_div (2 : ℝ), log_inv, ← sub_eq_add_neg, _root_.abs_sub_comm] at z
apply le_trans (_root_.abs_sub_le _ _ _) (add_le_add z _)
- norm_num [sum_range_succ]
+ norm_num
theorem log_two_gt_d9 : 0.6931471803 < log 2 :=
lt_of_lt_of_le (by norm_num1) (sub_le_comm.1 (abs_sub_le_iff.1 log_two_near_10).2)
diff --git a/Mathlib/Analysis/SpecialFunctions/Log/Basic.lean b/Mathlib/Analysis/SpecialFunctions/Log/Basic.lean
index 04ddb94fdc1d91..ab6a43aa827bb2 100644
--- a/Mathlib/Analysis/SpecialFunctions/Log/Basic.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Log/Basic.lean
@@ -336,6 +336,16 @@ theorem abs_log_mul_self_lt (x : ℝ) (h1 : 0 < x) (h2 : x ≤ 1) : |log x * x|
rw [← abs_of_nonneg aux, neg_mul, abs_neg] at this
exact this
+lemma le_log_one_add_of_nonneg {x : ℝ} (hx : 0 ≤ x) : 2 * x / (x + 2) ≤ log (1 + x) := by
+ rw [le_log_iff_exp_le (by grind)]
+ convert exp_le_two_add_div_two_sub (x := 2 * x / (x + 2)) (by positivity) _ using 1
+ all_goals field_simp; grind
+
+lemma lt_log_one_add_of_pos {x : ℝ} (hx : 0 < x) : 2 * x / (x + 2) < log (1 + x) := by
+ rw [lt_log_iff_exp_lt (by grind)]
+ convert exp_lt_two_add_div_two_sub (x := 2 * x / (x + 2)) (by positivity) _ using 1
+ all_goals field_simp; grind
+
/-- The real logarithm function tends to `+∞` at `+∞`. -/
theorem tendsto_log_atTop : Tendsto log atTop atTop :=
tendsto_comp_exp_atTop.1 <| by simpa only [log_exp] using! tendsto_id
diff --git a/Mathlib/Analysis/SpecialFunctions/Log/Deriv.lean b/Mathlib/Analysis/SpecialFunctions/Log/Deriv.lean
index a8ca2fea64a7fa..99ebf667e962ce 100644
--- a/Mathlib/Analysis/SpecialFunctions/Log/Deriv.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Log/Deriv.lean
@@ -423,14 +423,4 @@ theorem hasSum_log_one_add {a : ℝ} (h : 0 ≤ a) :
· convert! hasSum_log_one_add_inv (inv_pos.mpr (lt_of_le_of_ne h ha0.symm)) using 4
all_goals simp [field, add_comm]
-lemma le_log_one_add_of_nonneg {x : ℝ} (hx : 0 ≤ x) : 2 * x / (x + 2) ≤ log (1 + x) := by
- convert! le_hasSum (hasSum_log_one_add hx) 0 (by intros; positivity) using 1
- simp [field]
-
-lemma lt_log_one_add_of_pos {x : ℝ} (hx : 0 < x) : 2 * x / (x + 2) < log (1 + x) := by
- convert!
- lt_hasSum (hasSum_log_one_add hx.le) 0 (by intros; positivity) 1 (by positivity)
- (by positivity) using 1
- simp [field]
-
end Real
From 57249a69cc0b63f6eb5d0d3f1b06c34f16a22e52 Mon Sep 17 00:00:00 2001
From: Yongxi Lin
Date: Sat, 13 Jun 2026 19:59:05 -0700
Subject: [PATCH 0014/1300] Add to_dual tags for conditionally complete lattice
lemmas
---
.../ConditionallyCompleteLattice/Basic.lean | 17 +-
.../ConditionallyCompleteLattice/Indexed.lean | 229 ++++--------------
2 files changed, 58 insertions(+), 188 deletions(-)
diff --git a/Mathlib/Order/ConditionallyCompleteLattice/Basic.lean b/Mathlib/Order/ConditionallyCompleteLattice/Basic.lean
index 31ad01f242423e..9237a25656763b 100644
--- a/Mathlib/Order/ConditionallyCompleteLattice/Basic.lean
+++ b/Mathlib/Order/ConditionallyCompleteLattice/Basic.lean
@@ -68,7 +68,7 @@ theorem WithTop.sInf_eq [InfSet α] {s : Set (WithTop α)} (hs : ¬s ⊆ {⊤})
sInf s = ↑(sInf ((↑) ⁻¹' s) : α) :=
if_neg <| by simp [hs, h's]
-@[simp]
+@[to_dual (attr := simp)]
theorem WithTop.sInf_empty [InfSet α] : sInf (∅ : Set (WithTop α)) = ⊤ :=
if_pos <| by simp
@@ -105,10 +105,6 @@ theorem WithTop.coe_sSup' [SupSet α] {s : Set α} (hs : BddAbove s) :
· exact Option.some_injective _
· rintro ⟨x, _, ⟨⟩⟩
-@[simp]
-theorem WithBot.sSup_empty [SupSet α] : sSup (∅ : Set (WithBot α)) = ⊥ :=
- WithTop.sInf_empty (α := αᵒᵈ)
-
@[to_dual]
theorem WithTop.sSup_empty (α : Type*) [CompleteLattice α] : (sSup ∅ : WithTop α) = ⊥ := by
rw [sSup_eq (by simp) (OrderTop.bddAbove _), Set.preimage_empty, _root_.sSup_empty, coe_bot]
@@ -264,19 +260,16 @@ theorem notMem_of_csSup_lt {x : α} {s : Set α} (h : sSup s < x) (hs : BddAbove
/-- Introduction rule to prove that `b` is the supremum of `s`: it suffices to check that `b`
is larger than all elements of `s`, and that this is not the case of any `wb`.
+See `sInf_eq_of_forall_ge_of_forall_gt_exists_lt` for a version in complete lattices. -/]
theorem csSup_eq_of_forall_le_of_forall_lt_exists_gt (hs : s.Nonempty) (H : ∀ a ∈ s, a ≤ b)
(H' : ∀ w, w < b → ∃ a ∈ s, w < a) : sSup s = b :=
(eq_of_le_of_not_lt (csSup_le hs H)) fun hb =>
let ⟨_, ha, ha'⟩ := H' _ hb
lt_irrefl _ <| ha'.trans_le <| le_csSup ⟨b, H⟩ ha
-/-- Introduction rule to prove that `b` is the infimum of `s`: it suffices to check that `b`
-is smaller than all elements of `s`, and that this is not the case of any `w>b`.
-See `sInf_eq_of_forall_ge_of_forall_gt_exists_lt` for a version in complete lattices. -/
-theorem csInf_eq_of_forall_ge_of_forall_gt_exists_lt :
- s.Nonempty → (∀ a ∈ s, b ≤ a) → (∀ w, b < w → ∃ a ∈ s, a < w) → sInf s = b :=
- csSup_eq_of_forall_le_of_forall_lt_exists_gt (α := αᵒᵈ)
-
/-- `b < sSup s` when there is an element `a` in `s` with `b < a`, when `s` is bounded above.
This is essentially an iff, except that the assumptions for the two implications are
slightly different (one needs boundedness above for one direction, nonemptiness and linear
diff --git a/Mathlib/Order/ConditionallyCompleteLattice/Indexed.lean b/Mathlib/Order/ConditionallyCompleteLattice/Indexed.lean
index 65502dab75bcbf..558f50fc38df58 100644
--- a/Mathlib/Order/ConditionallyCompleteLattice/Indexed.lean
+++ b/Mathlib/Order/ConditionallyCompleteLattice/Indexed.lean
@@ -33,30 +33,21 @@ Extension of `iSup` and `iInf` from a preorder `α` to `WithTop α` and `WithBot
variable [Preorder α]
-@[simp]
+@[to_dual (attr := simp)]
theorem WithTop.iInf_empty [IsEmpty ι] [InfSet α] (f : ι → WithTop α) :
- ⨅ i, f i = ⊤ := by rw [iInf, range_eq_empty, WithTop.sInf_empty]
+ ⨅ i, f i = ⊤ := by
+ rw [iInf_of_isEmpty, WithTop.sInf_empty]
-@[norm_cast]
+@[to_dual (attr := norm_cast)]
theorem WithTop.coe_iInf [Nonempty ι] [InfSet α] {f : ι → α} (hf : BddBelow (range f)) :
↑(⨅ i, f i) = (⨅ i, f i : WithTop α) := by
rw [iInf, iInf, WithTop.coe_sInf' (range_nonempty f) hf, ← range_comp, Function.comp_def]
-@[norm_cast]
+@[to_dual (attr := norm_cast)]
theorem WithTop.coe_iSup [SupSet α] (f : ι → α) (h : BddAbove (Set.range f)) :
↑(⨆ i, f i) = (⨆ i, f i : WithTop α) := by
rw [iSup, iSup, WithTop.coe_sSup' h, ← range_comp, Function.comp_def]
-@[simp]
-theorem WithBot.ciSup_empty [IsEmpty ι] [SupSet α] (f : ι → WithBot α) :
- ⨆ i, f i = ⊥ :=
- WithTop.iInf_empty (α := αᵒᵈ) _
-
-@[norm_cast]
-theorem WithBot.coe_iSup [Nonempty ι] [SupSet α] {f : ι → α} (hf : BddAbove (range f)) :
- ↑(⨆ i, f i) = (⨆ i, f i : WithBot α) :=
- WithTop.coe_iInf (α := αᵒᵈ) hf
-
theorem WithBot.coe_biSup {ι : Type*} {s : Set ι} (hs : s.Nonempty)
{α : Type*} [CompleteLattice α] (f : ι → α) :
⨆ i ∈ s, f i = ⨆ i ∈ s, (f i : WithBot α) := by
@@ -69,11 +60,6 @@ theorem WithBot.coe_biSup {ι : Type*} {s : Set ι} (hs : s.Nonempty)
· simpa only [iSup_pos h] using by apply le_biSup _ h
· simpa only [iSup_neg h] using le_trans (by simp) (le_biSup _ hj)
-@[norm_cast]
-theorem WithBot.coe_iInf [InfSet α] (f : ι → α) (h : BddBelow (Set.range f)) :
- ↑(⨅ i, f i) = (⨅ i, f i : WithBot α) :=
- WithTop.coe_iSup (α := αᵒᵈ) _ h
-
theorem WithBot.coe_biInf {ι : Type*} {s : Set ι} {α : Type*} [CompleteLattice α] (f : ι → α) :
⨅ i ∈ s, f i = ⨅ i ∈ s, (f i : WithBot α) := by
refine le_antisymm (by simpa using fun _ ↦ biInf_le _) <|
@@ -88,65 +74,56 @@ section ConditionallyCompleteLattice
variable [ConditionallyCompleteLattice α] {a b : α}
+@[to_dual]
theorem isLUB_ciSup [Nonempty ι] {f : ι → α} (H : BddAbove (range f)) :
IsLUB (range f) (⨆ i, f i) :=
isLUB_csSup (range_nonempty f) H
+@[to_dual]
theorem isLUB_ciSup_set {f : β → α} {s : Set β} (H : BddAbove (f '' s)) (Hne : s.Nonempty) :
IsLUB (f '' s) (⨆ i : s, f i) := by
rw [← sSup_image']
exact isLUB_csSup (Hne.image _) H
-theorem isGLB_ciInf [Nonempty ι] {f : ι → α} (H : BddBelow (range f)) :
- IsGLB (range f) (⨅ i, f i) :=
- isGLB_csInf (range_nonempty f) H
-
-theorem isGLB_ciInf_set {f : β → α} {s : Set β} (H : BddBelow (f '' s)) (Hne : s.Nonempty) :
- IsGLB (f '' s) (⨅ i : s, f i) :=
- isLUB_ciSup_set (α := αᵒᵈ) H Hne
-
+@[to_dual le_ciInf_iff]
theorem ciSup_le_iff [Nonempty ι] {f : ι → α} {a : α} (hf : BddAbove (range f)) :
iSup f ≤ a ↔ ∀ i, f i ≤ a :=
(isLUB_le_iff <| isLUB_ciSup hf).trans forall_mem_range
-theorem le_ciInf_iff [Nonempty ι] {f : ι → α} {a : α} (hf : BddBelow (range f)) :
- a ≤ iInf f ↔ ∀ i, a ≤ f i :=
- (le_isGLB_iff <| isGLB_ciInf hf).trans forall_mem_range
-
+@[to_dual le_ciInf_set_iff]
theorem ciSup_set_le_iff {ι : Type*} {s : Set ι} {f : ι → α} {a : α} (hs : s.Nonempty)
(hf : BddAbove (f '' s)) : ⨆ i : s, f i ≤ a ↔ ∀ i ∈ s, f i ≤ a :=
(isLUB_le_iff <| isLUB_ciSup_set hf hs).trans forall_mem_image
-theorem le_ciInf_set_iff {ι : Type*} {s : Set ι} {f : ι → α} {a : α} (hs : s.Nonempty)
- (hf : BddBelow (f '' s)) : (a ≤ ⨅ i : s, f i) ↔ ∀ i ∈ s, a ≤ f i :=
- (le_isGLB_iff <| isGLB_ciInf_set hf hs).trans forall_mem_image
-
+@[to_dual]
theorem IsLUB.ciSup_eq [Nonempty ι] {f : ι → α} (H : IsLUB (range f) a) : ⨆ i, f i = a :=
H.csSup_eq (range_nonempty f)
+@[to_dual]
theorem IsLUB.ciSup_set_eq {s : Set β} {f : β → α} (H : IsLUB (f '' s) a) (Hne : s.Nonempty) :
⨆ i : s, f i = a :=
IsLUB.csSup_eq (image_eq_range f s ▸ H) (image_eq_range f s ▸ Hne.image f)
-theorem IsGLB.ciInf_eq [Nonempty ι] {f : ι → α} (H : IsGLB (range f) a) : ⨅ i, f i = a :=
- H.csInf_eq (range_nonempty f)
-
-theorem IsGLB.ciInf_set_eq {s : Set β} {f : β → α} (H : IsGLB (f '' s) a) (Hne : s.Nonempty) :
- ⨅ i : s, f i = a :=
- IsGLB.csInf_eq (image_eq_range f s ▸ H) (image_eq_range f s ▸ Hne.image f)
-
-/-- The indexed supremum of a function is bounded above by a uniform bound -/
+/-- The indexed supremum of a function is bounded above by a uniform bound. -/
+@[to_dual le_ciInf /-- The indexed minimum of a function is bounded below by a uniform lower
+bound. -/]
theorem ciSup_le [Nonempty ι] {f : ι → α} {c : α} (H : ∀ x, f x ≤ c) : iSup f ≤ c :=
csSup_le (range_nonempty f) (by rwa [forall_mem_range])
/-- The indexed supremum of a function is bounded below by the value taken at one point -/
+@[to_dual ciInf_le /-- The indexed infimum of a function is bounded above by the value taken
+at one point. -/]
theorem le_ciSup {f : ι → α} (H : BddAbove (range f)) (c : ι) : f c ≤ iSup f :=
le_csSup H (mem_range_self _)
+@[to_dual ciInf_le_of_le]
theorem le_ciSup_of_le {f : ι → α} (H : BddAbove (range f)) (c : ι) (h : a ≤ f c) : a ≤ iSup f :=
le_trans h (le_ciSup H c)
/-- If the set of all `f i j` is bounded above, then so is the set of the supremums of every row -/
+@[to_dual
+ /-- If the set of all `f i j` is bounded below, then so is the set of the infimums
+ of every row -/]
theorem BddAbove.range_iSup_of_iUnion_range {κ : ι → Sort*} {f : ∀ i, κ i → α}
(H : BddAbove <| ⋃ i, range (f i)) : BddAbove <| range fun i ↦ ⨆ j, f i j := by
have ⟨a, h⟩ := H
@@ -155,19 +132,23 @@ theorem BddAbove.range_iSup_of_iUnion_range {κ : ι → Sort*} {f : ∀ i, κ i
· exact iSup_of_empty' (f i) ▸ le_sup_right
exact ciSup_le fun j ↦ le_sup_of_le_left <| h ⟨_, ⟨i, rfl⟩, ⟨j, rfl⟩⟩
+@[to_dual ciInf₂_le]
theorem le_ciSup₂ {κ : ι → Sort*} {f : ∀ i, κ i → α} (H : BddAbove <| ⋃ i, range (f i)) (i : ι)
(j : κ i) : f i j ≤ ⨆ (i) (j), f i j :=
le_ciSup_of_le H.range_iSup_of_iUnion_range i <|
le_ciSup (H.mono <| subset_iUnion (range <| f ·) i) j
/-- The indexed suprema of two functions are comparable if the functions are pointwise comparable -/
-@[gcongr low]
+@[to_dual (attr := gcongr low)
+/-- The indexed infimum of two functions are comparable if the functions are pointwise
+comparable -/]
theorem ciSup_mono {f g : ι → α} (B : BddAbove (range g)) (H : ∀ x, f x ≤ g x) :
iSup f ≤ iSup g := by
cases isEmpty_or_nonempty ι
· rw [iSup_of_empty', iSup_of_empty']
· exact ciSup_le fun x => le_ciSup_of_le B x (H x)
+@[to_dual]
theorem ciSup_sup_eq {f g : ι → α} (Hf : BddAbove <| range f) (Hg : BddAbove <| range g) :
⨆ x, f x ⊔ g x = (⨆ x, f x) ⊔ (⨆ x, g x) := by
cases isEmpty_or_nonempty ι
@@ -176,60 +157,21 @@ theorem ciSup_sup_eq {f g : ι → α} (Hf : BddAbove <| range f) (Hg : BddAbove
have := bbdAbove_range_sup Hf Hg
exact sup_le (ciSup_mono this fun _ ↦ le_sup_left) (ciSup_mono this fun _ ↦ le_sup_right)
+@[to_dual ciInf_set_le]
theorem le_ciSup_set {f : β → α} {s : Set β} (H : BddAbove (f '' s)) {c : β} (hc : c ∈ s) :
f c ≤ ⨆ i : s, f i :=
(le_csSup H <| mem_image_of_mem f hc).trans_eq sSup_image'
-/-- The indexed infimum of two functions are comparable if the functions are pointwise comparable -/
-@[gcongr low]
-theorem ciInf_mono {f g : ι → α} (B : BddBelow (range f)) (H : ∀ x, f x ≤ g x) : iInf f ≤ iInf g :=
- ciSup_mono (α := αᵒᵈ) B H
-
-theorem ciInf_inf_eq {f g : ι → α} (Hf : BddBelow <| range f) (Hg : BddBelow <| range g) :
- ⨅ x, f x ⊓ g x = (⨅ x, f x) ⊓ (⨅ x, g x) :=
- ciSup_sup_eq (α := αᵒᵈ) Hf Hg
-
-/-- The indexed minimum of a function is bounded below by a uniform lower bound -/
-theorem le_ciInf [Nonempty ι] {f : ι → α} {c : α} (H : ∀ x, c ≤ f x) : c ≤ iInf f :=
- ciSup_le (α := αᵒᵈ) H
-
-/-- The indexed infimum of a function is bounded above by the value taken at one point -/
-theorem ciInf_le {f : ι → α} (H : BddBelow (range f)) (c : ι) : iInf f ≤ f c :=
- le_ciSup (α := αᵒᵈ) H c
-
-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
-
+@[to_dual]
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
- have ⟨a, h⟩ := H
- refine ⟨a ⊓ (sInf ∅), fun x ⟨i, hx⟩ ↦ hx ▸ ?_⟩
- cases isEmpty_or_nonempty <| κ i
- · exact iInf_of_isEmpty (f i) ▸ inf_le_right
- exact le_ciInf fun j ↦ inf_le_of_left_le <| h ⟨_, ⟨i, rfl⟩, ⟨j, rfl⟩⟩
-
-theorem ciInf₂_le {κ : ι → Sort*} {f : ∀ i, κ i → α} (H : BddBelow <| ⋃ i, range (f i)) (i : ι)
- (j : κ i) : ⨅ (i) (j), f i j ≤ f i j :=
- ciInf_le_of_le H.range_iInf_of_iUnion_range i <|
- ciInf_le (H.mono <| subset_iUnion (range <| f ·) i) j
-
-theorem ciInf_set_le {f : β → α} {s : Set β} (H : BddBelow (f '' s)) {c : β} (hc : c ∈ s) :
- ⨅ i : s, f i ≤ f c :=
- le_ciSup_set (α := αᵒᵈ) H hc
-
lemma ciInf_le_ciSup [Nonempty ι] {f : ι → α} (hf : BddBelow (range f)) (hf' : BddAbove (range f)) :
⨅ i, f i ≤ ⨆ i, f i :=
(ciInf_le hf (Classical.arbitrary _)).trans <| le_ciSup hf' (Classical.arbitrary _)
+@[to_dual]
lemma ciSup_prod {f : β × γ → α} (hf : BddAbove (Set.range f)) :
⨆ p, f p = ⨆ b, ⨆ c, f (b, c) := by
rcases isEmpty_or_nonempty β
@@ -251,34 +193,25 @@ lemma ciSup_prod {f : β × γ → α} (hf : BddAbove (Set.range f)) :
conv_rhs => enter [b]; rw [ciSup_le_iff (h₂ b)]
simp [Prod.forall]
-lemma ciInf_prod {f : β × γ → α} (hf : BddBelow (Set.range f)) :
- ⨅ p, f p = ⨅ b, ⨅ c, f (b, c) :=
- ciSup_prod (α := αᵒᵈ) hf
-
/-- Introduction rule to prove that `b` is the supremum of `f`: it suffices to check that `b`
is larger than `f i` for all `i`, and that this is not the case of any `wb`.
+See `iInf_eq_of_forall_ge_of_forall_gt_exists_lt` for a version in complete lattices. -/]
theorem ciSup_eq_of_forall_le_of_forall_lt_exists_gt [Nonempty ι] {f : ι → α} (h₁ : ∀ i, f i ≤ b)
(h₂ : ∀ w, w < b → ∃ i, w < f i) : ⨆ i : ι, f i = b :=
csSup_eq_of_forall_le_of_forall_lt_exists_gt (range_nonempty f) (forall_mem_range.mpr h₁)
fun w hw => exists_range_iff.mpr <| h₂ w hw
-/-- Introduction rule to prove that `b` is the infimum of `f`: it suffices to check that `b`
-is smaller than `f i` for all `i`, and that this is not the case of any `w>b`.
-See `iInf_eq_of_forall_ge_of_forall_gt_exists_lt` for a version in complete lattices. -/
-theorem ciInf_eq_of_forall_ge_of_forall_gt_exists_lt [Nonempty ι] {f : ι → α} (h₁ : ∀ i, b ≤ f i)
- (h₂ : ∀ w, b < w → ∃ i, f i < w) : ⨅ i : ι, f i = b :=
- ciSup_eq_of_forall_le_of_forall_lt_exists_gt (α := αᵒᵈ) h₁ h₂
-
+@[to_dual]
lemma Set.Iic_ciInf [Nonempty ι] {f : ι → α} (hf : BddBelow (range f)) :
Iic (⨅ i, f i) = ⋂ i, Iic (f i) := by
ext
simpa using le_ciInf_iff hf
-lemma Set.Ici_ciSup [Nonempty ι] {f : ι → α} (hf : BddAbove (range f)) :
- Ici (⨆ i, f i) = ⋂ i, Ici (f i) :=
- Iic_ciInf (α := αᵒᵈ) hf
-
+@[to_dual]
theorem ciSup_subtype {p : ι → Prop} {f : Subtype p → α}
(hf : BddAbove (Set.range f)) (hf' : sSup ∅ ≤ iSup f) :
iSup f = ⨆ (i) (h : p i), f ⟨i, h⟩ := by
@@ -300,11 +233,7 @@ theorem ciSup_subtype {p : ι → Prop} {f : Subtype p → α}
· exact le_ciSup hf ?_
· exact hf'
-theorem ciInf_subtype {p : ι → Prop} {f : Subtype p → α}
- (hf : BddBelow (Set.range f)) (hf' : iInf f ≤ sInf ∅) :
- iInf f = ⨅ (i) (h : p i), f ⟨i, h⟩ :=
- ciSup_subtype (α := αᵒᵈ) hf hf'
-
+@[to_dual]
theorem cbiSup_eq_ciSup_subtype {p : ι → Prop} {f : ∀ i, p i → α}
(hf : BddAbove (Set.range (fun i : Subtype p ↦ f i i.prop)))
(hf' : sSup ∅ ≤ ⨆ (i : Subtype p), f i i.prop) :
@@ -313,14 +242,9 @@ theorem cbiSup_eq_ciSup_subtype {p : ι → Prop} {f : ∀ i, p i → α}
@[deprecated (since := "2026-04-04")] alias ciSup_subtype' := cbiSup_eq_ciSup_subtype
-theorem cbiInf_eq_ciInf_subtype {p : ι → Prop} {f : ∀ i, p i → α}
- (hf : BddBelow (Set.range (fun i : Subtype p ↦ f i i.prop)))
- (hf' : ⨅ (i : Subtype p), f i i.prop ≤ sInf ∅) :
- ⨅ (i) (h), f i h = ⨅ x : Subtype p, f x x.property :=
- (ciInf_subtype (f := fun x => f x.val x.property) hf hf').symm
-
@[deprecated (since := "2026-04-04")] alias ciInf_subtype' := cbiInf_eq_ciInf_subtype
+@[to_dual]
theorem ciSup_subtype_fun {ι} {s : Set ι} {f : ι → α}
(hf : BddAbove (Set.range fun i : s ↦ f i)) (hf' : sSup ∅ ≤ ⨆ i : s, f i) :
⨆ i : s, f i = ⨆ (t : ι) (_ : t ∈ s), f t :=
@@ -328,33 +252,21 @@ theorem ciSup_subtype_fun {ι} {s : Set ι} {f : ι → α}
@[deprecated (since := "2026-04-04")] alias ciSup_subtype'' := ciSup_subtype_fun
-theorem ciInf_subtype_fun {ι} {s : Set ι} {f : ι → α}
- (hf : BddBelow (Set.range fun i : s ↦ f i)) (hf' : ⨅ i : s, f i ≤ sInf ∅) :
- ⨅ i : s, f i = ⨅ (t : ι) (_ : t ∈ s), f t :=
- ciInf_subtype hf hf'
-
@[deprecated (since := "2026-04-04")] alias ciInf_subtype'' := ciInf_subtype_fun
+@[to_dual]
theorem csSup_image {s : Set β} {f : β → α}
(hf : BddAbove (Set.range fun i : s ↦ f i)) (hf' : sSup ∅ ≤ ⨆ i : s, f i) :
sSup (f '' s) = ⨆ a ∈ s, f a := by
rw [← ciSup_subtype_fun hf hf', iSup, Set.image_eq_range]
-theorem csInf_image {s : Set β} {f : β → α}
- (hf : BddBelow (Set.range fun i : s ↦ f i)) (hf' : ⨅ i : s, f i ≤ sInf ∅) :
- sInf (f '' s) = ⨅ a ∈ s, f a :=
- csSup_image (α := αᵒᵈ) hf hf'
-
+@[to_dual]
theorem cbiSup_id {s : Set α} (hs : BddAbove s) (h : sSup ∅ ≤ sSup s) : ⨆ i ∈ s, i = sSup s := by
rw [← csSup_image (Subtype.range_coe ▸ hs), Set.image_id']
· convert! h
rw [← sSup_range, Subtype.range_coe]
-theorem cbiInf_id {s : Set α} (hs : BddBelow s) (h : sInf s ≤ sInf ∅) : ⨅ i ∈ s, i = sInf s := by
- rw [← csInf_image (Subtype.range_coe ▸ hs), Set.image_id']
- · convert! h
- rw [← sInf_range, Subtype.range_coe]
-
+@[to_dual]
lemma ciSup_image {ι ι' : Type*} {s : Set ι} {f : ι → ι'} {g : ι' → α}
(hf : BddAbove (Set.range fun i : s ↦ g (f i))) (hg' : sSup ∅ ≤ ⨆ i : s, g (f i)) :
⨆ i ∈ (f '' s), g i = ⨆ x ∈ s, g (f x) := by
@@ -375,13 +287,9 @@ lemma ciSup_image {ι ι' : Type*} {s : Set ι} {f : ι → ι'} {g : ι' → α
simpa [bddAbove_def] using hf
rw [← csSup_image hg hf', ← csSup_image hf hg', ← Set.image_comp, comp_def]
-lemma ciInf_image {ι ι' : Type*} {s : Set ι} {f : ι → ι'} {g : ι' → α}
- (hf : BddBelow (Set.range fun i : s ↦ g (f i))) (hg' : ⨅ i : s, g (f i) ≤ sInf ∅) :
- ⨅ 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`. -/
+@[to_dual]
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⟩
@@ -391,15 +299,10 @@ theorem ciSup_exists_le {p : ι → Prop} {f : Exists p → α} : ⨆ ih, f ih
· 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 (α := αᵒᵈ)
-
+@[to_dual]
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
@@ -421,16 +324,14 @@ theorem ciInf_inf_le {f g : ι → α} : (⨅ x, f x) ⊓ (⨅ x, g x) ≤ ⨅ x
/-- Indexed version of `exists_lt_of_lt_csSup`.
When `b < iSup f`, there is an element `i` such that `b < f i`.
-/
+@[to_dual
+/-- Indexed version of `exists_lt_of_csInf_lt`.
+When `iInf f < a`, there is an element `i` such that `f i < a`.
+-/]
theorem exists_lt_of_lt_ciSup [Nonempty ι] {f : ι → α} (h : b < iSup f) : ∃ i, b < f i :=
let ⟨_, ⟨i, rfl⟩, h⟩ := exists_lt_of_lt_csSup (range_nonempty f) h
⟨i, h⟩
-/-- Indexed version of `exists_lt_of_csInf_lt`.
-When `iInf f < a`, there is an element `i` such that `f i < a`.
--/
-theorem exists_lt_of_ciInf_lt [Nonempty ι] {f : ι → α} (h : iInf f < a) : ∃ i, f i < a :=
- exists_lt_of_lt_ciSup (α := αᵒᵈ) h
-
theorem lt_ciSup_iff [Nonempty ι] {f : ι → α} (hb : BddAbove (range f)) :
a < iSup f ↔ ∃ i, a < f i := by
simpa only [mem_range, exists_exists_eq_and] using! lt_csSup_iff hb (range_nonempty _)
@@ -566,38 +467,26 @@ namespace GaloisConnection
variable [ConditionallyCompleteLattice α] [ConditionallyCompleteLattice β] [Nonempty ι] {l : α → β}
{u : β → α}
+@[to_dual u_csInf]
theorem l_csSup (gc : GaloisConnection l u) {s : Set α} (hne : s.Nonempty) (hbdd : BddAbove s) :
l (sSup s) = ⨆ x : s, l x :=
Eq.symm <| IsLUB.ciSup_set_eq (gc.isLUB_l_image <| isLUB_csSup hne hbdd) hne
+@[to_dual u_csInf']
theorem l_csSup' (gc : GaloisConnection l u) {s : Set α} (hne : s.Nonempty) (hbdd : BddAbove s) :
l (sSup s) = sSup (l '' s) := by rw [gc.l_csSup hne hbdd, sSup_image']
+@[to_dual u_ciInf]
theorem l_ciSup (gc : GaloisConnection l u) {f : ι → α} (hf : BddAbove (range f)) :
l (⨆ i, f i) = ⨆ i, l (f i) := by rw [iSup, gc.l_csSup (range_nonempty _) hf, iSup_range']
+@[to_dual u_ciInf_set]
theorem l_ciSup_set (gc : GaloisConnection l u) {s : Set γ} {f : γ → α} (hf : BddAbove (f '' s))
(hne : s.Nonempty) : l (⨆ i : s, f i) = ⨆ i : s, l (f i) := by
haveI := hne.to_subtype
rw [image_eq_range] at hf
exact gc.l_ciSup hf
-theorem u_csInf (gc : GaloisConnection l u) {s : Set β} (hne : s.Nonempty) (hbdd : BddBelow s) :
- u (sInf s) = ⨅ x : s, u x :=
- gc.dual.l_csSup hne hbdd
-
-theorem u_csInf' (gc : GaloisConnection l u) {s : Set β} (hne : s.Nonempty) (hbdd : BddBelow s) :
- u (sInf s) = sInf (u '' s) :=
- gc.dual.l_csSup' hne hbdd
-
-theorem u_ciInf (gc : GaloisConnection l u) {f : ι → β} (hf : BddBelow (range f)) :
- u (⨅ i, f i) = ⨅ i, u (f i) :=
- gc.dual.l_ciSup hf
-
-theorem u_ciInf_set (gc : GaloisConnection l u) {s : Set γ} {f : γ → β} (hf : BddBelow (f '' s))
- (hne : s.Nonempty) : u (⨅ i : s, f i) = ⨅ i : s, u (f i) :=
- gc.dual.l_ciSup_set hf hne
-
end GaloisConnection
namespace OrderIso
@@ -605,38 +494,26 @@ namespace OrderIso
section ConditionallyCompleteLattice
variable [ConditionallyCompleteLattice α] [ConditionallyCompleteLattice β] [Nonempty ι]
+@[to_dual]
theorem map_csSup (e : α ≃o β) {s : Set α} (hne : s.Nonempty) (hbdd : BddAbove s) :
e (sSup s) = ⨆ x : s, e x :=
e.to_galoisConnection.l_csSup hne hbdd
+@[to_dual]
theorem map_csSup' (e : α ≃o β) {s : Set α} (hne : s.Nonempty) (hbdd : BddAbove s) :
e (sSup s) = sSup (e '' s) :=
e.to_galoisConnection.l_csSup' hne hbdd
+@[to_dual]
theorem map_ciSup (e : α ≃o β) {f : ι → α} (hf : BddAbove (range f)) :
e (⨆ i, f i) = ⨆ i, e (f i) :=
e.to_galoisConnection.l_ciSup hf
+@[to_dual]
theorem map_ciSup_set (e : α ≃o β) {s : Set γ} {f : γ → α} (hf : BddAbove (f '' s))
(hne : s.Nonempty) : e (⨆ i : s, f i) = ⨆ i : s, e (f i) :=
e.to_galoisConnection.l_ciSup_set hf hne
-theorem map_csInf (e : α ≃o β) {s : Set α} (hne : s.Nonempty) (hbdd : BddBelow s) :
- e (sInf s) = ⨅ x : s, e x :=
- e.dual.map_csSup hne hbdd
-
-theorem map_csInf' (e : α ≃o β) {s : Set α} (hne : s.Nonempty) (hbdd : BddBelow s) :
- e (sInf s) = sInf (e '' s) :=
- e.dual.map_csSup' hne hbdd
-
-theorem map_ciInf (e : α ≃o β) {f : ι → α} (hf : BddBelow (range f)) :
- e (⨅ i, f i) = ⨅ i, e (f i) :=
- e.dual.map_ciSup hf
-
-theorem map_ciInf_set (e : α ≃o β) {s : Set γ} {f : γ → α} (hf : BddBelow (f '' s))
- (hne : s.Nonempty) : e (⨅ i : s, f i) = ⨅ i : s, e (f i) :=
- e.dual.map_ciSup_set hf hne
-
end ConditionallyCompleteLattice
section ConditionallyCompleteLinearOrderBot
From 48e6e57aac375c42afb73d83e0007a4440dbd1d8 Mon Sep 17 00:00:00 2001
From: Yongxi Lin
Date: Sat, 13 Jun 2026 11:36:48 -0700
Subject: [PATCH 0015/1300] Add finset supremum convergence lemmas
---
.../Topology/Order/MonotoneConvergence.lean | 30 +++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/Mathlib/Topology/Order/MonotoneConvergence.lean b/Mathlib/Topology/Order/MonotoneConvergence.lean
index 525b8006cece8f..2ce07750fb97c4 100644
--- a/Mathlib/Topology/Order/MonotoneConvergence.lean
+++ b/Mathlib/Topology/Order/MonotoneConvergence.lean
@@ -5,6 +5,7 @@ Authors: Heather Macbeth, Yury Kudryashov
-/
module
+public import Mathlib.Order.CompleteLattice.Finset
public import Mathlib.Topology.Order.Basic
/-!
@@ -164,6 +165,35 @@ end iInf
end
+section FinsetSup
+
+variable {ι α : Type*} [TopologicalSpace α]
+
+theorem tendsto_finset_sup_iSup [CompleteLattice α] [SupConvergenceClass α] (a : ι → α) :
+ Tendsto (fun F : Finset ι => F.sup a) atTop (𝓝 (⨆ i, a i)) := by
+ have hmono : Monotone (fun F : Finset ι => F.sup a) :=
+ fun F G hFG => Finset.sup_mono hFG
+ simpa [Finset.sup_eq_iSup, ← iSup_eq_iSup_finset a] using tendsto_atTop_iSup hmono
+
+theorem tendsto_finset_sup_ciSup [ConditionallyCompleteLattice α] [OrderBot α]
+ [SupConvergenceClass α] [Nonempty ι] (a : ι → α) (ha : BddAbove (Set.range a)) :
+ Tendsto (fun F : Finset ι => F.sup a) atTop (𝓝 (⨆ i, a i)) := by
+ have hmono : Monotone (fun F : Finset ι => F.sup a) :=
+ fun F G hFG => Finset.sup_mono hFG
+ have hbdd : BddAbove (Set.range fun F : Finset ι => F.sup a) := by
+ refine ⟨⨆ i, a i, ?_⟩
+ rintro _ ⟨F, rfl⟩
+ exact Finset.sup_le fun i _ => le_ciSup ha i
+ have hsup : (⨆ F : Finset ι, F.sup a) = ⨆ i, a i := by
+ refine le_antisymm ?_ ?_
+ · exact ciSup_le fun F => Finset.sup_le fun i _ => le_ciSup ha i
+ · exact ciSup_le fun i =>
+ (Finset.le_sup (s := ({i} : Finset ι)) (f := a) (by simp)).trans
+ (le_ciSup hbdd ({i} : Finset ι))
+ simpa [hsup] using tendsto_atTop_ciSup hmono hbdd
+
+end FinsetSup
+
instance Prod.supConvergenceClass
[Preorder α] [Preorder β] [TopologicalSpace α] [TopologicalSpace β]
[SupConvergenceClass α] [SupConvergenceClass β] : SupConvergenceClass (α × β) := by
From 87213eeb1cd68058e298f6fb7797dedcb415d69a Mon Sep 17 00:00:00 2001
From: Yongxi Lin
Date: Sat, 13 Jun 2026 13:19:48 -0700
Subject: [PATCH 0016/1300] Add finset infimum convergence lemmas
---
.../Topology/Order/MonotoneConvergence.lean | 27 +++++++++++++++++--
1 file changed, 25 insertions(+), 2 deletions(-)
diff --git a/Mathlib/Topology/Order/MonotoneConvergence.lean b/Mathlib/Topology/Order/MonotoneConvergence.lean
index 2ce07750fb97c4..9ff4b140a8158d 100644
--- a/Mathlib/Topology/Order/MonotoneConvergence.lean
+++ b/Mathlib/Topology/Order/MonotoneConvergence.lean
@@ -165,7 +165,7 @@ end iInf
end
-section FinsetSup
+section FinsetSupInf
variable {ι α : Type*} [TopologicalSpace α]
@@ -192,7 +192,30 @@ theorem tendsto_finset_sup_ciSup [ConditionallyCompleteLattice α] [OrderBot α]
(le_ciSup hbdd ({i} : Finset ι))
simpa [hsup] using tendsto_atTop_ciSup hmono hbdd
-end FinsetSup
+theorem tendsto_finset_inf_iInf [CompleteLattice α] [InfConvergenceClass α] (a : ι → α) :
+ Tendsto (fun F : Finset ι => F.inf a) atTop (𝓝 (⨅ i, a i)) := by
+ have hanti : Antitone (fun F : Finset ι => F.inf a) :=
+ fun F G hFG => Finset.inf_mono hFG
+ simpa [Finset.inf_eq_iInf, ← iInf_eq_iInf_finset a] using tendsto_atTop_iInf hanti
+
+theorem tendsto_finset_inf_ciInf [ConditionallyCompleteLattice α] [OrderTop α]
+ [InfConvergenceClass α] [Nonempty ι] (a : ι → α) (ha : BddBelow (Set.range a)) :
+ Tendsto (fun F : Finset ι => F.inf a) atTop (𝓝 (⨅ i, a i)) := by
+ have hanti : Antitone (fun F : Finset ι => F.inf a) :=
+ fun F G hFG => Finset.inf_mono hFG
+ have hbdd : BddBelow (Set.range fun F : Finset ι => F.inf a) := by
+ refine ⟨⨅ i, a i, ?_⟩
+ rintro _ ⟨F, rfl⟩
+ exact Finset.le_inf fun i _ => ciInf_le ha i
+ have hinf : (⨅ F : Finset ι, F.inf a) = ⨅ i, a i := by
+ refine le_antisymm ?_ ?_
+ · exact le_ciInf fun i =>
+ (ciInf_le hbdd ({i} : Finset ι)).trans
+ (Finset.inf_le (s := ({i} : Finset ι)) (f := a) (by simp))
+ · exact le_ciInf fun F => Finset.le_inf fun i _ => ciInf_le ha i
+ simpa [hinf] using tendsto_atTop_ciInf hanti hbdd
+
+end FinsetSupInf
instance Prod.supConvergenceClass
[Preorder α] [Preorder β] [TopologicalSpace α] [TopologicalSpace β]
From d3d402c26130abdf5d079a2a41b02c2cd3637056 Mon Sep 17 00:00:00 2001
From: Yongxi Lin
Date: Sat, 13 Jun 2026 14:08:18 -0700
Subject: [PATCH 0017/1300] Rework finset convergence lemmas via ciSup
---
.../Topology/Order/MonotoneConvergence.lean | 79 +++++++++++--------
1 file changed, 48 insertions(+), 31 deletions(-)
diff --git a/Mathlib/Topology/Order/MonotoneConvergence.lean b/Mathlib/Topology/Order/MonotoneConvergence.lean
index 9ff4b140a8158d..205cb16c796ca6 100644
--- a/Mathlib/Topology/Order/MonotoneConvergence.lean
+++ b/Mathlib/Topology/Order/MonotoneConvergence.lean
@@ -5,7 +5,6 @@ Authors: Heather Macbeth, Yury Kudryashov
-/
module
-public import Mathlib.Order.CompleteLattice.Finset
public import Mathlib.Topology.Order.Basic
/-!
@@ -167,53 +166,71 @@ end
section FinsetSupInf
-variable {ι α : Type*} [TopologicalSpace α]
+variable {ι α : Type*}
-theorem tendsto_finset_sup_iSup [CompleteLattice α] [SupConvergenceClass α] (a : ι → α) :
- Tendsto (fun F : Finset ι => F.sup a) atTop (𝓝 (⨆ i, a i)) := by
- have hmono : Monotone (fun F : Finset ι => F.sup a) :=
- fun F G hFG => Finset.sup_mono hFG
- simpa [Finset.sup_eq_iSup, ← iSup_eq_iSup_finset a] using tendsto_atTop_iSup hmono
+theorem ciSup_eq_ciSup_finset [ConditionallyCompleteLattice α] [OrderBot α] [Nonempty ι]
+ (a : ι → α) (ha : BddAbove (Set.range a)) :
+ ⨆ i, a i = ⨆ F : Finset ι, F.sup a := by
+ have hbdd : BddAbove (Set.range fun F : Finset ι => F.sup a) := by
+ refine ⟨⨆ i, a i, ?_⟩
+ rintro _ ⟨F, rfl⟩
+ exact Finset.sup_le fun i _ => le_ciSup ha i
+ refine le_antisymm ?_ ?_
+ · exact ciSup_le fun i =>
+ (Finset.le_sup (s := ({i} : Finset ι)) (f := a) (by simp)).trans
+ (le_ciSup hbdd ({i} : Finset ι))
+ · exact ciSup_le fun F => Finset.sup_le fun i _ => le_ciSup ha i
+
+theorem ciInf_eq_ciInf_finset [ConditionallyCompleteLattice α] [OrderTop α] [Nonempty ι]
+ (a : ι → α) (ha : BddBelow (Set.range a)) :
+ ⨅ i, a i = ⨅ F : Finset ι, F.inf a := by
+ have hbdd : BddBelow (Set.range fun F : Finset ι => F.inf a) := by
+ refine ⟨⨅ i, a i, ?_⟩
+ rintro _ ⟨F, rfl⟩
+ exact Finset.le_inf fun i _ => ciInf_le ha i
+ refine le_antisymm ?_ ?_
+ · exact le_ciInf fun F => Finset.le_inf fun i _ => ciInf_le ha i
+ · exact le_ciInf fun i =>
+ (ciInf_le hbdd ({i} : Finset ι)).trans
+ (Finset.inf_le (s := ({i} : Finset ι)) (f := a) (by simp))
+
+variable [TopologicalSpace α]
theorem tendsto_finset_sup_ciSup [ConditionallyCompleteLattice α] [OrderBot α]
[SupConvergenceClass α] [Nonempty ι] (a : ι → α) (ha : BddAbove (Set.range a)) :
Tendsto (fun F : Finset ι => F.sup a) atTop (𝓝 (⨆ i, a i)) := by
- have hmono : Monotone (fun F : Finset ι => F.sup a) :=
- fun F G hFG => Finset.sup_mono hFG
+ have hmono : Monotone (fun F : Finset ι => F.sup a) := fun F G hFG => Finset.sup_mono hFG
have hbdd : BddAbove (Set.range fun F : Finset ι => F.sup a) := by
refine ⟨⨆ i, a i, ?_⟩
rintro _ ⟨F, rfl⟩
exact Finset.sup_le fun i _ => le_ciSup ha i
- have hsup : (⨆ F : Finset ι, F.sup a) = ⨆ i, a i := by
- refine le_antisymm ?_ ?_
- · exact ciSup_le fun F => Finset.sup_le fun i _ => le_ciSup ha i
- · exact ciSup_le fun i =>
- (Finset.le_sup (s := ({i} : Finset ι)) (f := a) (by simp)).trans
- (le_ciSup hbdd ({i} : Finset ι))
- simpa [hsup] using tendsto_atTop_ciSup hmono hbdd
-
-theorem tendsto_finset_inf_iInf [CompleteLattice α] [InfConvergenceClass α] (a : ι → α) :
- Tendsto (fun F : Finset ι => F.inf a) atTop (𝓝 (⨅ i, a i)) := by
- have hanti : Antitone (fun F : Finset ι => F.inf a) :=
- fun F G hFG => Finset.inf_mono hFG
- simpa [Finset.inf_eq_iInf, ← iInf_eq_iInf_finset a] using tendsto_atTop_iInf hanti
+ simpa [ciSup_eq_ciSup_finset a ha] using tendsto_atTop_ciSup hmono hbdd
theorem tendsto_finset_inf_ciInf [ConditionallyCompleteLattice α] [OrderTop α]
[InfConvergenceClass α] [Nonempty ι] (a : ι → α) (ha : BddBelow (Set.range a)) :
Tendsto (fun F : Finset ι => F.inf a) atTop (𝓝 (⨅ i, a i)) := by
- have hanti : Antitone (fun F : Finset ι => F.inf a) :=
- fun F G hFG => Finset.inf_mono hFG
+ have hanti : Antitone (fun F : Finset ι => F.inf a) := fun F G hFG => Finset.inf_mono hFG
have hbdd : BddBelow (Set.range fun F : Finset ι => F.inf a) := by
refine ⟨⨅ i, a i, ?_⟩
rintro _ ⟨F, rfl⟩
exact Finset.le_inf fun i _ => ciInf_le ha i
- have hinf : (⨅ F : Finset ι, F.inf a) = ⨅ i, a i := by
- refine le_antisymm ?_ ?_
- · exact le_ciInf fun i =>
- (ciInf_le hbdd ({i} : Finset ι)).trans
- (Finset.inf_le (s := ({i} : Finset ι)) (f := a) (by simp))
- · exact le_ciInf fun F => Finset.le_inf fun i _ => ciInf_le ha i
- simpa [hinf] using tendsto_atTop_ciInf hanti hbdd
+ simpa [ciInf_eq_ciInf_finset a ha] using tendsto_atTop_ciInf hanti hbdd
+
+theorem tendsto_finset_sup_iSup [CompleteLattice α] [SupConvergenceClass α] (a : ι → α) :
+ Tendsto (fun F : Finset ι => F.sup a) atTop (𝓝 (⨆ i, a i)) := by
+ cases isEmpty_or_nonempty ι
+ · haveI := ‹IsEmpty ι›
+ simpa [iSup_of_empty] using
+ (tendsto_const_nhds : Tendsto (fun _ : Finset ι => (⊥ : α)) atTop (𝓝 ⊥))
+ · exact tendsto_finset_sup_ciSup a (OrderTop.bddAbove _)
+
+theorem tendsto_finset_inf_iInf [CompleteLattice α] [InfConvergenceClass α] (a : ι → α) :
+ Tendsto (fun F : Finset ι => F.inf a) atTop (𝓝 (⨅ i, a i)) := by
+ cases isEmpty_or_nonempty ι
+ · haveI := ‹IsEmpty ι›
+ simpa [iInf_of_empty] using
+ (tendsto_const_nhds : Tendsto (fun _ : Finset ι => (⊤ : α)) atTop (𝓝 ⊤))
+ · exact tendsto_finset_inf_ciInf a (OrderBot.bddBelow _)
end FinsetSupInf
From b5e47b3543f7244d6f63b0c0f7d5e75f9c2e8f76 Mon Sep 17 00:00:00 2001
From: Yongxi Lin
Date: Sat, 13 Jun 2026 14:40:03 -0700
Subject: [PATCH 0018/1300] Use order dual for finset infimum lemmas
---
.../Topology/Order/MonotoneConvergence.lean | 26 ++++---------------
1 file changed, 5 insertions(+), 21 deletions(-)
diff --git a/Mathlib/Topology/Order/MonotoneConvergence.lean b/Mathlib/Topology/Order/MonotoneConvergence.lean
index 205cb16c796ca6..f31ea28c1053d7 100644
--- a/Mathlib/Topology/Order/MonotoneConvergence.lean
+++ b/Mathlib/Topology/Order/MonotoneConvergence.lean
@@ -184,15 +184,8 @@ theorem ciSup_eq_ciSup_finset [ConditionallyCompleteLattice α] [OrderBot α] [N
theorem ciInf_eq_ciInf_finset [ConditionallyCompleteLattice α] [OrderTop α] [Nonempty ι]
(a : ι → α) (ha : BddBelow (Set.range a)) :
⨅ i, a i = ⨅ F : Finset ι, F.inf a := by
- have hbdd : BddBelow (Set.range fun F : Finset ι => F.inf a) := by
- refine ⟨⨅ i, a i, ?_⟩
- rintro _ ⟨F, rfl⟩
- exact Finset.le_inf fun i _ => ciInf_le ha i
- refine le_antisymm ?_ ?_
- · exact le_ciInf fun F => Finset.le_inf fun i _ => ciInf_le ha i
- · exact le_ciInf fun i =>
- (ciInf_le hbdd ({i} : Finset ι)).trans
- (Finset.inf_le (s := ({i} : Finset ι)) (f := a) (by simp))
+ rw [← OrderDual.toDual_inj]
+ simpa using ciSup_eq_ciSup_finset (α := αᵒᵈ) (OrderDual.toDual ∘ a) ha
variable [TopologicalSpace α]
@@ -209,27 +202,18 @@ theorem tendsto_finset_sup_ciSup [ConditionallyCompleteLattice α] [OrderBot α]
theorem tendsto_finset_inf_ciInf [ConditionallyCompleteLattice α] [OrderTop α]
[InfConvergenceClass α] [Nonempty ι] (a : ι → α) (ha : BddBelow (Set.range a)) :
Tendsto (fun F : Finset ι => F.inf a) atTop (𝓝 (⨅ i, a i)) := by
- have hanti : Antitone (fun F : Finset ι => F.inf a) := fun F G hFG => Finset.inf_mono hFG
- have hbdd : BddBelow (Set.range fun F : Finset ι => F.inf a) := by
- refine ⟨⨅ i, a i, ?_⟩
- rintro _ ⟨F, rfl⟩
- exact Finset.le_inf fun i _ => ciInf_le ha i
- simpa [ciInf_eq_ciInf_finset a ha] using tendsto_atTop_ciInf hanti hbdd
+ convert! tendsto_finset_sup_ciSup (α := αᵒᵈ) (OrderDual.toDual ∘ a) ha using 1
theorem tendsto_finset_sup_iSup [CompleteLattice α] [SupConvergenceClass α] (a : ι → α) :
Tendsto (fun F : Finset ι => F.sup a) atTop (𝓝 (⨆ i, a i)) := by
cases isEmpty_or_nonempty ι
- · haveI := ‹IsEmpty ι›
- simpa [iSup_of_empty] using
- (tendsto_const_nhds : Tendsto (fun _ : Finset ι => (⊥ : α)) atTop (𝓝 ⊥))
+ · simp_all [iSup_of_empty, tendsto_const_nhds]
· exact tendsto_finset_sup_ciSup a (OrderTop.bddAbove _)
theorem tendsto_finset_inf_iInf [CompleteLattice α] [InfConvergenceClass α] (a : ι → α) :
Tendsto (fun F : Finset ι => F.inf a) atTop (𝓝 (⨅ i, a i)) := by
cases isEmpty_or_nonempty ι
- · haveI := ‹IsEmpty ι›
- simpa [iInf_of_empty] using
- (tendsto_const_nhds : Tendsto (fun _ : Finset ι => (⊤ : α)) atTop (𝓝 ⊤))
+ · simp_all [iInf_of_empty, tendsto_const_nhds]
· exact tendsto_finset_inf_ciInf a (OrderBot.bddBelow _)
end FinsetSupInf
From fd840e603783c4a1b999647b1ffb21828a4c9d4f Mon Sep 17 00:00:00 2001
From: Yongxi Lin
Date: Sat, 13 Jun 2026 20:03:17 -0700
Subject: [PATCH 0019/1300] Use to_dual for finset supremum identity
---
.../ConditionallyCompleteLattice/Finset.lean | 20 ++++++++++
.../Topology/Order/MonotoneConvergence.lean | 39 +++++--------------
2 files changed, 29 insertions(+), 30 deletions(-)
diff --git a/Mathlib/Order/ConditionallyCompleteLattice/Finset.lean b/Mathlib/Order/ConditionallyCompleteLattice/Finset.lean
index c6f70a19799255..a3e806afade5b3 100644
--- a/Mathlib/Order/ConditionallyCompleteLattice/Finset.lean
+++ b/Mathlib/Order/ConditionallyCompleteLattice/Finset.lean
@@ -179,6 +179,26 @@ end ListMultiset
end ConditionallyCompleteLinearOrder
+section ConditionallyCompleteLattice
+
+variable [ConditionallyCompleteLattice α]
+
+/-- Supremum of `a i`, `i : ι`, is equal to the supremum over finite suprema of `a`. -/
+@[to_dual
+/-- Infimum of `a i`, `i : ι`, is equal to the infimum over finite infima of `a`. -/]
+theorem ciSup_eq_ciSup_finset [OrderBot α] [Nonempty ι] {a : ι → α}
+ (ha : BddAbove (range a)) :
+ ⨆ i, a i = ⨆ F : Finset ι, F.sup a := by
+ have hbdd : BddAbove (Set.range fun F : Finset ι => F.sup a) := by
+ refine ⟨⨆ i, a i, ?_⟩
+ rintro _ ⟨F, rfl⟩
+ exact Finset.sup_le fun i _ => le_ciSup ha i
+ refine le_antisymm ?_ ?_
+ · exact ciSup_le fun i => (Finset.le_sup (by simp)).trans (le_ciSup hbdd ({i} : Finset ι))
+ · exact ciSup_le fun F => Finset.sup_le fun i _ => le_ciSup ha i
+
+end ConditionallyCompleteLattice
+
/-!
### Relation between `sSup` / `sInf` and `Finset.sup'` / `Finset.inf'`
diff --git a/Mathlib/Topology/Order/MonotoneConvergence.lean b/Mathlib/Topology/Order/MonotoneConvergence.lean
index f31ea28c1053d7..29b617cf3fac2f 100644
--- a/Mathlib/Topology/Order/MonotoneConvergence.lean
+++ b/Mathlib/Topology/Order/MonotoneConvergence.lean
@@ -168,53 +168,32 @@ section FinsetSupInf
variable {ι α : Type*}
-theorem ciSup_eq_ciSup_finset [ConditionallyCompleteLattice α] [OrderBot α] [Nonempty ι]
- (a : ι → α) (ha : BddAbove (Set.range a)) :
- ⨆ i, a i = ⨆ F : Finset ι, F.sup a := by
- have hbdd : BddAbove (Set.range fun F : Finset ι => F.sup a) := by
- refine ⟨⨆ i, a i, ?_⟩
- rintro _ ⟨F, rfl⟩
- exact Finset.sup_le fun i _ => le_ciSup ha i
- refine le_antisymm ?_ ?_
- · exact ciSup_le fun i =>
- (Finset.le_sup (s := ({i} : Finset ι)) (f := a) (by simp)).trans
- (le_ciSup hbdd ({i} : Finset ι))
- · exact ciSup_le fun F => Finset.sup_le fun i _ => le_ciSup ha i
-
-theorem ciInf_eq_ciInf_finset [ConditionallyCompleteLattice α] [OrderTop α] [Nonempty ι]
- (a : ι → α) (ha : BddBelow (Set.range a)) :
- ⨅ i, a i = ⨅ F : Finset ι, F.inf a := by
- rw [← OrderDual.toDual_inj]
- simpa using ciSup_eq_ciSup_finset (α := αᵒᵈ) (OrderDual.toDual ∘ a) ha
-
variable [TopologicalSpace α]
theorem tendsto_finset_sup_ciSup [ConditionallyCompleteLattice α] [OrderBot α]
- [SupConvergenceClass α] [Nonempty ι] (a : ι → α) (ha : BddAbove (Set.range a)) :
+ [SupConvergenceClass α] [Nonempty ι] {a : ι → α} (ha : BddAbove (range a)) :
Tendsto (fun F : Finset ι => F.sup a) atTop (𝓝 (⨆ i, a i)) := by
have hmono : Monotone (fun F : Finset ι => F.sup a) := fun F G hFG => Finset.sup_mono hFG
have hbdd : BddAbove (Set.range fun F : Finset ι => F.sup a) := by
refine ⟨⨆ i, a i, ?_⟩
rintro _ ⟨F, rfl⟩
exact Finset.sup_le fun i _ => le_ciSup ha i
- simpa [ciSup_eq_ciSup_finset a ha] using tendsto_atTop_ciSup hmono hbdd
+ simpa [ciSup_eq_ciSup_finset ha] using tendsto_atTop_ciSup hmono hbdd
theorem tendsto_finset_inf_ciInf [ConditionallyCompleteLattice α] [OrderTop α]
- [InfConvergenceClass α] [Nonempty ι] (a : ι → α) (ha : BddBelow (Set.range a)) :
- Tendsto (fun F : Finset ι => F.inf a) atTop (𝓝 (⨅ i, a i)) := by
- convert! tendsto_finset_sup_ciSup (α := αᵒᵈ) (OrderDual.toDual ∘ a) ha using 1
+ [InfConvergenceClass α] [Nonempty ι] (a : ι → α) (ha : BddBelow (range a)) :
+ Tendsto (fun F : Finset ι => F.inf a) atTop (𝓝 (⨅ i, a i)) :=
+ tendsto_finset_sup_ciSup (α := αᵒᵈ) ha
theorem tendsto_finset_sup_iSup [CompleteLattice α] [SupConvergenceClass α] (a : ι → α) :
Tendsto (fun F : Finset ι => F.sup a) atTop (𝓝 (⨆ i, a i)) := by
- cases isEmpty_or_nonempty ι
- · simp_all [iSup_of_empty, tendsto_const_nhds]
- · exact tendsto_finset_sup_ciSup a (OrderTop.bddAbove _)
+ have hmono : Monotone (fun F : Finset ι => F.sup a) := fun F G hFG => Finset.sup_mono hFG
+ simpa [Finset.sup_eq_iSup, ← iSup_eq_iSup_finset a] using tendsto_atTop_iSup hmono
theorem tendsto_finset_inf_iInf [CompleteLattice α] [InfConvergenceClass α] (a : ι → α) :
Tendsto (fun F : Finset ι => F.inf a) atTop (𝓝 (⨅ i, a i)) := by
- cases isEmpty_or_nonempty ι
- · simp_all [iInf_of_empty, tendsto_const_nhds]
- · exact tendsto_finset_inf_ciInf a (OrderBot.bddBelow _)
+ have hanti : Antitone (fun F : Finset ι => F.inf a) := fun F G hFG => Finset.inf_mono hFG
+ simpa [Finset.inf_eq_iInf, ← iInf_eq_iInf_finset a] using tendsto_atTop_iInf hanti
end FinsetSupInf
From 4e6adfdf75c263af0e8d78cfec95c9e64750de6d Mon Sep 17 00:00:00 2001
From: Yongxi Lin
Date: Sat, 13 Jun 2026 21:30:00 -0700
Subject: [PATCH 0020/1300] update MC
---
.../Topology/Order/MonotoneConvergence.lean | 73 ++++++++++---------
1 file changed, 40 insertions(+), 33 deletions(-)
diff --git a/Mathlib/Topology/Order/MonotoneConvergence.lean b/Mathlib/Topology/Order/MonotoneConvergence.lean
index 29b617cf3fac2f..a2e0766531a8ab 100644
--- a/Mathlib/Topology/Order/MonotoneConvergence.lean
+++ b/Mathlib/Topology/Order/MonotoneConvergence.lean
@@ -111,6 +111,8 @@ end IsGLB
section CiSup
+section ConditionallyCompletePartialOrder
+
variable [ConditionallyCompletePartialOrderSup α] [SupConvergenceClass α] {f : ι → α}
theorem tendsto_atTop_ciSup (h_mono : Monotone f) (hbdd : BddAbove <| range f) :
@@ -124,10 +126,28 @@ theorem tendsto_atTop_ciSup (h_mono : Monotone f) (hbdd : BddAbove <| range f) :
theorem tendsto_atBot_ciSup (h_anti : Antitone f) (hbdd : BddAbove <| range f) :
Tendsto f atBot (𝓝 (⨆ i, f i)) := by convert! tendsto_atTop_ciSup h_anti.dual hbdd.dual using 1
+end ConditionallyCompletePartialOrder
+
+section ConditionallyCompleteLattice
+
+theorem tendsto_finset_sup_ciSup {ι} [ConditionallyCompleteLattice α] [OrderBot α]
+ [SupConvergenceClass α] [Nonempty ι] {a : ι → α} (ha : BddAbove (range a)) :
+ Tendsto (fun F : Finset ι => F.sup a) atTop (𝓝 (⨆ i, a i)) := by
+ have hmono : Monotone (fun F : Finset ι => F.sup a) := fun F G hFG => Finset.sup_mono hFG
+ have hbdd : BddAbove (Set.range fun F : Finset ι => F.sup a) := by
+ refine ⟨⨆ i, a i, ?_⟩
+ rintro _ ⟨F, rfl⟩
+ exact Finset.sup_le fun i _ => le_ciSup ha i
+ simpa [ciSup_eq_ciSup_finset ha] using tendsto_atTop_ciSup hmono hbdd
+
+end ConditionallyCompleteLattice
+
end CiSup
section CiInf
+section ConditionallyCompletePartialOrder
+
variable [ConditionallyCompletePartialOrderInf α] [InfConvergenceClass α] {f : ι → α}
theorem tendsto_atBot_ciInf (h_mono : Monotone f) (hbdd : BddBelow <| range f) :
@@ -136,6 +156,17 @@ theorem tendsto_atBot_ciInf (h_mono : Monotone f) (hbdd : BddBelow <| range f) :
theorem tendsto_atTop_ciInf (h_anti : Antitone f) (hbdd : BddBelow <| range f) :
Tendsto f atTop (𝓝 (⨅ i, f i)) := by convert! tendsto_atBot_ciSup h_anti.dual hbdd.dual using 1
+end ConditionallyCompletePartialOrder
+
+section ConditionallyCompleteLattice
+
+theorem tendsto_finset_inf_ciInf {ι} [ConditionallyCompleteLattice α] [OrderTop α]
+ [InfConvergenceClass α] [Nonempty ι] (a : ι → α) (ha : BddBelow (range a)) :
+ Tendsto (fun F : Finset ι => F.inf a) atTop (𝓝 (⨅ i, a i)) :=
+ tendsto_finset_sup_ciSup (α := αᵒᵈ) ha
+
+end ConditionallyCompleteLattice
+
end CiInf
section iSup
@@ -145,6 +176,11 @@ variable [CompleteLattice α] [SupConvergenceClass α] {f : ι → α}
theorem tendsto_atTop_iSup (h_mono : Monotone f) : Tendsto f atTop (𝓝 (⨆ i, f i)) :=
tendsto_atTop_ciSup h_mono (OrderTop.bddAbove _)
+theorem tendsto_finset_sup_iSup {ι} (a : ι → α) :
+ Tendsto (fun F : Finset ι => F.sup a) atTop (𝓝 (⨆ i, a i)) := by
+ have hmono : Monotone (fun F : Finset ι => F.sup a) := fun F G hFG => Finset.sup_mono hFG
+ simpa [Finset.sup_eq_iSup, ← iSup_eq_iSup_finset a] using tendsto_atTop_iSup hmono
+
theorem tendsto_atBot_iSup (h_anti : Antitone f) : Tendsto f atBot (𝓝 (⨆ i, f i)) :=
tendsto_atBot_ciSup h_anti (OrderTop.bddAbove _)
@@ -157,6 +193,10 @@ variable [CompleteLattice α] [InfConvergenceClass α] {f : ι → α}
theorem tendsto_atBot_iInf (h_mono : Monotone f) : Tendsto f atBot (𝓝 (⨅ i, f i)) :=
tendsto_atBot_ciInf h_mono (OrderBot.bddBelow _)
+theorem tendsto_finset_inf_iInf {ι} (a : ι → α) :
+ Tendsto (fun F : Finset ι => F.inf a) atTop (𝓝 (⨅ i, a i)) :=
+ tendsto_finset_sup_iSup (α := αᵒᵈ) a
+
theorem tendsto_atTop_iInf (h_anti : Antitone f) : Tendsto f atTop (𝓝 (⨅ i, f i)) :=
tendsto_atTop_ciInf h_anti (OrderBot.bddBelow _)
@@ -164,39 +204,6 @@ end iInf
end
-section FinsetSupInf
-
-variable {ι α : Type*}
-
-variable [TopologicalSpace α]
-
-theorem tendsto_finset_sup_ciSup [ConditionallyCompleteLattice α] [OrderBot α]
- [SupConvergenceClass α] [Nonempty ι] {a : ι → α} (ha : BddAbove (range a)) :
- Tendsto (fun F : Finset ι => F.sup a) atTop (𝓝 (⨆ i, a i)) := by
- have hmono : Monotone (fun F : Finset ι => F.sup a) := fun F G hFG => Finset.sup_mono hFG
- have hbdd : BddAbove (Set.range fun F : Finset ι => F.sup a) := by
- refine ⟨⨆ i, a i, ?_⟩
- rintro _ ⟨F, rfl⟩
- exact Finset.sup_le fun i _ => le_ciSup ha i
- simpa [ciSup_eq_ciSup_finset ha] using tendsto_atTop_ciSup hmono hbdd
-
-theorem tendsto_finset_inf_ciInf [ConditionallyCompleteLattice α] [OrderTop α]
- [InfConvergenceClass α] [Nonempty ι] (a : ι → α) (ha : BddBelow (range a)) :
- Tendsto (fun F : Finset ι => F.inf a) atTop (𝓝 (⨅ i, a i)) :=
- tendsto_finset_sup_ciSup (α := αᵒᵈ) ha
-
-theorem tendsto_finset_sup_iSup [CompleteLattice α] [SupConvergenceClass α] (a : ι → α) :
- Tendsto (fun F : Finset ι => F.sup a) atTop (𝓝 (⨆ i, a i)) := by
- have hmono : Monotone (fun F : Finset ι => F.sup a) := fun F G hFG => Finset.sup_mono hFG
- simpa [Finset.sup_eq_iSup, ← iSup_eq_iSup_finset a] using tendsto_atTop_iSup hmono
-
-theorem tendsto_finset_inf_iInf [CompleteLattice α] [InfConvergenceClass α] (a : ι → α) :
- Tendsto (fun F : Finset ι => F.inf a) atTop (𝓝 (⨅ i, a i)) := by
- have hanti : Antitone (fun F : Finset ι => F.inf a) := fun F G hFG => Finset.inf_mono hFG
- simpa [Finset.inf_eq_iInf, ← iInf_eq_iInf_finset a] using tendsto_atTop_iInf hanti
-
-end FinsetSupInf
-
instance Prod.supConvergenceClass
[Preorder α] [Preorder β] [TopologicalSpace α] [TopologicalSpace β]
[SupConvergenceClass α] [SupConvergenceClass β] : SupConvergenceClass (α × β) := by
From 8fa96c90eb6224406164e1882e032b3eb38457ec Mon Sep 17 00:00:00 2001
From: "Yongxi (Aaron) Lin" <97214596+CoolRmal@users.noreply.github.com>
Date: Sat, 13 Jun 2026 21:32:09 -0700
Subject: [PATCH 0021/1300] trivial
---
Mathlib/Order/ConditionallyCompleteLattice/Finset.lean | 1 +
1 file changed, 1 insertion(+)
diff --git a/Mathlib/Order/ConditionallyCompleteLattice/Finset.lean b/Mathlib/Order/ConditionallyCompleteLattice/Finset.lean
index 25fe2ab2403330..2836b4ae6b05aa 100644
--- a/Mathlib/Order/ConditionallyCompleteLattice/Finset.lean
+++ b/Mathlib/Order/ConditionallyCompleteLattice/Finset.lean
@@ -198,6 +198,7 @@ theorem ciSup_eq_ciSup_finset [OrderBot α] [Nonempty ι] {a : ι → α}
· exact ciSup_le fun F => Finset.sup_le fun i _ => le_ciSup ha i
end ConditionallyCompleteLattice
+
section CompleteLinearOrder
variable {α : Type*} [CompleteLinearOrder α] {ι : Sort*}
From 63d139d1c3ec90874fdcc14e7b0a261324cca0c1 Mon Sep 17 00:00:00 2001
From: Yongxi Lin
Date: Sat, 13 Jun 2026 21:33:45 -0700
Subject: [PATCH 0022/1300] move section
---
.../ConditionallyCompleteLattice/Finset.lean | 40 +++++++++----------
1 file changed, 20 insertions(+), 20 deletions(-)
diff --git a/Mathlib/Order/ConditionallyCompleteLattice/Finset.lean b/Mathlib/Order/ConditionallyCompleteLattice/Finset.lean
index 2836b4ae6b05aa..525824db98d3d9 100644
--- a/Mathlib/Order/ConditionallyCompleteLattice/Finset.lean
+++ b/Mathlib/Order/ConditionallyCompleteLattice/Finset.lean
@@ -21,6 +21,26 @@ open Set
variable {ι α β γ : Type*}
+section ConditionallyCompleteLattice
+
+variable [ConditionallyCompleteLattice α]
+
+/-- Supremum of `a i`, `i : ι`, is equal to the supremum over finite suprema of `a`. -/
+@[to_dual
+/-- Infimum of `a i`, `i : ι`, is equal to the infimum over finite infima of `a`. -/]
+theorem ciSup_eq_ciSup_finset [OrderBot α] [Nonempty ι] {a : ι → α}
+ (ha : BddAbove (range a)) :
+ ⨆ i, a i = ⨆ F : Finset ι, F.sup a := by
+ have hbdd : BddAbove (Set.range fun F : Finset ι => F.sup a) := by
+ refine ⟨⨆ i, a i, ?_⟩
+ rintro _ ⟨F, rfl⟩
+ exact Finset.sup_le fun i _ => le_ciSup ha i
+ refine le_antisymm ?_ ?_
+ · exact ciSup_le fun i => (Finset.le_sup (by simp)).trans (le_ciSup hbdd ({i} : Finset ι))
+ · exact ciSup_le fun F => Finset.sup_le fun i _ => le_ciSup ha i
+
+end ConditionallyCompleteLattice
+
section ConditionallyCompleteLinearOrder
variable [ConditionallyCompleteLinearOrder α] {s t : Set α} {a b : α}
@@ -179,26 +199,6 @@ end ListMultiset
end ConditionallyCompleteLinearOrder
-section ConditionallyCompleteLattice
-
-variable [ConditionallyCompleteLattice α]
-
-/-- Supremum of `a i`, `i : ι`, is equal to the supremum over finite suprema of `a`. -/
-@[to_dual
-/-- Infimum of `a i`, `i : ι`, is equal to the infimum over finite infima of `a`. -/]
-theorem ciSup_eq_ciSup_finset [OrderBot α] [Nonempty ι] {a : ι → α}
- (ha : BddAbove (range a)) :
- ⨆ i, a i = ⨆ F : Finset ι, F.sup a := by
- have hbdd : BddAbove (Set.range fun F : Finset ι => F.sup a) := by
- refine ⟨⨆ i, a i, ?_⟩
- rintro _ ⟨F, rfl⟩
- exact Finset.sup_le fun i _ => le_ciSup ha i
- refine le_antisymm ?_ ?_
- · exact ciSup_le fun i => (Finset.le_sup (by simp)).trans (le_ciSup hbdd ({i} : Finset ι))
- · exact ciSup_le fun F => Finset.sup_le fun i _ => le_ciSup ha i
-
-end ConditionallyCompleteLattice
-
section CompleteLinearOrder
variable {α : Type*} [CompleteLinearOrder α] {ι : Sort*}
From bf6f8bccc9ace85e38711a2cc0a10021b27c71dd Mon Sep 17 00:00:00 2001
From: Yongxi Lin
Date: Sat, 13 Jun 2026 21:34:11 -0700
Subject: [PATCH 0023/1300] trivial
---
Mathlib/Order/ConditionallyCompleteLattice/Finset.lean | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Mathlib/Order/ConditionallyCompleteLattice/Finset.lean b/Mathlib/Order/ConditionallyCompleteLattice/Finset.lean
index 525824db98d3d9..7d6f6da9bbd401 100644
--- a/Mathlib/Order/ConditionallyCompleteLattice/Finset.lean
+++ b/Mathlib/Order/ConditionallyCompleteLattice/Finset.lean
@@ -36,7 +36,7 @@ theorem ciSup_eq_ciSup_finset [OrderBot α] [Nonempty ι] {a : ι → α}
rintro _ ⟨F, rfl⟩
exact Finset.sup_le fun i _ => le_ciSup ha i
refine le_antisymm ?_ ?_
- · exact ciSup_le fun i => (Finset.le_sup (by simp)).trans (le_ciSup hbdd ({i} : Finset ι))
+ · exact ciSup_le fun i => (Finset.le_sup (by simp)).trans (le_ciSup hbdd {i})
· exact ciSup_le fun F => Finset.sup_le fun i _ => le_ciSup ha i
end ConditionallyCompleteLattice
From 0c9776382e5c018ed12647a6c0704296457c3fc5 Mon Sep 17 00:00:00 2001
From: dannyply <8302246+dannyply@users.noreply.github.com>
Date: Sun, 14 Jun 2026 04:43:55 +0000
Subject: [PATCH 0024/1300] feat(Topology/Algebra/PontryaginDual): prove
compact monoids have discrete duals (#38669)
Proves that the Pontryagin dual of a compact monoid is discrete. As a consequence, it also adds the corresponding finite-type instances for compact discrete monoids.
This upstreams a result first added downstream in [`YaelDillies/APAP`](https://github.com/YaelDillies/APAP), where this fact was needed to close a `sorry`. The proof separates the trivial character from all the others using the right half of the circle and adds a few reusable `Circle` helper lemmas near the existing related API.
The original APAP proof was AI-assisted and then reviewed/rewritten during downstream review. For this PR, I used Codex to help adapt the APAP proof to mathlib and to refactor the supporting lemmas.
---
.../SpecialFunctions/Complex/Circle.lean | 88 ++++++++++++++++++-
Mathlib/Topology/Algebra/PontryaginDual.lean | 61 +++++++++----
Mathlib/Topology/MetricSpace/Pseudo/Defs.lean | 12 +++
3 files changed, 141 insertions(+), 20 deletions(-)
diff --git a/Mathlib/Analysis/SpecialFunctions/Complex/Circle.lean b/Mathlib/Analysis/SpecialFunctions/Complex/Circle.lean
index b525d1a43dd6a6..33a581fdc10383 100644
--- a/Mathlib/Analysis/SpecialFunctions/Complex/Circle.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Complex/Circle.lean
@@ -34,6 +34,10 @@ theorem injective_arg : Injective fun z : Circle => arg z := fun z w h =>
theorem arg_eq_arg {z w : Circle} : arg z = arg w ↔ z = w :=
injective_arg.eq_iff
+@[simp]
+theorem arg_eq_zero {z : Circle} : arg z = 0 ↔ z = 1 := by
+ simpa using arg_eq_arg (w := 1)
+
theorem arg_exp {x : ℝ} (h₁ : -π < x) (h₂ : x ≤ π) : arg (exp x) = x := by
rw [coe_exp, exp_mul_I, arg_cos_add_sin_mul_I ⟨h₁, h₂⟩]
@@ -112,6 +116,54 @@ lemma exp_injOn_Ico {a b : ℝ} (h : b - a ≤ 2 * π) : InjOn exp (Ico a b) :=
lemma exp_injOn_Ioc {a b : ℝ} (h : b - a ≤ 2 * π) : InjOn exp (Ioc a b) :=
exp_injOn_of_forall_sub_mem_Ioo <| fun x ⟨hx1, hx2⟩ y ⟨hy1, hy2⟩ ↦ by constructor <;> linarith
+/-- The image under `Circle.exp` of the interval of angles `(-r, r)`. -/
+def centeredArc (r : ℝ) : Set Circle :=
+ exp '' {x | |x| < r}
+
+theorem bijOn_exp_Ioo_centeredArc {r : ℝ} (hr : r ≤ π) :
+ BijOn Circle.exp (Ioo (-r) r) (centeredArc r) := by
+ simp_rw [centeredArc, abs_lt, Set.Ioo_def]
+ refine exp_injOn_Ico ?_ |>.mono Ioo_subset_Ico_self |>.bijOn_image
+ grind
+
+theorem centeredArc_mono {r s : ℝ} (h : r ≤ s) : centeredArc r ⊆ centeredArc s := by
+ rintro _ ⟨x, hx, rfl⟩
+ exact ⟨x, hx.trans_le h, rfl⟩
+
+theorem mem_centeredArc {r : ℝ} (hr : r ≤ π) {z : Circle} :
+ z ∈ centeredArc r ↔ |arg z| < r := by
+ refine ⟨?_, fun hz ↦ ⟨arg z, hz, exp_arg z⟩⟩
+ rintro ⟨t, ht, rfl⟩
+ have htπ : |t| < π := ht.trans_le hr
+ rwa [arg_exp (neg_lt_of_abs_lt htπ) (lt_of_abs_lt htπ).le]
+
+theorem centeredArc_eq_empty {r : ℝ} (hr : r ≤ 0) : centeredArc r = ∅ := by
+ contrapose! hr
+ obtain ⟨-, x, hx, rfl⟩ := hr
+ exact (abs_nonneg x).trans_lt hx
+
+@[simp]
+theorem centeredArc_zero : centeredArc 0 = ∅ :=
+ centeredArc_eq_empty le_rfl
+
+theorem mem_centeredArc_div {z : Circle} {s : ℝ} {n : ℕ} (hs : s ≤ π)
+ (h1 : z ∈ centeredArc (π / n)) (h2 : z ^ n ∈ centeredArc s) :
+ z ∈ centeredArc (s / n) := by
+ have hs0 : 0 < s := by
+ contrapose! h2
+ simp [centeredArc_eq_empty h2]
+ have hn0 : n ≠ 0 := by
+ contrapose! h1
+ simp [h1]
+ have hn : 1 ≤ (n : ℝ) := by simpa [Nat.one_le_iff_ne_zero]
+ rw [mem_centeredArc ((div_le_self hs0.le hn).trans hs),
+ lt_div_iff₀' (one_pos.trans_le hn)]
+ rw [mem_centeredArc (div_le_self pi_nonneg hn)] at h1
+ rwa [mem_centeredArc hs, coe_pow, ← arg_coe_angle_toReal_eq_arg, arg_pow_coe_angle,
+ (Angle.nsmul_toReal_eq_mul hn0).mpr (mem_Ioc_of_Ioo ?_), abs_mul, Nat.abs_cast,
+ arg_coe_angle_toReal_eq_arg] at h2
+ rwa [neg_div, mem_Ioo, ← abs_lt, arg_coe_angle_toReal_eq_arg]
+
lemma exp_surjective : Surjective exp := fun z => ⟨z.val.arg, exp_arg z⟩
instance : PathConnectedSpace Circle := exp_surjective.pathConnectedSpace exp.continuous
@@ -403,7 +455,41 @@ theorem Circle.isCoveringMap_exp : IsCoveringMap exp := isAddQuotientCoveringMap
lemma isLocalHomeomorph_circleExp : IsLocalHomeomorph Circle.exp :=
Circle.isCoveringMap_exp.isLocalHomeomorph
-/-- TODO: this generalizes to a large class of groups, but requires an open mapping theorem for
+/-- The centered arcs `centeredArc (π / 2 ^ (n + 1))` form a neighborhood basis at `1`.
+This basis is useful because multiplying two sufficiently small arcs stays inside an earlier arc. -/
+theorem Circle.hasBasis_centeredArc_div_two_pow :
+ (nhds (1 : Circle)).HasBasis (fun _ ↦ True) (fun n ↦ centeredArc (π / 2 ^ (n + 1))) := by
+ rw [← Circle.exp_zero, ← isLocalHomeomorph_circleExp.map_nhds_eq 0]
+ simp_rw [centeredArc, abs_lt, Set.Ioo_def, ← Real.ball_zero_eq_Ioo]
+ apply Filter.HasBasis.map
+ refine nhds_basis_uniformity <| Metric.mk_uniformity_basis_of_tendsto (l := Filter.atTop)
+ (fun _ _ ↦ by positivity) (by simp) ?_
+ simp_rw [div_eq_mul_inv, pow_succ, mul_inv_rev, ← mul_assoc]
+ rw [← mul_zero (π * 2⁻¹)]
+ exact tendsto_inv_atTop_zero.comp (tendsto_pow_atTop_atTop_of_one_lt (by norm_num))
+ |>.const_mul _
+
+theorem Circle.isOpen_centeredArc (r : ℝ) : IsOpen (centeredArc r) := by
+ have hset : {x : ℝ | |x| < r} = Ioo (-r) r := by
+ ext x
+ simp [abs_lt]
+ simpa [centeredArc, hset] using
+ isLocalHomeomorph_circleExp.isOpenMap (Ioo (-r) r) isOpen_Ioo
+
+/-- If all positive powers of a point on the circle lie in the right half centered arc,
+then the point is `1`. -/
+theorem Circle.eq_one_of_forall_pow_mem_centeredArc_pi_div_two {z : Circle}
+ (hz : ∀ n > 0, z ^ n ∈ centeredArc (π / 2)) : z = 1 := by
+ have hz1 : z ∈ centeredArc (π / 2) := by simpa using hz 1
+ have h (n : ℕ) : z ∈ centeredArc (π / 2 ^ (n + 1)) := by
+ induction n with
+ | zero => simpa using hz1
+ | succ n ih =>
+ simpa [div_div, ← pow_succ'] using mem_centeredArc_div
+ (div_le_self pi_nonneg one_le_two) (by simpa) (hz (2 ^ (n + 1)) (by positivity))
+ simpa [h] using Set.ext_iff.mp hasBasis_centeredArc_div_two_pow.ker z
+
+/- TODO: this generalizes to a large class of groups, but requires an open mapping theorem for
topological groups to show the `n`th power map is open (see https://www.mathematik.tu-darmstadt.de/media/mathematik/forschung/preprint/preprints/2480.pdf
and https://www.math.uwaterloo.ca/~cgodsil/pdfs/topology/topgr.pdf), and discreteness of the
kernel (see https://gemini.google.com/share/6e9ab4abcb95). -/
diff --git a/Mathlib/Topology/Algebra/PontryaginDual.lean b/Mathlib/Topology/Algebra/PontryaginDual.lean
index 15a396ef4737e5..769f32e134617a 100644
--- a/Mathlib/Topology/Algebra/PontryaginDual.lean
+++ b/Mathlib/Topology/Algebra/PontryaginDual.lean
@@ -25,6 +25,7 @@ isomorphic to its double dual.
@[expose] public section
open scoped Pointwise
+open Real
variable (A B C G H : Type*) [Monoid A] [Monoid B] [Monoid C] [CommGroup G] [Group H]
[TopologicalSpace A] [TopologicalSpace B] [TopologicalSpace C]
@@ -38,13 +39,10 @@ def PontryaginDual :=
deriving TopologicalSpace
instance [LocallyCompactSpace H] : LocallyCompactSpace (PontryaginDual H) := by
- let Vn : ℕ → Set Circle :=
- fun n ↦ Circle.exp '' { x | |x| < Real.pi / 2 ^ (n + 1)}
- have hVn : ∀ n x, x ∈ Vn n ↔ |Complex.arg x| < Real.pi / 2 ^ (n + 1) := by
- refine fun n x ↦ ⟨?_, fun hx ↦ ⟨Complex.arg x, hx, Circle.exp_arg x⟩⟩
- rintro ⟨t, ht : |t| < _, rfl⟩
- have ht' := ht.trans_le (div_le_self Real.pi_nonneg (one_le_pow₀ one_le_two))
- rwa [Circle.arg_exp (neg_lt_of_abs_lt ht') (lt_of_abs_lt ht').le]
+ let Vn : ℕ → Set Circle := fun n ↦ Circle.centeredArc (π / 2 ^ (n + 1))
+ have hVn : ∀ n x, x ∈ Vn n ↔ |Complex.arg x| < π / 2 ^ (n + 1) :=
+ fun n x ↦ Circle.mem_centeredArc (z := x)
+ (div_le_self pi_nonneg (one_le_pow₀ one_le_two))
refine ContinuousMonoidHom.locallyCompactSpace_of_hasBasis Vn ?_ ?_
· intro n x h1 h2
rw [hVn] at h1 h2 ⊢
@@ -55,15 +53,7 @@ instance [LocallyCompactSpace H] : LocallyCompactSpace (PontryaginDual H) := by
refine h1.trans_le ?_
gcongr
exact le_self_pow₀ one_le_two n.succ_ne_zero
- · rw [← Circle.exp_zero, ← isLocalHomeomorph_circleExp.map_nhds_eq 0]
- refine ((nhds_basis_zero_abs_lt ℝ).to_hasBasis
- (fun x hx ↦ ⟨Nat.ceil (Real.pi / x), trivial, fun t ht ↦ ?_⟩)
- fun k _ ↦ ⟨Real.pi / 2 ^ (k + 1), by positivity, le_rfl⟩).map Circle.exp
- rw [Set.mem_setOf_eq] at ht ⊢
- refine lt_of_lt_of_le ht ?_
- rw [div_le_iff₀' (pow_pos two_pos _), ← div_le_iff₀ hx]
- refine (Nat.le_ceil (Real.pi / x)).trans ?_
- exact_mod_cast (Nat.le_succ _).trans Nat.lt_two_pow_self.le
+ · simpa [Vn] using Circle.hasBasis_centeredArc_div_two_pow
variable {A B C G}
@@ -83,9 +73,42 @@ deriving instance
[DiscreteTopology A] → CompactSpace _
for PontryaginDual A
+@[ext]
+theorem ext {ψ φ : PontryaginDual A} (h : ∀ a, ψ a = φ a) : ψ = φ :=
+ DFunLike.ext _ _ h
+
+@[simp]
+theorem one_apply (a : A) : (1 : PontryaginDual A) a = 1 :=
+ rfl
+
/-- A discrete monoid has compact Pontryagin dual. -/
add_decl_doc instLocallyCompactSpacePontryaginDual
+/-- A compact monoid has discrete Pontryagin dual. -/
+instance [CompactSpace A] : DiscreteTopology (PontryaginDual A) := by
+ let V : Set (PontryaginDual A) := {ψ | Set.MapsTo ψ Set.univ (Circle.centeredArc (π / 2))}
+ have hVopen : IsOpen V := by
+ dsimp only [V]
+ exact isOpen_induced (ContinuousMap.isOpen_setOf_mapsTo isCompact_univ
+ (Circle.isOpen_centeredArc (π / 2)))
+ have hVeq : V = ({1} : Set (PontryaginDual A)) := by
+ ext ψ
+ rw [Set.mem_singleton_iff]
+ refine ⟨fun hψ ↦ ?_, ?_⟩
+ · ext1 a
+ refine Circle.eq_one_of_forall_pow_mem_centeredArc_pi_div_two fun n hn ↦ ?_
+ simpa using hψ (Set.mem_univ (a ^ n))
+ · rintro rfl _ _
+ rw [Circle.mem_centeredArc (by linarith [pi_pos])]
+ simp [pi_pos]
+ exact discreteTopology_of_isOpen_singleton_one (by simpa [hVeq] using hVopen)
+
+instance [DiscreteTopology A] [CompactSpace A] : Finite (PontryaginDual A) :=
+ finite_of_compact_of_discrete
+
+noncomputable instance [DiscreteTopology A] [CompactSpace A] : Fintype (PontryaginDual A) :=
+ .ofFinite _
+
/-- `PontryaginDual` is a contravariant functor. -/
def map (f : A →ₜ* B) :
(PontryaginDual B) →ₜ* (PontryaginDual A) :=
@@ -98,16 +121,16 @@ theorem map_apply (f : A →ₜ* B) (x : PontryaginDual B) (y : A) :
@[simp]
theorem map_one : map (1 : A →ₜ* B) = 1 :=
- ext fun x => ext (fun _y => OneHomClass.map_one x)
+ ContinuousMonoidHom.ext fun x => PontryaginDual.ext fun _y => OneHomClass.map_one x
@[simp]
theorem map_comp (g : B →ₜ* C) (f : A →ₜ* B) :
map (comp g f) = ContinuousMonoidHom.comp (map f) (map g) :=
- ext fun _x => ext fun _y => rfl
+ ContinuousMonoidHom.ext fun _x => PontryaginDual.ext fun _y => rfl
@[simp]
nonrec theorem map_mul (f g : A →ₜ* G) : map (f * g) = map f * map g :=
- ext fun x => ext fun y => map_mul x (f y) (g y)
+ ContinuousMonoidHom.ext fun x => PontryaginDual.ext fun y => map_mul x (f y) (g y)
variable (A B C G)
diff --git a/Mathlib/Topology/MetricSpace/Pseudo/Defs.lean b/Mathlib/Topology/MetricSpace/Pseudo/Defs.lean
index e2d4122228196e..e3694f57974cec 100644
--- a/Mathlib/Topology/MetricSpace/Pseudo/Defs.lean
+++ b/Mathlib/Topology/MetricSpace/Pseudo/Defs.lean
@@ -1214,6 +1214,18 @@ namespace Metric
variable {x y z : α} {ε ε₁ ε₂ : ℝ} {s : Set α}
+/-- If `f` is a positive radius tending to zero, then the sets of pairs with distance less than
+`f i` form a basis of the uniformity. -/
+lemma mk_uniformity_basis_of_tendsto {β : Type*} {p : β → Prop} {f : β → ℝ}
+ {l : Filter β} [l.NeBot] (hf₀ : ∀ i, p i → 0 < f i) (hf₁ : ∀ᶠ i in l, p i)
+ (hf : Tendsto f l (𝓝 0)) :
+ (𝓤 α).HasBasis p fun i ↦ {x | dist x.1 x.2 < f i} := by
+ apply Metric.mk_uniformity_basis hf₀
+ rw [nhds_basis_closedBall.tendsto_right_iff] at hf
+ refine fun ε hε ↦ hf₁.and (hf ε hε) |>.exists.imp fun i ↦ and_imp.mpr fun hp hi ↦ ?_
+ exact ⟨hp, by
+ simpa [Metric.mem_closedBall, Real.dist_eq, abs_of_nonneg (hf₀ i hp).le] using hi⟩
+
theorem ball_subset_interior_closedBall : ball x ε ⊆ interior (closedBall x ε) :=
interior_maximal ball_subset_closedBall isOpen_ball
From 76021236640b1d116a5d6007245ef520601e502a Mon Sep 17 00:00:00 2001
From: Yongxi Lin
Date: Sat, 13 Jun 2026 21:58:22 -0700
Subject: [PATCH 0025/1300] Fix generated ciInf theorem name
---
Mathlib/Order/ConditionallyCompleteLattice/Indexed.lean | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Mathlib/Order/ConditionallyCompleteLattice/Indexed.lean b/Mathlib/Order/ConditionallyCompleteLattice/Indexed.lean
index 558f50fc38df58..217a256864676b 100644
--- a/Mathlib/Order/ConditionallyCompleteLattice/Indexed.lean
+++ b/Mathlib/Order/ConditionallyCompleteLattice/Indexed.lean
@@ -324,7 +324,7 @@ theorem ciInf_inf_le {f g : ι → α} : (⨅ x, f x) ⊓ (⨅ x, g x) ≤ ⨅ x
/-- Indexed version of `exists_lt_of_lt_csSup`.
When `b < iSup f`, there is an element `i` such that `b < f i`.
-/
-@[to_dual
+@[to_dual exists_lt_of_ciInf_lt
/-- Indexed version of `exists_lt_of_csInf_lt`.
When `iInf f < a`, there is an element `i` such that `f i < a`.
-/]
From e59d9d4058be0d9de5d54a30cd775901580a01e0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ya=C3=ABl=20Dillies?=
Date: Sun, 14 Jun 2026 07:12:20 +0000
Subject: [PATCH 0026/1300] feat(Algebra/BigOperators/Expect): expectation of
an indicator (#40388)
and replace the generic variable `M` with `K` in the `Semifield` section
From MeanFourier
---
Mathlib/Algebra/BigOperators/Expect.lean | 26 +++++++++++++++---------
1 file changed, 16 insertions(+), 10 deletions(-)
diff --git a/Mathlib/Algebra/BigOperators/Expect.lean b/Mathlib/Algebra/BigOperators/Expect.lean
index 155aae5dc8a9af..1c0505e075c2ad 100644
--- a/Mathlib/Algebra/BigOperators/Expect.lean
+++ b/Mathlib/Algebra/BigOperators/Expect.lean
@@ -14,6 +14,8 @@ public import Mathlib.Data.Finset.Density
public import Mathlib.Data.Fintype.BigOperators
public import Mathlib.Algebra.Group.Pointwise.Finset.Basic
+import Mathlib.Algebra.BigOperators.Group.Finset.Indicator
+
/-!
# Average over a finset
@@ -51,7 +53,7 @@ open Finset Function
open Fintype (card)
open scoped Pointwise
-variable {ι κ M N : Type*}
+variable {ι κ K M N : Type*}
local notation a " /ℚ " q => (q : ℚ≥0)⁻¹ • a
@@ -350,26 +352,30 @@ lemma expect_pow (s : Finset ι) (f : ι → M) (n : ℕ) :
end CommSemiring
section Semifield
-variable [Semifield M] [CharZero M]
+variable [Semifield K] [CharZero K]
+
+@[simp] lemma expect_indicator_one [Fintype ι] (s : Finset ι) :
+ 𝔼 i : ι, (Set.indicator s 1 i : K) = s.dens := by
+ classical simp [expect, sum_indicator_eq_sum_inter, dens, div_eq_inv_mul, NNRat.smul_def]
-lemma expect_boole_mul [Fintype ι] [Nonempty ι] [DecidableEq ι] (f : ι → M) (i : ι) :
- 𝔼 j, ite (i = j) (Fintype.card ι : M) 0 * f j = f i := by
+lemma expect_boole_mul [Fintype ι] [Nonempty ι] [DecidableEq ι] (f : ι → K) (i : ι) :
+ 𝔼 j, ite (i = j) (Fintype.card ι : K) 0 * f j = f i := by
simp_rw [expect_univ, ite_mul, zero_mul, sum_ite_eq, if_pos (mem_univ _)]
- rw [← @NNRat.cast_natCast M, ← NNRat.smul_def, inv_smul_smul₀]
+ rw [← @NNRat.cast_natCast K, ← NNRat.smul_def, inv_smul_smul₀]
simp [Fintype.card_ne_zero]
-lemma expect_boole_mul' [Fintype ι] [Nonempty ι] [DecidableEq ι] (f : ι → M) (i : ι) :
- 𝔼 j, ite (j = i) (Fintype.card ι : M) 0 * f j = f i := by
+lemma expect_boole_mul' [Fintype ι] [Nonempty ι] [DecidableEq ι] (f : ι → K) (i : ι) :
+ 𝔼 j, ite (j = i) (Fintype.card ι : K) 0 * f j = f i := by
simp_rw [@eq_comm _ _ i, expect_boole_mul]
-lemma expect_eq_sum_div_card (s : Finset ι) (f : ι → M) :
+lemma expect_eq_sum_div_card (s : Finset ι) (f : ι → K) :
𝔼 i ∈ s, f i = (∑ i ∈ s, f i) / #s := by
rw [expect, NNRat.smul_def, div_eq_inv_mul, NNRat.cast_inv, NNRat.cast_natCast]
-lemma _root_.Fintype.expect_eq_sum_div_card [Fintype ι] (f : ι → M) :
+lemma _root_.Fintype.expect_eq_sum_div_card [Fintype ι] (f : ι → K) :
𝔼 i, f i = (∑ i, f i) / Fintype.card ι := Finset.expect_eq_sum_div_card _ _
-lemma expect_div (s : Finset ι) (f : ι → M) (a : M) : (𝔼 i ∈ s, f i) / a = 𝔼 i ∈ s, f i / a := by
+lemma expect_div (s : Finset ι) (f : ι → K) (a : K) : (𝔼 i ∈ s, f i) / a = 𝔼 i ∈ s, f i / a := by
simp_rw [div_eq_mul_inv, expect_mul]
end Semifield
From e80d076d5230771e8dd8e3d1907fb1af9ffd3a85 Mon Sep 17 00:00:00 2001
From: "mathlib-update-dependencies[bot]"
<258990618+mathlib-update-dependencies[bot]@users.noreply.github.com>
Date: Sun, 14 Jun 2026 08:29:24 +0000
Subject: [PATCH 0027/1300] chore: update Mathlib dependencies 2026-06-14
(#40589)
This PR updates the Mathlib dependencies.
---
lake-manifest.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lake-manifest.json b/lake-manifest.json
index a46005d646bbed..eafa08243a29de 100644
--- a/lake-manifest.json
+++ b/lake-manifest.json
@@ -65,7 +65,7 @@
"type": "git",
"subDir": null,
"scope": "leanprover-community",
- "rev": "5dd219c775e402f818b42cd3997b5cf21017babf",
+ "rev": "c43d7789ff29244c1f6f7c8480342a2d2d6c0d30",
"name": "batteries",
"manifestFile": "lake-manifest.json",
"inputRev": "main",
From 4fcc8d62a41f9bb65a78d97d3a8d9017ad165602 Mon Sep 17 00:00:00 2001
From: Laurance <60111599+LLaurance@users.noreply.github.com>
Date: Sun, 14 Jun 2026 08:55:00 +0000
Subject: [PATCH 0028/1300] chore: replace refine with intro (#40581)
Replace `refine` with `intro` where the purpose is to solely `intro` variables.
---
Mathlib/Analysis/Convex/Quasiconvex.lean | 2 +-
Mathlib/CategoryTheory/Sites/PrecoverageToGrothendieck.lean | 2 +-
Mathlib/Data/DFinsupp/Interval.lean | 2 +-
Mathlib/Data/Nat/Totient.lean | 2 +-
Mathlib/Data/Set/Subsingleton.lean | 2 +-
Mathlib/LinearAlgebra/AffineSpace/Independent.lean | 2 +-
Mathlib/LinearAlgebra/CliffordAlgebra/Basic.lean | 2 +-
Mathlib/MeasureTheory/Measure/Restrict.lean | 2 +-
Mathlib/ModelTheory/Satisfiability.lean | 2 +-
Mathlib/NumberTheory/LegendreSymbol/AddCharacter.lean | 2 +-
Mathlib/RingTheory/Algebraic/StronglyTranscendental.lean | 4 ++--
Mathlib/RingTheory/Polynomial/Basic.lean | 4 ++--
Mathlib/Topology/MetricSpace/HolderNorm.lean | 2 +-
13 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/Mathlib/Analysis/Convex/Quasiconvex.lean b/Mathlib/Analysis/Convex/Quasiconvex.lean
index 88136225ccbd97..2f6e3f05c870da 100644
--- a/Mathlib/Analysis/Convex/Quasiconvex.lean
+++ b/Mathlib/Analysis/Convex/Quasiconvex.lean
@@ -221,7 +221,7 @@ theorem quasilinearOn_iff_mem_uIcc : QuasilinearOn 𝕜 s f ↔ Convex 𝕜 s
theorem QuasiconvexOn.convex_lt (hf : QuasiconvexOn 𝕜 s f) (r : β) :
Convex 𝕜 ({ x ∈ s | f x < r }) := by
- refine fun x hx y hy a b ha hb hab => ?_
+ intro x hx y hy a b ha hb hab
have h := hf _ ⟨hx.1, le_max_left _ _⟩ ⟨hy.1, le_max_right _ _⟩ ha hb hab
exact ⟨h.1, h.2.trans_lt <| max_lt hx.2 hy.2⟩
diff --git a/Mathlib/CategoryTheory/Sites/PrecoverageToGrothendieck.lean b/Mathlib/CategoryTheory/Sites/PrecoverageToGrothendieck.lean
index e1c3ba1fce316f..9ae4abb0b79848 100644
--- a/Mathlib/CategoryTheory/Sites/PrecoverageToGrothendieck.lean
+++ b/Mathlib/CategoryTheory/Sites/PrecoverageToGrothendieck.lean
@@ -100,7 +100,7 @@ theorem isSheaf_toGrothendieck_iff (P : Cᵒᵖ ⥤ Type*) :
(∀ {X Y : C} {f : Y ⟶ X} (R : Presieve X), R ∈ J X →
Presieve.IsSheafFor P ((Sieve.generate R).pullback f).arrows) := by
constructor
- · refine fun H _ _ _ _ hR => ?_
+ · intro H _ _ _ _ hR
apply H.isSheafFor
rw [Sieve.generate_sieve]
exact J.toGrothendieck.pullback_stable _ (Saturate.of _ _ hR)
diff --git a/Mathlib/Data/DFinsupp/Interval.lean b/Mathlib/Data/DFinsupp/Interval.lean
index fb5a449a44609f..4559962fd6af73 100644
--- a/Mathlib/Data/DFinsupp/Interval.lean
+++ b/Mathlib/Data/DFinsupp/Interval.lean
@@ -114,7 +114,7 @@ theorem mem_rangeIcc_apply_iff : a ∈ f.rangeIcc g i ↔ f i ≤ a ∧ a ≤ g
theorem support_rangeIcc_subset [DecidableEq ι] [∀ i, DecidableEq (α i)] :
(f.rangeIcc g).support ⊆ f.support ∪ g.support := by
- refine fun x hx => ?_
+ intro x hx
by_contra h
refine notMem_support_iff.2 ?_ hx
rw [rangeIcc_apply, notMem_support_iff.1 (notMem_mono subset_union_left h),
diff --git a/Mathlib/Data/Nat/Totient.lean b/Mathlib/Data/Nat/Totient.lean
index b49ccd4787777e..9f59ec38d113ca 100644
--- a/Mathlib/Data/Nat/Totient.lean
+++ b/Mathlib/Data/Nat/Totient.lean
@@ -152,7 +152,7 @@ theorem totient_div_of_dvd {n d : ℕ} (hnd : d ∣ n) :
rw [gcd_mul_left, ha2, mul_one]
· simp [hd0.ne']
· simp only [mem_filter, mem_range, exists_prop, and_imp]
- refine fun b hb1 hb2 => ?_
+ intro b hb1 hb2
have : d ∣ b := by
rw [← hb2]
apply gcd_dvd_right
diff --git a/Mathlib/Data/Set/Subsingleton.lean b/Mathlib/Data/Set/Subsingleton.lean
index b66554094adb13..948c01f09b8c53 100644
--- a/Mathlib/Data/Set/Subsingleton.lean
+++ b/Mathlib/Data/Set/Subsingleton.lean
@@ -121,7 +121,7 @@ theorem eq_empty_or_singleton_of_unique [Unique α] (s : Set α) :
@[simp, norm_cast]
theorem subsingleton_coe (s : Set α) : Subsingleton s ↔ s.Subsingleton := by
constructor
- · refine fun h => fun a ha b hb => ?_
+ · intro h a ha b hb
exact SetCoe.ext_iff.2 (@Subsingleton.elim s h ⟨a, ha⟩ ⟨b, hb⟩)
· exact fun h => Subsingleton.intro fun a b => SetCoe.ext (h a.property b.property)
diff --git a/Mathlib/LinearAlgebra/AffineSpace/Independent.lean b/Mathlib/LinearAlgebra/AffineSpace/Independent.lean
index e751e95544cebe..77f39139c1b99f 100644
--- a/Mathlib/LinearAlgebra/AffineSpace/Independent.lean
+++ b/Mathlib/LinearAlgebra/AffineSpace/Independent.lean
@@ -115,7 +115,7 @@ theorem affineIndependent_iff_linearIndependent_vsub (p : ι → P) (i1 : ι) :
set g2 : { x // x ≠ i1 } → V := fun x => g x • (p x -ᵥ p i1)
have hf2g2 : ∀ x : { x // x ≠ i1 }, f2 x = g2 x := by
simp only [g2, hf2def]
- refine fun x => ?_
+ intro x
rw [hfg]
rw [Finset.weightedVSub_eq_weightedVSubOfPoint_of_sum_eq_zero s2 f p hf (p i1),
Finset.weightedVSubOfPoint_insert, Finset.weightedVSubOfPoint_apply,
diff --git a/Mathlib/LinearAlgebra/CliffordAlgebra/Basic.lean b/Mathlib/LinearAlgebra/CliffordAlgebra/Basic.lean
index 51f80547faaf2a..b07967f77a8080 100644
--- a/Mathlib/LinearAlgebra/CliffordAlgebra/Basic.lean
+++ b/Mathlib/LinearAlgebra/CliffordAlgebra/Basic.lean
@@ -347,7 +347,7 @@ a linear retraction `g` that also preserves the quadratic forms, then `CliffordA
is a retraction of `CliffordAlgebra.map f`. -/
lemma leftInverse_map_of_leftInverse {Q₁ : QuadraticForm R M₁} {Q₂ : QuadraticForm R M₂}
(f : Q₁ →qᵢ Q₂) (g : Q₂ →qᵢ Q₁) (h : LeftInverse g f) : LeftInverse (map g) (map f) := by
- refine fun x => ?_
+ intro x
replace h : g.comp f = QuadraticMap.Isometry.id Q₁ := DFunLike.ext _ _ h
rw [← AlgHom.comp_apply, map_comp_map, h, map_id, AlgHom.coe_id, id_eq]
diff --git a/Mathlib/MeasureTheory/Measure/Restrict.lean b/Mathlib/MeasureTheory/Measure/Restrict.lean
index a70d88a3a69a1b..7d145ac05dc563 100644
--- a/Mathlib/MeasureTheory/Measure/Restrict.lean
+++ b/Mathlib/MeasureTheory/Measure/Restrict.lean
@@ -128,7 +128,7 @@ theorem _root_.IsCountablySpanning.null_of_forall_restrict_null {C : Set (Set α
(hC : IsCountablySpanning C) (hm : C ⊆ MeasurableSet) (ht : ∀ t ∈ C, μ.restrict t s = 0) :
μ s = 0 := by
rw [← forall_measure_inter_isCountablySpanning_eq_zero hC]
- refine fun t htc => ?_
+ intro t htc
simpa [← μ.restrict_apply' (hm htc)] using ht t htc
theorem restrict_apply₀' (hs : NullMeasurableSet s μ) : μ.restrict s t = μ (t ∩ s) := by
diff --git a/Mathlib/ModelTheory/Satisfiability.lean b/Mathlib/ModelTheory/Satisfiability.lean
index f0ff75d836b059..ff95887b9bc350 100644
--- a/Mathlib/ModelTheory/Satisfiability.lean
+++ b/Mathlib/ModelTheory/Satisfiability.lean
@@ -136,7 +136,7 @@ theorem isSatisfiable_union_distinctConstantsTheory_of_card_le (T : L.Theory) (s
have : M ⊨ (L.lhomWithConstants α).onTheory T ∪ L.distinctConstantsTheory s := by
refine ((LHom.onTheory_model _ _).2 inferInstance).union ?_
rw [model_distinctConstantsTheory]
- refine fun a as b bs ab => ?_
+ intro a as b bs ab
rw [← Subtype.coe_mk a as, ← Subtype.coe_mk b bs, ← Subtype.ext_iff]
exact
h.some.injective
diff --git a/Mathlib/NumberTheory/LegendreSymbol/AddCharacter.lean b/Mathlib/NumberTheory/LegendreSymbol/AddCharacter.lean
index 851328dcaa1ba0..9c8589c4e800b9 100644
--- a/Mathlib/NumberTheory/LegendreSymbol/AddCharacter.lean
+++ b/Mathlib/NumberTheory/LegendreSymbol/AddCharacter.lean
@@ -173,7 +173,7 @@ theorem IsPrimitive.zmod_char_eq_one_iff (n : ℕ) [NeZero n]
then it is primitive. -/
theorem zmod_char_primitive_of_eq_one_only_at_zero (n : ℕ) (ψ : AddChar (ZMod n) C)
(hψ : ∀ a, ψ a = 1 → a = 0) : IsPrimitive ψ := by
- refine fun a ha hf => ?_
+ intro a ha hf
have h : mulShift ψ a 1 = (1 : AddChar (ZMod n) C) (1 : ZMod n) :=
congr_fun (congr_arg (↑) hf) 1
rw [mulShift_apply, mul_one] at h; norm_cast at h
diff --git a/Mathlib/RingTheory/Algebraic/StronglyTranscendental.lean b/Mathlib/RingTheory/Algebraic/StronglyTranscendental.lean
index 14f6763901e8c3..a185e2c338883e 100644
--- a/Mathlib/RingTheory/Algebraic/StronglyTranscendental.lean
+++ b/Mathlib/RingTheory/Algebraic/StronglyTranscendental.lean
@@ -51,7 +51,7 @@ lemma isStronglyTranscendental_iff_of_field {K : Type*} [Field K] [Algebra R K]
lemma IsStronglyTranscendental.of_map {x : S} {f : S →ₐ[R] T} (hf : Function.Injective f)
(h : IsStronglyTranscendental R (f x)) :
IsStronglyTranscendental R x := by
- refine fun u p hp ↦ ?_
+ intro u p hp
have := h (f u) p (by rw [aeval_algHom_apply, ← map_mul, hp, map_zero])
rwa [← f.comp_algebraMap, ← map_map, ← f.coe_toRingHom, ← map_C, ← Polynomial.map_mul,
← coe_mapRingHom, map_eq_zero_iff] at this
@@ -61,7 +61,7 @@ lemma IsStronglyTranscendental.of_isLocalization [Algebra S T] (M : Submonoid S)
[IsLocalization M T] [IsScalarTower R S T]
{x : S} (h : IsStronglyTranscendental R x) :
IsStronglyTranscendental R (algebraMap S T x) := by
- refine fun u p hp ↦ ?_
+ intro u p hp
obtain ⟨u, s, rfl⟩ := IsLocalization.exists_mk'_eq M u
obtain ⟨a, haM, e⟩ : ∃ a ∈ M, a * ((aeval x) p * u) = 0 := by
simpa [aeval_algebraMap_apply, ← Algebra.smul_def, IsLocalization.smul_mk',
diff --git a/Mathlib/RingTheory/Polynomial/Basic.lean b/Mathlib/RingTheory/Polynomial/Basic.lean
index 8bc610e192f05e..c186c48bb63ee7 100644
--- a/Mathlib/RingTheory/Polynomial/Basic.lean
+++ b/Mathlib/RingTheory/Polynomial/Basic.lean
@@ -424,7 +424,7 @@ theorem mem_map_C_iff {I : Ideal R} {f : R[X]} :
· simp [h]
· simp
· exact fun f g _ _ hf hg n => by simp [I.add_mem (hf n) (hg n)]
- · refine fun f g _ hg n => ?_
+ · intro f g _ hg n
rw [smul_eq_mul, coeff_mul]
exact I.sum_mem fun c _ => I.mul_mem_left (f.coeff c.fst) (hg c.snd)
· intro hf
@@ -1009,7 +1009,7 @@ theorem mem_map_C_iff {I : Ideal R} {f : MvPolynomial σ R} :
· simp [Ne.symm h]
· simp
· exact fun f g _ _ hf hg n => by simp [I.add_mem (hf n) (hg n)]
- · refine fun f g _ hg n => ?_
+ · intro f g _ hg n
rw [smul_eq_mul, coeff_mul]
exact I.sum_mem fun c _ => I.mul_mem_left (f.coeff c.fst) (hg c.snd)
· intro hf
diff --git a/Mathlib/Topology/MetricSpace/HolderNorm.lean b/Mathlib/Topology/MetricSpace/HolderNorm.lean
index 38814b0dc9fda0..966b5a9b538ed3 100644
--- a/Mathlib/Topology/MetricSpace/HolderNorm.lean
+++ b/Mathlib/Topology/MetricSpace/HolderNorm.lean
@@ -202,7 +202,7 @@ variable [MetricSpace X] [EMetricSpace Y]
lemma eHolderNorm_eq_zero {r : ℝ≥0} {f : X → Y} :
eHolderNorm r f = 0 ↔ ∀ x₁ x₂, f x₁ = f x₂ := by
constructor
- · refine fun h x₁ x₂ => ?_
+ · intro h x₁ x₂
by_cases hx : x₁ = x₂
· rw [hx]
· rw [eHolderNorm, ← ENNReal.bot_eq_zero, iInf₂_eq_bot] at h
From 6923f2f17585e9f2ef76e10ad91efe1b9cb8500d Mon Sep 17 00:00:00 2001
From: Albert Smith <10266947+ChiCubed@users.noreply.github.com>
Date: Sun, 14 Jun 2026 09:14:23 +0000
Subject: [PATCH 0029/1300] feat(NumberTheory/Padics/PadicVal): remove
redundant hypotheses + add `padicValRat.zpow` (#40558)
We remove redundant hypotheses q != 0 on pow / inv theorems (which can now be simp), and add the missing theorem `padicValRat.zpow`
---
.../NumberTheory/Padics/PadicVal/Basic.lean | 29 ++++++++++++-------
1 file changed, 18 insertions(+), 11 deletions(-)
diff --git a/Mathlib/NumberTheory/Padics/PadicVal/Basic.lean b/Mathlib/NumberTheory/Padics/PadicVal/Basic.lean
index 18333fad51b625..80b6ae2ff73f00 100644
--- a/Mathlib/NumberTheory/Padics/PadicVal/Basic.lean
+++ b/Mathlib/NumberTheory/Padics/PadicVal/Basic.lean
@@ -237,19 +237,28 @@ protected theorem mul {q r : ℚ} (hq : q ≠ 0) (hr : r ≠ 0) :
· simp [finite_int_prime_iff]
· simp [finite_int_prime_iff, hq, hr]
-/-- A rewrite lemma for `padicValRat p (q^k)` with condition `q ≠ 0`. -/
-protected theorem pow {q : ℚ} (hq : q ≠ 0) {k : ℕ} :
+/-- A rewrite lemma for `padicValRat p (q^k)`. -/
+@[simp]
+protected theorem pow (q : ℚ) {k : ℕ} :
padicValRat p (q ^ k) = k * padicValRat p q := by
+ obtain rfl | hq := eq_or_ne q 0
+ · cases k <;> simp
induction k <;>
simp [*, padicValRat.mul hq (pow_ne_zero _ hq), _root_.pow_succ', add_mul, add_comm]
-/-- A rewrite lemma for `padicValRat p (q⁻¹)` with condition `q ≠ 0`. -/
+/-- A rewrite lemma for `padicValRat p (q⁻¹)`. -/
+@[simp]
protected theorem inv (q : ℚ) : padicValRat p q⁻¹ = -padicValRat p q := by
by_cases hq : q = 0
· simp [hq]
· rw [eq_neg_iff_add_eq_zero, ← padicValRat.mul (inv_ne_zero hq) hq, inv_mul_cancel₀ hq,
padicValRat.one]
+@[simp]
+protected theorem zpow (q : ℚ) {k : ℤ} :
+ padicValRat p (q ^ k) = k * padicValRat p q := by
+ induction k using Int.negInduction <;> simp
+
/-- A rewrite lemma for `padicValRat p (q / r)` with conditions `q ≠ 0`, `r ≠ 0`. -/
protected theorem div {q r : ℚ} (hq : q ≠ 0) (hr : r ≠ 0) :
padicValRat p (q / r) = padicValRat p q - padicValRat p r := by
@@ -329,10 +338,8 @@ lemma lt_add_of_lt {q r₁ r₂ : ℚ} (hqr : r₁ + r₂ ≠ 0)
padicValRat p q < padicValRat p (r₁ + r₂) :=
lt_of_lt_of_le (lt_min hval₁ hval₂) (padicValRat.min_le_padicValRat_add hqr)
-@[simp]
lemma self_pow_inv (r : ℕ) : padicValRat p ((p : ℚ) ^ r)⁻¹ = -r := by
- rw [padicValRat.inv, neg_inj, padicValRat.pow (Nat.cast_ne_zero.mpr hp.elim.ne_zero),
- padicValRat.self hp.elim.one_lt, mul_one]
+ rw [padicValRat.inv, neg_inj, padicValRat.pow p, padicValRat.self hp.elim.one_lt, mul_one]
/-- A finite sum of rationals with positive `p`-adic valuation has positive `p`-adic valuation
(if the sum is non-zero). -/
@@ -388,12 +395,12 @@ protected theorem div (dvd : p ∣ b) : padicValNat p (b / p) = padicValNat p b
rw [padicValNat.div_of_dvd dvd, padicValNat_self]
/-- A version of `padicValRat.pow` for `padicValNat`. -/
-protected theorem pow (n : ℕ) (ha : a ≠ 0) : padicValNat p (a ^ n) = n * padicValNat p a := by
- simpa only [← @Nat.cast_inj ℤ, push_cast] using padicValRat.pow (Nat.cast_ne_zero.mpr ha)
-
@[simp]
+protected theorem pow (a n : ℕ) : padicValNat p (a ^ n) = n * padicValNat p a := by
+ simpa only [← @Nat.cast_inj ℤ, push_cast] using padicValRat.pow a
+
protected theorem prime_pow (n : ℕ) : padicValNat p (p ^ n) = n := by
- rw [padicValNat.pow _ (@Fact.out p.Prime).ne_zero, padicValNat_self, mul_one]
+ rw [padicValNat.pow p, padicValNat_self, mul_one]
protected theorem div_pow (dvd : p ^ a ∣ b) : padicValNat p (b / p ^ a) = padicValNat p b - a := by
rw [padicValNat.div_of_dvd dvd, padicValNat.prime_pow]
@@ -444,7 +451,7 @@ theorem padicValNat_primes {q : ℕ} [hp : Fact p.Prime] [hq : Fact q.Prime] (ne
theorem padicValNat_prime_prime_pow {q : ℕ} [hp : Fact p.Prime] [hq : Fact q.Prime]
(n : ℕ) (ne : p ≠ q) : padicValNat p (q ^ n) = 0 := by
- rw [padicValNat.pow _ <| Nat.Prime.ne_zero hq.elim, padicValNat_primes ne, mul_zero]
+ rw [padicValNat.pow _, padicValNat_primes ne, mul_zero]
theorem padicValNat_mul_pow_left {q : ℕ} [hp : Fact p.Prime] [hq : Fact q.Prime]
(n m : ℕ) (ne : p ≠ q) : padicValNat p (p ^ n * q ^ m) = n := by
From 271d273e99cbdbeae1cfce4cee1a0eaa1736bbcd Mon Sep 17 00:00:00 2001
From: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com>
Date: Sun, 14 Jun 2026 19:00:39 +0000
Subject: [PATCH 0030/1300] feat(Combinatorics/SimpleGraph/Basic): more
`neighborSet` and `IsIsolated` lemmas (#38747)
---
Mathlib/Combinatorics/SimpleGraph/Basic.lean | 65 +++++++++++++++++++
Mathlib/Combinatorics/SimpleGraph/Finite.lean | 17 ++---
Mathlib/Combinatorics/SimpleGraph/Maps.lean | 5 --
.../SimpleGraph/StronglyRegular.lean | 2 +-
4 files changed, 72 insertions(+), 17 deletions(-)
diff --git a/Mathlib/Combinatorics/SimpleGraph/Basic.lean b/Mathlib/Combinatorics/SimpleGraph/Basic.lean
index 78c9d1e7668b4c..8ca659a86b0a21 100644
--- a/Mathlib/Combinatorics/SimpleGraph/Basic.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/Basic.lean
@@ -749,6 +749,18 @@ theorem mem_neighborSet (v w : V) : w ∈ G.neighborSet v ↔ G.Adj v w :=
lemma notMem_neighborSet_self : a ∉ G.neighborSet a := by simp
+variable {G} in
+theorem nonempty_neighborSet : (G.neighborSet v).Nonempty ↔ ∃ u, G.Adj v u :=
+ .rfl
+
+variable (v) in
+theorem neighborSet_subset_compl : G.neighborSet v ⊆ {v}ᶜ := by
+ simp
+
+variable (v) in
+theorem neighborSet_ne_univ : G.neighborSet v ≠ .univ :=
+ Set.ne_univ_iff_exists_notMem _ |>.mpr ⟨v, G.notMem_neighborSet_self⟩
+
@[simp]
theorem mem_incidenceSet (v w : V) : s(v, w) ∈ G.incidenceSet v ↔ G.Adj v w := by
simp [incidenceSet]
@@ -792,6 +804,23 @@ theorem neighborSet_compl (G : SimpleGraph V) (v : V) :
ext w
simp [and_comm, eq_comm]
+variable {G} in
+@[gcongr]
+theorem neighborSet_mono {G' : SimpleGraph V} (hle : G ≤ G') (v : V) :
+ G.neighborSet v ⊆ G'.neighborSet v :=
+ fun _ hadj ↦ hle hadj
+
+@[simp]
+theorem neighborSet_top : neighborSet ⊤ v = {v}ᶜ := by
+ grind [mem_neighborSet, top_adj]
+
+theorem neighborSet_bot : neighborSet ⊥ v = ∅ := by
+ grind [mem_neighborSet, bot_adj]
+
+variable {G} in
+theorem Adj.nontrivial (hadj : G.Adj u v) : Nontrivial V :=
+ ⟨u, v, hadj.ne⟩
+
/-- The set of common neighbors between two vertices `v` and `w` in a graph `G` is the
intersection of the neighbor sets of `v` and `w`. -/
def commonNeighbors (v w : V) : Set V :=
@@ -829,6 +858,10 @@ theorem commonNeighbors_top_eq {v w : V} :
ext u
simp [commonNeighbors, eq_comm, not_or]
+@[simp]
+theorem commonNeighbors_bot_eq : commonNeighbors ⊥ u v = ∅ := by
+ simp [commonNeighbors, neighborSet_bot]
+
section Incidence
variable [DecidableEq V]
@@ -916,4 +949,36 @@ attribute [simp] IsIsolated.neighborSet_eq_empty
lemma mem_support_iff_not_isIsolated : v ∈ G.support ↔ ¬ G.IsIsolated v := by
simp [mem_support, IsIsolated]
+@[simp]
+theorem notMem_support_iff_isIsolated : v ∉ G.support ↔ G.IsIsolated v := by
+ simp [mem_support_iff_not_isIsolated]
+
+variable {G} in
+theorem exists_adj_iff_not_isIsolated : (∃ u, G.Adj v u) ↔ ¬G.IsIsolated v := by
+ simp [IsIsolated]
+
+@[simp]
+theorem IsIsolated.of_subsingleton [Subsingleton V] (G : SimpleGraph V) (v : V) :
+ G.IsIsolated v :=
+ fun _ hadj ↦ not_nontrivial V hadj.nontrivial
+
+variable {G} in
+theorem nontrivial_of_not_isIsolated (h : ¬G.IsIsolated v) : Nontrivial V :=
+ exists_adj_iff_not_isIsolated.mpr h |>.elim fun _ ↦ Adj.nontrivial
+
+variable {G} in
+theorem Adj.not_isIsolated_left (h : G.Adj u v) : ¬G.IsIsolated u :=
+ exists_adj_iff_not_isIsolated.mp ⟨_, h⟩
+
+variable {G} in
+theorem Adj.not_isIsolated_right (h : G.Adj u v) : ¬G.IsIsolated v :=
+ h.symm.not_isIsolated_left
+
+@[simp]
+theorem isIsolated_bot : IsIsolated ⊥ v :=
+ neighborSet_eq_empty _ |>.mp neighborSet_bot
+
+theorem eq_bot_iff_isIsolated : G = ⊥ ↔ ∀ v, G.IsIsolated v := by
+ simp [eq_bot_iff_forall_not_adj, ← neighborSet_eq_empty, Set.eq_empty_iff_forall_notMem]
+
end SimpleGraph
diff --git a/Mathlib/Combinatorics/SimpleGraph/Finite.lean b/Mathlib/Combinatorics/SimpleGraph/Finite.lean
index 82242f1778d3ad..c5f2e356c5f83c 100644
--- a/Mathlib/Combinatorics/SimpleGraph/Finite.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/Finite.lean
@@ -233,22 +233,19 @@ theorem degree_pos_iff_mem_support : 0 < G.degree v ↔ v ∈ G.support := by
theorem degree_eq_zero_iff_notMem_support : G.degree v = 0 ↔ v ∉ G.support := by
rw [← G.degree_pos_iff_mem_support v, Nat.pos_iff_ne_zero, not_ne_iff]
-@[simp]
theorem degree_eq_zero_of_subsingleton {G : SimpleGraph V} (v : V) [Fintype (G.neighborSet v)]
[Subsingleton V] : G.degree v = 0 := by
- have := G.degree_pos_iff_exists_adj v
- simp_all [subsingleton_iff_forall_eq v]
+ simp
+
+theorem nontrivial_of_degree_ne_zero {G : SimpleGraph V} {v : V} [Fintype (G.neighborSet v)]
+ (h : G.degree v ≠ 0) : Nontrivial V :=
+ nontrivial_of_not_isIsolated <| G.degree_eq_zero v |>.not.mp h
theorem degree_eq_one_iff_existsUnique_adj {G : SimpleGraph V} {v : V} [Fintype (G.neighborSet v)] :
G.degree v = 1 ↔ ∃! w : V, G.Adj v w := by
rw [degree, Finset.card_eq_one, Finset.singleton_iff_unique_mem]
simp only [mem_neighborFinset]
-theorem nontrivial_of_degree_ne_zero {G : SimpleGraph V} {v : V} [Fintype (G.neighborSet v)]
- (h : G.degree v ≠ 0) : Nontrivial V := by
- by_contra!
- simp_all [degree_eq_zero_of_subsingleton]
-
theorem degree_compl [Fintype (Gᶜ.neighborSet v)] [Fintype V] :
Gᶜ.degree v = Fintype.card V - 1 - G.degree v := by
classical
@@ -364,10 +361,8 @@ theorem complete_graph_degree [DecidableEq V] (v : V) :
simp_rw [degree, neighborFinset_eq_filter, top_adj, filter_ne]
rw [card_erase_of_mem (mem_univ v), card_univ]
-@[simp]
theorem bot_degree (v : V) : (⊥ : SimpleGraph V).degree v = 0 := by
- simp_rw [degree, neighborFinset_eq_filter, bot_adj, filter_false]
- exact Finset.card_empty
+ simp
theorem IsRegularOfDegree.top [DecidableEq V] :
(⊤ : SimpleGraph V).IsRegularOfDegree (Fintype.card V - 1) := by
diff --git a/Mathlib/Combinatorics/SimpleGraph/Maps.lean b/Mathlib/Combinatorics/SimpleGraph/Maps.lean
index 3fbde0b4f959eb..e2aec80aab3607 100644
--- a/Mathlib/Combinatorics/SimpleGraph/Maps.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/Maps.lean
@@ -387,11 +387,6 @@ theorem mapEdgeSet.injective (hinj : Function.Injective f) : Function.Injective
repeat rw [Subtype.mk_eq_mk]
apply Sym2.map.injective hinj
-@[gcongr]
-theorem _root_.SimpleGraph.neighborSet_mono (hle : G₁ ≤ G₂) (v : V) :
- G₁.neighborSet v ⊆ G₂.neighborSet v :=
- subset_preimage_neighborSet v <| .ofLE hle
-
/-- Every graph homomorphism from a complete graph is injective. -/
theorem injective_of_top_hom (f : (⊤ : SimpleGraph V) →g G') : Function.Injective f := by
intro v w h
diff --git a/Mathlib/Combinatorics/SimpleGraph/StronglyRegular.lean b/Mathlib/Combinatorics/SimpleGraph/StronglyRegular.lean
index b65a73bf3cc8ee..bda19262960e8e 100644
--- a/Mathlib/Combinatorics/SimpleGraph/StronglyRegular.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/StronglyRegular.lean
@@ -64,7 +64,7 @@ theorem bot_strongly_regular : (⊥ : SimpleGraph V).IsSRGWith (Fintype.card V)
of_not_adj v w _ := by
simp only [card_eq_zero, Fintype.card_ofFinset, forall_true_left, not_false_iff, bot_adj]
ext
- simp [mem_commonNeighbors]
+ simp
theorem IsSRGWith.ediam_eq_two [Nontrivial V] (h : G.IsSRGWith n k ℓ μ) (ht : G ≠ ⊤) (hm : μ ≠ 0) :
G.ediam = 2 := by
From fdfd7c5f4fbcd7a9e7102f59428c36ca6aed7e87 Mon Sep 17 00:00:00 2001
From: Michael Rothgang <10105016+grunweg@users.noreply.github.com>
Date: Sun, 14 Jun 2026 19:14:40 +0000
Subject: [PATCH 0031/1300] =?UTF-8?q?chore:=20replace=20:=3D=20=E2=86=90?=
=?UTF-8?q?=20by=20=E2=86=90=20when=20possible=20(#40594)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Minimize the diff on the upcoming mathlib bump, with the new do elaborator.
---
Cache/Requests.lean | 4 ++--
Mathlib/Algebra/Order/BigOperators/Expect.lean | 2 +-
Mathlib/Analysis/InnerProductSpace/PiL2.lean | 2 +-
Mathlib/Tactic/Algebra/Basic.lean | 2 +-
Mathlib/Tactic/ComputeDegree.lean | 12 ++++++------
Mathlib/Tactic/DeprecateTo.lean | 2 +-
Mathlib/Tactic/FieldSimp.lean | 4 ++--
Mathlib/Tactic/LinearCombination.lean | 2 +-
Mathlib/Tactic/Linter/TextBased.lean | 4 ++--
Mathlib/Tactic/Module.lean | 2 +-
Mathlib/Tactic/NormNum/Irrational.lean | 4 ++--
Mathlib/Tactic/NormNum/Pow.lean | 2 +-
Mathlib/Tactic/Simps/Basic.lean | 2 +-
Mathlib/Util/DischargerAsTactic.lean | 2 +-
Mathlib/Util/GetAllModules.lean | 4 ++--
MathlibTest/ClickSuggestions/TestImpl.lean | 2 +-
scripts/create_deprecated_modules.lean | 2 +-
17 files changed, 27 insertions(+), 27 deletions(-)
diff --git a/Cache/Requests.lean b/Cache/Requests.lean
index 8c9e156e4fe2f0..cca1dc42314b1c 100644
--- a/Cache/Requests.lean
+++ b/Cache/Requests.lean
@@ -669,7 +669,7 @@ def getFiles
-- Skip when forceDownload is set, since downloadFiles will re-download (and pipeline-decompress)
-- all files including already-cached ones, which would race with this background task.
let bgDecomp ← if decompress && !forceDownload then
- if let some plan := ← IO.prepareDecompConfig hashMap forceUnpack then
+ if let some plan ← IO.prepareDecompConfig hashMap forceUnpack then
if plan.alreadyDecompressed > 0 then
IO.println s!"Decompressing {plan.needsDecomp} already-cached file(s) \
({plan.alreadyDecompressed} already decompressed)"
@@ -702,7 +702,7 @@ def getFiles
let mut failed : Nat := 0
for h : i in [0:repos.length] do
- failed := ← downloadFiles repos[i] hashMap forceDownload parallel
+ failed ← downloadFiles repos[i] hashMap forceDownload parallel
(warnOnMissing := i = repos.length - 1)
(decompress := decompress) (forceUnpack := forceUnpack)
isMathlibRoot mathlibDepPath
diff --git a/Mathlib/Algebra/Order/BigOperators/Expect.lean b/Mathlib/Algebra/Order/BigOperators/Expect.lean
index 440665a2a5d223..8f77dc667d84d0 100644
--- a/Mathlib/Algebra/Order/BigOperators/Expect.lean
+++ b/Mathlib/Algebra/Order/BigOperators/Expect.lean
@@ -226,7 +226,7 @@ meta def evalFinsetExpect : PositivityExt where eval {u α} zα pα e := do
let i : Q($ι) ← mkFreshExprMVarQ q($ι) .syntheticOpaque
have body : Q($α) := .betaRev f #[i]
let rbody ← core zα pα body
- let p_pos : Option Q(0 < $e) := ← (do
+ let p_pos : Option Q(0 < $e) ← (do
let .positive pbody := rbody | pure none -- Fail if the body is not provably positive
let some ps ← proveFinsetNonempty s | pure none
let .some pα' ← trySynthInstanceQ q(IsOrderedCancelAddMonoid $α) | pure none
diff --git a/Mathlib/Analysis/InnerProductSpace/PiL2.lean b/Mathlib/Analysis/InnerProductSpace/PiL2.lean
index 004b5bfaf977e8..9836ea7f5a2ede 100644
--- a/Mathlib/Analysis/InnerProductSpace/PiL2.lean
+++ b/Mathlib/Analysis/InnerProductSpace/PiL2.lean
@@ -133,7 +133,7 @@ meta def EuclideanSpace.delabVecNotation : Delab :=
let p : Term ← withNaryArg 0 <| delab
-- to be conservative, only allow subscripts which are numerals
guard <| p matches `($_:num)
- let `(![$elems,*]) := ← withNaryArg 2 delab | failure
+ let `(![$elems,*]) ← withNaryArg 2 delab | failure
`(!$p[$elems,*])
end Notation
diff --git a/Mathlib/Tactic/Algebra/Basic.lean b/Mathlib/Tactic/Algebra/Basic.lean
index 37d55dfd2eb81c..1af2eff93f1afd 100644
--- a/Mathlib/Tactic/Algebra/Basic.lean
+++ b/Mathlib/Tactic/Algebra/Basic.lean
@@ -128,7 +128,7 @@ def evalCast (cR : Algebra.Cache q($sR)) (cA : Algebra.Cache q($sA)):
into casts. -/
def pushCast (e : Expr) : MetaM Simp.Result := do
-- collect the available `push_cast` lemmas
- let mut thms : SimpTheorems := ← NormCast.pushCastExt.getTheorems
+ let mut thms : SimpTheorems ← NormCast.pushCastExt.getTheorems
let simps : Array Name := #[``eq_natCast, ``eq_intCast, ``eq_ratCast]
for thm in simps do
let ⟨levelParams, _, proof⟩ ← abstractMVars (mkConst thm)
diff --git a/Mathlib/Tactic/ComputeDegree.lean b/Mathlib/Tactic/ComputeDegree.lean
index d00e44bfc8e562..aad91fcb2100ef 100644
--- a/Mathlib/Tactic/ComputeDegree.lean
+++ b/Mathlib/Tactic/ComputeDegree.lean
@@ -379,11 +379,11 @@ It returns the list of `MVarId`s, beginning with the ones that initially involve
metavariables followed by the rest.
-/
def tryRfl (mvs : List MVarId) : MetaM (List MVarId) := do
- let (yesMV, noMV) := ← mvs.partitionM fun mv =>
+ let (yesMV, noMV) ← mvs.partitionM fun mv =>
return hasExprMVar (← instantiateMVars (← mv.getDecl).type)
- let tried_rfl := ← noMV.mapM fun g => g.applyConst ``rfl <|> return [g]
- let assignable := ← yesMV.mapM fun g => do
- let tgt := ← instantiateMVars (← g.getDecl).type
+ let tried_rfl ← noMV.mapM fun g => g.applyConst ``rfl <|> return [g]
+ let assignable ← yesMV.mapM fun g => do
+ let tgt ← instantiateMVars (← g.getDecl).type
match tgt.eq? with
| some (_, lhs, rhs) =>
if (isMVar rhs && (! hasExprMVar lhs)) ||
@@ -407,9 +407,9 @@ lemma and returns two lists: the left-over goals of all the applications, follow
concatenation of the previous `static` list, followed by the newly discovered goals outside of the
scope of `compute_degree`. -/
def splitApply (mvs static : List MVarId) : MetaM ((List MVarId) × (List MVarId)) := do
- let (can_progress, curr_static) := ← mvs.partitionM fun mv => do
+ let (can_progress, curr_static) ← mvs.partitionM fun mv => do
return dispatchLemma (twoHeadsArgs (← mv.getType'')) != ``id
- let progress := ← can_progress.mapM fun mv => do
+ let progress ← can_progress.mapM fun mv => do
let lem := dispatchLemma <| twoHeadsArgs (← mv.getType'')
mv.applyConst <| lem
return (progress.flatten, static ++ curr_static)
diff --git a/Mathlib/Tactic/DeprecateTo.lean b/Mathlib/Tactic/DeprecateTo.lean
index 975dbb7fcf67a9..6c4805d681021e 100644
--- a/Mathlib/Tactic/DeprecateTo.lean
+++ b/Mathlib/Tactic/DeprecateTo.lean
@@ -129,7 +129,7 @@ elab tk:"deprecate" "to" id:ident* dat:(ppSpace str ppSpace)? ppLine cmd:command
for i in id.toList.drop news.size do logErrorAt i ""
warn := warn.push s!"Unused names: {id.toList.drop news.size}"
let (oldId, newCmd) := renameTheorem id[0]! cmd
- let oldNames := ← resolveGlobalName (oldId.raw.getArg 0).getId.eraseMacroScopes
+ let oldNames ← resolveGlobalName (oldId.raw.getArg 0).getId.eraseMacroScopes
let fil := news.filter fun n => n.toString.endsWith oldNames[0]!.1.toString
if fil.size != 1 && oldId != default then
logError m!"Expected to find one declaration called {oldNames[0]!.1}, found {fil.size}"
diff --git a/Mathlib/Tactic/FieldSimp.lean b/Mathlib/Tactic/FieldSimp.lean
index a7bea5f1a206ce..4848295ba6a635 100644
--- a/Mathlib/Tactic/FieldSimp.lean
+++ b/Mathlib/Tactic/FieldSimp.lean
@@ -433,7 +433,7 @@ partial def normalize (disch : ∀ {u : Level} (type : Q(Sort u)), MetaM Q($type
let ⟨y₂, ⟨g₂, pf₂_sgn⟩, l₂, pf₂⟩ ← normalize disch iM x₂
-- build the new list and proof
have pf := qNF.mkMulProof iM l₁ l₂
- let ⟨G, pf_y⟩ := ← Sign.mul iM y₁ y₂ g₁ g₂
+ let ⟨G, pf_y⟩ ← Sign.mul iM y₁ y₂ g₁ g₂
pure ⟨q($y₁ * $y₂), ⟨G, q(Eq.trans (congr_arg₂ HMul.hMul $pf₁_sgn $pf₂_sgn) $pf_y)⟩,
qNF.mul l₁ l₂, q(NF.mul_eq_eval $pf₁ $pf₂ $pf)⟩
/- normalize a division: `x₁ / x₂` -/
@@ -442,7 +442,7 @@ partial def normalize (disch : ∀ {u : Level} (type : Q(Sort u)), MetaM Q($type
let ⟨y₂, ⟨g₂, pf₂_sgn⟩, l₂, pf₂⟩ ← normalize disch iM x₂
-- build the new list and proof
let pf := qNF.mkDivProof iM l₁ l₂
- let ⟨G, pf_y⟩ := ← Sign.div iM y₁ y₂ g₁ g₂
+ let ⟨G, pf_y⟩ ← Sign.div iM y₁ y₂ g₁ g₂
pure ⟨q($y₁ / $y₂), ⟨G, q(Eq.trans (congr_arg₂ HDiv.hDiv $pf₁_sgn $pf₂_sgn) $pf_y)⟩,
qNF.div l₁ l₂, q(NF.div_eq_eval $pf₁ $pf₂ $pf)⟩
/- normalize an inversion: `y⁻¹` -/
diff --git a/Mathlib/Tactic/LinearCombination.lean b/Mathlib/Tactic/LinearCombination.lean
index d3a1453f64453c..f6d218b9c8661e 100644
--- a/Mathlib/Tactic/LinearCombination.lean
+++ b/Mathlib/Tactic/LinearCombination.lean
@@ -171,7 +171,7 @@ def elabLinearCombination (tk : Syntax)
Prod.mk eq <$> `(Eq.refl 0)
| .proof hypRel p => pure (hypRel, p)
-- look up the lemma for the central `refine` in `linear_combination`
- let (reduceLem, newGoalRel) : Name × Ineq := ← do
+ let (reduceLem, newGoalRel) : Name × Ineq ← do
match Ineq.relImpRelData hypRel goalRel with
| none => throwError "cannot prove an equality from inequality hypotheses"
| some n => pure n
diff --git a/Mathlib/Tactic/Linter/TextBased.lean b/Mathlib/Tactic/Linter/TextBased.lean
index f22c1d62769539..e88d6761db1ec3 100644
--- a/Mathlib/Tactic/Linter/TextBased.lean
+++ b/Mathlib/Tactic/Linter/TextBased.lean
@@ -526,10 +526,10 @@ def lintModules (opts : LinterOptions) (nolints : Array String) (moduleNames : A
-- Convert the module name to a file name, then lint that file.
let path := mkFilePath (module.components.map toString)|>.addExtension "lean"
- let (errors, changed) := ← lintFile opts path styleExceptions
+ let (errors, changed) ← lintFile opts path styleExceptions
if let some c := changed then
if fix then
- let _ := ← IO.FS.writeFile path ("\n".intercalate c.toList)
+ let _ ← IO.FS.writeFile path ("\n".intercalate c.toList)
if errors.size > 0 then
allUnexpectedErrors := allUnexpectedErrors.append errors
numberErrorFiles := numberErrorFiles + 1
diff --git a/Mathlib/Tactic/Module.lean b/Mathlib/Tactic/Module.lean
index ea19a03a9a39ae..dd5d1b1bc065b4 100644
--- a/Mathlib/Tactic/Module.lean
+++ b/Mathlib/Tactic/Module.lean
@@ -571,7 +571,7 @@ most commonly occurring `algebraMap`s (those out of `ℕ`, `ℤ` and `ℚ`) into
(`ℕ`, `ℤ` and `ℚ` casts) and then try to disperse the casts using the various `push_cast` lemmas. -/
def postprocess (mvarId : MVarId) : MetaM MVarId := do
-- collect the available `push_cast` lemmas
- let mut thms : SimpTheorems := ← NormCast.pushCastExt.getTheorems
+ let mut thms : SimpTheorems ← NormCast.pushCastExt.getTheorems
-- augment this list with the `algebraMapThms` lemmas, which handle `algebraMap` operations
for thm in algebraMapThms do
let ⟨levelParams, _, proof⟩ ← abstractMVars (mkConst thm)
diff --git a/Mathlib/Tactic/NormNum/Irrational.lean b/Mathlib/Tactic/NormNum/Irrational.lean
index 13cb6cc4ebdfad..8528c4739b4e17 100644
--- a/Mathlib/Tactic/NormNum/Irrational.lean
+++ b/Mathlib/Tactic/NormNum/Irrational.lean
@@ -248,8 +248,8 @@ def findNotPowerCertificateCore (m n : ℕ) : Option ℕ := Id.run do
/-- Finds `NotPowerCertificate` showing that `m` is not `n`-power. -/
def findNotPowerCertificate (m n : Q(ℕ)) : MetaM (NotPowerCertificate m n) := do
- let .isNat (_ : Q(AddMonoidWithOne ℕ)) m _ := ← derive m | failure
- let .isNat (_ : Q(AddMonoidWithOne ℕ)) n _ := ← derive n | failure
+ let .isNat (_ : Q(AddMonoidWithOne ℕ)) m _ ← derive m | failure
+ let .isNat (_ : Q(AddMonoidWithOne ℕ)) n _ ← derive n | failure
let mVal := m.natLit!
let nVal := n.natLit!
let some k := findNotPowerCertificateCore mVal nVal | failure
diff --git a/Mathlib/Tactic/NormNum/Pow.lean b/Mathlib/Tactic/NormNum/Pow.lean
index 2151187c30aec5..a05e9b46eee0ea 100644
--- a/Mathlib/Tactic/NormNum/Pow.lean
+++ b/Mathlib/Tactic/NormNum/Pow.lean
@@ -137,7 +137,7 @@ partial def evalIntPow (za : ℤ) (a : Q(ℤ)) (b : Q(ℕ)) :
have : $a =Q .negOfNat $a' := ⟨⟩
let b' := b.natLit!
have b₀ : Q(ℕ) := mkRawNatLit (b' >>> 1)
- let ⟨c₀, p⟩ := ← evalNatPow a' b₀
+ let ⟨c₀, p⟩ ← evalNatPow a' b₀
let c' := c₀.natLit!
if b' &&& 1 == 0 then
have c : Q(ℕ) := mkRawNatLit (c' * c')
diff --git a/Mathlib/Tactic/Simps/Basic.lean b/Mathlib/Tactic/Simps/Basic.lean
index e9917bf5a80fcb..91bd1091d93821 100644
--- a/Mathlib/Tactic/Simps/Basic.lean
+++ b/Mathlib/Tactic/Simps/Basic.lean
@@ -756,7 +756,7 @@ def findAutomaticProjections (str : Name) (projs : Array ParsedProjectionData) :
MetaM.run' <| TermElabM.run' (s := {levelNames := strDecl.levelParams}) <|
forallTelescope strDecl.type fun args _ ↦ do
let projs ← projs.mapM fun proj => do
- if let some (projExpr, projName) := ← findAutomaticProjectionsAux str proj args then
+ if let some (projExpr, projName) ← findAutomaticProjectionsAux str proj args then
unless ← isDefEq projExpr proj.expr?.get! do
throwError "The projection {proj.newName} is not definitionally equal to an application \
of {projName}:{indentExpr proj.expr?.get!}\nvs{indentExpr projExpr}"
diff --git a/Mathlib/Util/DischargerAsTactic.lean b/Mathlib/Util/DischargerAsTactic.lean
index 131473e5c45d2a..a6be9961ee49d7 100644
--- a/Mathlib/Util/DischargerAsTactic.lean
+++ b/Mathlib/Util/DischargerAsTactic.lean
@@ -25,7 +25,7 @@ so that it can be passed as an argument to `simp (discharger := foo)`.
This is inverse to `mkDischargeWrapper`. -/
def wrapSimpDischarger (dis : Simp.Discharge) : TacticM Unit := do
let eS : Lean.Meta.Simp.State := {}
- let eC : Lean.Meta.Simp.Context := ← Simp.mkContext {}
+ let eC : Lean.Meta.Simp.Context ← Simp.mkContext {}
let eM : Lean.Meta.Simp.Methods := {}
let (some a, _) ← liftM <| StateRefT'.run (ReaderT.run (ReaderT.run (dis <| ← getMainTarget)
eM.toMethodsRef) eC) eS | failure
diff --git a/Mathlib/Util/GetAllModules.lean b/Mathlib/Util/GetAllModules.lean
index da4f4e51be49b0..5da0463be9851c 100644
--- a/Mathlib/Util/GetAllModules.lean
+++ b/Mathlib/Util/GetAllModules.lean
@@ -53,6 +53,6 @@ i.e. names of the form `Mathlib/Algebra/Algebra/Basic.lean`.
In addition, these names are sorted in a platform-independent order. -/
def getAllModulesSorted (git : Bool) (ml : String) : IO (Array String) := do
let files ← getAllFiles git ml
- let names := ← files.mapM fun f => do
- return (← moduleNameOfFileName f none).toString
+ let names ← files.mapM fun f => do
+ return (← moduleNameOfFileName f none).toString
return names.qsort (· < ·)
diff --git a/MathlibTest/ClickSuggestions/TestImpl.lean b/MathlibTest/ClickSuggestions/TestImpl.lean
index 244a3cfee64c49..67db395e1ee068 100644
--- a/MathlibTest/ClickSuggestions/TestImpl.lean
+++ b/MathlibTest/ClickSuggestions/TestImpl.lean
@@ -67,7 +67,7 @@ elab "click_test" onGoal?:(num)? hyp?:(ident)? pos?:(str)? "=>" expecteds:str+ :
| .ok pos => return pos
| .error s => throwError "{s}"
let expecteds := expecteds.map (·.getString)
- let loc : GoalLocation := ← match hyp?, pos? with
+ let loc : GoalLocation ← match hyp?, pos? with
| some h, some pos => pure <| .hypType h pos
| none , some pos => pure <| .target pos
| some h, none => pure <| .hyp h
diff --git a/scripts/create_deprecated_modules.lean b/scripts/create_deprecated_modules.lean
index 2bcfc4ece90828..31705550029ad0 100644
--- a/scripts/create_deprecated_modules.lean
+++ b/scripts/create_deprecated_modules.lean
@@ -233,7 +233,7 @@ def deprecateFilePath (fname : String) (rename comment : Option String) :
-- Retrieve the final version of the file, before it was deleted.
let file ← runCmd s!"git show {modifiedHash}:{fname}"
-- Generate a module deprecation for the file `fname`.
- let fileHeader := ← match rename with
+ let fileHeader ← match rename with
| some rename => do
let modName := mkModName rename
pure s!"import {modName}"
From a76f26fe763efc700df79ccc628133be56dc378d Mon Sep 17 00:00:00 2001
From: Jack McCarthy <37917934+Deicyde@users.noreply.github.com>
Date: Sun, 14 Jun 2026 20:07:23 +0000
Subject: [PATCH 0032/1300] doc: add wikidata attributes (#40440)
This PR adds a batch of 25 `@[wikidata]` attributes.
Follows the same template as #40004.
Claude helped generate the list of crossrefs (by scanning Wikidata + Mathlib) and then wrote each of the `@[wikidata]` tags. Comments were generated by [Johan's script](https://github.com/jcommelin/mathlib-crossref-report) (Thanks Snir Broshi for telling me about this!)
I manually verified all of the cross refs for mathematical correctness, and made sure they're attached to the appropriate declaration.
---
Mathlib/Algebra/Group/Defs.lean | 2 +-
Mathlib/Algebra/Polynomial/Basic.lean | 1 +
Mathlib/Algebra/Ring/Defs.lean | 1 +
Mathlib/Analysis/Complex/Exponential.lean | 2 +-
Mathlib/Analysis/Distribution/Distribution.lean | 1 +
Mathlib/Analysis/Fourier/LpSpace.lean | 1 +
Mathlib/Analysis/SpecialFunctions/Log/Basic.lean | 2 +-
Mathlib/Analysis/SpecialFunctions/Trigonometric/Basic.lean | 1 +
Mathlib/Computability/Partrec.lean | 1 +
Mathlib/Data/Int/ModEq.lean | 1 +
Mathlib/Data/Nat/Factorial/Basic.lean | 2 ++
Mathlib/Data/Nat/Prime/Defs.lean | 2 +-
Mathlib/Geometry/Euclidean/Angle/Unoriented/Basic.lean | 1 +
Mathlib/Geometry/Manifold/IsManifold/Basic.lean | 2 +-
Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean | 1 +
Mathlib/NumberTheory/NumberField/Basic.lean | 2 +-
Mathlib/Order/RelClasses.lean | 2 ++
Mathlib/Probability/Moments/Variance.lean | 1 +
Mathlib/SetTheory/Cardinal/Cofinality/Basic.lean | 1 +
Mathlib/SetTheory/Cardinal/Defs.lean | 3 ++-
Mathlib/SetTheory/Ordinal/Basic.lean | 2 +-
Mathlib/Topology/Connected/Basic.lean | 1 +
Mathlib/Topology/Defs/Filter.lean | 1 +
Mathlib/Topology/Homotopy/Basic.lean | 1 +
Mathlib/Topology/MetricSpace/Defs.lean | 1 +
25 files changed, 28 insertions(+), 8 deletions(-)
diff --git a/Mathlib/Algebra/Group/Defs.lean b/Mathlib/Algebra/Group/Defs.lean
index d7e8c24a4763d8..8da0e62370f626 100644
--- a/Mathlib/Algebra/Group/Defs.lean
+++ b/Mathlib/Algebra/Group/Defs.lean
@@ -1276,7 +1276,7 @@ class AddCommGroup (G : Type u) extends AddGroup G, AddCommMonoid G
/-- A commutative group is a group with commutative `(*)`. -/
-- There is intentionally no `IsMulCommutative` for `CommGroup` instance for performance reasons.
-@[to_additive]
+@[to_additive (attr := wikidata Q181296)]
class CommGroup (G : Type u) extends Group G, CommMonoid G
section CommGroup
diff --git a/Mathlib/Algebra/Polynomial/Basic.lean b/Mathlib/Algebra/Polynomial/Basic.lean
index c6cc31c85d43e6..bedc15cd7f6966 100644
--- a/Mathlib/Algebra/Polynomial/Basic.lean
+++ b/Mathlib/Algebra/Polynomial/Basic.lean
@@ -68,6 +68,7 @@ denoted as `R[X]` within the `Polynomial` namespace.
Polynomials should be seen as (semi-)rings with the additional constructor `X`.
The embedding from `R` is called `C`. -/
+@[wikidata Q43260]
structure Polynomial (R : Type*) [Semiring R] where ofFinsupp ::
/-- The coefficients `ℕ →₀ R` of a polynomial in `R[X]`. -/
toFinsupp : AddMonoidAlgebra R ℕ
diff --git a/Mathlib/Algebra/Ring/Defs.lean b/Mathlib/Algebra/Ring/Defs.lean
index 0ff309f0832c35..5bcb9b4e73ec77 100644
--- a/Mathlib/Algebra/Ring/Defs.lean
+++ b/Mathlib/Algebra/Ring/Defs.lean
@@ -411,6 +411,7 @@ instance (priority := 100) NonUnitalCommRing.toNonUnitalCommSemiring [s : NonUni
{ s with }
/-- A commutative ring is a ring with commutative multiplication. -/
+@[wikidata Q858656]
class CommRing (α : Type u) extends Ring α, CommMonoid α
instance (priority := 100) CommRing.toNonAssocCommRing [CommRing α] : NonAssocCommRing α where
diff --git a/Mathlib/Analysis/Complex/Exponential.lean b/Mathlib/Analysis/Complex/Exponential.lean
index 4fbc28c76c4d3e..58e394719d257c 100644
--- a/Mathlib/Analysis/Complex/Exponential.lean
+++ b/Mathlib/Analysis/Complex/Exponential.lean
@@ -76,7 +76,7 @@ open Complex
noncomputable section
/-- The real exponential function, defined as the real part of the complex exponential -/
-@[pp_nodot]
+@[pp_nodot, wikidata Q168698]
nonrec def exp (x : ℝ) : ℝ :=
(exp x).re
diff --git a/Mathlib/Analysis/Distribution/Distribution.lean b/Mathlib/Analysis/Distribution/Distribution.lean
index edc44da2dcf9ff..a9731fcfaa4774 100644
--- a/Mathlib/Analysis/Distribution/Distribution.lean
+++ b/Mathlib/Analysis/Distribution/Distribution.lean
@@ -192,6 +192,7 @@ end mapCLM
section DiracDelta
/-- The Dirac delta distribution. This is zero if `x` does not belong to `Ω`. -/
+@[wikidata Q209675]
noncomputable def delta (x : E) : 𝓓'^{n}(Ω, ℝ) where
toFun f := f x
map_add' _ _ := rfl
diff --git a/Mathlib/Analysis/Fourier/LpSpace.lean b/Mathlib/Analysis/Fourier/LpSpace.lean
index 702a38dd177a69..5475d647eb65ba 100644
--- a/Mathlib/Analysis/Fourier/LpSpace.lean
+++ b/Mathlib/Analysis/Fourier/LpSpace.lean
@@ -46,6 +46,7 @@ namespace MeasureTheory.Lp
variable (E F) in
/-- The Fourier transform on `L2` as a linear isometry equivalence. -/
+@[wikidata Q6520159]
def fourierTransformₗᵢ : (Lp (α := E) F 2) ≃ₗᵢ[ℂ] (Lp (α := E) F 2) :=
(fourierEquiv ℂ 𝓢(E, F)).extendOfIsometry
(toLpCLM ℂ (E := E) F 2 volume) (toLpCLM ℂ (E := E) F 2 volume)
diff --git a/Mathlib/Analysis/SpecialFunctions/Log/Basic.lean b/Mathlib/Analysis/SpecialFunctions/Log/Basic.lean
index ab6a43aa827bb2..f64147755f1c5c 100644
--- a/Mathlib/Analysis/SpecialFunctions/Log/Basic.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Log/Basic.lean
@@ -40,7 +40,7 @@ variable {x y : ℝ}
to `log |x|` for `x < 0`, and to `0` for `0`. We use this unconventional extension to
`(-∞, 0]` as it gives the formula `log (x * y) = log x + log y` for all nonzero `x` and `y`, and
the derivative of `log` is `1/x` away from `0`. -/
-@[pp_nodot]
+@[pp_nodot, wikidata Q11197]
noncomputable def log (x : ℝ) : ℝ :=
if hx : x = 0 then 0 else expOrderIso.symm ⟨|x|, abs_pos.2 hx⟩
diff --git a/Mathlib/Analysis/SpecialFunctions/Trigonometric/Basic.lean b/Mathlib/Analysis/SpecialFunctions/Trigonometric/Basic.lean
index fe5091bdf1e323..f673be94a073fd 100644
--- a/Mathlib/Analysis/SpecialFunctions/Trigonometric/Basic.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Trigonometric/Basic.lean
@@ -123,6 +123,7 @@ from which one can derive all its properties. For explicit bounds on π,
see `Mathlib/Analysis/Real/Pi/Bounds.lean`.
Denoted `π`, once the `Real` namespace is opened. -/
+@[wikidata Q167]
protected noncomputable def pi : ℝ :=
2 * Classical.choose exists_cos_eq_zero
diff --git a/Mathlib/Computability/Partrec.lean b/Mathlib/Computability/Partrec.lean
index 43697b6b771a02..f73cb614714702 100644
--- a/Mathlib/Computability/Partrec.lean
+++ b/Mathlib/Computability/Partrec.lean
@@ -236,6 +236,7 @@ def Partrec₂ {α β σ} [Primcodable α] [Primcodable β] [Primcodable σ] (f
/-- Computable functions `α → σ` between `Primcodable` types:
a function is computable if and only if it is partially recursive (as a partial function) -/
+@[wikidata Q1148456]
def Computable {α σ} [Primcodable α] [Primcodable σ] (f : α → σ) :=
Partrec (f : α →. σ)
diff --git a/Mathlib/Data/Int/ModEq.lean b/Mathlib/Data/Int/ModEq.lean
index 5a8cb427cd8b0b..2af03e37ca02ab 100644
--- a/Mathlib/Data/Int/ModEq.lean
+++ b/Mathlib/Data/Int/ModEq.lean
@@ -26,6 +26,7 @@ modeq, congruence, mod, MOD, modulo, integers
/-- `a ≡ b [ZMOD n]` when `a % n = b % n`. -/
+@[wikidata Q3773677]
def Int.ModEq (n a b : ℤ) :=
a % n = b % n
diff --git a/Mathlib/Data/Nat/Factorial/Basic.lean b/Mathlib/Data/Nat/Factorial/Basic.lean
index aebc402568578f..4a434c8b88ebfa 100644
--- a/Mathlib/Data/Nat/Factorial/Basic.lean
+++ b/Mathlib/Data/Nat/Factorial/Basic.lean
@@ -7,6 +7,7 @@ module
public import Mathlib.Data.Nat.Basic
public import Mathlib.Tactic.Common
+public import Mathlib.Tactic.CrossRefAttribute
public import Mathlib.Tactic.Monotonicity.Attr
/-!
@@ -31,6 +32,7 @@ see `Fintype.card_perm`.
namespace Nat
/-- `Nat.factorial n` is the factorial of `n`. -/
+@[wikidata Q120976]
def factorial : ℕ → ℕ
| 0 => 1
| succ n => succ n * factorial n
diff --git a/Mathlib/Data/Nat/Prime/Defs.lean b/Mathlib/Data/Nat/Prime/Defs.lean
index 4c6a3aa35c4fb2..852308e4137880 100644
--- a/Mathlib/Data/Nat/Prime/Defs.lean
+++ b/Mathlib/Data/Nat/Prime/Defs.lean
@@ -38,7 +38,7 @@ variable {n : ℕ}
/-- `Nat.Prime p` means that `p` is a prime number, that is, a natural number
at least 2 whose only divisors are `p` and `1`.
The theorem `Nat.prime_def` witnesses this description of a prime number. -/
-@[pp_nodot]
+@[pp_nodot, wikidata Q49008]
def Prime (p : ℕ) :=
Irreducible p
diff --git a/Mathlib/Geometry/Euclidean/Angle/Unoriented/Basic.lean b/Mathlib/Geometry/Euclidean/Angle/Unoriented/Basic.lean
index aef62d15dfab1d..a63db76cb04807 100644
--- a/Mathlib/Geometry/Euclidean/Angle/Unoriented/Basic.lean
+++ b/Mathlib/Geometry/Euclidean/Angle/Unoriented/Basic.lean
@@ -37,6 +37,7 @@ variable {V : Type*} [NormedAddCommGroup V] [InnerProductSpace ℝ V] {x y : V}
/-- The undirected angle between two vectors. If either vector is 0,
this is π/2. See `Orientation.oangle` for the corresponding oriented angle
definition. -/
+@[wikidata Q11352]
def angle (x y : V) : ℝ :=
Real.arccos (⟪x, y⟫ / (‖x‖ * ‖y‖))
diff --git a/Mathlib/Geometry/Manifold/IsManifold/Basic.lean b/Mathlib/Geometry/Manifold/IsManifold/Basic.lean
index b3a41ea3b8d56c..79619e7d1384c9 100644
--- a/Mathlib/Geometry/Manifold/IsManifold/Basic.lean
+++ b/Mathlib/Geometry/Manifold/IsManifold/Basic.lean
@@ -1037,7 +1037,7 @@ set_option linter.unusedVariables false in
The definition of `TangentSpace` is not reducible so that type class inference
does not pick wrong instances.
-/
-@[nolint unusedArguments]
+@[nolint unusedArguments, wikidata Q909601]
def TangentSpace {𝕜 : Type*} [NontriviallyNormedField 𝕜]
{E : Type u} [NormedAddCommGroup E] [NormedSpace 𝕜 E]
{H : Type*} [TopologicalSpace H] (I : ModelWithCorners 𝕜 E H)
diff --git a/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean b/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean
index bb576bab423ae3..87b6da663393c1 100644
--- a/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean
+++ b/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean
@@ -56,6 +56,7 @@ def detRowAlternating : (n → R) [⋀^n]→ₗ[R] R :=
MultilinearMap.alternatization ((MultilinearMap.mkPiAlgebra R n R).compLinearMap LinearMap.proj)
/-- The determinant of a matrix given by the Leibniz formula. -/
+@[wikidata Q178546]
def det (M : Matrix n n R) : R :=
detRowAlternating M
diff --git a/Mathlib/NumberTheory/NumberField/Basic.lean b/Mathlib/NumberTheory/NumberField/Basic.lean
index 14a4d13f5d746f..c9e3ddec84ca4b 100644
--- a/Mathlib/NumberTheory/NumberField/Basic.lean
+++ b/Mathlib/NumberTheory/NumberField/Basic.lean
@@ -38,7 +38,7 @@ number field, ring of integers
/-- A number field is a field which has characteristic zero and is finite
dimensional over ℚ. -/
-@[stacks 09GA]
+@[stacks 09GA, wikidata Q616608]
class NumberField (K : Type*) [Field K] : Prop where
[to_charZero : CharZero K]
[to_finiteDimensional : FiniteDimensional ℚ K]
diff --git a/Mathlib/Order/RelClasses.lean b/Mathlib/Order/RelClasses.lean
index d91f6c50cd389b..63bc0a93b2ab8d 100644
--- a/Mathlib/Order/RelClasses.lean
+++ b/Mathlib/Order/RelClasses.lean
@@ -7,6 +7,7 @@ module
public import Mathlib.Logic.IsEmpty.Basic
public import Mathlib.Order.OrderDual
+public import Mathlib.Tactic.CrossRefAttribute
public import Mathlib.Tactic.MkIffOfInductiveProp
/-!
@@ -266,6 +267,7 @@ theorem wellFoundedGT_dual_iff (α : Type*) [LT α] : WellFoundedGT αᵒᵈ ↔
⟨fun h => ⟨h.wf⟩, fun h => ⟨h.wf⟩⟩
/-- A well order is a well-founded linear order. -/
+@[wikidata Q659746]
class IsWellOrder (α : Type u) (r : α → α → Prop) : Prop
extends IsWellFounded α r, Std.Trichotomous r
diff --git a/Mathlib/Probability/Moments/Variance.lean b/Mathlib/Probability/Moments/Variance.lean
index 790187496a04c3..c5b09a75a54bd2 100644
--- a/Mathlib/Probability/Moments/Variance.lean
+++ b/Mathlib/Probability/Moments/Variance.lean
@@ -60,6 +60,7 @@ def evariance : ℝ≥0∞ := ∫⁻ ω, ‖X ω - μ[X]‖ₑ ^ 2 ∂μ
variable (X μ) in
/-- The `ℝ`-valued variance of a real-valued random variable defined by applying `ENNReal.toReal`
to `evariance`. -/
+@[wikidata Q175199]
def variance : ℝ := (evariance X μ).toReal
/-- The `ℝ≥0∞`-valued variance of the real-valued random variable `X` according to the measure `μ`.
diff --git a/Mathlib/SetTheory/Cardinal/Cofinality/Basic.lean b/Mathlib/SetTheory/Cardinal/Cofinality/Basic.lean
index 7daf0c1328c12e..945495351dc27f 100644
--- a/Mathlib/SetTheory/Cardinal/Cofinality/Basic.lean
+++ b/Mathlib/SetTheory/Cardinal/Cofinality/Basic.lean
@@ -31,6 +31,7 @@ variable [Preorder α]
variable (α) in
/-- The cofinality of a preorder is the smallest cardinality of a cofinal subset. -/
+@[wikidata Q1283623]
def cof : Cardinal :=
⨅ s : {s : Set α // IsCofinal s}, #s
diff --git a/Mathlib/SetTheory/Cardinal/Defs.lean b/Mathlib/SetTheory/Cardinal/Defs.lean
index a169cf2dc00fa0..7889e0f375c920 100644
--- a/Mathlib/SetTheory/Cardinal/Defs.lean
+++ b/Mathlib/SetTheory/Cardinal/Defs.lean
@@ -6,6 +6,7 @@ Authors: Johannes Hölzl, Mario Carneiro, Floris van Doorn
module
public import Mathlib.Data.ULift
+public import Mathlib.Tactic.CrossRefAttribute
public import Mathlib.Tactic.PPWithUniv
public import Mathlib.Util.Delaborators
@@ -76,7 +77,7 @@ instance Cardinal.isEquivalent : Setoid (Type u) where
/-- `Cardinal.{u}` is the type of cardinal numbers in `Type u`,
defined as the quotient of `Type u` by existence of an equivalence
(a bijection with explicit inverse). -/
-@[pp_with_univ]
+@[pp_with_univ, wikidata Q163875]
def Cardinal : Type (u + 1) :=
Quotient Cardinal.isEquivalent
diff --git a/Mathlib/SetTheory/Ordinal/Basic.lean b/Mathlib/SetTheory/Ordinal/Basic.lean
index 1f3663a02c95f3..d5641068269f63 100644
--- a/Mathlib/SetTheory/Ordinal/Basic.lean
+++ b/Mathlib/SetTheory/Ordinal/Basic.lean
@@ -101,7 +101,7 @@ instance Ordinal.isEquivalent : Setoid WellOrder where
⟨fun _ => ⟨RelIso.refl _⟩, fun ⟨e⟩ => ⟨e.symm⟩, fun ⟨e₁⟩ ⟨e₂⟩ => ⟨e₁.trans e₂⟩⟩
/-- `Ordinal.{u}` is the type of well orders in `Type u`, up to order isomorphism. -/
-@[pp_with_univ]
+@[pp_with_univ, wikidata Q191780]
def Ordinal : Type (u + 1) :=
Quotient Ordinal.isEquivalent
diff --git a/Mathlib/Topology/Connected/Basic.lean b/Mathlib/Topology/Connected/Basic.lean
index c8b1080eac1bca..6d2c298d792167 100644
--- a/Mathlib/Topology/Connected/Basic.lean
+++ b/Mathlib/Topology/Connected/Basic.lean
@@ -649,6 +649,7 @@ class PreconnectedSpace (α : Type u) [TopologicalSpace α] : Prop where
export PreconnectedSpace (isPreconnected_univ)
/-- A connected space is a nonempty one where there is no non-trivial open partition. -/
+@[wikidata Q1491995]
class ConnectedSpace (α : Type u) [TopologicalSpace α] : Prop extends PreconnectedSpace α where
/-- A connected space is nonempty. -/
toNonempty : Nonempty α
diff --git a/Mathlib/Topology/Defs/Filter.lean b/Mathlib/Topology/Defs/Filter.lean
index 7b96de027f3295..9dadbcb88ff98e 100644
--- a/Mathlib/Topology/Defs/Filter.lean
+++ b/Mathlib/Topology/Defs/Filter.lean
@@ -279,6 +279,7 @@ def IsCompact (s : Set X) :=
variable (X) in
/-- Type class for compact spaces. Separation is sometimes included in the definition, especially
in the French literature, but we do not include it here. -/
+@[wikidata Q381892]
class CompactSpace : Prop where
/-- In a compact space, `Set.univ` is a compact set. -/
isCompact_univ : IsCompact (Set.univ : Set X)
diff --git a/Mathlib/Topology/Homotopy/Basic.lean b/Mathlib/Topology/Homotopy/Basic.lean
index 80f21dab4daeed..fdebd05ecd645e 100644
--- a/Mathlib/Topology/Homotopy/Basic.lean
+++ b/Mathlib/Topology/Homotopy/Basic.lean
@@ -73,6 +73,7 @@ When possible, instead of parametrizing results over `(f : ContinuousMap.Homotop
you should parametrize over `{F : Type*} [HomotopyLike F f₀ f₁] (f : F)`.
When you extend this structure, make sure to extend `ContinuousMap.HomotopyLike`. -/
+@[wikidata Q746083]
structure Homotopy (f₀ f₁ : C(X, Y)) extends C(I × X, Y) where
/-- value of the homotopy at 0 -/
map_zero_left : ∀ x, toFun (0, x) = f₀ x
diff --git a/Mathlib/Topology/MetricSpace/Defs.lean b/Mathlib/Topology/MetricSpace/Defs.lean
index ab9838b78440c5..e580baef4437d1 100644
--- a/Mathlib/Topology/MetricSpace/Defs.lean
+++ b/Mathlib/Topology/MetricSpace/Defs.lean
@@ -66,6 +66,7 @@ This e.g. ensures that we do not get a diamond when doing
`[MetricSpace α] [MetricSpace β] : TopologicalSpace (α × β)`:
The product metric and product topology agree, but not definitionally so.
See Note [forgetful inheritance]. -/
+@[wikidata Q180953]
class MetricSpace (α : Type u) : Type u extends PseudoMetricSpace α where
eq_of_dist_eq_zero : ∀ {x y : α}, dist x y = 0 → x = y
From 26a595e71188b7f8cfc013bd4cf0d5b3d7469993 Mon Sep 17 00:00:00 2001
From: Vlad Tsyrklevich
Date: Sun, 14 Jun 2026 20:07:25 +0000
Subject: [PATCH 0033/1300] doc(SimpleGraph): fix incorrect theorem/instance
names (#40596)
Taken from this [Zulip thread.](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/.22Main.20declarations.22.20which.20do.20not.20exist/with/602887714)
---
Mathlib/Combinatorics/SimpleGraph/DeleteEdges.lean | 2 +-
Mathlib/Combinatorics/SimpleGraph/LapMatrix.lean | 2 +-
Mathlib/Combinatorics/SimpleGraph/Subgraph.lean | 2 +-
Mathlib/Combinatorics/SimpleGraph/Tutte.lean | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/Mathlib/Combinatorics/SimpleGraph/DeleteEdges.lean b/Mathlib/Combinatorics/SimpleGraph/DeleteEdges.lean
index fc01f89848f26f..5e901085b73c54 100644
--- a/Mathlib/Combinatorics/SimpleGraph/DeleteEdges.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/DeleteEdges.lean
@@ -24,7 +24,7 @@ case.
* `SimpleGraph.deleteIncidenceSet G v` is the simple graph `G` with the incidence set of `v`
removed from the edge set.
-* `SimpleGraph.deleteFar G p r` is the predicate that a graph is `r`-*delete-far* from a property
+* `SimpleGraph.DeleteFar G p r` is the predicate that a graph is `r`-*delete-far* from a property
`p`, that is, at least `r` edges must be deleted to satisfy `p`.
-/
diff --git a/Mathlib/Combinatorics/SimpleGraph/LapMatrix.lean b/Mathlib/Combinatorics/SimpleGraph/LapMatrix.lean
index 736609b3af813f..ee5bd217f3925c 100644
--- a/Mathlib/Combinatorics/SimpleGraph/LapMatrix.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/LapMatrix.lean
@@ -19,7 +19,7 @@ This module defines the Laplacian matrix of a graph, and proves some of its elem
* `SimpleGraph.degMatrix`: The degree matrix of a simple graph
* `SimpleGraph.lapMatrix`: The Laplacian matrix of a simple graph, defined as the difference
between the degree matrix and the adjacency matrix.
-* `isPosSemidef_lapMatrix`: The Laplacian matrix is positive semidefinite.
+* `posSemidef_lapMatrix`: The Laplacian matrix is positive semidefinite.
* `card_connectedComponent_eq_finrank_ker_toLin'_lapMatrix`:
The number of connected components in a graph
is the dimension of the nullspace of its Laplacian matrix.
diff --git a/Mathlib/Combinatorics/SimpleGraph/Subgraph.lean b/Mathlib/Combinatorics/SimpleGraph/Subgraph.lean
index 34b7f8f96a0cc0..bc9ccfecd07846 100644
--- a/Mathlib/Combinatorics/SimpleGraph/Subgraph.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/Subgraph.lean
@@ -28,7 +28,7 @@ sub-relation of the adjacency relation of the simple graph.
* `Subgraph.IsSpanning` for whether a subgraph is a spanning subgraph and
`Subgraph.IsInduced` for whether a subgraph is an induced subgraph.
-* Instances for `Lattice (Subgraph G)` and `BoundedOrder (Subgraph G)`.
+* Instances for `DistribLattice G.Subgraph` and `BoundedOrder (Subgraph G)`.
* `SimpleGraph.toSubgraph`: If a `SimpleGraph` is a subgraph of another, then you can turn it
into a member of the larger graph's `SimpleGraph.Subgraph` type.
diff --git a/Mathlib/Combinatorics/SimpleGraph/Tutte.lean b/Mathlib/Combinatorics/SimpleGraph/Tutte.lean
index 8e8cad783bda75..e905473f5226e5 100644
--- a/Mathlib/Combinatorics/SimpleGraph/Tutte.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/Tutte.lean
@@ -16,7 +16,7 @@ public import Mathlib.Data.Fintype.Card
## Main definitions
-* `SimpleGraph.TutteViolator G u` is a set of vertices `u` such that the amount of
+* `SimpleGraph.IsTutteViolator G u` is a set of vertices `u` such that the amount of
odd components left after deleting `u` from `G` is larger than the number of vertices in `u`.
This certifies non-existence of a perfect matching.
From 439c664b0475c29168b177bc60839c7549e5d894 Mon Sep 17 00:00:00 2001
From: Michael Rothgang <10105016+grunweg@users.noreply.github.com>
Date: Mon, 15 Jun 2026 00:50:38 +0000
Subject: [PATCH 0034/1300] chore(Counterexamples/DirectSumIsInternal): fix
defLemma error (#40592)
As could be seen from running #lint in that file.
The new `defProp` linter in Lean core also flagged this.
---
Counterexamples/DirectSumIsInternal.lean | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/Counterexamples/DirectSumIsInternal.lean b/Counterexamples/DirectSumIsInternal.lean
index c0f2aff71c4e92..c46125e7464b48 100644
--- a/Counterexamples/DirectSumIsInternal.lean
+++ b/Counterexamples/DirectSumIsInternal.lean
@@ -19,7 +19,6 @@ This file demonstrates why `DirectSum.isInternal_submodule_of_iSupIndep_of_iSup_
take `Ring R` and not `Semiring R`.
-/
-
namespace Counterexample
theorem UnitsInt.one_ne_neg_one : (1 : ℤˣ) ≠ -1 := by decide
@@ -57,9 +56,8 @@ theorem withSign.isCompl : IsCompl ℤ≥0 ℤ≤0 := by
· exact Submodule.mem_sup_left (mem_withSign_one.mpr hp)
· exact Submodule.mem_sup_right (mem_withSign_neg_one.mpr hn)
-def withSign.independent : iSupIndep withSign := by
- apply
- (iSupIndep_pair UnitsInt.one_ne_neg_one _).mpr withSign.isCompl.disjoint
+lemma withSign.independent : iSupIndep withSign := by
+ apply (iSupIndep_pair UnitsInt.one_ne_neg_one _).mpr withSign.isCompl.disjoint
intro i
fin_cases i <;> simp
From 261b5e314a7e71cff47a151b610fa834b3e6a7ae Mon Sep 17 00:00:00 2001
From: "mathlib-nolints[bot]"
<258989889+mathlib-nolints[bot]@users.noreply.github.com>
Date: Mon, 15 Jun 2026 01:30:21 +0000
Subject: [PATCH 0035/1300] chore: remove unnecessary set_option lines (#40609)
I removed 24 unnecessary `set_option` line(s) across 9 file(s).
---
Mathlib/Analysis/CStarAlgebra/Matrix.lean | 1 -
Mathlib/Analysis/LocallyConvex/WeakDual.lean | 4 ----
Mathlib/Analysis/Normed/Order/Lattice.lean | 1 -
Mathlib/LinearAlgebra/RootSystem/Irreducible.lean | 2 --
Mathlib/RepresentationTheory/Irreducible.lean | 6 ------
Mathlib/RepresentationTheory/Rep/Iso.lean | 3 ---
Mathlib/RepresentationTheory/Semisimple.lean | 1 -
Mathlib/RingTheory/Extension/Cotangent/Basis.lean | 1 -
Mathlib/Topology/UniformSpace/Dini.lean | 5 -----
9 files changed, 24 deletions(-)
diff --git a/Mathlib/Analysis/CStarAlgebra/Matrix.lean b/Mathlib/Analysis/CStarAlgebra/Matrix.lean
index 2dae014aca2719..c304aa4b11eae3 100644
--- a/Mathlib/Analysis/CStarAlgebra/Matrix.lean
+++ b/Mathlib/Analysis/CStarAlgebra/Matrix.lean
@@ -151,7 +151,6 @@ def l2OpNormedRingAux : NormedRing (Matrix n n 𝕜) :=
open Bornology Filter
open scoped Topology Uniformity
-set_option backward.isDefEq.respectTransparency false in
/-- The metric on `Matrix m n 𝕜` arising from the operator norm given by the identification with
(continuous) linear maps of `EuclideanSpace`. -/
@[instance_reducible]
diff --git a/Mathlib/Analysis/LocallyConvex/WeakDual.lean b/Mathlib/Analysis/LocallyConvex/WeakDual.lean
index d88c1193fa6504..92f685b76fa21a 100644
--- a/Mathlib/Analysis/LocallyConvex/WeakDual.lean
+++ b/Mathlib/Analysis/LocallyConvex/WeakDual.lean
@@ -179,7 +179,6 @@ theorem mem_span_iff_bound {f : ι → E →ₗ[𝕜] 𝕜} (φ : E →ₗ[𝕜]
variable [AddCommGroup F] [Module 𝕜 F] (B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜)
-set_option backward.isDefEq.respectTransparency false in
/-- The Weak Representation Theorem: Every continuous functional on `E` endowed with
the `σ(E, F; B)`-topology is of the form `x ↦ B(x, y)` for some `y : F`. -/
theorem dualEmbedding_surjective : Function.Surjective (WeakBilin.eval B) := fun f ↦ by
@@ -212,14 +211,12 @@ section Topology
variable [NormedField 𝕜] [AddCommGroup E] [Module 𝕜 E] [AddCommGroup F] [Module 𝕜 F]
-set_option backward.isDefEq.respectTransparency false in
theorem LinearMap.weakBilin_withSeminorms (B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜) :
WithSeminorms (LinearMap.toSeminormFamily B : F → Seminorm 𝕜 (WeakBilin B)) :=
let e : F ≃ (Σ _ : F, Fin 1) := .symm <| .sigmaUnique _ _
withSeminorms_induced (withSeminorms_pi (fun _ ↦ norm_withSeminorms 𝕜 𝕜))
(LinearMap.ltoFun 𝕜 F 𝕜 𝕜 ∘ₗ B : (WeakBilin B) →ₗ[𝕜] (F → 𝕜)) |>.congr_equiv e
-set_option backward.isDefEq.respectTransparency false in
theorem LinearMap.hasBasis_weakBilin (B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜) :
(𝓝 (0 : WeakBilin B)).HasBasis (· ∈ B.toSeminormFamily.basisSets) _root_.id :=
LinearMap.weakBilin_withSeminorms B |>.hasBasis
@@ -231,7 +228,6 @@ section LocallyConvex
variable [NormedField 𝕜] [AddCommGroup E] [Module 𝕜 E] [AddCommGroup F] [Module 𝕜 F]
variable [NormedSpace ℝ 𝕜] [Module ℝ E] [IsScalarTower ℝ 𝕜 E]
-set_option backward.isDefEq.respectTransparency false in
instance WeakBilin.locallyConvexSpace {B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜} :
LocallyConvexSpace ℝ (WeakBilin B) :=
B.weakBilin_withSeminorms.toLocallyConvexSpace
diff --git a/Mathlib/Analysis/Normed/Order/Lattice.lean b/Mathlib/Analysis/Normed/Order/Lattice.lean
index 898f8fc84305e4..dc4bd9ccc0a8b4 100644
--- a/Mathlib/Analysis/Normed/Order/Lattice.lean
+++ b/Mathlib/Analysis/Normed/Order/Lattice.lean
@@ -137,7 +137,6 @@ instance (priority := 100) HasSolidNorm.continuousInf : ContinuousInf α := by
((continuous_snd.tendsto q).sub <| tendsto_const_nhds).norm
simp
-set_option backward.isDefEq.respectTransparency false in
-- see Note [lower instance priority]
instance (priority := 100) HasSolidNorm.continuousSup {α : Type*}
[NormedAddCommGroup α] [Lattice α] [HasSolidNorm α] [IsOrderedAddMonoid α] : ContinuousSup α :=
diff --git a/Mathlib/LinearAlgebra/RootSystem/Irreducible.lean b/Mathlib/LinearAlgebra/RootSystem/Irreducible.lean
index d108d2127bac23..db9d53ee6aad0c 100644
--- a/Mathlib/LinearAlgebra/RootSystem/Irreducible.lean
+++ b/Mathlib/LinearAlgebra/RootSystem/Irreducible.lean
@@ -122,7 +122,6 @@ lemma invtRootSubmodule.eq_span_root {K : Type*} [Field K] [NeZero (2 : K)]
· exact LinearMap.mem_ker.mp (invtRootSubmodule.le_ker_coroot' q hk htQ)
exact P.eq_zero_iff_forall_coroot'_eq_zero.mpr h_ker
-set_option backward.isDefEq.respectTransparency false in
lemma isSimpleModule_weylGroupRootRep_iff [Nontrivial M] :
IsSimpleModule R[P.weylGroup] P.weylGroupRootRep.asModule ↔
∀ (q : Submodule R M), (∀ i, q ∈ invtSubmodule (P.reflection i)) → q ≠ ⊥ → q = ⊤ := by
@@ -160,7 +159,6 @@ instance [P.IsIrreducible] : P.flip.IsIrreducible where
eq_top_of_invtSubmodule_reflection := IsIrreducible.eq_top_of_invtSubmodule_coreflection (P := P)
eq_top_of_invtSubmodule_coreflection := IsIrreducible.eq_top_of_invtSubmodule_reflection (P := P)
-set_option backward.isDefEq.respectTransparency false in
lemma isSimpleModule_weylGroupRootRep [P.IsIrreducible] :
IsSimpleModule R[P.weylGroup] P.weylGroupRootRep.asModule :=
have := IsIrreducible.nontrivial P
diff --git a/Mathlib/RepresentationTheory/Irreducible.lean b/Mathlib/RepresentationTheory/Irreducible.lean
index ceaf936a5ef501..85891bec9b62f0 100644
--- a/Mathlib/RepresentationTheory/Irreducible.lean
+++ b/Mathlib/RepresentationTheory/Irreducible.lean
@@ -30,7 +30,6 @@ subrepresentations. -/
abbrev IsIrreducible :=
IsSimpleOrder (Subrepresentation ρ)
-set_option backward.isDefEq.respectTransparency false in
theorem irreducible_iff_isSimpleModule_asModule :
IsIrreducible ρ ↔ IsSimpleModule k[G] ρ.asModule := by
rw [isSimpleModule_iff]
@@ -49,18 +48,15 @@ namespace IsIrreducible
variable {ρ σ} (f : IntertwiningMap ρ σ) [IsIrreducible ρ]
-set_option backward.isDefEq.respectTransparency false in
instance : IsSimpleModule k[G] ρ.asModule :=
(irreducible_iff_isSimpleModule_asModule ρ).mp inferInstance
open Function IntertwiningMap
-set_option backward.isDefEq.respectTransparency false in
theorem injective_or_eq_zero : Injective f ∨ f = 0 := by
rw [← LinearEquiv.map_eq_zero_iff (equivLinearMapAsModule ρ σ)]
exact LinearMap.injective_or_eq_zero (equivLinearMapAsModule ρ σ f)
-set_option backward.isDefEq.respectTransparency false in
theorem bijective_or_eq_zero [IsIrreducible σ] : Bijective f ∨ f = 0 := by
rw [← LinearEquiv.map_eq_zero_iff (equivLinearMapAsModule ρ σ)]
exact LinearMap.bijective_or_eq_zero (equivLinearMapAsModule ρ σ f)
@@ -70,7 +66,6 @@ instance [IsIrreducible σ] [IsEmpty (Equiv ρ σ)] : Subsingleton (Intertwining
fun h ↦ isEmpty_iff.mp inferInstance <| (f - g).ofBijective h⟩
variable [FiniteDimensional k V] [IsAlgClosed k]
-set_option backward.isDefEq.respectTransparency false in
variable (f : IntertwiningMap ρ ρ) in
theorem algebraMap_intertwiningMap_bijective_of_isAlgClosed :
Bijective (algebraMap k (IntertwiningMap ρ ρ)) := by
@@ -85,7 +80,6 @@ variable (ρ) in
exact CommSemiring.finrank_self k
open scoped IsMulCommutative in
-set_option backward.isDefEq.respectTransparency false in
include ρ in
variable (ρ) in
theorem finrank_eq_one_of_isMulCommutative [IsMulCommutative G] : Module.finrank k V = 1 := by
diff --git a/Mathlib/RepresentationTheory/Rep/Iso.lean b/Mathlib/RepresentationTheory/Rep/Iso.lean
index 8699f639dfaab0..e9c9a47ceff40a 100644
--- a/Mathlib/RepresentationTheory/Rep/Iso.lean
+++ b/Mathlib/RepresentationTheory/Rep/Iso.lean
@@ -83,7 +83,6 @@ theorem to_Module_monoidAlgebra_map_aux {k G : Type*} [CommRing k] [Monoid G] (V
· intro r g w
simp only [map_smul, w, LinearMap.smul_apply]
-set_option backward.isDefEq.respectTransparency false in
/-- Auxiliary definition for `toModuleMonoidAlgebra`. -/
def toModuleMonoidAlgebraMap {V W : Rep.{w} k G} (f : V ⟶ W) :
ModuleCat.of k[G] V.ρ.asModule ⟶ ModuleCat.of k[G] W.ρ.asModule :=
@@ -92,7 +91,6 @@ def toModuleMonoidAlgebraMap {V W : Rep.{w} k G} (f : V ⟶ W) :
map_smul' := fun r x => to_Module_monoidAlgebra_map_aux V.V W.V V.ρ W.ρ
f.hom.toLinearMap f.hom.2 r x }
-set_option backward.isDefEq.respectTransparency false in
/-- Functorially convert a representation of `G` into a module over `k[G]`. -/
def toModuleMonoidAlgebra : Rep.{w} k G ⥤ ModuleCat k[G] where
obj V := ModuleCat.of _ V.ρ.asModule
@@ -196,7 +194,6 @@ variable {k G : Type u} [CommRing k] [Monoid G] in
instance : CategoryTheory.EnoughProjectives (Rep.{max w u} k G) :=
equivalenceModuleMonoidAlgebra.enoughProjectives_iff.2 ModuleCat.enoughProjectives.{max w u}
-set_option backward.isDefEq.respectTransparency false in
instance free_projective {α : Type (max w u)} :
Projective (free k G α) :=
equivalenceModuleMonoidAlgebra.toAdjunction.projective_of_map_projective _ <|
diff --git a/Mathlib/RepresentationTheory/Semisimple.lean b/Mathlib/RepresentationTheory/Semisimple.lean
index 9ed66a1397f387..b21260eca4f9b7 100644
--- a/Mathlib/RepresentationTheory/Semisimple.lean
+++ b/Mathlib/RepresentationTheory/Semisimple.lean
@@ -34,7 +34,6 @@ variable [Monoid G] [Field k] [AddCommGroup V] [Module k V]
abbrev IsSemisimpleRepresentation :=
ComplementedLattice (Subrepresentation ρ)
-set_option backward.isDefEq.respectTransparency false in
theorem isSemisimpleRepresentation_iff_isSemisimpleModule_asModule :
IsSemisimpleRepresentation ρ ↔ IsSemisimpleModule k[G] ρ.asModule := by
rw [isSemisimpleModule_iff]
diff --git a/Mathlib/RingTheory/Extension/Cotangent/Basis.lean b/Mathlib/RingTheory/Extension/Cotangent/Basis.lean
index df9d59125b1c4b..0d6ac75f554800 100644
--- a/Mathlib/RingTheory/Extension/Cotangent/Basis.lean
+++ b/Mathlib/RingTheory/Extension/Cotangent/Basis.lean
@@ -150,7 +150,6 @@ lemma tensorCotangentHom_tmul (x : D.presLeft.toExtension.ker) :
def tensorCotangentInv : P.toExtension.Cotangent →ₗ[S] S ⊗[D.T] D.presLeft.toExtension.Cotangent :=
b.constr S fun i : σ ↦ 1 ⊗ₜ Extension.Cotangent.mk (D.kerGen i)
-set_option backward.isDefEq.respectTransparency false in
@[simp]
lemma tensorCotangentInv_apply (i : σ) :
D.tensorCotangentInv (b i) = 1 ⊗ₜ Extension.Cotangent.mk (D.kerGen i) :=
diff --git a/Mathlib/Topology/UniformSpace/Dini.lean b/Mathlib/Topology/UniformSpace/Dini.lean
index 34c07a3509d4fb..e5daead8c0747d 100644
--- a/Mathlib/Topology/UniformSpace/Dini.lean
+++ b/Mathlib/Topology/UniformSpace/Dini.lean
@@ -103,7 +103,6 @@ end Monotone
namespace Antitone
-set_option backward.isDefEq.respectTransparency false in
/-- **Dini's theorem**: if `F n` is a monotone decreasing collection of continuous functions on a
converging pointwise to a continuous function `f`, then `F n` converges locally uniformly to `f`. -/
lemma tendstoLocallyUniformly_of_forall_tendsto
@@ -112,7 +111,6 @@ lemma tendstoLocallyUniformly_of_forall_tendsto
TendstoLocallyUniformly F f atTop :=
Monotone.tendstoLocallyUniformly_of_forall_tendsto (G := Gᵒᵈ) hF_cont hF_anti hf h_tendsto
-set_option backward.isDefEq.respectTransparency false in
/-- **Dini's theorem**: if `F n` is a monotone decreasing collection of continuous functions on a
set `s` converging pointwise to a continuous function `f`, then `F n` converges locally uniformly
to `f`. -/
@@ -122,7 +120,6 @@ lemma tendstoLocallyUniformlyOn_of_forall_tendsto {s : Set α}
TendstoLocallyUniformlyOn F f atTop s :=
Monotone.tendstoLocallyUniformlyOn_of_forall_tendsto (G := Gᵒᵈ) hF_cont hF_anti hf h_tendsto
-set_option backward.isDefEq.respectTransparency false in
/-- **Dini's theorem**: if `F n` is a monotone decreasing collection of continuous functions on a
compact space converging pointwise to a continuous function `f`, then `F n` converges uniformly
to `f`. -/
@@ -131,7 +128,6 @@ lemma tendstoUniformly_of_forall_tendsto [CompactSpace α] (hF_cont : ∀ i, Con
TendstoUniformly F f atTop :=
Monotone.tendstoUniformly_of_forall_tendsto (G := Gᵒᵈ) hF_cont hF_anti hf h_tendsto
-set_option backward.isDefEq.respectTransparency false in
/-- **Dini's theorem**: if `F n` is a monotone decreasing collection of continuous functions on a
compact set `s` converging pointwise to a continuous `f`, then `F n` converges uniformly to `f`. -/
lemma tendstoUniformlyOn_of_forall_tendsto {s : Set α} (hs : IsCompact s)
@@ -157,7 +153,6 @@ lemma tendsto_of_monotone_of_pointwise (hF_mono : Monotone F)
tendsto_of_tendstoLocallyUniformly <|
hF_mono.tendstoLocallyUniformly_of_forall_tendsto (F · |>.continuous) f.continuous h_tendsto
-set_option backward.isDefEq.respectTransparency false in
/-- **Dini's theorem**: if `F n` is a monotone decreasing collection of continuous functions
converging pointwise to a continuous function `f`, then `F n` converges to `f` in the
compact-open topology. -/
From e47b3cbac62dfb6ddb095cd4a2863ab3843d0238 Mon Sep 17 00:00:00 2001
From: Bryan Gin-ge Chen <5209952+bryangingechen@users.noreply.github.com>
Date: Mon, 15 Jun 2026 08:19:09 +0000
Subject: [PATCH 0036/1300] ci: update SpliceBot action (#40494)
This bump should allow `splice-bot maintainer merge? ` to work even on PRs that haven't merged `master` yet. (The core logic for that was moved into the `workflow_run` half of the action, which reads from `master` rather than the PR branch.)
---
.github/workflows/splice_bot.yaml | 2 +-
.github/workflows/splice_bot_wf_run.yaml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/splice_bot.yaml b/.github/workflows/splice_bot.yaml
index e46bce162219e8..19e1915aa0c8fe 100644
--- a/.github/workflows/splice_bot.yaml
+++ b/.github/workflows/splice_bot.yaml
@@ -9,7 +9,7 @@ permissions: {}
jobs:
call-splice-bot:
if: ${{ contains(github.event.comment.body, 'splice-bot') }}
- uses: leanprover-community/SpliceBot/.github/workflows/splice.yaml@61d21ff3cb6fde3c49b81a3016a8718e59e52f60 # master
+ uses: leanprover-community/SpliceBot/.github/workflows/splice.yaml@d86d20efffbd18f839199fdd5ec126570f7eceda # master
with:
# Optional override; omit to use the reusable workflow's default "master"
base_ref: master
diff --git a/.github/workflows/splice_bot_wf_run.yaml b/.github/workflows/splice_bot_wf_run.yaml
index d8b98abdb20c0d..b5777a10cfdd8c 100644
--- a/.github/workflows/splice_bot_wf_run.yaml
+++ b/.github/workflows/splice_bot_wf_run.yaml
@@ -45,7 +45,7 @@ jobs:
owner: leanprover-community
- name: Run splice bot action
- uses: leanprover-community/SpliceBot/.github/actions/splice-wf-run@61d21ff3cb6fde3c49b81a3016a8718e59e52f60
+ uses: leanprover-community/SpliceBot/.github/actions/splice-wf-run@d86d20efffbd18f839199fdd5ec126570f7eceda
with:
source_workflow: ${{ github.event.workflow_run.name }}
push_to_fork: leanprover-community/mathlib4_copy
From 43f7f5c74481a10840e1f071e82345be3c03b8cf Mon Sep 17 00:00:00 2001
From: Sebastien Gouezel <10818434+sgouezel@users.noreply.github.com>
Date: Mon, 15 Jun 2026 09:36:28 +0000
Subject: [PATCH 0037/1300] chore: fix non-reducible diamond in AEval (#40429)
The following fails before the PR:
```example : ((Module.AEval.instAddCommGroup a).toAdd : Add (Module.AEval R M a)) =
(Module.AEval.instAddCommMonoid a).toAddCommSemigroup.toAddCommMagma.toAdd := by
with_reducible_and_instances rfl
```
It works after the PR, modulo the instance renaming.
Co-authored-by: sgouezel
---
Mathlib/Algebra/Polynomial/Module/AEval.lean | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/Mathlib/Algebra/Polynomial/Module/AEval.lean b/Mathlib/Algebra/Polynomial/Module/AEval.lean
index ed21373297fda9..b6eb75e695dd37 100644
--- a/Mathlib/Algebra/Polynomial/Module/AEval.lean
+++ b/Mathlib/Algebra/Polynomial/Module/AEval.lean
@@ -39,6 +39,7 @@ and the action of `f` is `f • (of R M a m) = of R M a ((aeval a f) • m)`.
@[nolint unusedArguments]
def AEval (R M : Type*) {A : Type*} [CommSemiring R] [Semiring A] [Algebra R A]
[AddCommMonoid M] [Module A M] [Module R M] [IsScalarTower R A M] (_ : A) := M
+ deriving AddCommMonoid, Module R
instance AEval.instAddCommGroup {R A M} [CommSemiring R] [Semiring A] (a : A) [Algebra R A]
[AddCommGroup M] [Module A M] [Module R M] [IsScalarTower R A M] :
@@ -49,12 +50,8 @@ variable {R A M} [CommSemiring R] [Semiring A] (a : A) [Algebra R A] [AddCommMon
namespace AEval
-instance instAddCommMonoid : AddCommMonoid <| AEval R M a := inferInstanceAs (AddCommMonoid M)
-
-instance instModuleOrig : Module R <| AEval R M a := inferInstanceAs (Module R M)
-
instance instFiniteOrig [Module.Finite R M] : Module.Finite R <| AEval R M a :=
- ‹Module.Finite R M›
+ inferInstanceAs <| Module.Finite R M
noncomputable instance instModulePolynomial : Module R[X] <| AEval R M a :=
compHom M (aeval a).toRingHom
From b5ae1ef3ff123cfd169c2289640cb67f69f30bed Mon Sep 17 00:00:00 2001
From: Sebastien Gouezel <10818434+sgouezel@users.noreply.github.com>
Date: Mon, 15 Jun 2026 09:53:12 +0000
Subject: [PATCH 0038/1300] chore: remove non-reducible diamond in splitting
field (#40420)
Currently, `f.SplittingField` has two `K`-algebra structures. The PR fixes this.
Co-authored-by: sgouezel
---
Mathlib/FieldTheory/SplittingField/Construction.lean | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/Mathlib/FieldTheory/SplittingField/Construction.lean b/Mathlib/FieldTheory/SplittingField/Construction.lean
index 9123fb3c2664bb..bdeff93804b3a7 100644
--- a/Mathlib/FieldTheory/SplittingField/Construction.lean
+++ b/Mathlib/FieldTheory/SplittingField/Construction.lean
@@ -214,7 +214,7 @@ end SplittingFieldAux
def SplittingField (f : K[X]) :=
MvPolynomial (SplittingFieldAux f.natDegree f) K ⧸
RingHom.ker (MvPolynomial.aeval (R := K) id).toRingHom
-deriving Inhabited, CommRing, Algebra K
+deriving Inhabited, CommRing
namespace SplittingField
@@ -226,6 +226,8 @@ deriving instance SMul S for SplittingField f
variable {R : Type*} [CommSemiring R] [Algebra R K] in
deriving instance Algebra R, IsScalarTower R K for SplittingField f
+instance : Algebra K f.SplittingField := inferInstance
+
/-- The algebra equivalence with `SplittingFieldAux`,
which we will use to construct the field structure. -/
def algEquivSplittingFieldAux (f : K[X]) : SplittingField f ≃ₐ[K] SplittingFieldAux f.natDegree f :=
From d706b80d5719f25955af00cd0d33e2bfc3b2287f Mon Sep 17 00:00:00 2001
From: Sebastien Gouezel <10818434+sgouezel@users.noreply.github.com>
Date: Mon, 15 Jun 2026 09:53:14 +0000
Subject: [PATCH 0039/1300] chore: fix non-reducible diamond in tensor product
norms (#40452)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The following fails before the PR, succeeds after it:
```
example : (PiTensorProduct.instSeminormedAddCommGroup.toNorm : Norm (PiTensorProduct 𝕜 fun i => E i)) =
PiTensorProduct.instNorm := by
with_reducible_and_instances rfl
```
Co-authored-by: sgouezel
---
.../Normed/Module/PiTensorProduct/InjectiveSeminorm.lean | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Mathlib/Analysis/Normed/Module/PiTensorProduct/InjectiveSeminorm.lean b/Mathlib/Analysis/Normed/Module/PiTensorProduct/InjectiveSeminorm.lean
index 7669b31e356f1a..50460c1ef4893c 100644
--- a/Mathlib/Analysis/Normed/Module/PiTensorProduct/InjectiveSeminorm.lean
+++ b/Mathlib/Analysis/Normed/Module/PiTensorProduct/InjectiveSeminorm.lean
@@ -213,10 +213,10 @@ theorem injectiveSeminorm_tprod_le (m : Π (i : ι), E i) :
le_trans (injectiveSeminorm_le_projectiveSeminorm _) (projectiveSeminorm_tprod_le m)
-- Use `projectiveSeminorm` to turn the `PiTensorProduct` into a seminormed space.
--- The definition `injectiveSeminorm` is subject to deprecication in a follow-up PR. See:
+-- The definition `injectiveSeminorm` is subject to deprecation in a follow-up PR. See:
-- https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/injectiveSeminorm/with/568798633
noncomputable instance : SeminormedAddCommGroup (⨂[𝕜] i, E i) :=
- AddGroupSeminorm.toSeminormedAddCommGroup projectiveSeminorm.toAddGroupSeminorm
+ fast_instance% AddGroupSeminorm.toSeminormedAddCommGroup projectiveSeminorm.toAddGroupSeminorm
noncomputable instance : NormedSpace 𝕜 (⨂[𝕜] i, E i) := ⟨projectiveSeminorm_smul_le⟩
From 9a62f87bb95793abaaf4d1cb48d2c3ca27a17780 Mon Sep 17 00:00:00 2001
From: "mathlib-update-dependencies[bot]"
<258990618+mathlib-update-dependencies[bot]@users.noreply.github.com>
Date: Mon, 15 Jun 2026 11:02:22 +0000
Subject: [PATCH 0040/1300] chore: update Mathlib dependencies 2026-06-15
(#40623)
This PR updates the Mathlib dependencies.
---
lake-manifest.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lake-manifest.json b/lake-manifest.json
index eafa08243a29de..289958f5764546 100644
--- a/lake-manifest.json
+++ b/lake-manifest.json
@@ -65,7 +65,7 @@
"type": "git",
"subDir": null,
"scope": "leanprover-community",
- "rev": "c43d7789ff29244c1f6f7c8480342a2d2d6c0d30",
+ "rev": "c6f7103faab35720af56784a9553733832f17349",
"name": "batteries",
"manifestFile": "lake-manifest.json",
"inputRev": "main",
From ed00c7a87269a76c1eddb6e12fbed24d2fa0e3e9 Mon Sep 17 00:00:00 2001
From: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com>
Date: Mon, 15 Jun 2026 11:19:50 +0000
Subject: [PATCH 0041/1300] feat(Combinatorics/SimpleGraph/Copy):
`Is(Ind)Contained` `completeGraph` lemmas (#38549)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- `G ⊑ completeGraph W ↔ Nonempty (V ↪ W)`
- `completeGraph V ⊴ completeGraph W ↔ Nonempty (V ↪ W)`
- `G ⊴ completeGraph W → G = ⊤`
---
Mathlib/Combinatorics/SimpleGraph/Copy.lean | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/Mathlib/Combinatorics/SimpleGraph/Copy.lean b/Mathlib/Combinatorics/SimpleGraph/Copy.lean
index 7ec6f8e34559ab..3bc1aeb011955b 100644
--- a/Mathlib/Combinatorics/SimpleGraph/Copy.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/Copy.lean
@@ -462,6 +462,15 @@ theorem isIndContained_iff_exists_iso_induce : G ⊴ H ↔ ∃ s, Nonempty (G
(⊤ : SimpleGraph V) ⊴ H ↔ (⊤ : SimpleGraph V) ⊑ H :=
⟨IsIndContained.isContained, fun ⟨f⟩ ↦ ⟨f.topEmbedding⟩⟩
+theorem isContained_top_iff {G : SimpleGraph V} : G ⊑ completeGraph W ↔ Nonempty (V ↪ W) :=
+ ⟨(⟨·.some.toEmbedding⟩), (.trans (.of_le le_top) ⟨Embedding.completeGraph ·.some |>.toCopy⟩)⟩
+
+theorem top_isIndContained_top_iff : completeGraph V ⊴ completeGraph W ↔ Nonempty (V ↪ W) :=
+ ⟨(⟨·.some.toEmbedding⟩), (⟨.completeGraph ·.some⟩)⟩
+
+theorem eq_top_of_isIndContained_top (h : G ⊴ completeGraph W) : G = ⊤ :=
+ h.some.comap_eq ▸ comap_top h.some.injective
+
@[simp] lemma compl_isIndContained_compl : Gᶜ ⊴ Hᶜ ↔ G ⊴ H :=
Embedding.complEquiv.symm.nonempty_congr
From dd9f867f729dd85e5fe94ed84ca323acb2e57132 Mon Sep 17 00:00:00 2001
From: Michael Rothgang <10105016+grunweg@users.noreply.github.com>
Date: Mon, 15 Jun 2026 11:33:55 +0000
Subject: [PATCH 0042/1300] feat: custom elaborators for TangentSpace and
tangentMap(Within) (#36155)
And use these to golf the differential geometry files a bit further.
---
.../Geometry/Manifold/ContMDiffMFDeriv.lean | 18 +--
.../Geometry/Manifold/GroupLieAlgebra.lean | 8 +-
.../Manifold/IntegralCurve/Basic.lean | 9 +-
.../Manifold/MFDeriv/NormedSpace.lean | 14 +-
.../Manifold/MFDeriv/SpecificFunctions.lean | 121 +++++++++---------
.../Geometry/Manifold/MFDeriv/Tangent.lean | 6 +-
Mathlib/Geometry/Manifold/Notation.lean | 26 ++++
.../Geometry/Manifold/Riemannian/Basic.lean | 24 ++--
.../Manifold/Riemannian/PathELength.lean | 10 +-
.../Topology/FiberBundle/Constructions.lean | 2 +-
.../DifferentialGeometry/Notation/Basic.lean | 29 ++++-
11 files changed, 161 insertions(+), 106 deletions(-)
diff --git a/Mathlib/Geometry/Manifold/ContMDiffMFDeriv.lean b/Mathlib/Geometry/Manifold/ContMDiffMFDeriv.lean
index e6ab158996ea6c..fe370981f4d96c 100644
--- a/Mathlib/Geometry/Manifold/ContMDiffMFDeriv.lean
+++ b/Mathlib/Geometry/Manifold/ContMDiffMFDeriv.lean
@@ -274,18 +274,18 @@ variable [Is : IsManifold I 1 M] [I's : IsManifold I' 1 M']
is `C^m` when `m+1 ≤ n`. -/
theorem ContMDiffOn.contMDiffOn_tangentMapWithin
(hf : CMDiff[s] n f) (hmn : m + 1 ≤ n) (hs : UniqueMDiffOn I s) :
- CMDiff[(π E (TangentSpace I) ⁻¹' s)] m (tangentMapWithin I I' f s) := by
+ CMDiff[(π E (TangentSpace I) ⁻¹' s)] m (tangentMap[s] f) := by
intro x₀ hx₀
let s' : Set (TangentBundle I M) := (π E (TangentSpace I) ⁻¹' s)
let b₁ : TangentBundle I M → M := fun p ↦ p.1
- let v : Π (y : TangentBundle I M), TangentSpace I (b₁ y) := fun y ↦ y.2
+ let v : Π (y : TangentBundle I M), TangentSpace% (b₁ y) := fun y ↦ y.2
have hv : ContMDiffWithinAt I.tangent I.tangent m (fun y ↦ (v y : TangentBundle I M)) s' x₀ :=
contMDiffWithinAt_id
let b₂ : TangentBundle I M → M' := f ∘ b₁
have hb₂ : CMDiffAt[s'] m b₂ x₀ :=
((hf (b₁ x₀) hx₀).of_le (le_self_add.trans hmn)).comp _
(contMDiffWithinAt_proj (TangentSpace I)) (fun x h ↦ h)
- let ϕ : Π (y : TangentBundle I M), TangentSpace I (b₁ y) →L[𝕜] TangentSpace I' (b₂ y) :=
+ let ϕ : Π (y : TangentBundle I M), TangentSpace% (b₁ y) →L[𝕜] TangentSpace% (b₂ y) :=
fun y ↦ mfderiv[s] f (b₁ y)
have hϕ : CMDiffAt[s'] m (fun y ↦ ContinuousLinearMap.inCoordinates E (TangentSpace I (M := M)) E'
(TangentSpace I' (M := M')) (b₁ x₀) (b₁ y) (b₂ x₀) (b₂ y) (ϕ y)) x₀ := by
@@ -299,21 +299,21 @@ theorem ContMDiffOn.contMDiffOn_tangentMapWithin
derivative is continuous there. -/
theorem ContMDiffOn.continuousOn_tangentMapWithin (hf : CMDiff[s] n f) (hmn : 1 ≤ n)
(hs : UniqueMDiffOn I s) :
- ContinuousOn (tangentMapWithin I I' f s) (π E (TangentSpace I) ⁻¹' s) := by
- have : CMDiff[π E (TangentSpace I) ⁻¹' s] 0 (tangentMapWithin I I' f s) :=
+ ContinuousOn (tangentMap[s] f) (π E (TangentSpace I) ⁻¹' s) := by
+ have : CMDiff[π E (TangentSpace I) ⁻¹' s] 0 (tangentMap[s] f) :=
hf.contMDiffOn_tangentMapWithin hmn hs
exact this.continuousOn
/-- If a function is `C^n`, then its bundled derivative is `C^m` when `m+1 ≤ n`. -/
theorem ContMDiff.contMDiff_tangentMap (hf : CMDiff n f) (hmn : m + 1 ≤ n) :
- CMDiff m (tangentMap I I' f) := by
+ CMDiff m (tangentMap% f) := by
rw [← contMDiffOn_univ] at hf ⊢
convert! hf.contMDiffOn_tangentMapWithin hmn uniqueMDiffOn_univ
rw [tangentMapWithin_univ]
/-- If a function is `C^n`, with `1 ≤ n`, then its bundled derivative is continuous. -/
theorem ContMDiff.continuous_tangentMap (hf : CMDiff n f) (hmn : 1 ≤ n) :
- Continuous (tangentMap I I' f) := by
+ Continuous (tangentMap% f) := by
rw [← contMDiffOn_univ] at hf
rw [← continuousOn_univ]
convert! hf.continuousOn_tangentMapWithin hmn uniqueMDiffOn_univ
@@ -342,7 +342,7 @@ may seem.
TODO define splittings of vector bundles; state this result invariantly. -/
theorem tangentMap_tangentBundle_pure [Is : IsManifold I 1 M]
(p : TangentBundle I M) :
- tangentMap I I.tangent (zeroSection E (TangentSpace I)) p = ⟨⟨p.proj, 0⟩, ⟨p.2, 0⟩⟩ := by
+ tangentMap% (zeroSection (B := M) E (TangentSpace I)) p = ⟨⟨p.proj, 0⟩, ⟨p.2, 0⟩⟩ := by
rcases p with ⟨x, v⟩
have N : I.symm ⁻¹' (chartAt H x).target ∈ 𝓝 (I ((chartAt H x) x)) := by
apply IsOpen.mem_nhds
@@ -404,7 +404,7 @@ bundles. -/
lemma equivTangentBundleProd_eq_tangentMap_prod_tangentMap :
equivTangentBundleProd I M I' M' = fun (p : TangentBundle (I.prod I') (M × M')) ↦
- (tangentMap (I.prod I') I Prod.fst p, tangentMap (I.prod I') I' Prod.snd p) := by
+ (tangentMap% (@Prod.fst M M') p, tangentMap% (@Prod.snd M M') p) := by
simp only [tangentMap_prodFst, tangentMap_prodSnd]; rfl
variable [IsManifold I 1 M] [IsManifold I' 1 M']
diff --git a/Mathlib/Geometry/Manifold/GroupLieAlgebra.lean b/Mathlib/Geometry/Manifold/GroupLieAlgebra.lean
index 010d41d7d763a8..0a011da1035f3d 100644
--- a/Mathlib/Geometry/Manifold/GroupLieAlgebra.lean
+++ b/Mathlib/Geometry/Manifold/GroupLieAlgebra.lean
@@ -53,13 +53,13 @@ variable (I G) in
`GroupLieAlgebra` instead of `LieAlgebra` as the latter is taken as a generic class. -/
@[to_additive /-- The Lie algebra of an additive Lie group, i.e., its tangent space at zero. We use
the word `AddGroupLieAlgebra` instead of `LieAlgebra` as the latter is taken as a generic class. -/]
-abbrev GroupLieAlgebra : Type _ := TangentSpace I (1 : G)
+abbrev GroupLieAlgebra : Type _ := TangentSpace% (1 : G)
/-- The invariant vector field associated to a vector `v` in the Lie algebra. At a point `g`, it
is given by the image of `v` under left-multiplication by `g`. -/
@[to_additive /-- The invariant vector field associated to a vector `v` in the Lie algebra. At a
point `g`, it is given by the image of `v` under left-addition by `g`. -/]
-noncomputable def mulInvariantVectorField (v : GroupLieAlgebra I G) (g : G) : TangentSpace I g :=
+noncomputable def mulInvariantVectorField (v : GroupLieAlgebra I G) (g : G) : TangentSpace% g :=
mfderiv% (g * ·) (1 : G) v
set_option backward.isDefEq.respectTransparency false in
@@ -134,7 +134,7 @@ lemma mpullback_mulInvariantVectorField (g : G) (v : GroupLieAlgebra I G) :
set_option backward.isDefEq.respectTransparency false in
@[to_additive]
-lemma mulInvariantVectorField_eq_mpullback (g : G) (V : Π (g : G), TangentSpace I g) :
+lemma mulInvariantVectorField_eq_mpullback (g : G) (V : Π (g : G), TangentSpace% g) :
mulInvariantVectorField (V 1) g = mpullback I I (g⁻¹ * ·) V g := by
have A : 1 = g⁻¹ * g := by simp
simp only [mulInvariantVectorField, mpullback, inverse_mfderiv_mul_left]
@@ -171,7 +171,7 @@ theorem contMDiff_mulInvariantVectorField (v : GroupLieAlgebra I G) :
(equivTangentBundleProd I G I G).symm
have S₂ : CMDiff (minSmoothness 𝕜 2) F₂ := contMDiff_equivTangentBundleProd_symm
let F₃ : TangentBundle (I.prod I) (G × G) → TangentBundle I G :=
- tangentMap (I.prod I) I (fun (p : G × G) ↦ p.1 * p.2)
+ tangentMap% (fun (p : G × G) ↦ p.1 * p.2)
have S₃ : CMDiff (minSmoothness 𝕜 2) F₃ := by
apply ContMDiff.contMDiff_tangentMap _ (m := minSmoothness 𝕜 2) le_rfl
rw [A]
diff --git a/Mathlib/Geometry/Manifold/IntegralCurve/Basic.lean b/Mathlib/Geometry/Manifold/IntegralCurve/Basic.lean
index 39836315da45e3..be0efc65d54a50 100644
--- a/Mathlib/Geometry/Manifold/IntegralCurve/Basic.lean
+++ b/Mathlib/Geometry/Manifold/IntegralCurve/Basic.lean
@@ -6,6 +6,7 @@ Authors: Winston Yin
module
public import Mathlib.Geometry.Manifold.MFDeriv.Tangent
+public import Mathlib.Geometry.Manifold.Notation
/-!
# Integral curves of vector fields on a manifold
@@ -62,21 +63,21 @@ variable
/-- If `γ : ℝ → M` is $C^1$ on `s : Set ℝ` and `v` is a vector field on `M`,
`IsMIntegralCurveOn γ v s` means `γ t` is tangent to `v (γ t)` for all `t ∈ s`. The value of `γ`
outside of `s` is irrelevant and considered junk. -/
-def IsMIntegralCurveOn (γ : ℝ → M) (v : (x : M) → TangentSpace I x) (s : Set ℝ) : Prop :=
+def IsMIntegralCurveOn (γ : ℝ → M) (v : (x : M) → TangentSpace% x) (s : Set ℝ) : Prop :=
∀ t ∈ s, HasMFDerivAt[s] γ t ((1 : ℝ →L[ℝ] ℝ).smulRight <| v (γ t))
/-- If `v` is a vector field on `M` and `t₀ : ℝ`, `IsMIntegralCurveAt γ v t₀` means `γ : ℝ → M` is a
local integral curve of `v` in a neighbourhood containing `t₀`. The value of `γ` outside of this
interval is irrelevant and considered junk. -/
-def IsMIntegralCurveAt (γ : ℝ → M) (v : (x : M) → TangentSpace I x) (t₀ : ℝ) : Prop :=
+def IsMIntegralCurveAt (γ : ℝ → M) (v : (x : M) → TangentSpace% x) (t₀ : ℝ) : Prop :=
∀ᶠ t in 𝓝 t₀, HasMFDerivAt% γ t ((1 : ℝ →L[ℝ] ℝ).smulRight <| v (γ t))
/-- If `v : M → TM` is a vector field on `M`, `IsMIntegralCurve γ v` means `γ : ℝ → M` is a global
integral curve of `v`. That is, `γ t` is tangent to `v (γ t)` for all `t : ℝ`. -/
-def IsMIntegralCurve (γ : ℝ → M) (v : (x : M) → TangentSpace I x) : Prop :=
+def IsMIntegralCurve (γ : ℝ → M) (v : (x : M) → TangentSpace% x) : Prop :=
∀ t : ℝ, HasMFDerivAt% γ t ((1 : ℝ →L[ℝ] ℝ).smulRight (v (γ t)))
-variable {γ γ' : ℝ → M} {v : (x : M) → TangentSpace I x} {s s' : Set ℝ} {t₀ : ℝ}
+variable {γ γ' : ℝ → M} {v : (x : M) → TangentSpace% x} {s s' : Set ℝ} {t₀ : ℝ}
lemma IsMIntegralCurve.isMIntegralCurveOn (h : IsMIntegralCurve γ v) (s : Set ℝ) :
IsMIntegralCurveOn γ v s := fun t _ ↦ (h t).hasMFDerivWithinAt
diff --git a/Mathlib/Geometry/Manifold/MFDeriv/NormedSpace.lean b/Mathlib/Geometry/Manifold/MFDeriv/NormedSpace.lean
index 45c0236b5d06fe..0cc8f598e3708b 100644
--- a/Mathlib/Geometry/Manifold/MFDeriv/NormedSpace.lean
+++ b/Mathlib/Geometry/Manifold/MFDeriv/NormedSpace.lean
@@ -276,15 +276,15 @@ This lemma phrases the formula using the equiv `NormedSpace.fromTangentSpace`, w
canonical identification. (It would also be possible to phrase the formula without this equiv,
instead using casting and definitional abuse.) -/
private lemma HasMFDerivAt.smul
- {f' : TangentSpace I x →L[𝕜] 𝕜}
+ {f' : TangentSpace% x →L[𝕜] 𝕜}
(hs : HasMFDerivAt% f x ((fromTangentSpace (f x)).symm.toContinuousLinearMap ∘L f'))
- {g' : TangentSpace I x →L[𝕜] V}
+ {g' : TangentSpace% x →L[𝕜] V}
(hg : HasMFDerivAt% g x ((fromTangentSpace (g x)).symm.toContinuousLinearMap ∘L g')) :
-- canonically identify `g'` with a linear map into the tangent space at `(f • g) x`
- letI g'_ : TangentSpace I x →L[𝕜] TangentSpace 𝓘(𝕜, V) ((f • g) x) :=
+ letI g'_ : TangentSpace% x →L[𝕜] TangentSpace 𝓘(𝕜, V) ((f • g) x) :=
(fromTangentSpace _).symm.toContinuousLinearMap ∘L g'
-- canonically identify `g x` with a linear map into a tangent space at `(f • g) x`
- letI gx : 𝕜 →L[𝕜] TangentSpace 𝓘(𝕜, V) ((f • g) x) :=
+ letI gx : 𝕜 →L[𝕜] TangentSpace% ((f • g) x) :=
toSpanSingleton 𝕜 ((fromTangentSpace _).symm (g x))
-- now the main statement typechecks
HasMFDerivAt% (f • g) x (f x • g'_ + gx ∘L f') := by
@@ -382,7 +382,7 @@ typecheck we need a phrasing involving the canonical identification `NormedSpace
between the vector space `V` and the tangent space to this vector space at any point. This is
because two different tangent spaces (at `(f • g) x` and `g x`) appear in the equation. -/
private lemma fromTangentSpace_mfderiv_smul_apply (hf : MDiffAt f x) (hg : MDiffAt g x)
- (v : TangentSpace I x) :
+ (v : TangentSpace% x) :
fromTangentSpace _ (mfderiv% (f • g) x v)
= f x • fromTangentSpace _ (mfderiv% g x v) + fromTangentSpace _ (mfderiv% f x v) • g x := by
simpa using congr($(fromTangentSpace_mfderiv_smul hf hg) v)
@@ -400,7 +400,7 @@ because two different tangent spaces (at `(f • g) x` and `g x`) appear in the
This is a defeq variant of the main lemma `fromTangentSpace_mfderiv_smul_apply`, in which we work in
the tangent space at `f x • g x` (the simp-normal form) rather than at `(f • g) x`. -/
private lemma fromTangentSpace_mfderiv_smul_apply' (hf : MDiffAt f x) (hg : MDiffAt g x)
- (v : TangentSpace I x) :
+ (v : TangentSpace% x) :
fromTangentSpace (f x • g x) (mfderiv% (f • g) x v)
= f x • fromTangentSpace _ (mfderiv% g x v) + fromTangentSpace _ (mfderiv% f x v) • g x :=
fromTangentSpace_mfderiv_smul_apply hf hg v
@@ -428,7 +428,7 @@ Future: this could be generalised to functions into additive torsors over abelia
-/
@[expose]
noncomputable def mvfderiv (g : M → F) :
- Π x : M, TangentSpace I x →L[𝕜] F :=
+ Π x : M, TangentSpace% x →L[𝕜] F :=
fun x ↦ (NormedSpace.fromTangentSpace <| g x).toContinuousLinearMap ∘L (mfderiv% g x)
@[deprecated (since := "2026-05-17")] alias extDerivFun := mvfderiv
diff --git a/Mathlib/Geometry/Manifold/MFDeriv/SpecificFunctions.lean b/Mathlib/Geometry/Manifold/MFDeriv/SpecificFunctions.lean
index 28254093a16323..60b917645a0878 100644
--- a/Mathlib/Geometry/Manifold/MFDeriv/SpecificFunctions.lean
+++ b/Mathlib/Geometry/Manifold/MFDeriv/SpecificFunctions.lean
@@ -7,7 +7,7 @@ module
public import Mathlib.Analysis.Calculus.FDeriv.Mul
public import Mathlib.Geometry.Manifold.MFDeriv.FDeriv
-import Mathlib.Geometry.Manifold.Notation
+public import Mathlib.Geometry.Manifold.Notation
/-!
# Differentiability of specific functions
@@ -126,7 +126,7 @@ section id
/-! #### Identity -/
theorem hasMFDerivAt_id (x : M) :
- HasMFDerivAt% (@id M) x (ContinuousLinearMap.id 𝕜 (TangentSpace I x)) := by
+ HasMFDerivAt% (@id M) x (ContinuousLinearMap.id 𝕜 (TangentSpace% x)) := by
refine ⟨continuousAt_id, ?_⟩
have : ∀ᶠ y in 𝓝[range I] (extChartAt I x) x, (extChartAt I x ∘ (extChartAt I x).symm) y = y := by
apply Filter.mem_of_superset (extChartAt_target_mem_nhdsWithin x)
@@ -135,7 +135,7 @@ theorem hasMFDerivAt_id (x : M) :
simp only [mfld_simps]
theorem hasMFDerivWithinAt_id (s : Set M) (x : M) :
- HasMFDerivAt[s] (@id M) x (ContinuousLinearMap.id 𝕜 (TangentSpace I x)) :=
+ HasMFDerivAt[s] (@id M) x (ContinuousLinearMap.id 𝕜 (TangentSpace% x)) :=
(hasMFDerivAt_id x).hasMFDerivWithinAt
theorem mdifferentiableAt_id : MDiffAt (@id M) x :=
@@ -150,20 +150,20 @@ theorem mdifferentiableOn_id : MDiff[s] (@id M) :=
mdifferentiable_id.mdifferentiableOn
@[simp, mfld_simps]
-theorem mfderiv_id : mfderiv% (@id M) x = ContinuousLinearMap.id 𝕜 (TangentSpace I x) :=
+theorem mfderiv_id : mfderiv% (@id M) x = ContinuousLinearMap.id 𝕜 (TangentSpace% x) :=
(hasMFDerivAt_id x).mfderiv
theorem mfderivWithin_id (hxs : UniqueMDiffWithinAt I s x) :
- mfderiv[s] (@id M) x = ContinuousLinearMap.id 𝕜 (TangentSpace I x) := by
+ mfderiv[s] (@id M) x = ContinuousLinearMap.id 𝕜 (TangentSpace% x) := by
rw [MDifferentiable.mfderivWithin mdifferentiableAt_id hxs]
exact mfderiv_id
set_option backward.isDefEq.respectTransparency false in
@[simp, mfld_simps]
-theorem tangentMap_id : tangentMap I I (id : M → M) = id := by ext1 ⟨x, v⟩; simp [tangentMap]
+theorem tangentMap_id : tangentMap% (@id M) = id := by ext1 ⟨x, v⟩; simp [tangentMap]
theorem tangentMapWithin_id {p : TangentBundle I M} (hs : UniqueMDiffWithinAt I s p.proj) :
- tangentMapWithin I I (id : M → M) s p = p := by
+ tangentMap[s] (id : M → M) p = p := by
simp only [tangentMapWithin, id]
rw [mfderivWithin_id]
· rcases p with ⟨⟩; rfl
@@ -180,11 +180,11 @@ variable {c : M'}
set_option backward.isDefEq.respectTransparency false in
theorem hasMFDerivAt_const (c : M') (x : M) :
- HasMFDerivAt% (fun _ : M ↦ c) x (0 : TangentSpace I x →L[𝕜] TangentSpace I' c) :=
+ HasMFDerivAt% (fun _ : M ↦ c) x (0 : TangentSpace% x →L[𝕜] TangentSpace% c) :=
⟨by fun_prop, by simp [Function.comp_def, hasFDerivWithinAt_const]⟩
theorem hasMFDerivWithinAt_const (c : M') (s : Set M) (x : M) :
- HasMFDerivAt[s] (fun _ : M ↦ c) x (0 : TangentSpace I x →L[𝕜] TangentSpace I' c) :=
+ HasMFDerivAt[s] (fun _ : M ↦ c) x (0 : TangentSpace% x →L[𝕜] TangentSpace% c) :=
(hasMFDerivAt_const c x).hasMFDerivWithinAt
theorem mdifferentiableAt_const : MDiffAt (fun _ : M ↦ c) x :=
@@ -200,11 +200,11 @@ theorem mdifferentiableOn_const : MDiff[s] (fun _ : M ↦ c) :=
@[simp, mfld_simps]
theorem mfderiv_const :
- mfderiv% (fun _ : M ↦ c) x = (0 : TangentSpace I x →L[𝕜] TangentSpace I' c) :=
+ mfderiv% (fun _ : M ↦ c) x = (0 : TangentSpace% x →L[𝕜] TangentSpace% c) :=
(hasMFDerivAt_const c x).mfderiv
theorem mfderivWithin_const :
- mfderiv[s] (fun _ : M ↦ c) x = (0 : TangentSpace I x →L[𝕜] TangentSpace I' c) :=
+ mfderiv[s] (fun _ : M ↦ c) x = (0 : TangentSpace% x →L[𝕜] TangentSpace% c) :=
(hasMFDerivWithinAt_const _ _ _).mfderivWithin_eq_zero
end Const
@@ -221,8 +221,8 @@ theorem MDifferentiableWithinAt.prodMk {f : M → M'} {g : M → M''}
/-- If `f` and `g` have derivatives `df` and `dg` within `s` at `x`, respectively,
then `x ↦ (f x, g x)` has derivative `df.prod dg` within `s`. -/
theorem HasMFDerivWithinAt.prodMk {f : M → M'} {g : M → M''}
- {df : TangentSpace I x →L[𝕜] TangentSpace I' (f x)} (hf : HasMFDerivAt[s] f x df)
- {dg : TangentSpace I x →L[𝕜] TangentSpace I'' (g x)} (hg : HasMFDerivAt[s] g x dg) :
+ {df : TangentSpace% x →L[𝕜] TangentSpace% (f x)} (hf : HasMFDerivAt[s] f x df)
+ {dg : TangentSpace% x →L[𝕜] TangentSpace% (g x)} (hg : HasMFDerivAt[s] g x dg) :
HasMFDerivAt[s] (fun y ↦ (f y, g y)) x (df.prod dg) :=
⟨hf.1.prodMk hg.1, hf.2.prodMk hg.2⟩
@@ -244,8 +244,8 @@ theorem MDifferentiableAt.prodMk {f : M → M'} {g : M → M''} (hf : MDiffAt f
/-- If `f` and `g` have derivatives `df` and `dg` at `x`, respectively,
then `x ↦ (f x, g x)` has derivative `df.prod dg`. -/
theorem HasMFDerivAt.prodMk {f : M → M'} {g : M → M''}
- {df : TangentSpace I x →L[𝕜] TangentSpace I' (f x)} (hf : HasMFDerivAt% f x df)
- {dg : TangentSpace I x →L[𝕜] TangentSpace I'' (g x)} (hg : HasMFDerivAt% g x dg) :
+ {df : TangentSpace% x →L[𝕜] TangentSpace% (f x)} (hf : HasMFDerivAt% f x df)
+ {dg : TangentSpace% x →L[𝕜] TangentSpace% (g x)} (hg : HasMFDerivAt% g x dg) :
HasMFDerivAt% (fun y ↦ (f y, g y)) x (df.prod dg) :=
⟨hf.1.prodMk hg.1, hf.2.prodMk hg.2⟩
@@ -276,7 +276,7 @@ fun x ↦ (hf x).prodMk_space (hg x)
theorem hasMFDerivAt_fst (x : M × M') :
HasMFDerivAt% (@Prod.fst M M') x
- (ContinuousLinearMap.fst 𝕜 (TangentSpace I x.1) (TangentSpace I' x.2)) := by
+ (ContinuousLinearMap.fst 𝕜 (TangentSpace% x.1) (TangentSpace% x.2)) := by
refine ⟨continuous_fst.continuousAt, ?_⟩
have :
∀ᶠ y in 𝓝[range (I.prod I')] extChartAt (I.prod I') x x,
@@ -294,7 +294,7 @@ theorem hasMFDerivAt_fst (x : M × M') :
theorem hasMFDerivWithinAt_fst (s : Set (M × M')) (x : M × M') :
HasMFDerivAt[s] (@Prod.fst M M') x
- (ContinuousLinearMap.fst 𝕜 (TangentSpace I x.1) (TangentSpace I' x.2)) :=
+ (ContinuousLinearMap.fst 𝕜 (TangentSpace% x.1) (TangentSpace% x.2)) :=
(hasMFDerivAt_fst x).hasMFDerivWithinAt
theorem mdifferentiableAt_fst {x : M × M'} : MDiffAt (@Prod.fst M M') x :=
@@ -312,23 +312,23 @@ theorem mdifferentiableOn_fst {s : Set (M × M')} : MDiff[s] (@Prod.fst M M') :=
@[simp, mfld_simps]
theorem mfderiv_fst {x : M × M'} :
mfderiv% (@Prod.fst M M') x =
- ContinuousLinearMap.fst 𝕜 (TangentSpace I x.1) (TangentSpace I' x.2) :=
+ ContinuousLinearMap.fst 𝕜 (TangentSpace% x.1) (TangentSpace% x.2) :=
(hasMFDerivAt_fst x).mfderiv
theorem mfderivWithin_fst {s : Set (M × M')} {x : M × M'}
(hxs : UniqueMDiffWithinAt (I.prod I') s x) :
mfderiv[s] (@Prod.fst M M') x =
- ContinuousLinearMap.fst 𝕜 (TangentSpace I x.1) (TangentSpace I' x.2) := by
+ ContinuousLinearMap.fst 𝕜 (TangentSpace% x.1) (TangentSpace% x.2) := by
rw [MDifferentiable.mfderivWithin mdifferentiableAt_fst hxs]; exact mfderiv_fst
@[simp, mfld_simps]
theorem tangentMap_prodFst {p : TangentBundle (I.prod I') (M × M')} :
- tangentMap (I.prod I') I Prod.fst p = ⟨p.proj.1, p.2.1⟩ := by
+ tangentMap% (@Prod.fst M M') p = ⟨p.proj.1, p.2.1⟩ := by
simp [tangentMap]; rfl
theorem tangentMapWithin_prodFst {s : Set (M × M')} {p : TangentBundle (I.prod I') (M × M')}
(hs : UniqueMDiffWithinAt (I.prod I') s p.proj) :
- tangentMapWithin (I.prod I') I Prod.fst s p = ⟨p.proj.1, p.2.1⟩ := by
+ tangentMap[s] (@Prod.fst M M') p = ⟨p.proj.1, p.2.1⟩ := by
simp only [tangentMapWithin]
rw [mfderivWithin_fst]
· rcases p with ⟨⟩; rfl
@@ -336,7 +336,7 @@ theorem tangentMapWithin_prodFst {s : Set (M × M')} {p : TangentBundle (I.prod
theorem hasMFDerivAt_snd (x : M × M') :
HasMFDerivAt% (@Prod.snd M M') x
- (ContinuousLinearMap.snd 𝕜 (TangentSpace I x.1) (TangentSpace I' x.2)) := by
+ (ContinuousLinearMap.snd 𝕜 (TangentSpace% x.1) (TangentSpace% x.2)) := by
refine ⟨continuous_snd.continuousAt, ?_⟩
have :
∀ᶠ y in 𝓝[range (I.prod I')] extChartAt (I.prod I') x x,
@@ -354,7 +354,7 @@ theorem hasMFDerivAt_snd (x : M × M') :
theorem hasMFDerivWithinAt_snd (s : Set (M × M')) (x : M × M') :
HasMFDerivAt[s] (@Prod.snd M M') x
- (ContinuousLinearMap.snd 𝕜 (TangentSpace I x.1) (TangentSpace I' x.2)) :=
+ (ContinuousLinearMap.snd 𝕜 (TangentSpace% x.1) (TangentSpace% x.2)) :=
(hasMFDerivAt_snd x).hasMFDerivWithinAt
theorem mdifferentiableAt_snd {x : M × M'} : MDiffAt (@Prod.snd M M') x :=
@@ -371,13 +371,13 @@ theorem mdifferentiableOn_snd {s : Set (M × M')} : MDiff[s] (@Prod.snd M M') :=
@[simp, mfld_simps]
theorem mfderiv_snd {x : M × M'} :
mfderiv% (@Prod.snd M M') x =
- ContinuousLinearMap.snd 𝕜 (TangentSpace I x.1) (TangentSpace I' x.2) :=
+ ContinuousLinearMap.snd 𝕜 (TangentSpace% x.1) (TangentSpace% x.2) :=
(hasMFDerivAt_snd x).mfderiv
theorem mfderivWithin_snd {s : Set (M × M')} {x : M × M'}
(hxs : UniqueMDiffWithinAt (I.prod I') s x) :
mfderiv[s] (@Prod.snd M M') x =
- ContinuousLinearMap.snd 𝕜 (TangentSpace I x.1) (TangentSpace I' x.2) := by
+ ContinuousLinearMap.snd 𝕜 (TangentSpace% x.1) (TangentSpace% x.2) := by
rw [MDifferentiable.mfderivWithin mdifferentiableAt_snd hxs]; exact mfderiv_snd
theorem MDifferentiableWithinAt.fst {f : N → M × M'} {s : Set N} {x : N}
@@ -481,9 +481,9 @@ theorem MDifferentiable.prodMap (hf : MDiff f) (hg : MDiff g) : MDiff (Prod.map
set_option backward.isDefEq.respectTransparency false in
lemma HasMFDerivWithinAt.prodMap {s : Set <| M × M'} {p : M × M'} {f : M → N} {g : M' → N'}
- {df : TangentSpace I p.1 →L[𝕜] TangentSpace J (f p.1)}
+ {df : TangentSpace% p.1 →L[𝕜] TangentSpace% (f p.1)}
(hf : HasMFDerivAt[Prod.fst '' s] f p.1 df)
- {dg : TangentSpace I' p.2 →L[𝕜] TangentSpace J' (g p.2)}
+ {dg : TangentSpace% p.2 →L[𝕜] TangentSpace% (g p.2)}
(hg : HasMFDerivAt[Prod.snd '' s] g p.2 dg) :
HasMFDerivAt[s] (Prod.map f g) p (df.prodMap dg) := by
refine ⟨hf.1.prodMap hg.1 |>.mono (by grind), ?_⟩
@@ -501,8 +501,8 @@ lemma HasMFDerivWithinAt.prodMap {s : Set <| M × M'} {p : M × M'} {f : M → N
set_option backward.isDefEq.respectTransparency false in
lemma HasMFDerivAt.prodMap {p : M × M'} {f : M → N} {g : M' → N'}
- {df : TangentSpace I p.1 →L[𝕜] TangentSpace J (f p.1)} (hf : HasMFDerivAt% f p.1 df)
- {dg : TangentSpace I' p.2 →L[𝕜] TangentSpace J' (g p.2)} (hg : HasMFDerivAt% g p.2 dg) :
+ {df : TangentSpace% p.1 →L[𝕜] TangentSpace% (f p.1)} (hf : HasMFDerivAt% f p.1 df)
+ {dg : TangentSpace% p.2 →L[𝕜] TangentSpace% (g p.2)} (hg : HasMFDerivAt% g p.2 dg) :
HasMFDerivAt% (Prod.map f g) p
((mfderiv% f p.1).prodMap (mfderiv% g p.2)) := by
simp_rw [← hasMFDerivWithinAt_univ, ← mfderivWithin_univ, ← univ_prod_univ]
@@ -535,12 +535,12 @@ end prodMap
@[simp, mfld_simps]
theorem tangentMap_prodSnd {p : TangentBundle (I.prod I') (M × M')} :
- tangentMap (I.prod I') I' Prod.snd p = ⟨p.proj.2, p.2.2⟩ := by
+ tangentMap% (@Prod.snd M M') p = ⟨p.proj.2, p.2.2⟩ := by
simp [tangentMap]; rfl
theorem tangentMapWithin_prodSnd {s : Set (M × M')} {p : TangentBundle (I.prod I') (M × M')}
(hs : UniqueMDiffWithinAt (I.prod I') s p.proj) :
- tangentMapWithin (I.prod I') I' Prod.snd s p = ⟨p.proj.2, p.2.2⟩ := by
+ tangentMap[s] (@Prod.snd M M') p = ⟨p.proj.2, p.2.2⟩ := by
simp only [tangentMapWithin]
rw [mfderivWithin_snd hs]
rcases p with ⟨⟩; rfl
@@ -551,24 +551,25 @@ alias MDifferentiableAt.mfderiv_prod := mfderiv_prodMk
set_option backward.isDefEq.respectTransparency false in
theorem mfderiv_prod_left {x₀ : M} {y₀ : M'} :
mfderiv% (fun (x : M) ↦ (x, y₀)) x₀ =
- ContinuousLinearMap.inl 𝕜 (TangentSpace I x₀) (TangentSpace I' y₀) := by
+ ContinuousLinearMap.inl 𝕜 (TangentSpace% x₀) (TangentSpace% y₀) := by
refine (mdifferentiableAt_id.mfderiv_prod mdifferentiableAt_const).trans ?_
rw [mfderiv_id, mfderiv_const, ContinuousLinearMap.inl]
+-- TODO: better error when the type of x is left open
theorem tangentMap_prod_left {p : TangentBundle I M} {y₀ : M'} :
- tangentMap I (I.prod I') (fun x ↦ (x, y₀)) p = ⟨(p.1, y₀), (p.2, 0)⟩ := by
+ tangentMap% (fun (x : M) ↦ (x, y₀)) p = ⟨(p.1, y₀), (p.2, 0)⟩ := by
simp only [tangentMap, mfderiv_prod_left, TotalSpace.mk_inj]
rfl
set_option backward.isDefEq.respectTransparency false in
theorem mfderiv_prod_right {x₀ : M} {y₀ : M'} :
mfderiv% (fun (y : M') ↦ (x₀, y)) y₀ =
- ContinuousLinearMap.inr 𝕜 (TangentSpace I x₀) (TangentSpace I' y₀) := by
+ ContinuousLinearMap.inr 𝕜 (TangentSpace% x₀) (TangentSpace% y₀) := by
refine (mdifferentiableAt_const.mfderiv_prod mdifferentiableAt_id).trans ?_
rw [mfderiv_id, mfderiv_const, ContinuousLinearMap.inr]
theorem tangentMap_prod_right {p : TangentBundle I' M'} {x₀ : M} :
- tangentMap I' (I.prod I') (fun y ↦ (x₀, y)) p = ⟨(x₀, p.1), (0, p.2)⟩ := by
+ tangentMap% (fun (y : M') ↦ (x₀, y)) p = ⟨(x₀, p.1), (0, p.2)⟩ := by
simp only [tangentMap, mfderiv_prod_right, TotalSpace.mk_inj]
rfl
@@ -596,9 +597,9 @@ theorem mfderiv_prod_eq_add {f : M × M' → M''} {p : M × M'}
theorem mfderiv_prod_eq_add_comp {f : M × M' → M''} {p : M × M'} (hf : MDiffAt f p) :
mfderiv% f p =
(mfderiv% (fun z : M ↦ f (z, p.2)) p.1) ∘L (id (ContinuousLinearMap.fst 𝕜 E E') :
- (TangentSpace (I.prod I') p) →L[𝕜] (TangentSpace I p.1)) +
+ (TangentSpace% p) →L[𝕜] (TangentSpace% p.1)) +
(mfderiv% (fun z : M' ↦ f (p.1, z)) p.2) ∘L (id (ContinuousLinearMap.snd 𝕜 E E') :
- (TangentSpace (I.prod I') p) →L[𝕜] (TangentSpace I' p.2)) := by
+ (TangentSpace% p) →L[𝕜] (TangentSpace% p.2)) := by
rw [mfderiv_prod_eq_add hf]
congr
· have : (fun z : M × M' ↦ f (z.1, p.2)) = (fun z : M ↦ f (z, p.2)) ∘ Prod.fst := rfl
@@ -617,7 +618,7 @@ theorem mfderiv_prod_eq_add_comp {f : M × M' → M''} {p : M × M'} (hf : MDiff
/-- The total derivative of a function in two variables is the sum of the partial derivatives.
Note that to state this (without casts) we need to be able to see through the definition of
`TangentSpace`. Version in terms of the one-variable derivatives. -/
-theorem mfderiv_prod_eq_add_apply {f : M × M' → M''} {p : M × M'} {v : TangentSpace (I.prod I') p}
+theorem mfderiv_prod_eq_add_apply {f : M × M' → M''} {p : M × M'} {v : TangentSpace% p}
(hf : MDiffAt f p) :
mfderiv% f p v =
mfderiv% (fun z : M ↦ f (z, p.2)) p.1 v.1 + mfderiv% (fun z : M' ↦ f (p.1, z)) p.2 v.2 := by
@@ -662,7 +663,7 @@ lemma writtenInExtChartAt_sumSwap_eventuallyEq_id :
exact (chartAt H x).open_target.mem_nhds (by simp)
theorem hasMFDerivAt_sumSwap :
- HasMFDerivAt% (@Sum.swap M M') p (ContinuousLinearMap.id 𝕜 (TangentSpace I p)) := by
+ HasMFDerivAt% (@Sum.swap M M') p (ContinuousLinearMap.id 𝕜 (TangentSpace% p)) := by
refine ⟨by fun_prop, ?_⟩
apply (hasFDerivWithinAt_id _ (range I)).congr_of_eventuallyEq
· exact writtenInExtChartAt_sumSwap_eventuallyEq_id
@@ -671,12 +672,12 @@ theorem hasMFDerivAt_sumSwap :
@[simp]
theorem mfderivWithin_sumSwap {s : Set (M ⊕ M')} (hs : UniqueMDiffWithinAt I s p) :
- mfderiv[s] (@Sum.swap M M') p = ContinuousLinearMap.id 𝕜 (TangentSpace I p) :=
+ mfderiv[s] (@Sum.swap M M') p = ContinuousLinearMap.id 𝕜 (TangentSpace% p) :=
hasMFDerivAt_sumSwap.hasMFDerivWithinAt.mfderivWithin hs
@[simp]
theorem mfderiv_sumSwap :
- mfderiv% (@Sum.swap M M') p = ContinuousLinearMap.id 𝕜 (TangentSpace I p) := by
+ mfderiv% (@Sum.swap M M') p = ContinuousLinearMap.id 𝕜 (TangentSpace% p) := by
simpa [mfderivWithin_univ] using (mfderivWithin_sumSwap (uniqueMDiffWithinAt_univ I))
variable {f : M → N} (g : M' → N') {q : M} {q' : M'}
@@ -706,7 +707,7 @@ lemma writtenInExtChartAt_sumInr_eventuallyEq_id :
(chartAt H q').right_inv (by simpa [Set.mem_preimage, I.left_inv] using hyT)]
theorem hasMFDerivWithinAt_inl :
- HasMFDerivAt[s] (@Sum.inl M M') q (ContinuousLinearMap.id 𝕜 (TangentSpace I q)) := by
+ HasMFDerivAt[s] (@Sum.inl M M') q (ContinuousLinearMap.id 𝕜 (TangentSpace% q)) := by
refine ⟨by fun_prop, ?_⟩
have : (writtenInExtChartAt I I q (@Sum.inl M M'))
=ᶠ[𝓝[(extChartAt I q).symm ⁻¹' s ∩ Set.range I] (extChartAt I q q)] id :=
@@ -715,11 +716,11 @@ theorem hasMFDerivWithinAt_inl :
(by simp [writtenInExtChartAt, extChartAt])
theorem hasMFDerivAt_inl :
- HasMFDerivAt% (@Sum.inl M M') q (ContinuousLinearMap.id 𝕜 (TangentSpace I p)) := by
+ HasMFDerivAt% (@Sum.inl M M') q (ContinuousLinearMap.id 𝕜 (TangentSpace% p)) := by
simpa [HasMFDerivAt, hasMFDerivWithinAt_univ] using! hasMFDerivWithinAt_inl (s := Set.univ)
theorem hasMFDerivWithinAt_inr {t : Set M'} :
- HasMFDerivAt[t] (@Sum.inr M M') q' (ContinuousLinearMap.id 𝕜 (TangentSpace I q')) := by
+ HasMFDerivAt[t] (@Sum.inr M M') q' (ContinuousLinearMap.id 𝕜 (TangentSpace% q')) := by
refine ⟨by fun_prop, ?_⟩
have : (writtenInExtChartAt I I q' (@Sum.inr M M'))
=ᶠ[𝓝[(extChartAt I q').symm ⁻¹' t ∩ Set.range I] (extChartAt I q' q')] id :=
@@ -728,23 +729,23 @@ theorem hasMFDerivWithinAt_inr {t : Set M'} :
(by simp [writtenInExtChartAt, extChartAt])
theorem hasMFDerivAt_inr :
- HasMFDerivAt% (@Sum.inr M M') q' (ContinuousLinearMap.id 𝕜 (TangentSpace I p)) := by
+ HasMFDerivAt% (@Sum.inr M M') q' (ContinuousLinearMap.id 𝕜 (TangentSpace% p)) := by
simpa [HasMFDerivAt, hasMFDerivWithinAt_univ] using! hasMFDerivWithinAt_inr (t := Set.univ)
theorem mfderivWithin_sumInl (hU : UniqueMDiffWithinAt I s q) :
- mfderiv[s] (@Sum.inl M M') q = ContinuousLinearMap.id 𝕜 (TangentSpace I p) :=
+ mfderiv[s] (@Sum.inl M M') q = ContinuousLinearMap.id 𝕜 (TangentSpace% p) :=
hasMFDerivWithinAt_inl.mfderivWithin hU
theorem mfderiv_sumInl :
- mfderiv% (@Sum.inl M M') q = ContinuousLinearMap.id 𝕜 (TangentSpace I p) := by
+ mfderiv% (@Sum.inl M M') q = ContinuousLinearMap.id 𝕜 (TangentSpace% p) := by
simpa [mfderivWithin_univ] using (mfderivWithin_sumInl (uniqueMDiffWithinAt_univ I))
theorem mfderivWithin_sumInr {t : Set M'} (hU : UniqueMDiffWithinAt I t q') :
- mfderiv[t] (@Sum.inr M M') q' = ContinuousLinearMap.id 𝕜 (TangentSpace I q') :=
+ mfderiv[t] (@Sum.inr M M') q' = ContinuousLinearMap.id 𝕜 (TangentSpace% q') :=
hasMFDerivWithinAt_inr.mfderivWithin hU
theorem mfderiv_sumInr :
- mfderiv% (@Sum.inr M M') q' = ContinuousLinearMap.id 𝕜 (TangentSpace I q') := by
+ mfderiv% (@Sum.inr M M') q' = ContinuousLinearMap.id 𝕜 (TangentSpace% q') := by
simpa [mfderivWithin_univ] using (mfderivWithin_sumInr (uniqueMDiffWithinAt_univ I))
end disjointUnion
@@ -760,7 +761,7 @@ canonical, but in this case (the tangent space of a vector space) it is canonica
section Group
-variable {z : M} {f g : M → E'} {f' g' : TangentSpace I z →L[𝕜] E'}
+variable {z : M} {f g : M → E'} {f' g' : TangentSpace% z →L[𝕜] E'}
theorem HasMFDerivWithinAt.add {s : Set M}
(hf : HasMFDerivAt[s] f z f') (hg : HasMFDerivAt[s] g z g') :
@@ -788,18 +789,18 @@ theorem MDifferentiable.add (hf : MDiff f) (hg : MDiff g) : MDiff (f + g) :=
-- Deprecate all these lemmas in favour of a version using `mvfderiv(Within)`
-- Porting note: forcing types using `by exact`
theorem mfderiv_add (hf : MDiffAt f z) (hg : MDiffAt g z) :
- (mfderiv% (f + g) z : TangentSpace I z →L[𝕜] E') =
+ (mfderiv% (f + g) z : TangentSpace% z →L[𝕜] E') =
(by exact mfderiv% f z) + (by exact mfderiv% g z) :=
(hf.hasMFDerivAt.add hg.hasMFDerivAt).mfderiv
theorem mfderivWithin_add (hf : MDiffAt[s] f z) (hg : MDiffAt[s] g z)
(hs : UniqueMDiffWithinAt I s z) :
- (mfderiv[s] (f + g) z : TangentSpace I z →L[𝕜] E') =
+ (mfderiv[s] (f + g) z : TangentSpace% z →L[𝕜] E') =
(by exact mfderiv[s] f z) + (by exact mfderiv[s] g z) :=
(hf.hasMFDerivWithinAt.add hg.hasMFDerivWithinAt).mfderivWithin hs
section sum
-variable {ι : Type} {t : Finset ι} {f : ι → M → E'} {f' : ι → TangentSpace I z →L[𝕜] E'}
+variable {ι : Type} {t : Finset ι} {f : ι → M → E'} {f' : ι → TangentSpace% z →L[𝕜] E'}
lemma HasMFDerivWithinAt.sum (hf : ∀ i ∈ t, HasMFDerivAt[s] (f i) z (f' i)) :
HasMFDerivAt[s] (∑ i ∈ t, f i) z (∑ i ∈ t, f' i) := by
@@ -914,12 +915,12 @@ theorem MDifferentiable.sub (hf : MDiff f) (hg : MDiff g) : MDiff (f - g) :=
theorem mfderivWithin_sub (hf : MDiffAt[s] f z) (hg : MDiffAt[s] g z)
(hs : UniqueMDiffWithinAt I s z) :
- (mfderiv[s] (f - g) z : TangentSpace I z →L[𝕜] E') =
+ (mfderiv[s] (f - g) z : TangentSpace% z →L[𝕜] E') =
(by exact mfderiv[s] f z) - (by exact mfderiv[s] g z) :=
(hf.hasMFDerivWithinAt.sub hg.hasMFDerivWithinAt).mfderivWithin hs
theorem mfderiv_sub (hf : MDiffAt f z) (hg : MDiffAt g z) :
- (mfderiv% (f - g) z : TangentSpace I z →L[𝕜] E') =
+ (mfderiv% (f - g) z : TangentSpace% z →L[𝕜] E') =
(by exact mfderiv% f z) - (by exact mfderiv% g z) :=
(hf.hasMFDerivAt.sub hg.hasMFDerivAt).mfderiv
@@ -929,7 +930,7 @@ section AlgebraOverRing
open scoped RightActions
variable {z : M} {F' : Type*} [NormedRing F'] [NormedAlgebra 𝕜 F'] {p q : M → F'}
- {p' q' : TangentSpace I z →L[𝕜] F'}
+ {p' q' : TangentSpace% z →L[𝕜] F'}
theorem HasMFDerivWithinAt.mul' (hp : HasMFDerivWithinAt I 𝓘(𝕜, F') p s z p')
(hq : HasMFDerivWithinAt I 𝓘(𝕜, F') q s z q') :
@@ -979,7 +980,7 @@ end AlgebraOverRing
section AlgebraOverCommRing
variable {z : M} {F' : Type*} [NormedCommRing F'] [NormedAlgebra 𝕜 F'] {p q : M → F'}
- {p' q' : TangentSpace I z →L[𝕜] F'}
+ {p' q' : TangentSpace% z →L[𝕜] F'}
set_option backward.isDefEq.respectTransparency false in
theorem HasMFDerivWithinAt.mul (hp : HasMFDerivWithinAt I 𝓘(𝕜, F') p s z p')
@@ -993,7 +994,7 @@ theorem HasMFDerivAt.mul (hp : HasMFDerivAt I 𝓘(𝕜, F') p z p')
hasMFDerivWithinAt_univ.mp <| hp.hasMFDerivWithinAt.mul hq.hasMFDerivWithinAt
section prod
-variable {ι : Type} {t : Finset ι} {f : ι → M → F'} {f' : ι → TangentSpace I z →L[𝕜] F'}
+variable {ι : Type} {t : Finset ι} {f : ι → M → F'} {f' : ι → TangentSpace% z →L[𝕜] F'}
set_option backward.isDefEq.respectTransparency false in
lemma HasMFDerivWithinAt.prod [DecidableEq ι]
@@ -1046,7 +1047,7 @@ section DivisionRing
open scoped RightActions
variable {z : M} {F' : Type*} [NormedDivisionRing F'] [NormedAlgebra 𝕜 F'] {p q : M → F'}
- {p' q' : TangentSpace I z →L[𝕜] F'}
+ {p' q' : TangentSpace% z →L[𝕜] F'}
lemma HasMFDerivWithinAt.inv' (hp : HasMFDerivWithinAt I 𝓘(𝕜, F') p s z p') (hp_ne : p z ≠ 0) :
HasMFDerivWithinAt I 𝓘(𝕜, F') (p⁻¹) s z (-((p z)⁻¹ •> p' <• (p z)⁻¹) : E →L[𝕜] F') :=
@@ -1097,7 +1098,7 @@ end DivisionRing
section Field
variable {z : M} {F' : Type*} [NormedField F'] [NormedAlgebra 𝕜 F'] {p q : M → F'}
- {p' q' : TangentSpace I z →L[𝕜] F'}
+ {p' q' : TangentSpace% z →L[𝕜] F'}
lemma HasMFDerivWithinAt.inv (hp : HasMFDerivWithinAt I 𝓘(𝕜, F') p s z p') (hp_ne : p z ≠ 0) :
HasMFDerivWithinAt I 𝓘(𝕜, F') (p⁻¹) s z (-(p z ^ 2)⁻¹ • p' : E →L[𝕜] F') := by
diff --git a/Mathlib/Geometry/Manifold/MFDeriv/Tangent.lean b/Mathlib/Geometry/Manifold/MFDeriv/Tangent.lean
index bbde671e83da07..ea2f7fa9b139b9 100644
--- a/Mathlib/Geometry/Manifold/MFDeriv/Tangent.lean
+++ b/Mathlib/Geometry/Manifold/MFDeriv/Tangent.lean
@@ -35,7 +35,7 @@ variable {𝕜 : Type*} [NontriviallyNormedField 𝕜]
/-- The derivative of the chart at a base point is the chart of the tangent bundle, composed with
the identification between the tangent bundle of the model space and the product space. -/
theorem tangentMap_chart {p q : TangentBundle I M} (h : q.1 ∈ (chartAt H p.1).source) :
- tangentMap I I (chartAt H p.1) q =
+ tangentMap% (chartAt H p.1) q =
(TotalSpace.toProd _ _).symm
((chartAt (ModelProd H E) p : TangentBundle I M → ModelProd H E) q) := by
dsimp [tangentMap]
@@ -48,7 +48,7 @@ tangent bundle, composed with the identification between the tangent bundle of t
the product space. -/
theorem tangentMap_chart_symm {p : TangentBundle I M} {q : TangentBundle I H}
(h : q.1 ∈ (chartAt H p.1).target) :
- tangentMap I I (chartAt H p.1).symm q =
+ tangentMap% (chartAt H p.1).symm q =
(chartAt (ModelProd H E) p).symm (TotalSpace.toProd H E q) := by
dsimp only [tangentMap]
rw [MDifferentiableAt.mfderiv (mdifferentiableAt_atlas_symm (chart_mem_atlas _ _) h)]
@@ -74,7 +74,7 @@ postcomposing it with derivatives of extended charts.
Concrete version of `inTangentCoordinates_eq`. -/
lemma inTangentCoordinates_eq_mfderiv_comp
{N : Type*} {f : N → M} {g : N → M'}
- {ϕ : Π x : N, TangentSpace I (f x) →L[𝕜] TangentSpace I' (g x)} {x₀ : N} {x : N}
+ {ϕ : Π x : N, TangentSpace% (f x) →L[𝕜] TangentSpace% (g x)} {x₀ : N} {x : N}
(hx : f x ∈ (chartAt H (f x₀)).source) (hy : g x ∈ (chartAt H' (g x₀)).source) :
inTangentCoordinates I I' f g ϕ x₀ x =
(mfderiv% (extChartAt I' (g x₀)) (g x)) ∘L (ϕ x) ∘L
diff --git a/Mathlib/Geometry/Manifold/Notation.lean b/Mathlib/Geometry/Manifold/Notation.lean
index cc7ede9c610b4d..24e6737558c2c5 100644
--- a/Mathlib/Geometry/Manifold/Notation.lean
+++ b/Mathlib/Geometry/Manifold/Notation.lean
@@ -37,6 +37,9 @@ including inference of the model with corners.
| `mfderiv% f x` | `mfderiv I J f x` |
| `HasMFDerivAt[s] f x f'` | `HasMFDerivWithinAt I J f s x f'` |
| `HasMFDerivAt% f x f'` | `HasMFDerivAt I J f x f'` |
+| `TangentSpace% x` | `TangentSpace I x` |
+| `tangentMap[s] f` | `tangentMapWithin I J f s` |
+| `tangentMap% f` | `tangentMap I J f` |
In each of these cases, the models with corners are inferred from the domain and codomain of `f`.
The search for models with corners uses the local context and is (almost) only based on expression
@@ -941,6 +944,29 @@ scoped elab:max "HasMFDerivAt%" ppSpace
let (srcI, tgtI) ← findModels ef none
mkAppM ``HasMFDerivAt #[srcI, tgtI, ef, ex, ef']
+/-- `TangentSpace% x` elaborates to `TangentSpace I x`,
+trying to determine `I` from the local context. -/
+scoped elab:max "TangentSpace%" ppSpace x:term:arg : term => do
+ let ex ← Term.elabTerm x none
+ let extype ← instantiateMVars <| ← inferType ex
+ let src ← findModel extype
+ mkAppM ``TangentSpace #[src, ex]
+
+/-- `tangentMap[s] f` elaborates to `tangentMapWithin I J f s`,
+trying to determine `I` and `J` from the local context. -/
+scoped elab:max "tangentMap[" s:term "]" ppSpace f:term:arg : term => do
+ let es ← Term.elabTerm s none
+ let ef ← ensureIsFunction <|← Term.elabTerm f none
+ let (srcI, tgtI) ← findModels ef none
+ mkAppM ``tangentMapWithin #[srcI, tgtI, ef, es]
+
+/-- `tangentMap% f` elaborates to `tangentMap I J f`,
+trying to determine `I` and `J` from the local context. -/
+scoped elab:max "tangentMap%" ppSpace f:term:arg : term => do
+ let ef ← ensureIsFunction <|← Term.elabTerm f none
+ let (srcI, tgtI) ← findModels ef none
+ mkAppM ``tangentMap #[srcI, tgtI, ef]
+
end Manifold
section trace
diff --git a/Mathlib/Geometry/Manifold/Riemannian/Basic.lean b/Mathlib/Geometry/Manifold/Riemannian/Basic.lean
index aac89e42f29fcd..7ddda591cfd451 100644
--- a/Mathlib/Geometry/Manifold/Riemannian/Basic.lean
+++ b/Mathlib/Geometry/Manifold/Riemannian/Basic.lean
@@ -66,7 +66,7 @@ variable
section
variable [PseudoEMetricSpace M] [ChartedSpace H M]
- [RiemannianBundle (fun (x : M) ↦ TangentSpace I x)]
+ [RiemannianBundle (fun (x : M) ↦ TangentSpace% x)]
variable (I M) in
/-- Consider a manifold in which the tangent spaces are already endowed with an inner product, and
@@ -101,7 +101,7 @@ variable (F) in
/-- The standard Riemannian metric on a vector space with an inner product, given by this inner
product on each tangent space. -/
noncomputable def riemannianMetricVectorSpace :
- ContMDiffRiemannianMetric 𝓘(ℝ, F) ω F (fun (x : F) ↦ TangentSpace 𝓘(ℝ, F) x) where
+ ContMDiffRiemannianMetric 𝓘(ℝ, F) ω F (fun (x : F) ↦ TangentSpace% x) where
inner x := (innerSL ℝ (E := F) : F →L[ℝ] F →L[ℝ] ℝ)
symm x v w := real_inner_comm _ _
pos x v hv := real_inner_self_pos.2 hv
@@ -123,19 +123,19 @@ noncomputable def riemannianMetricVectorSpace :
ext v w
simp [hom_trivializationAt_apply, ContinuousLinearMap.inCoordinates, TangentSpace]
-noncomputable instance : RiemannianBundle (fun (x : F) ↦ TangentSpace 𝓘(ℝ, F) x) :=
+noncomputable instance : RiemannianBundle (fun (x : F) ↦ TangentSpace% x) :=
⟨(riemannianMetricVectorSpace F).toRiemannianMetric⟩
set_option backward.isDefEq.respectTransparency false in
-lemma norm_tangentSpace_vectorSpace {x : F} {v : TangentSpace 𝓘(ℝ, F) x} :
+lemma norm_tangentSpace_vectorSpace {x : F} {v : TangentSpace% x} :
‖v‖ = ‖letI V : F := v; V‖ := by
rw [norm_eq_sqrt_real_inner, norm_eq_sqrt_real_inner]
-lemma nnnorm_tangentSpace_vectorSpace {x : F} {v : TangentSpace 𝓘(ℝ, F) x} :
+lemma nnnorm_tangentSpace_vectorSpace {x : F} {v : TangentSpace% x} :
‖v‖₊ = ‖letI V : F := v; V‖₊ := by
simp [nnnorm, norm_tangentSpace_vectorSpace]
-lemma enorm_tangentSpace_vectorSpace {x : F} {v : TangentSpace 𝓘(ℝ, F) x} :
+lemma enorm_tangentSpace_vectorSpace {x : F} {v : TangentSpace% x} :
‖v‖ₑ = ‖letI V : F := v; V‖ₑ := by
simp [enorm, nnnorm_tangentSpace_vectorSpace]
@@ -210,8 +210,8 @@ the image of the neighborhood in the extended chart.
open Manifold Metric
open scoped NNReal
-variable [RiemannianBundle (fun (x : M) ↦ TangentSpace I x)]
- [IsManifold I 1 M] [IsContinuousRiemannianBundle E (fun (x : M) ↦ TangentSpace I x)]
+variable [RiemannianBundle (fun (x : M) ↦ TangentSpace% x)]
+ [IsManifold I 1 M] [IsContinuousRiemannianBundle E (fun (x : M) ↦ TangentSpace% x)]
/-- Register on the tangent space to a normed vector space the same `NormedAddCommGroup` structure
as in the vector space.
@@ -220,7 +220,7 @@ Should not be a global instance, as it does not coincide definitionally with the
structure for inner product spaces, but can be activated locally. -/
@[instance_reducible]
def normedAddCommGroupTangentSpaceVectorSpace (x : E) :
- NormedAddCommGroup (TangentSpace 𝓘(ℝ, E) x) :=
+ NormedAddCommGroup (TangentSpace% x) :=
inferInstanceAs (NormedAddCommGroup E)
attribute [local instance] normedAddCommGroupTangentSpaceVectorSpace
@@ -231,7 +231,7 @@ as in the vector space.
Should not be a global instance, as it does not coincide definitionally with the Riemannian
structure for inner product spaces, but can be activated locally. -/
@[instance_reducible]
-def normedSpaceTangentSpaceVectorSpace (x : E) : NormedSpace ℝ (TangentSpace 𝓘(ℝ, E) x) :=
+def normedSpaceTangentSpaceVectorSpace (x : E) : NormedSpace ℝ (TangentSpace% x) :=
inferInstanceAs (NormedSpace ℝ E)
attribute [local instance] normedSpaceTangentSpaceVectorSpace
@@ -241,7 +241,7 @@ variable (I)
set_option backward.isDefEq.respectTransparency false in
lemma eventually_norm_mfderiv_extChartAt_lt (x : M) :
∃ C > 0, ∀ᶠ y in 𝓝 x, ‖mfderiv% (extChartAt I x) y‖ < C := by
- rcases eventually_norm_trivializationAt_lt E (fun (x : M) ↦ TangentSpace I x) x
+ rcases eventually_norm_trivializationAt_lt E (fun (x : M) ↦ TangentSpace% x) x
with ⟨C, C_pos, hC⟩
refine ⟨C, C_pos, ?_⟩
have hx : (chartAt H x).source ∈ 𝓝 x := chart_source_mem_nhds H x
@@ -262,7 +262,7 @@ lemma eventually_enorm_mfderiv_extChartAt_lt (x : M) :
set_option backward.isDefEq.respectTransparency false in
lemma eventually_norm_mfderivWithin_symm_extChartAt_comp_lt (x : M) :
∃ C > 0, ∀ᶠ y in 𝓝 x, ‖mfderiv[range I] (extChartAt I x).symm (extChartAt I x y)‖ < C := by
- rcases eventually_norm_symmL_trivializationAt_lt E (fun (x : M) ↦ TangentSpace I x) x
+ rcases eventually_norm_symmL_trivializationAt_lt E (fun (x : M) ↦ TangentSpace% x) x
with ⟨C, C_pos, hC⟩
refine ⟨C, C_pos, ?_⟩
have hx : (chartAt H x).source ∈ 𝓝 x := chart_source_mem_nhds H x
diff --git a/Mathlib/Geometry/Manifold/Riemannian/PathELength.lean b/Mathlib/Geometry/Manifold/Riemannian/PathELength.lean
index bc1507a2f8fa13..f41e39caaafe9b 100644
--- a/Mathlib/Geometry/Manifold/Riemannian/PathELength.lean
+++ b/Mathlib/Geometry/Manifold/Riemannian/PathELength.lean
@@ -50,7 +50,7 @@ variable
namespace Manifold
-variable [∀ (x : M), ENorm (TangentSpace I x)] {a b c a' b' : ℝ} {γ γ' : ℝ → M}
+variable [∀ (x : M), ENorm (TangentSpace% x)] {a b c a' b' : ℝ} {γ γ' : ℝ → M}
variable (I) in
/-- The length on `Icc a b` of a path into a manifold, where the path is defined on the whole real
@@ -131,7 +131,7 @@ lemma lintegral_norm_mfderiv_Icc_eq_pathELength_projIcc {a b : ℝ}
open MeasureTheory
-variable [∀ (x : M), ENormSMulClass ℝ (TangentSpace I x)]
+variable [∀ (x : M), ENormSMulClass ℝ (TangentSpace% x)]
set_option backward.isDefEq.respectTransparency false in
/-- The length of a path in a manifold is invariant under a monotone reparametrization. -/
@@ -158,7 +158,7 @@ lemma pathELength_comp_of_monotoneOn {f : ℝ → ℝ} (h : a ≤ b) (hf : Monot
exact uniqueDiffOn_Icc h _ ht
rw [this]
simp only [Function.comp_apply, ContinuousLinearMap.comp_apply]
- have : mfderiv[Icc a b] f t 1 = derivWithin f (Icc a b) t • (1 : TangentSpace 𝓘(ℝ) (f t)) := by
+ have : mfderiv[Icc a b] f t 1 = derivWithin f (Icc a b) t • (1 : TangentSpace% (f t)) := by
simp only [mfderivWithin_eq_fderivWithin, ← fderivWithin_derivWithin, smul_eq_mul, mul_one]
rfl
rw [this]
@@ -191,7 +191,7 @@ lemma pathELength_comp_of_antitoneOn {f : ℝ → ℝ} (h : a ≤ b) (hf : Antit
rw [this]
simp only [Function.comp_apply, ContinuousLinearMap.comp_apply]
have : mfderiv[Icc a b] f t 1
- = derivWithin f (Icc a b) t • (1 : TangentSpace 𝓘(ℝ) (f t)) := by
+ = derivWithin f (Icc a b) t • (1 : TangentSpace% (f t)) := by
simp only [mfderivWithin_eq_fderivWithin, ← fderivWithin_derivWithin, smul_eq_mul, mul_one]
rfl
rw [this]
@@ -242,7 +242,7 @@ lemma riemannianEDist_le_pathELength {γ : ℝ → M} (hγ : CMDiff[Icc a b] 1
· simpa [η, ContinuousAffineMap.coe_lineMap_eq] using hγ.mdifferentiableOn one_ne_zero
· apply (AffineMap.lineMap_mono hab).monotoneOn
-omit [∀ (x : M), ENormSMulClass ℝ (TangentSpace I x)] in
+omit [∀ (x : M), ENormSMulClass ℝ (TangentSpace% x)] in
/-- If some `r` is strictly larger than the Riemannian edistance between two points, there exists
a path between these two points of length `< r`. Here, we get such a path on `[0, 1]`.
For a more precise version giving locally constant paths around the endpoints, see
diff --git a/Mathlib/Topology/FiberBundle/Constructions.lean b/Mathlib/Topology/FiberBundle/Constructions.lean
index d3dca3611b0892..0e44b40be93d8c 100644
--- a/Mathlib/Topology/FiberBundle/Constructions.lean
+++ b/Mathlib/Topology/FiberBundle/Constructions.lean
@@ -308,7 +308,7 @@ variable [∀ _b, Zero (E _b)] {K : Type U} [FunLike K B' B] [ContinuousMapClass
noncomputable def Bundle.Trivialization.pullback (e : Trivialization F (π F E)) (f : K) :
Trivialization F (π F ((f : B' → B) *ᵖ E)) where
toFun z := (z.proj, (e (Pullback.lift f z)).2)
- invFun y := @TotalSpace.mk _ F (f *ᵖ E) y.1 (e.symm (f y.1) y.2)
+ invFun y := TotalSpace.mk' F y.1 (e.symm (f y.1) y.2)
source := Pullback.lift f ⁻¹' e.source
baseSet := f ⁻¹' e.baseSet
target := (f ⁻¹' e.baseSet) ×ˢ univ
diff --git a/MathlibTest/DifferentialGeometry/Notation/Basic.lean b/MathlibTest/DifferentialGeometry/Notation/Basic.lean
index 2976d90b7c9fc8..862e96f248d2da 100644
--- a/MathlibTest/DifferentialGeometry/Notation/Basic.lean
+++ b/MathlibTest/DifferentialGeometry/Notation/Basic.lean
@@ -931,7 +931,7 @@ info: ContMDiffWithinAt (modelWithCornersSelf 𝕜 E) (modelWithCornersSelf 𝕜
end smoothness
--- Inferring the type of `x` for all ContMDiff/MDifferentiable{Within}At elaborators.
+/-! Inferring the type of `x` for all ContMDiff/MDifferentiable{Within}At elaborators. -/
section
variable {EM' : Type*} [NormedAddCommGroup EM']
@@ -970,6 +970,33 @@ open ContDiff in -- for the ∞ notation
end
+/-! Tests for the elaborators for `tangentMap(Within)` and `TangentSpace` -/
+section
+
+variable {f : M → M} {s : Set M} {x : M} {X : TangentSpace% x}
+
+/-- info: TangentSpace I x : Type u_2 -/
+#guard_msgs in
+#check TangentSpace% x
+
+/-- info: tangentMap I I f : TangentBundle I M → TangentBundle I M -/
+#guard_msgs in
+#check tangentMap% f
+
+/-- info: tangentMapWithin I I f s : TangentBundle I M → TangentBundle I M -/
+#guard_msgs in
+#check tangentMap[s] f
+
+/-- info: tangentMap I I f { proj := x, snd := X } : TangentBundle I M -/
+#guard_msgs in
+#check tangentMap% f X
+
+/-- info: tangentMapWithin I I f s { proj := x, snd := X } : TangentBundle I M -/
+#guard_msgs in
+#check tangentMap[s] f X
+
+end
+
/-! Products of models with corners: TODO, add lots of further tests -/
section
From 0be66d77ba290828a5260d883ace636f56bce89a Mon Sep 17 00:00:00 2001
From: Michael Rothgang <10105016+grunweg@users.noreply.github.com>
Date: Mon, 15 Jun 2026 12:27:20 +0000
Subject: [PATCH 0043/1300] feat: add a few basic checks for PR descriptions
(#40181)
Not exhaustive, but hopefully this is already useful.
---
.github/workflows/build_template.yml | 17 ++++-
.github/workflows/check_pr_titles.yaml | 3 +-
Mathlib/Tactic/Linter/ValidatePRTitle.lean | 48 +++++++++++++
MathlibTest/ValidatePRTitle.lean | 83 +++++++++++++++++++++-
scripts/check_title_labels.lean | 15 ++--
5 files changed, 159 insertions(+), 7 deletions(-)
diff --git a/.github/workflows/build_template.yml b/.github/workflows/build_template.yml
index 6eea0109569af5..333c646aa06c86 100644
--- a/.github/workflows/build_template.yml
+++ b/.github/workflows/build_template.yml
@@ -804,7 +804,22 @@ jobs:
run: |
lake env lean scripts/create_deprecated_modules.lean
lake env lean scripts/autolabel.lean
- lake exe check_title_labels --labels "t-algebra" "feat: dummy PR for testing"
+ lake exe check_title_labels --labels "t-algebra" "feat: dummy PR for testing" "dummy PR body" > title_output.txt
+ if grep -qE "Missing positional argument " "title_output.txt"; then
+ echo ""
+ echo "=============================================================================="
+ echo "ERROR: Your branch predates the current 'check title' configuration."
+ echo "Please merge 'master' into your PR branch and push again."
+ echo ""
+ echo "You can do this with:"
+ echo " git fetch upstream"
+ echo " git merge upstream/master"
+ echo " git push"
+ echo "=============================================================================="
+ echo ""
+ exit 1
+ fi
+ cat title_output.txt
- name: build everything
# make sure everything is available for test/import_all.lean
diff --git a/.github/workflows/check_pr_titles.yaml b/.github/workflows/check_pr_titles.yaml
index edbe9058ecd2a8..491b023c9b522f 100644
--- a/.github/workflows/check_pr_titles.yaml
+++ b/.github/workflows/check_pr_titles.yaml
@@ -34,6 +34,7 @@ jobs:
if: github.event.pull_request.draft == false && github.event.pull_request.base.ref == 'master'
env:
TITLE: ${{ github.event.pull_request.title }}
+ BODY: ${{ github.event.pull_request.body }}
PR_LABELS: ${{ toJson(github.event.pull_request.labels) }}
run: |
set -o pipefail
@@ -53,7 +54,7 @@ jobs:
set +e
# Capture output and exit code
- output=$(lake exe check_title_labels --labels "$label_names" "$TITLE" 2>&1)
+ output=$(lake exe check_title_labels --labels "$label_names" "$TITLE" "$BODY" 2>&1)
exit_code=$?
set -e
diff --git a/Mathlib/Tactic/Linter/ValidatePRTitle.lean b/Mathlib/Tactic/Linter/ValidatePRTitle.lean
index feb10809e868a7..09e4f1e5f87a19 100644
--- a/Mathlib/Tactic/Linter/ValidatePRTitle.lean
+++ b/Mathlib/Tactic/Linter/ValidatePRTitle.lean
@@ -11,10 +11,13 @@ import Mathlib.Tactic.Linter.TextBased.UnicodeLinter
/-!
# Checker for well-formed title and labels
+
This script checks if a PR title matches
[mathlib's commit conventions](https://leanprover-community.github.io/contribute/commit.html).
Not all checks from the commit conventions are implemented: for instance, no effort is made to
verify whether the title or body are written in present imperative tense.
+
+It also verifies if the PR description matches some basic sanity checks for good descriptions.
-/
open Std.Internal.Parsec String
@@ -136,3 +139,48 @@ public def validateTitle (title : String) : Array String := Id.run do
errors := errors.push s!"error: the PR contains {badChars.length} Unicode characters \
which are not allowed: {err}"
return errors
+
+/-- Check if `description` matches some basic checks for good PR descriptions
+(a subset of the ones at ,
+plus a few basic sanity checks).
+
+`isLabelledEasy` denotes whether a PR is labelled as easy: if so, an empty description is allowed.
+
+Return all error messages for violations found.
+-/
+public def validatePRBody (description : String) (isLabelledEasy : Bool) : Array String := Id.run do
+ if description.trimAscii.isEmpty && !isLabelledEasy then
+ return #["error: the PR description is empty"]
+
+ -- Find all lines in the PR description before a "fold". bors truncates the squash
+ -- commit message at the first line starting with "---" (its `cut_body_after = "\n---"`),
+ -- so that is exactly what we treat as a fold here.
+ let before := description.lines.toList.takeWhile (fun l ↦ !l.startsWith "---")
+ -- If `after` is non-empty, there is a fold.
+ let after := description.lines.toList.drop before.length
+ let mut errors := #[]
+ if let some l := before.getLast? then
+ if !after.isEmpty && l != "" then
+ errors := errors.push
+ "error: there should be a blank line between the PR description and the fold"
+ if before.any (· == "## Summary") then
+ errors := errors.push "error: do not include a 'summary' header in the PR body"
+ if before.any (· == "## Testing plan") then
+ errors := errors.push "error: usually, a section 'Testing plan' is superfluous \
+ (particularly if it only mentions checks done in CI anyway)\n\
+ If you have done particular testing, please mention this --- but no need for the header."
+ -- Should this error on any headings in the PR description?
+
+ -- Just whitespace, or a period before the fold also count as empty descriptions.
+ let beforeContainsText := before.any (·.any (·.isAlpha))
+ if !beforeContainsText then
+ -- We drop the leading "---" line.
+ let afterContainsText := after.drop 1 |>.any (·.any (·.isAlpha))
+ if afterContainsText then
+ errors := errors.push
+ "warning: your PR description is non-empty, but everything is after the '---' line\n\
+ note: the final PR commit message only uses what is above that line"
+ else
+ errors := errors.push "error: the PR description is empty"
+
+ return errors
diff --git a/MathlibTest/ValidatePRTitle.lean b/MathlibTest/ValidatePRTitle.lean
index aacda293a7e90d..66a9d8e9c03356 100644
--- a/MathlibTest/ValidatePRTitle.lean
+++ b/MathlibTest/ValidatePRTitle.lean
@@ -1,6 +1,8 @@
import Mathlib.Tactic.Linter.ValidatePRTitle
--- Tests for the PR title validation logic.
+/-! Tests for the PR title validation logic. -/
+section title
+
open Lean in
/--
`#check_title title` takes as input the `String` `title`, expected to be a mathlib PR title.
@@ -186,3 +188,82 @@ info: Message: 'error: the PR title contains multiple consecutive spaces; please
-/
#guard_msgs in
#check_title "feat(Mathlib/Algebra.lean): title."
+
+end title
+
+-- Tests for the PR description validation logic.
+section description
+
+open Lean in
+/--
+`#check_description desc` takes as input the `String` `desc`, expected to be a mathlib PR body.
+It logs details of what the linter would report if the description is "malformed".
+-/
+elab "#check_description " desc:str : command => do
+ let title := desc.getString
+ for err in validatePRBody title false do
+ logInfo m!"Message: '{err}'"
+
+/-- info: Message: 'error: the PR description is empty' -/
+#guard_msgs in
+#check_description ""
+
+/-- info: Message: 'error: the PR description is empty' -/
+#guard_msgs in
+#check_description " " -- whitespace only PR bodies should also get linted
+
+-- This description is virtually empty: just whitespace.
+/-- info: Message: 'error: the PR description is empty' -/
+#guard_msgs in
+#check_description "\n\n---"
+
+/-- info: Message: 'error: the PR description is empty' -/
+#guard_msgs in
+#check_description ".\n\n---"
+
+/-- info: Message: 'error: there should be a blank line between the PR description and the fold' -/
+#guard_msgs in
+#check_description "A word\n----\n"
+
+/-- info: Message: 'error: there should be a blank line between the PR description and the fold' -/
+#guard_msgs in
+#check_description "A word\n----\nSome content\nAnother fold\n"
+
+-- Regression test against confusing errors with just a fold.
+/-- info: Message: 'error: the PR description is empty' -/
+#guard_msgs in
+#check_description "---"
+
+/--
+info: Message: 'warning: your PR description is non-empty, but everything is after the '---' line
+note: the final PR commit message only uses what is above that line'
+-/
+#guard_msgs in
+#check_description "----\nA helpful description after the fold\n"
+
+/-- info: Message: 'error: do not include a 'summary' header in the PR body' -/
+#guard_msgs in
+#check_description "## Summary\nSome other content\n"
+
+/--
+info: Message: 'error: usually, a section 'Testing plan' is superfluous (particularly if it only mentions checks done in CI anyway)
+If you have done particular testing, please mention this --- but no need for the header.'
+-/
+#guard_msgs in
+#check_description "Some actual PR body containing useful content.\n## Testing plan\nLake build passes\n"
+
+/--
+info: Message: 'error: do not include a 'summary' header in the PR body'
+---
+info: Message: 'error: usually, a section 'Testing plan' is superfluous (particularly if it only mentions checks done in CI anyway)
+If you have done particular testing, please mention this --- but no need for the header.'
+-/
+#guard_msgs in
+#check_description "Some actual PR body containing useful content.\n## Testing plan\n## Summary\nLake build passes\n"
+
+-- Tests for false positives around a bug in recognising the "fold".
+#guard_msgs in #check_description "First paragraph.\n\nSecond paragraph.\n"
+
+#guard_msgs in #check_description "My description.\n\n---\n\nMeta info after the fold.\n"
+
+end description
diff --git a/scripts/check_title_labels.lean b/scripts/check_title_labels.lean
index 0f3504dd51aa1f..fd82ded1e9de7c 100644
--- a/scripts/check_title_labels.lean
+++ b/scripts/check_title_labels.lean
@@ -21,10 +21,10 @@ open Cli in
The exit code is the number of violations found. -/
def checkTitleLabelsCLI (args : Parsed) : IO UInt32 := do
let title := (args.positionalArg! "title").value
+ let body := (args.positionalArg! "body").value
let labels : List String := match args.flag? "labels" with
| some f => (f.as! String).splitOn "\n"
| none => []
- IO.println s!"labels are {labels}"
-- We do not validate titles of WIP PRs.
if labels.contains "WIP" then return 0
@@ -34,6 +34,11 @@ def checkTitleLabelsCLI (args : Parsed) : IO UInt32 := do
numberErrors := UInt32.ofNat titleErrors.size
for err in titleErrors do
IO.println err
+ -- Enforce some properties of the PR description.
+ let descriptionErrors : Array String := validatePRBody body (labels.contains "easy")
+ numberErrors := numberErrors + UInt32.ofNat descriptionErrors.size
+ for err in descriptionErrors do
+ IO.println err
return min numberErrors 125
open Cli in
@@ -45,16 +50,18 @@ def checkTitleLabels : Cmd := `[Cli|
If this PR is a feature PR, also verify that it has a topic label,
and that there are no contradictory labels.
- If the inpupt title does not pass validation, output a list of errors."
+ If the input title does not pass validation, output a list of errors."
FLAGS:
"labels" : String; "newline-separated list of label names of this PR\
- These are optional; we merely use a WIP label to skip any checks of the PR title"
+ These are optional; we use a WIP label to skip all checks, and an `easy` label \
+ to skip some PR description checks"
ARGS:
title : String; "this PR's title"
+ body : String; "this PR's body"
]
-/-- The entrypoint to the `lake exe check-title-labels` command. -/
+/-- The entrypoint to the `lake exe check_title_labels` command. -/
def main (args : List String) : IO UInt32 := checkTitleLabels.validate args
From 2025c51063750c45d6cf959608bd46a2c596cb2e Mon Sep 17 00:00:00 2001
From: Eric Wieser <425260+eric-wieser@users.noreply.github.com>
Date: Mon, 15 Jun 2026 12:40:28 +0000
Subject: [PATCH 0044/1300] feat: missing instances for RingCon (#40563)
This adds all the missing commutative instances, as well as instances for when only the additive or multiplicative structure is lawful.
---
Mathlib/RingTheory/Congruence/Defs.lean | 79 +++++++++++++++++++++++++
1 file changed, 79 insertions(+)
diff --git a/Mathlib/RingTheory/Congruence/Defs.lean b/Mathlib/RingTheory/Congruence/Defs.lean
index 60ca0cc2e57c46..c4521330d750d4 100644
--- a/Mathlib/RingTheory/Congruence/Defs.lean
+++ b/Mathlib/RingTheory/Congruence/Defs.lean
@@ -339,19 +339,84 @@ The operations above on the quotient by `c : RingCon R` preserve the algebraic s
section Algebraic
+section Add
+
+instance [AddZeroClass R] [Mul R] (c : RingCon R) : AddZeroClass c.Quotient :=
+ inferInstanceAs <| AddZeroClass c.toAddCon.Quotient
+
+instance [AddSemigroup R] [Mul R] (c : RingCon R) : AddSemigroup c.Quotient :=
+ inferInstanceAs <| AddSemigroup c.toAddCon.Quotient
+
+instance [AddCommMagma R] [Mul R] (c : RingCon R) : AddCommMagma c.Quotient :=
+ inferInstanceAs <| AddCommMagma c.toAddCon.Quotient
+
+instance [AddCommSemigroup R] [Mul R] (c : RingCon R) : AddCommSemigroup c.Quotient :=
+ inferInstanceAs <| AddCommSemigroup c.toAddCon.Quotient
+
+instance [AddMonoid R] [Mul R] (c : RingCon R) : AddMonoid c.Quotient :=
+ inferInstanceAs <| AddMonoid c.toAddCon.Quotient
+
+instance [AddCommMonoid R] [Mul R] (c : RingCon R) : AddCommMonoid c.Quotient :=
+ inferInstanceAs <| AddCommMonoid c.toAddCon.Quotient
+
+instance [AddGroup R] [Mul R] (c : RingCon R) : AddGroup c.Quotient :=
+ inferInstanceAs <| AddGroup c.toAddCon.Quotient
+
+instance [AddCommGroup R] [Mul R] (c : RingCon R) : AddCommGroup c.Quotient :=
+ inferInstanceAs <| AddCommGroup c.toAddCon.Quotient
+
+end Add
+
+section Mul
+
+instance [Add R] [MulOneClass R] (c : RingCon R) : MulOneClass c.Quotient :=
+ inferInstanceAs <| MulOneClass c.toCon.Quotient
+
+instance [Add R] [Semigroup R] (c : RingCon R) : Semigroup c.Quotient :=
+ inferInstanceAs <| Semigroup c.toCon.Quotient
+
+instance [Add R] [CommMagma R] (c : RingCon R) : CommMagma c.Quotient :=
+ inferInstanceAs <| CommMagma c.toCon.Quotient
+
+instance [Add R] [CommSemigroup R] (c : RingCon R) : CommSemigroup c.Quotient :=
+ inferInstanceAs <| CommSemigroup c.toCon.Quotient
+
+instance [Add R] [Monoid R] (c : RingCon R) : Monoid c.Quotient :=
+ inferInstanceAs <| Monoid c.toCon.Quotient
+
+instance [Add R] [CommMonoid R] (c : RingCon R) : CommMonoid c.Quotient :=
+ inferInstanceAs <| CommMonoid c.toCon.Quotient
+
+end Mul
+
instance [NonUnitalNonAssocSemiring R] (c : RingCon R) :
NonUnitalNonAssocSemiring c.Quotient := fast_instance%
Function.Surjective.nonUnitalNonAssocSemiring _ Quotient.mk''_surjective rfl
(fun _ _ => rfl) (fun _ _ => rfl) fun _ _ => rfl
+instance [NonUnitalNonAssocCommSemiring R] (c : RingCon R) :
+ NonUnitalNonAssocCommSemiring c.Quotient := fast_instance%
+ Function.Surjective.nonUnitalNonAssocCommSemiring _ Quotient.mk''_surjective rfl
+ (fun _ _ => rfl) (fun _ _ => rfl) fun _ _ => rfl
+
instance [NonAssocSemiring R] (c : RingCon R) : NonAssocSemiring c.Quotient := fast_instance%
Function.Surjective.nonAssocSemiring _ Quotient.mk''_surjective rfl rfl (fun _ _ => rfl)
(fun _ _ => rfl) (fun _ _ => rfl) fun _ => rfl
+instance [NonAssocCommSemiring R] (c : RingCon R) :
+ NonAssocCommSemiring c.Quotient := fast_instance%
+ Function.Surjective.nonAssocCommSemiring _ Quotient.mk''_surjective rfl rfl (fun _ _ => rfl)
+ (fun _ _ => rfl) (fun _ _ => rfl) fun _ => rfl
+
instance [NonUnitalSemiring R] (c : RingCon R) : NonUnitalSemiring c.Quotient := fast_instance%
Function.Surjective.nonUnitalSemiring _ Quotient.mk''_surjective rfl (fun _ _ => rfl)
(fun _ _ => rfl) fun _ _ => rfl
+instance [NonUnitalCommSemiring R] (c : RingCon R) :
+ NonUnitalCommSemiring c.Quotient := fast_instance%
+ Function.Surjective.nonUnitalCommSemiring _ Quotient.mk''_surjective rfl (fun _ _ => rfl)
+ (fun _ _ => rfl) fun _ _ => rfl
+
instance [Semiring R] (c : RingCon R) : Semiring c.Quotient := fast_instance%
Function.Surjective.semiring _ Quotient.mk''_surjective rfl rfl (fun _ _ => rfl)
(fun _ _ => rfl) (fun _ _ => rfl) (fun _ _ => rfl) fun _ => rfl
@@ -365,15 +430,29 @@ instance [NonUnitalNonAssocRing R] (c : RingCon R) :
Function.Surjective.nonUnitalNonAssocRing _ Quotient.mk''_surjective rfl (fun _ _ => rfl)
(fun _ _ => rfl) (fun _ => rfl) (fun _ _ => rfl) (fun _ _ => rfl) fun _ _ => rfl
+instance [NonUnitalNonAssocCommRing R] (c : RingCon R) :
+ NonUnitalNonAssocCommRing c.Quotient := fast_instance%
+ Function.Surjective.nonUnitalNonAssocCommRing _ Quotient.mk''_surjective rfl (fun _ _ => rfl)
+ (fun _ _ => rfl) (fun _ => rfl) (fun _ _ => rfl) (fun _ _ => rfl) fun _ _ => rfl
+
instance [NonAssocRing R] (c : RingCon R) : NonAssocRing c.Quotient := fast_instance%
Function.Surjective.nonAssocRing _ Quotient.mk''_surjective rfl rfl (fun _ _ => rfl)
(fun _ _ => rfl) (fun _ => rfl) (fun _ _ => rfl) (fun _ _ => rfl) (fun _ _ => rfl)
(fun _ => rfl) fun _ => rfl
+instance [NonAssocCommRing R] (c : RingCon R) : NonAssocCommRing c.Quotient := fast_instance%
+ Function.Surjective.nonAssocCommRing _ Quotient.mk''_surjective rfl rfl (fun _ _ => rfl)
+ (fun _ _ => rfl) (fun _ => rfl) (fun _ _ => rfl) (fun _ _ => rfl) (fun _ _ => rfl)
+ (fun _ => rfl) fun _ => rfl
+
instance [NonUnitalRing R] (c : RingCon R) : NonUnitalRing c.Quotient := fast_instance%
Function.Surjective.nonUnitalRing _ Quotient.mk''_surjective rfl (fun _ _ => rfl)
(fun _ _ => rfl) (fun _ => rfl) (fun _ _ => rfl) (fun _ _ => rfl) fun _ _ => rfl
+instance [NonUnitalCommRing R] (c : RingCon R) : NonUnitalCommRing c.Quotient := fast_instance%
+ Function.Surjective.nonUnitalCommRing _ Quotient.mk''_surjective rfl (fun _ _ => rfl)
+ (fun _ _ => rfl) (fun _ => rfl) (fun _ _ => rfl) (fun _ _ => rfl) fun _ _ => rfl
+
instance [Ring R] (c : RingCon R) : Ring c.Quotient := fast_instance%
Function.Surjective.ring _ Quotient.mk''_surjective rfl rfl (fun _ _ => rfl)
(fun _ _ => rfl) (fun _ => rfl) (fun _ _ => rfl) (fun _ _ => rfl) (fun _ _ => rfl)
From ddd592eb364828f0a506648596c38bbf580a3165 Mon Sep 17 00:00:00 2001
From: Thomas Browning <13339017+tb65536@users.noreply.github.com>
Date: Mon, 15 Jun 2026 13:16:03 +0000
Subject: [PATCH 0045/1300] feat(RingTheory/Invariant/Basic): generalize
`Ideal.Quotient.normal` to `IsFractionRing` (#40247)
This PR generalizes `Ideal.Quotient.normal` to `IsFractionRing` (currently it only holds for quotients of maximal ideals). I have left both versions since that is the pattern of the file (to have both an `IsFractionRing` version and an `Ideal.Quotient` version).
Co-authored-by: tb65536
---
.../IntegralClosure/IntegralRestrict.lean | 12 +--
Mathlib/RingTheory/Invariant/Basic.lean | 89 ++++++++++++++-----
Mathlib/RingTheory/Localization/Integral.lean | 2 +-
3 files changed, 73 insertions(+), 30 deletions(-)
diff --git a/Mathlib/RingTheory/IntegralClosure/IntegralRestrict.lean b/Mathlib/RingTheory/IntegralClosure/IntegralRestrict.lean
index 4632e56e931129..a5c4644937b610 100644
--- a/Mathlib/RingTheory/IntegralClosure/IntegralRestrict.lean
+++ b/Mathlib/RingTheory/IntegralClosure/IntegralRestrict.lean
@@ -220,7 +220,7 @@ instance (priority := 900) [IsDomain A] [IsDomain B] [IsIntegrallyClosed B]
IsIntegralClosure.of_isIntegrallyClosed _ _ _
-- TODO: How is this even supposed to fire? `R` and `S` cannot be inferred.
haveI : Algebra.IsAlgebraic (FractionRing A) (FractionRing B) :=
- isAlgebraic_of_isFractionRing (R := A) (S := B) ..
+ isAlgebraic_of_isFractionRing A B ..
haveI : IsLocalization (Algebra.algebraMapSubmonoid B A⁰) (FractionRing B) :=
IsIntegralClosure.isLocalization _ (FractionRing A) _ _
haveI : FiniteDimensional (FractionRing A) (FractionRing B) := .of_isLocalization A B A⁰
@@ -262,7 +262,7 @@ def Algebra.intTrace : B →ₗ[A] A :=
IsIntegralClosure.of_isIntegrallyClosed _ _ _
-- TODO: How is this even supposed to fire? `R` and `S` cannot be inferred.
haveI : Algebra.IsAlgebraic (FractionRing A) (FractionRing B) :=
- isAlgebraic_of_isFractionRing (R := A) (S := B) ..
+ isAlgebraic_of_isFractionRing A B ..
haveI : IsLocalization (algebraMapSubmonoid B A⁰) (FractionRing B) :=
IsIntegralClosure.isLocalization _ (FractionRing A) _ _
haveI : FiniteDimensional (FractionRing A) (FractionRing B) := .of_isLocalization A B A⁰
@@ -276,7 +276,7 @@ lemma Algebra.algebraMap_intTrace (x : B) :
IsIntegralClosure.of_isIntegrallyClosed _ _ _
-- TODO: How is this even supposed to fire? `R` and `S` cannot be inferred.
haveI : Algebra.IsAlgebraic (FractionRing A) (FractionRing B) :=
- isAlgebraic_of_isFractionRing (R := A) (S := B) ..
+ isAlgebraic_of_isFractionRing A B ..
haveI : IsLocalization (algebraMapSubmonoid B A⁰) (FractionRing B) :=
IsIntegralClosure.isLocalization _ (FractionRing A) _ _
haveI : FiniteDimensional (FractionRing A) (FractionRing B) := .of_isLocalization A B A⁰
@@ -296,7 +296,7 @@ lemma Algebra.algebraMap_intTrace_fractionRing (x : B) :
IsIntegralClosure.of_isIntegrallyClosed _ _ _
-- TODO: How is this even supposed to fire? `R` and `S` cannot be inferred.
haveI : Algebra.IsAlgebraic (FractionRing A) (FractionRing B) :=
- isAlgebraic_of_isFractionRing (R := A) (S := B) ..
+ isAlgebraic_of_isFractionRing A B ..
haveI : IsLocalization (algebraMapSubmonoid B A⁰) (FractionRing B) :=
IsIntegralClosure.isLocalization _ (FractionRing A) _ _
haveI : FiniteDimensional (FractionRing A) (FractionRing B) := .of_isLocalization A B A⁰
@@ -310,7 +310,7 @@ lemma Algebra.intTrace_eq_trace [Module.Free A B] : Algebra.intTrace A B = Algeb
IsIntegralClosure.of_isIntegrallyClosed _ _ _
-- TODO: How is this even supposed to fire? `R` and `S` cannot be inferred.
haveI : Algebra.IsAlgebraic (FractionRing A) (FractionRing B) :=
- isAlgebraic_of_isFractionRing (R := A) (S := B) ..
+ isAlgebraic_of_isFractionRing A B ..
haveI : IsLocalization (algebraMapSubmonoid B A⁰) (FractionRing B) :=
IsIntegralClosure.isLocalization _ (FractionRing A) _ _
apply IsFractionRing.injective A (FractionRing A)
@@ -334,7 +334,7 @@ lemma Algebra.intTrace_eq_of_isLocalization
IsIntegralClosure.of_isIntegrallyClosed _ _ _
-- TODO: How is this even supposed to fire? `R` and `S` cannot be inferred.
haveI : Algebra.IsAlgebraic (FractionRing A) (FractionRing B) :=
- isAlgebraic_of_isFractionRing (R := A) (S := B) ..
+ isAlgebraic_of_isFractionRing A B ..
have : IsLocalization (algebraMapSubmonoid B A⁰) L :=
IsIntegralClosure.isLocalization _ (FractionRing A) _ _
let f : Aₘ →+* K := IsLocalization.map _ (T := A⁰) (RingHom.id A) hM
diff --git a/Mathlib/RingTheory/Invariant/Basic.lean b/Mathlib/RingTheory/Invariant/Basic.lean
index dfe951fc2a6b42..83972a5c02b355 100644
--- a/Mathlib/RingTheory/Invariant/Basic.lean
+++ b/Mathlib/RingTheory/Invariant/Basic.lean
@@ -7,6 +7,7 @@ module
public import Mathlib.RingTheory.Invariant.Defs
public import Mathlib.RingTheory.IntegralClosure.IntegralRestrict
+public import Mathlib.RingTheory.LocalRing.ResidueField.Ideal
/-!
# Invariant Extensions of Rings
@@ -146,6 +147,9 @@ theorem charpoly_eq_prod_smul (b : B) : charpoly G b = ∏ g : G, g • (X - C b
theorem monic_charpoly (b : B) : (charpoly G b).Monic :=
monic_prod_of_monic _ _ (fun _ _ ↦ monic_X_sub_C _)
+theorem splits_charpoly (b : B) : (charpoly G b).Splits :=
+ .prod fun g _ ↦ .X_sub_C (g • b)
+
theorem eval_charpoly (b : B) : (charpoly G b).eval b = 0 := by
rw [charpoly_eq, eval_prod]
apply Finset.prod_eq_zero (Finset.mem_univ (1 : G))
@@ -511,41 +515,80 @@ lemma Ideal.Quotient.exists_algEquiv_fixedPoint_quotient_under
refine .trans ?_ (σ.apply_symm_apply _)
rw [← h₂, ← e, h₁]
+namespace Ideal.IsFractionRing
+
+variable [P.IsPrime] [Q.IsPrime] (K L : Type*) [Field K] [Field L] [Algebra K L]
+ [Algebra (A ⧸ P) K] [IsFractionRing (A ⧸ P) K] [Algebra (B ⧸ Q) L] [IsFractionRing (B ⧸ Q) L]
+ [Algebra (A ⧸ P) L] [IsScalarTower (A ⧸ P) (B ⧸ Q) L] [IsScalarTower (A ⧸ P) K L]
+
+open Polynomial in
+include P Q G in
+lemma normal : Normal K L := by
+ have := Algebra.IsInvariant.isIntegral A B G
+ have := isAlgebraic_of_isFractionRing (A ⧸ P) (B ⧸ Q) K L
+ constructor
+ intro x
+ obtain ⟨x, y, hy, rfl⟩ := IsFractionRing.div_surjective (B ⧸ Q) x
+ obtain ⟨b, a, ha, h⟩ := (Algebra.IsAlgebraic.isAlgebraic (R := A ⧸ P) y).exists_smul_eq_mul x hy
+ obtain ⟨a, rfl⟩ := Quotient.mk_surjective a
+ obtain ⟨b, rfl⟩ := Quotient.mk_surjective b
+ simp_rw [← Quotient.algebraMap_eq] at *
+ cases nonempty_fintype G
+ obtain ⟨p, hp, -, h_monic⟩ := lifts_and_natDegree_eq_and_monic
+ (Algebra.IsInvariant.charpoly_mem_lifts A B G b) (MulSemiringAction.monic_charpoly ..)
+ have h_eval : p.aeval b = 0 := by
+ rw [← eval_map_algebraMap, hp, MulSemiringAction.eval_charpoly]
+ let q := p.comp (C a * X)
+ let d := (algebraMap (B ⧸ Q) L) x / (algebraMap (B ⧸ Q) L) y
+ have comm₁ : (algebraMap K L).comp (algebraMap (A ⧸ P) K) =
+ (algebraMap (B ⧸ Q) L).comp (algebraMap (A ⧸ P) (B ⧸ Q)) := by
+ simp_rw [← IsScalarTower.algebraMap_eq]
+ have comm₂ : (algebraMap (A ⧸ P) (B ⧸ Q)).comp (algebraMap A (A ⧸ P)) =
+ (algebraMap B (B ⧸ Q)).comp (algebraMap A B) := by
+ simp_rw [← IsScalarTower.algebraMap_eq]
+ replace h_eval : ((q.map (algebraMap A (A ⧸ P))).map (algebraMap (A ⧸ P) K)).aeval d = 0 := by
+ simp_rw [q, map_comp, Polynomial.map_mul, map_C, map_X, aeval_comp, aeval_mul, aeval_C, aeval_X,
+ ← RingHom.comp_apply, ← RingHom.comp_assoc, comm₁, RingHom.comp_apply, d, mul_div, ← map_mul]
+ rw [← Algebra.smul_def, h, map_mul, mul_div_cancel_left₀ _ (by simpa using hy),
+ aeval_map_algebraMap, aeval_algebraMap_apply, aeval_map_algebraMap, aeval_algebraMap_apply,
+ h_eval, map_zero, map_zero]
+ replace h_splits : (p.map (algebraMap A B)).Splits := by
+ rw [hp]
+ exact MulSemiringAction.splits_charpoly G b
+ refine .of_dvd ?_ ?_ (map_dvd (algebraMap K L) (minpoly.dvd K d h_eval))
+ · simp_rw [q, map_comp, Polynomial.map_mul, map_C, map_X]
+ refine .comp_of_degree_le_one ?_ (degree_C_mul_X_le _)
+ rw [Polynomial.map_map, Polynomial.map_map, comm₁, RingHom.comp_assoc, comm₂,
+ ← RingHom.comp_assoc, ← Polynomial.map_map]
+ apply h_splits.map
+ · simp_rw [q, map_comp, Polynomial.map_mul, map_C, map_X, Polynomial.map_map]
+ exact mt (comp_C_mul_X_eq_zero_iff (by simpa)).mp (map_monic_ne_zero h_monic)
+
+include P Q in
+lemma finite_of_isInvariant [SMulCommClass G A B] [Algebra.IsSeparable K L] :
+ Module.Finite K L := by
+ have : IsGalois K L := { __ := normal G P Q K L }
+ have := Finite.of_surjective _ (IsFractionRing.stabilizerHom_surjective G P Q K L)
+ apply IsGalois.finiteDimensional_of_finite
+
+end Ideal.IsFractionRing
+
attribute [local instance] Ideal.Quotient.field in
include G in
/--
For any domain `k` containing `B ⧸ Q`,
any endomorphism of `k` can be restricted to an endomorphism of `B ⧸ Q`. -/
lemma Ideal.Quotient.normal [P.IsMaximal] [Q.IsMaximal] :
- Normal (A ⧸ P) (B ⧸ Q) := by
- cases subsingleton_or_nontrivial B
- · cases ‹Q.IsMaximal›.ne_top (Subsingleton.elim _ _)
- have := Algebra.IsInvariant.isIntegral A B G
- constructor
- intro x
- obtain ⟨x, rfl⟩ := Ideal.Quotient.mk_surjective x
- cases nonempty_fintype G
- obtain ⟨p, hp, h₁, h₂⟩ := Polynomial.lifts_and_degree_eq_and_monic
- (Algebra.IsInvariant.charpoly_mem_lifts A B G x) (MulSemiringAction.monic_charpoly _ _)
- have H : Polynomial.aeval x p = 0 := by
- rw [Polynomial.aeval_def, ← Polynomial.eval_map, hp, MulSemiringAction.eval_charpoly]
- have := minpoly.dvd _ (algebraMap _ (B ⧸ Q) x) (p := p.map (algebraMap _ (A ⧸ P)))
- (by rw [Polynomial.aeval_map_algebraMap, Polynomial.aeval_algebraMap_apply, H, map_zero])
- refine Polynomial.Splits.of_dvd ?_ ?_ ((Polynomial.map_dvd_map' _).mpr this)
- · rw [Polynomial.map_map, ← IsScalarTower.algebraMap_eq, IsScalarTower.algebraMap_eq A B,
- ← Polynomial.map_map, hp, MulSemiringAction.charpoly_eq, Polynomial.map_prod]
- exact Polynomial.Splits.prod (fun _ _ ↦ (Polynomial.Splits.X_sub_C _).map _)
- · exact ((h₂.map _).map _).ne_zero
+ Normal (A ⧸ P) (B ⧸ Q) :=
+ IsFractionRing.normal G P Q (A ⧸ P) (B ⧸ Q)
attribute [local instance] Ideal.Quotient.field in
include G in
/-- If the extension `B/Q` over `A/P` is separable, then it is finite dimensional. -/
lemma Ideal.Quotient.finite_of_isInvariant [P.IsMaximal] [Q.IsMaximal]
[SMulCommClass G A B] [Algebra.IsSeparable (A ⧸ P) (B ⧸ Q)] :
- Module.Finite (A ⧸ P) (B ⧸ Q) := by
- have : IsGalois (A ⧸ P) (B ⧸ Q) := { __ := Ideal.Quotient.normal (A := A) G P Q }
- have := Finite.of_surjective _ (Ideal.Quotient.stabilizerHom_surjective G P Q)
- exact IsGalois.finiteDimensional_of_finite _ _
+ Module.Finite (A ⧸ P) (B ⧸ Q) :=
+ IsFractionRing.finite_of_isInvariant G P Q (A ⧸ P) (B ⧸ Q)
end normal
diff --git a/Mathlib/RingTheory/Localization/Integral.lean b/Mathlib/RingTheory/Localization/Integral.lean
index 8ad6870f80e83f..8d608d1116c9c8 100644
--- a/Mathlib/RingTheory/Localization/Integral.lean
+++ b/Mathlib/RingTheory/Localization/Integral.lean
@@ -551,7 +551,7 @@ theorem ideal_span_singleton_map_subset {L : Type*} [IsDomain R] [IsDomain S] [F
end IsFractionRing
open nonZeroDivisors in
-lemma isAlgebraic_of_isFractionRing {R S} (K L) [CommRing R] [CommRing S] [Field K] [CommRing L]
+lemma isAlgebraic_of_isFractionRing (R S K L) [CommRing R] [CommRing S] [Field K] [CommRing L]
[Algebra R S] [Algebra R K] [Algebra R L] [Algebra S L] [Algebra K L] [IsScalarTower R S L]
[IsScalarTower R K L] [IsFractionRing S L]
[Algebra.IsIntegral R S] : Algebra.IsAlgebraic K L := by
From 24ef8a8c893173686f1ce65b5961175cd6f6413d Mon Sep 17 00:00:00 2001
From: Michael Rothgang <10105016+grunweg@users.noreply.github.com>
Date: Mon, 15 Jun 2026 13:35:23 +0000
Subject: [PATCH 0046/1300] fix: revert #40181 (#40630)
This revert #40181: right now, any PR with empty description breaks the "check PR title" workflow.
Let's revert the change while we investigate.
---
.github/workflows/build_template.yml | 17 +----
.github/workflows/check_pr_titles.yaml | 3 +-
Mathlib/Tactic/Linter/ValidatePRTitle.lean | 48 -------------
MathlibTest/ValidatePRTitle.lean | 83 +---------------------
scripts/check_title_labels.lean | 15 ++--
5 files changed, 7 insertions(+), 159 deletions(-)
diff --git a/.github/workflows/build_template.yml b/.github/workflows/build_template.yml
index 333c646aa06c86..6eea0109569af5 100644
--- a/.github/workflows/build_template.yml
+++ b/.github/workflows/build_template.yml
@@ -804,22 +804,7 @@ jobs:
run: |
lake env lean scripts/create_deprecated_modules.lean
lake env lean scripts/autolabel.lean
- lake exe check_title_labels --labels "t-algebra" "feat: dummy PR for testing" "dummy PR body" > title_output.txt
- if grep -qE "Missing positional argument " "title_output.txt"; then
- echo ""
- echo "=============================================================================="
- echo "ERROR: Your branch predates the current 'check title' configuration."
- echo "Please merge 'master' into your PR branch and push again."
- echo ""
- echo "You can do this with:"
- echo " git fetch upstream"
- echo " git merge upstream/master"
- echo " git push"
- echo "=============================================================================="
- echo ""
- exit 1
- fi
- cat title_output.txt
+ lake exe check_title_labels --labels "t-algebra" "feat: dummy PR for testing"
- name: build everything
# make sure everything is available for test/import_all.lean
diff --git a/.github/workflows/check_pr_titles.yaml b/.github/workflows/check_pr_titles.yaml
index 491b023c9b522f..edbe9058ecd2a8 100644
--- a/.github/workflows/check_pr_titles.yaml
+++ b/.github/workflows/check_pr_titles.yaml
@@ -34,7 +34,6 @@ jobs:
if: github.event.pull_request.draft == false && github.event.pull_request.base.ref == 'master'
env:
TITLE: ${{ github.event.pull_request.title }}
- BODY: ${{ github.event.pull_request.body }}
PR_LABELS: ${{ toJson(github.event.pull_request.labels) }}
run: |
set -o pipefail
@@ -54,7 +53,7 @@ jobs:
set +e
# Capture output and exit code
- output=$(lake exe check_title_labels --labels "$label_names" "$TITLE" "$BODY" 2>&1)
+ output=$(lake exe check_title_labels --labels "$label_names" "$TITLE" 2>&1)
exit_code=$?
set -e
diff --git a/Mathlib/Tactic/Linter/ValidatePRTitle.lean b/Mathlib/Tactic/Linter/ValidatePRTitle.lean
index 09e4f1e5f87a19..feb10809e868a7 100644
--- a/Mathlib/Tactic/Linter/ValidatePRTitle.lean
+++ b/Mathlib/Tactic/Linter/ValidatePRTitle.lean
@@ -11,13 +11,10 @@ import Mathlib.Tactic.Linter.TextBased.UnicodeLinter
/-!
# Checker for well-formed title and labels
-
This script checks if a PR title matches
[mathlib's commit conventions](https://leanprover-community.github.io/contribute/commit.html).
Not all checks from the commit conventions are implemented: for instance, no effort is made to
verify whether the title or body are written in present imperative tense.
-
-It also verifies if the PR description matches some basic sanity checks for good descriptions.
-/
open Std.Internal.Parsec String
@@ -139,48 +136,3 @@ public def validateTitle (title : String) : Array String := Id.run do
errors := errors.push s!"error: the PR contains {badChars.length} Unicode characters \
which are not allowed: {err}"
return errors
-
-/-- Check if `description` matches some basic checks for good PR descriptions
-(a subset of the ones at ,
-plus a few basic sanity checks).
-
-`isLabelledEasy` denotes whether a PR is labelled as easy: if so, an empty description is allowed.
-
-Return all error messages for violations found.
--/
-public def validatePRBody (description : String) (isLabelledEasy : Bool) : Array String := Id.run do
- if description.trimAscii.isEmpty && !isLabelledEasy then
- return #["error: the PR description is empty"]
-
- -- Find all lines in the PR description before a "fold". bors truncates the squash
- -- commit message at the first line starting with "---" (its `cut_body_after = "\n---"`),
- -- so that is exactly what we treat as a fold here.
- let before := description.lines.toList.takeWhile (fun l ↦ !l.startsWith "---")
- -- If `after` is non-empty, there is a fold.
- let after := description.lines.toList.drop before.length
- let mut errors := #[]
- if let some l := before.getLast? then
- if !after.isEmpty && l != "" then
- errors := errors.push
- "error: there should be a blank line between the PR description and the fold"
- if before.any (· == "## Summary") then
- errors := errors.push "error: do not include a 'summary' header in the PR body"
- if before.any (· == "## Testing plan") then
- errors := errors.push "error: usually, a section 'Testing plan' is superfluous \
- (particularly if it only mentions checks done in CI anyway)\n\
- If you have done particular testing, please mention this --- but no need for the header."
- -- Should this error on any headings in the PR description?
-
- -- Just whitespace, or a period before the fold also count as empty descriptions.
- let beforeContainsText := before.any (·.any (·.isAlpha))
- if !beforeContainsText then
- -- We drop the leading "---" line.
- let afterContainsText := after.drop 1 |>.any (·.any (·.isAlpha))
- if afterContainsText then
- errors := errors.push
- "warning: your PR description is non-empty, but everything is after the '---' line\n\
- note: the final PR commit message only uses what is above that line"
- else
- errors := errors.push "error: the PR description is empty"
-
- return errors
diff --git a/MathlibTest/ValidatePRTitle.lean b/MathlibTest/ValidatePRTitle.lean
index 66a9d8e9c03356..aacda293a7e90d 100644
--- a/MathlibTest/ValidatePRTitle.lean
+++ b/MathlibTest/ValidatePRTitle.lean
@@ -1,8 +1,6 @@
import Mathlib.Tactic.Linter.ValidatePRTitle
-/-! Tests for the PR title validation logic. -/
-section title
-
+-- Tests for the PR title validation logic.
open Lean in
/--
`#check_title title` takes as input the `String` `title`, expected to be a mathlib PR title.
@@ -188,82 +186,3 @@ info: Message: 'error: the PR title contains multiple consecutive spaces; please
-/
#guard_msgs in
#check_title "feat(Mathlib/Algebra.lean): title."
-
-end title
-
--- Tests for the PR description validation logic.
-section description
-
-open Lean in
-/--
-`#check_description desc` takes as input the `String` `desc`, expected to be a mathlib PR body.
-It logs details of what the linter would report if the description is "malformed".
--/
-elab "#check_description " desc:str : command => do
- let title := desc.getString
- for err in validatePRBody title false do
- logInfo m!"Message: '{err}'"
-
-/-- info: Message: 'error: the PR description is empty' -/
-#guard_msgs in
-#check_description ""
-
-/-- info: Message: 'error: the PR description is empty' -/
-#guard_msgs in
-#check_description " " -- whitespace only PR bodies should also get linted
-
--- This description is virtually empty: just whitespace.
-/-- info: Message: 'error: the PR description is empty' -/
-#guard_msgs in
-#check_description "\n\n---"
-
-/-- info: Message: 'error: the PR description is empty' -/
-#guard_msgs in
-#check_description ".\n\n---"
-
-/-- info: Message: 'error: there should be a blank line between the PR description and the fold' -/
-#guard_msgs in
-#check_description "A word\n----\n"
-
-/-- info: Message: 'error: there should be a blank line between the PR description and the fold' -/
-#guard_msgs in
-#check_description "A word\n----\nSome content\nAnother fold\n"
-
--- Regression test against confusing errors with just a fold.
-/-- info: Message: 'error: the PR description is empty' -/
-#guard_msgs in
-#check_description "---"
-
-/--
-info: Message: 'warning: your PR description is non-empty, but everything is after the '---' line
-note: the final PR commit message only uses what is above that line'
--/
-#guard_msgs in
-#check_description "----\nA helpful description after the fold\n"
-
-/-- info: Message: 'error: do not include a 'summary' header in the PR body' -/
-#guard_msgs in
-#check_description "## Summary\nSome other content\n"
-
-/--
-info: Message: 'error: usually, a section 'Testing plan' is superfluous (particularly if it only mentions checks done in CI anyway)
-If you have done particular testing, please mention this --- but no need for the header.'
--/
-#guard_msgs in
-#check_description "Some actual PR body containing useful content.\n## Testing plan\nLake build passes\n"
-
-/--
-info: Message: 'error: do not include a 'summary' header in the PR body'
----
-info: Message: 'error: usually, a section 'Testing plan' is superfluous (particularly if it only mentions checks done in CI anyway)
-If you have done particular testing, please mention this --- but no need for the header.'
--/
-#guard_msgs in
-#check_description "Some actual PR body containing useful content.\n## Testing plan\n## Summary\nLake build passes\n"
-
--- Tests for false positives around a bug in recognising the "fold".
-#guard_msgs in #check_description "First paragraph.\n\nSecond paragraph.\n"
-
-#guard_msgs in #check_description "My description.\n\n---\n\nMeta info after the fold.\n"
-
-end description
diff --git a/scripts/check_title_labels.lean b/scripts/check_title_labels.lean
index fd82ded1e9de7c..0f3504dd51aa1f 100644
--- a/scripts/check_title_labels.lean
+++ b/scripts/check_title_labels.lean
@@ -21,10 +21,10 @@ open Cli in
The exit code is the number of violations found. -/
def checkTitleLabelsCLI (args : Parsed) : IO UInt32 := do
let title := (args.positionalArg! "title").value
- let body := (args.positionalArg! "body").value
let labels : List String := match args.flag? "labels" with
| some f => (f.as! String).splitOn "\n"
| none => []
+ IO.println s!"labels are {labels}"
-- We do not validate titles of WIP PRs.
if labels.contains "WIP" then return 0
@@ -34,11 +34,6 @@ def checkTitleLabelsCLI (args : Parsed) : IO UInt32 := do
numberErrors := UInt32.ofNat titleErrors.size
for err in titleErrors do
IO.println err
- -- Enforce some properties of the PR description.
- let descriptionErrors : Array String := validatePRBody body (labels.contains "easy")
- numberErrors := numberErrors + UInt32.ofNat descriptionErrors.size
- for err in descriptionErrors do
- IO.println err
return min numberErrors 125
open Cli in
@@ -50,18 +45,16 @@ def checkTitleLabels : Cmd := `[Cli|
If this PR is a feature PR, also verify that it has a topic label,
and that there are no contradictory labels.
- If the input title does not pass validation, output a list of errors."
+ If the inpupt title does not pass validation, output a list of errors."
FLAGS:
"labels" : String; "newline-separated list of label names of this PR\
- These are optional; we use a WIP label to skip all checks, and an `easy` label \
- to skip some PR description checks"
+ These are optional; we merely use a WIP label to skip any checks of the PR title"
ARGS:
title : String; "this PR's title"
- body : String; "this PR's body"
]
-/-- The entrypoint to the `lake exe check_title_labels` command. -/
+/-- The entrypoint to the `lake exe check-title-labels` command. -/
def main (args : List String) : IO UInt32 := checkTitleLabels.validate args
From fabf563a7c95a166b8d7b6efca11c8b4dc9d911f Mon Sep 17 00:00:00 2001
From: Garmelon <11077553+Garmelon@users.noreply.github.com>
Date: Mon, 15 Jun 2026 14:22:22 +0000
Subject: [PATCH 0047/1300] chore: bump toolchain to v4.31.0 (#40633)
Upgrade the toolchain to v4.31.0 as part of the release process of Lean.
Co-authored-by: Joscha
---
lake-manifest.json | 16 ++++++++--------
lean-toolchain | 2 +-
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/lake-manifest.json b/lake-manifest.json
index 289958f5764546..1db86ab034fd85 100644
--- a/lake-manifest.json
+++ b/lake-manifest.json
@@ -5,7 +5,7 @@
"type": "git",
"subDir": null,
"scope": "leanprover-community",
- "rev": "744117af710b1c0400cd297c9ce91f8d0ad3a347",
+ "rev": "63045536fe95024e6c18fc7b48e03f506701c5bc",
"name": "plausible",
"manifestFile": "lake-manifest.json",
"inputRev": "main",
@@ -25,7 +25,7 @@
"type": "git",
"subDir": null,
"scope": "leanprover-community",
- "rev": "99c763c8a96d3d44fb4994e96eaa51ca4568449d",
+ "rev": "5c7542ed018c78194f1e2b903eaf6a792b74c03d",
"name": "importGraph",
"manifestFile": "lake-manifest.json",
"inputRev": "main",
@@ -35,7 +35,7 @@
"type": "git",
"subDir": null,
"scope": "leanprover-community",
- "rev": "b2da7698bdf22804095ea5b5007f23c09398f687",
+ "rev": "24b0d9dc081c5423f8eec7e866c441e5184f29d9",
"name": "proofwidgets",
"manifestFile": "lake-manifest.json",
"inputRev": "main",
@@ -45,7 +45,7 @@
"type": "git",
"subDir": null,
"scope": "leanprover-community",
- "rev": "7897ea6e5cfc6522d355083bdfa798377ab35e11",
+ "rev": "e3cb2f741431ce31bf73549fb52316a57368b06f",
"name": "aesop",
"manifestFile": "lake-manifest.json",
"inputRev": "master",
@@ -55,7 +55,7 @@
"type": "git",
"subDir": null,
"scope": "leanprover-community",
- "rev": "94346b7b49c36ae871639d1434232f057c193d60",
+ "rev": "f46324995fca5f0483b742e4eb4daec7f4ee50d2",
"name": "Qq",
"manifestFile": "lake-manifest.json",
"inputRev": "master",
@@ -65,7 +65,7 @@
"type": "git",
"subDir": null,
"scope": "leanprover-community",
- "rev": "c6f7103faab35720af56784a9553733832f17349",
+ "rev": "fa08db58b30eb033edcdab331bba000827f9f785",
"name": "batteries",
"manifestFile": "lake-manifest.json",
"inputRev": "main",
@@ -75,10 +75,10 @@
"type": "git",
"subDir": null,
"scope": "leanprover",
- "rev": "baf3e62fbb3502305076ca077e004aea78157c63",
+ "rev": "92564e5770e4d09f2d86dfbf8ada1e9c715b384c",
"name": "Cli",
"manifestFile": "lake-manifest.json",
- "inputRev": "v4.31.0-rc2",
+ "inputRev": "v4.31.0",
"inherited": true,
"configFile": "lakefile.toml"}],
"name": "mathlib",
diff --git a/lean-toolchain b/lean-toolchain
index e6a8c3c1fa203a..18640c8b066b18 100644
--- a/lean-toolchain
+++ b/lean-toolchain
@@ -1 +1 @@
-leanprover/lean4:v4.31.0-rc2
+leanprover/lean4:v4.31.0
From c0477ad6b77161888036499c30cfaaeb0b50d46f Mon Sep 17 00:00:00 2001
From: Jovan Gerbscheid <56355248+JovanGerb@users.noreply.github.com>
Date: Mon, 15 Jun 2026 16:33:48 +0000
Subject: [PATCH 0048/1300] perf: don't import `Lean.Meta` or
`Lean.Elab.Tactic` (#40625)
This PR gets rid of the broad imports of `Lean.Meta`/`Lean.Elab.Tactic`, and adds them to the broad imports linter to avoid future violations.
As a result, imports in some other files had to be updated.
---
Mathlib/Algebra/GradedMonoid.lean | 1 -
Mathlib/Lean/Meta/RefinedDiscrTree/Encode.lean | 1 -
Mathlib/Tactic/Algebra/Basic.lean | 3 +--
Mathlib/Tactic/Linarith/Oracle/FourierMotzkin.lean | 3 +--
Mathlib/Tactic/Linter/Header.lean | 2 +-
Mathlib/Tactic/Module.lean | 2 +-
Mathlib/Util/DelabNonCanonical.lean | 1 -
MathlibTest/Tactic/Grind/Lint.lean | 3 +++
8 files changed, 7 insertions(+), 9 deletions(-)
diff --git a/Mathlib/Algebra/GradedMonoid.lean b/Mathlib/Algebra/GradedMonoid.lean
index 9cd3249feff46e..e979eb59e8d2e8 100644
--- a/Mathlib/Algebra/GradedMonoid.lean
+++ b/Mathlib/Algebra/GradedMonoid.lean
@@ -11,7 +11,6 @@ public import Mathlib.Algebra.Group.Submonoid.Defs
public import Mathlib.Data.List.FinRange
public import Mathlib.Data.SetLike.Basic
public import Mathlib.Data.Sigma.Basic
-public import Lean.Elab.Tactic
public import Mathlib.Algebra.BigOperators.Group.Finset.Basic
/-!
diff --git a/Mathlib/Lean/Meta/RefinedDiscrTree/Encode.lean b/Mathlib/Lean/Meta/RefinedDiscrTree/Encode.lean
index d4b44397882ed4..6baef24ebebd58 100644
--- a/Mathlib/Lean/Meta/RefinedDiscrTree/Encode.lean
+++ b/Mathlib/Lean/Meta/RefinedDiscrTree/Encode.lean
@@ -9,7 +9,6 @@ public import Mathlib.Lean.Meta.RefinedDiscrTree.Basic
public import Lean.Meta.DiscrTree
public import Lean.Meta.LazyDiscrTree
import all Lean.Meta.DiscrTree
-public import Lean.Meta
/-!
# Encoding an `Expr` as a sequence of `Key`s
diff --git a/Mathlib/Tactic/Algebra/Basic.lean b/Mathlib/Tactic/Algebra/Basic.lean
index 1af2eff93f1afd..d71ca00b7cd5db 100644
--- a/Mathlib/Tactic/Algebra/Basic.lean
+++ b/Mathlib/Tactic/Algebra/Basic.lean
@@ -5,8 +5,7 @@ Authors: Arend Mellendijk
-/
module
-public import Mathlib.Algebra.Algebra.Basic
-public import Mathlib.Algebra.Algebra.Defs
+public meta import Lean.Meta.Tactic.NormCast
public import Mathlib.Tactic.Algebra.Lemmas
public import Mathlib.Tactic.Ring.RingNF
diff --git a/Mathlib/Tactic/Linarith/Oracle/FourierMotzkin.lean b/Mathlib/Tactic/Linarith/Oracle/FourierMotzkin.lean
index 3464db91905817..4a88eaaeda8c8f 100644
--- a/Mathlib/Tactic/Linarith/Oracle/FourierMotzkin.lean
+++ b/Mathlib/Tactic/Linarith/Oracle/FourierMotzkin.lean
@@ -5,9 +5,8 @@ Authors: Robert Y. Lewis
-/
module
+public meta import Std.Data.HashMap.AdditionalOperations
public meta import Batteries.Lean.HashMap
-public meta import Mathlib.Tactic.Linarith.Datatypes
-public import Batteries.Lean.HashMap
public import Mathlib.Tactic.Linarith.Datatypes
/-!
diff --git a/Mathlib/Tactic/Linter/Header.lean b/Mathlib/Tactic/Linter/Header.lean
index 24a13be0b14023..73ecfad1d38d45 100644
--- a/Mathlib/Tactic/Linter/Header.lean
+++ b/Mathlib/Tactic/Linter/Header.lean
@@ -306,7 +306,7 @@ namespace Style.header
def broadImportsCheck (imports : Array Syntax) (mainModule : Name) : CommandElabM Unit := do
for i in imports do
match i.getId with
- | `Mathlib.Tactic | `Lean | `Lean.Elab | `Std =>
+ | `Mathlib.Tactic | `Lean | `Lean.Meta | `Lean.Elab | `Lean.Elab.Tactic | `Std =>
Linter.logLint linter.style.header i
s!"Files in mathlib cannot import the whole `{i.getId}` folder. \
Doing so would cause imports to be unnecessarily slow."
diff --git a/Mathlib/Tactic/Module.lean b/Mathlib/Tactic/Module.lean
index dd5d1b1bc065b4..b894df41611c37 100644
--- a/Mathlib/Tactic/Module.lean
+++ b/Mathlib/Tactic/Module.lean
@@ -5,11 +5,11 @@ Authors: Heather Macbeth
-/
module
+public meta import Lean.Meta.Tactic.NormCast
public import Mathlib.Algebra.Algebra.Tower
public import Mathlib.Algebra.BigOperators.GroupWithZero.Action
public import Mathlib.Tactic.Ring
public import Mathlib.Util.AtomM
-public meta import Mathlib.Algebra.Algebra.Defs
/-! # A tactic for normalization over modules
diff --git a/Mathlib/Util/DelabNonCanonical.lean b/Mathlib/Util/DelabNonCanonical.lean
index 1c3e8e88cc7120..9430e9fedeee05 100644
--- a/Mathlib/Util/DelabNonCanonical.lean
+++ b/Mathlib/Util/DelabNonCanonical.lean
@@ -6,7 +6,6 @@ Authors: Robert Maxton
module
public import Mathlib.Init
-public meta import Lean.Meta
public meta import Lean.PrettyPrinter.Delaborator.Builtins
/-! Delab checking canonicity.
diff --git a/MathlibTest/Tactic/Grind/Lint.lean b/MathlibTest/Tactic/Grind/Lint.lean
index d0d30e6dd9b87b..628f45a63eebbc 100644
--- a/MathlibTest/Tactic/Grind/Lint.lean
+++ b/MathlibTest/Tactic/Grind/Lint.lean
@@ -1,4 +1,7 @@
+module
+
import Mathlib
+import Lean.Elab.Tactic.Grind.LintExceptions
-- These each instantiate 24 further lemmas (pretty much the same ones), but they seem reasonable.
-- We'll make an exception for this one,
From 6a37ffc6da38a4c46eb1a09e77301fe19881a70b Mon Sep 17 00:00:00 2001
From: Sebastien Gouezel <10818434+sgouezel@users.noreply.github.com>
Date: Mon, 15 Jun 2026 18:17:50 +0000
Subject: [PATCH 0049/1300] chore: fix non-reducible diamond in `ConjAct`
(#40427)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The following fails before the PR, suceeds after it
```
example : (ConjAct.instGroupWithZero.toDivInvMonoid : DivInvMonoid (ConjAct G₀)) =
ConjAct.instDivInvMonoid := by
with_reducible_and_instances rfl
```
Co-authored-by: sgouezel
---
Mathlib/Algebra/GroupWithZero/Action/ConjAct.lean | 2 +-
Mathlib/GroupTheory/GroupAction/ConjAct.lean | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/Mathlib/Algebra/GroupWithZero/Action/ConjAct.lean b/Mathlib/Algebra/GroupWithZero/Action/ConjAct.lean
index 56471c6a4e888c..93f00b66517270 100644
--- a/Mathlib/Algebra/GroupWithZero/Action/ConjAct.lean
+++ b/Mathlib/Algebra/GroupWithZero/Action/ConjAct.lean
@@ -21,7 +21,7 @@ variable {α G₀ : Type*}
namespace ConjAct
variable [GroupWithZero G₀]
-instance : GroupWithZero (ConjAct G₀) := ‹GroupWithZero G₀›
+instance : GroupWithZero (ConjAct G₀) := inferInstanceAs <| GroupWithZero G₀
@[simp] lemma ofConjAct_zero : ofConjAct 0 = (0 : G₀) := rfl
@[simp] lemma toConjAct_zero : toConjAct (0 : G₀) = 0 := rfl
diff --git a/Mathlib/GroupTheory/GroupAction/ConjAct.lean b/Mathlib/GroupTheory/GroupAction/ConjAct.lean
index f6243e6eabcb82..e63abfbcc6fb5f 100644
--- a/Mathlib/GroupTheory/GroupAction/ConjAct.lean
+++ b/Mathlib/GroupTheory/GroupAction/ConjAct.lean
@@ -49,11 +49,11 @@ open MulAction Subgroup
variable {M G}
-instance [Group G] : Group (ConjAct G) := ‹Group G›
+instance [DivInvMonoid G] : DivInvMonoid (ConjAct G) := inferInstanceAs <| DivInvMonoid G
-instance [DivInvMonoid G] : DivInvMonoid (ConjAct G) := ‹DivInvMonoid G›
+instance [Group G] : Group (ConjAct G) := inferInstanceAs <| Group G
-instance [Fintype G] : Fintype (ConjAct G) := ‹Fintype G›
+instance [Fintype G] : Fintype (ConjAct G) := inferInstanceAs <| Fintype G
@[simp]
theorem card [Fintype G] : Fintype.card (ConjAct G) = Fintype.card G :=
@@ -255,7 +255,7 @@ theorem _root_.MulAut.conjNormal_apply {H : Subgroup G} [H.Normal] (g : G) (h :
@[simp]
theorem _root_.MulAut.conjNormal_symm_apply {H : Subgroup G} [H.Normal] (g : G) (h : H) :
↑((MulAut.conjNormal g).symm h) = g⁻¹ * h * g := by
- change _ * _⁻¹⁻¹ = _
+ change _ * g⁻¹⁻¹ = _
rw [inv_inv]
rfl
From 3cd5cf90f02bfb11b3b39fde2aab54574b171dea Mon Sep 17 00:00:00 2001
From: Robert Hawkins
Date: Mon, 15 Jun 2026 19:03:16 +0000
Subject: [PATCH 0050/1300] feat(RingTheory): quotients of
coalgebra/bialgebra/Hopf algebras (#39790)
Co-authored-by: Eric Wieser
---
Mathlib.lean | 3 +
Mathlib/RingTheory/Bialgebra/Quotient.lean | 82 +++++++++++++
Mathlib/RingTheory/Coalgebra/Quotient.lean | 92 ++++++++++++++
Mathlib/RingTheory/HopfAlgebra/Quotient.lean | 119 +++++++++++++++++++
4 files changed, 296 insertions(+)
create mode 100644 Mathlib/RingTheory/Bialgebra/Quotient.lean
create mode 100644 Mathlib/RingTheory/Coalgebra/Quotient.lean
create mode 100644 Mathlib/RingTheory/HopfAlgebra/Quotient.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 2ba33f02117f1e..cbf1ae94070768 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -6419,6 +6419,7 @@ public import Mathlib.RingTheory.Bialgebra.Equiv
public import Mathlib.RingTheory.Bialgebra.GroupLike
public import Mathlib.RingTheory.Bialgebra.Hom
public import Mathlib.RingTheory.Bialgebra.MonoidAlgebra
+public import Mathlib.RingTheory.Bialgebra.Quotient
public import Mathlib.RingTheory.Bialgebra.SymmetricAlgebra
public import Mathlib.RingTheory.Bialgebra.TensorProduct
public import Mathlib.RingTheory.Binomial
@@ -6434,6 +6435,7 @@ public import Mathlib.RingTheory.Coalgebra.GroupLike
public import Mathlib.RingTheory.Coalgebra.Hom
public import Mathlib.RingTheory.Coalgebra.MonoidAlgebra
public import Mathlib.RingTheory.Coalgebra.MulOpposite
+public import Mathlib.RingTheory.Coalgebra.Quotient
public import Mathlib.RingTheory.Coalgebra.TensorProduct
public import Mathlib.RingTheory.Complex
public import Mathlib.RingTheory.Conductor
@@ -6576,6 +6578,7 @@ public import Mathlib.RingTheory.HopfAlgebra.Basic
public import Mathlib.RingTheory.HopfAlgebra.Convolution
public import Mathlib.RingTheory.HopfAlgebra.GroupLike
public import Mathlib.RingTheory.HopfAlgebra.MonoidAlgebra
+public import Mathlib.RingTheory.HopfAlgebra.Quotient
public import Mathlib.RingTheory.HopfAlgebra.TensorProduct
public import Mathlib.RingTheory.HopkinsLevitzki
public import Mathlib.RingTheory.Ideal.AssociatedPrime.Basic
diff --git a/Mathlib/RingTheory/Bialgebra/Quotient.lean b/Mathlib/RingTheory/Bialgebra/Quotient.lean
new file mode 100644
index 00000000000000..29b1c29309ef53
--- /dev/null
+++ b/Mathlib/RingTheory/Bialgebra/Quotient.lean
@@ -0,0 +1,82 @@
+/-
+Copyright (c) 2026 Robert Hawkins. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Robert Hawkins
+-/
+module
+
+public import Mathlib.RingTheory.Bialgebra.Hom
+public import Mathlib.RingTheory.Coalgebra.Quotient
+public import Mathlib.RingTheory.Ideal.Quotient.Operations
+public import Mathlib.RingTheory.TensorProduct.Maps
+
+/-!
+# Bialgebra structure on quotients
+
+If `I` is a two-sided ideal of an `R`-bialgebra `A` whose underlying `R`-submodule is a
+coideal, then the quotient `A ⧸ I` inherits a bialgebra structure.
+
+## Main definitions
+
+* `Bialgebra.Quotient.counitAlgHom` : the counit on `A ⧸ I`, as an `R`-algebra homomorphism.
+* `Bialgebra.Quotient.comulAlgHom` : comultiplication on `A ⧸ I` as an `R`-algebra homomorphism.
+* `Bialgebra.Quotient.mkBialgHom` : `Ideal.Quotient.mkₐ` as a bialgebra homomorphism.
+
+## Main results
+
+* `Bialgebra R (A ⧸ I)` instance when `[I.IsTwoSided]` and `[(I.restrictScalars R).IsCoideal]`.
+-/
+
+@[expose] public section
+
+open Bialgebra Coalgebra LinearMap TensorProduct
+
+variable {R A : Type*} [CommRing R] [Ring A] [Bialgebra R A]
+variable (I : Ideal A) [I.IsTwoSided] [(I.restrictScalars R).IsCoideal]
+
+namespace Bialgebra.Quotient
+
+/-- The counit on `A ⧸ I`, as an `R`-algebra homomorphism. -/
+def counitAlgHom : (A ⧸ I) →ₐ[R] R :=
+ Ideal.Quotient.liftₐ I (Bialgebra.counitAlgHom R A)
+ (Submodule.IsCoideal.counit_eq_zero (I := I.restrictScalars R))
+
+/-- The comultiplication on `A ⧸ I`, as an `R`-algebra homomorphism. -/
+def comulAlgHom : (A ⧸ I) →ₐ[R] (A ⧸ I) ⊗[R] (A ⧸ I) :=
+ Ideal.Quotient.liftₐ I
+ ((Algebra.TensorProduct.map (Ideal.Quotient.mkₐ R I) (Ideal.Quotient.mkₐ R I)).comp
+ (Bialgebra.comulAlgHom R A))
+ (Submodule.IsCoideal.map_mkQ_comul_eq_zero (I := I.restrictScalars R))
+
+lemma counit_comp_mkₐ :
+ (counitAlgHom I).toLinearMap ∘ₗ (Ideal.Quotient.mkₐ R I).toLinearMap = counit := rfl
+
+lemma comul_comp_mkₐ :
+ (comulAlgHom (R := R) I).toLinearMap ∘ₗ (Ideal.Quotient.mkₐ R I).toLinearMap =
+ map (Ideal.Quotient.mkₐ R I).toLinearMap (Ideal.Quotient.mkₐ R I).toLinearMap ∘ₗ comul := rfl
+
+/-- The bialgebra structure on `A ⧸ I` when `I` is a biideal. -/
+instance : Bialgebra R (A ⧸ I) := by
+ refine .ofAlgHom (comulAlgHom I) (counitAlgHom I) ?_ ?_ ?_ <;>
+ refine Ideal.Quotient.algHom_ext R (AlgHom.toLinearMap_injective ?_) <;>
+ simp only [coassoc_simps, AlgHom.comp_toLinearMap, Algebra.TensorProduct.toLinearMap_map,
+ comul_comp_mkₐ, counit_comp_mkₐ]
+ · simp [coassoc_simps]
+ · rw [CoassocSimps.map_counit_comp_comul_left]; rfl
+ · rw [CoassocSimps.map_counit_comp_comul_right]; rfl
+
+@[simp] lemma counit_mk (a : A) :
+ counit (R := R) (Ideal.Quotient.mk I a) = counit a := rfl
+
+@[simp] lemma comul_mk (a : A) :
+ comul (R := R) (Ideal.Quotient.mk I a) =
+ map (Ideal.Quotient.mkₐ R I).toLinearMap (Ideal.Quotient.mkₐ R I).toLinearMap (comul a) :=
+ rfl
+
+/-- `Ideal.Quotient.mkₐ` as a bialgebra homomorphism. -/
+def mkBialgHom : A →ₐc[R] A ⧸ I := .ofAlgHom (Ideal.Quotient.mkₐ R I) rfl rfl
+
+@[simp] lemma mkBialgHom_apply (a : A) :
+ mkBialgHom (R := R) I a = Ideal.Quotient.mk I a := rfl
+
+end Bialgebra.Quotient
diff --git a/Mathlib/RingTheory/Coalgebra/Quotient.lean b/Mathlib/RingTheory/Coalgebra/Quotient.lean
new file mode 100644
index 00000000000000..87f053509589db
--- /dev/null
+++ b/Mathlib/RingTheory/Coalgebra/Quotient.lean
@@ -0,0 +1,92 @@
+/-
+Copyright (c) 2026 Robert Hawkins. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Robert Hawkins
+-/
+module
+
+public import Mathlib.LinearAlgebra.Quotient.Basic
+public import Mathlib.LinearAlgebra.TensorProduct.RightExactness
+public import Mathlib.RingTheory.Coalgebra.CoassocSimps
+public import Mathlib.RingTheory.Coalgebra.Hom
+
+/-!
+# Coalgebra structure on the quotient by a coideal
+
+## Main definitions
+
+* `Submodule.IsCoideal I` : the submodule `I : Submodule R C` is a coideal.
+* `Coalgebra.Quotient.mkQCoalgHom` : `Submodule.mkQ` as a coalgebra homomorphism.
+
+## Main results
+
+* `Coalgebra` instance on `C ⧸ I` when `[I.IsCoideal]`.
+-/
+
+public section
+
+open Coalgebra LinearMap TensorProduct
+
+variable {R C : Type*} [CommRing R] [AddCommGroup C] [Module R C]
+
+section CoalgebraStruct
+
+variable [CoalgebraStruct R C]
+
+/-- An `R`-submodule `I` of an `R`-coalgebra `C` is a *coideal* if the counit vanishes on
+`I` and the comultiplication descends through the module quotient `C ⧸ I`. -/
+@[mk_iff]
+class Submodule.IsCoideal (I : Submodule R C) : Prop where
+ counit_eq_zero : ∀ ⦃x : C⦄, x ∈ I → counit (R := R) x = 0
+ map_mkQ_comul_eq_zero : ∀ ⦃x : C⦄, x ∈ I → TensorProduct.map I.mkQ I.mkQ (comul x) = 0
+
+/-- A submodule is a coideal iff the counit vanishes on it and its comultiplication image lies
+in `I ⊗ C + C ⊗ I`, the textbook form of the coideal condition. -/
+lemma Submodule.isCoideal_iff_comul_mem (I : Submodule R C) :
+ I.IsCoideal ↔ (∀ x ∈ I, counit (R := R) x = 0) ∧
+ ∀ x ∈ I, comul x ∈
+ LinearMap.range (lTensor C I.subtype) ⊔ LinearMap.range (rTensor C I.subtype) := by
+ simp_rw [isCoideal_iff, ← LinearMap.mem_ker,
+ TensorProduct.map_ker (LinearMap.exact_subtype_mkQ I) I.mkQ_surjective
+ (LinearMap.exact_subtype_mkQ I) I.mkQ_surjective]
+
+end CoalgebraStruct
+
+namespace Coalgebra.Quotient
+
+section CoalgebraStruct
+
+variable [CoalgebraStruct R C] (I : Submodule R C) [I.IsCoideal]
+
+instance : CoalgebraStruct R (C ⧸ I) where
+ comul := I.liftQ (map I.mkQ I.mkQ ∘ₗ comul) Submodule.IsCoideal.map_mkQ_comul_eq_zero
+ counit := I.liftQ counit Submodule.IsCoideal.counit_eq_zero
+
+lemma comul_comp_mkQ : comul ∘ₗ I.mkQ = map I.mkQ I.mkQ ∘ₗ (comul : C →ₗ[R] _) := rfl
+
+lemma counit_comp_mkQ : counit ∘ₗ I.mkQ = (counit : C →ₗ[R] R) := rfl
+
+@[simp]
+lemma counit_mk (x : C) : counit (R := R) (Submodule.Quotient.mk (p := I) x) = counit x := rfl
+
+@[simp]
+lemma comul_mk (x : C) :
+ comul (R := R) (Submodule.Quotient.mk (p := I) x) = map I.mkQ I.mkQ (comul x) := rfl
+
+/-- `Submodule.mkQ` as a coalgebra homomorphism. -/
+@[expose] def mkQCoalgHom : C →ₗc[R] C ⧸ I := ⟨I.mkQ, rfl, rfl⟩
+
+@[simp] lemma mkQCoalgHom_apply (x : C) :
+ mkQCoalgHom (R := R) I x = Submodule.Quotient.mk x := rfl
+
+end CoalgebraStruct
+
+variable [Coalgebra R C] (I : Submodule R C) [I.IsCoideal]
+
+instance : Coalgebra R (C ⧸ I) := by
+ constructor <;> ext : 1 <;>
+ simp only [coassoc_simps, comul_comp_mkQ, counit_comp_mkQ]
+ · rw [CoassocSimps.map_counit_comp_comul_left]; rfl
+ · rw [CoassocSimps.map_counit_comp_comul_right]; rfl
+
+end Coalgebra.Quotient
diff --git a/Mathlib/RingTheory/HopfAlgebra/Quotient.lean b/Mathlib/RingTheory/HopfAlgebra/Quotient.lean
new file mode 100644
index 00000000000000..4e04f0e785894a
--- /dev/null
+++ b/Mathlib/RingTheory/HopfAlgebra/Quotient.lean
@@ -0,0 +1,119 @@
+/-
+Copyright (c) 2026 Robert Hawkins. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Robert Hawkins
+-/
+module
+
+public import Mathlib.RingTheory.Bialgebra.Quotient
+public import Mathlib.RingTheory.HopfAlgebra.Convolution
+
+/-!
+# Hopf algebra structure on quotients by Hopf ideals
+
+A *Hopf ideal* of an `R`-Hopf algebra `A` is a biideal stable under the antipode. The quotient
+by a Hopf ideal inherits a Hopf algebra structure.
+
+## Main definitions
+
+* `Ideal.IsHopfIdeal R I` : `I` is a coideal (as an `R`-submodule) stable under the antipode.
+
+## Main results
+
+* `HopfAlgebra.ofSurjective` : the Hopf algebra axioms transfer along a surjective bialgebra
+ homomorphism intertwining the antipodes.
+* `HopfAlgebra R (A ⧸ I)` instance when `[I.IsTwoSided]` and `[I.IsHopfIdeal R]`.
+-/
+
+public section
+
+open Bialgebra Bialgebra.Quotient Coalgebra HopfAlgebra Ideal.Quotient LinearMap
+ TensorProduct WithConv
+
+namespace HopfAlgebra
+
+section ofSurjective
+
+variable {R A B : Type*} [CommSemiring R] [Semiring A] [Semiring B]
+ [HopfAlgebra R A] [HopfAlgebraStruct R B]
+
+/-- Post-composition by an algebra homomorphism preserves the convolution unit. -/
+lemma _root_.LinearMap.algHom_comp_convOne (g : A →ₐ[R] B) :
+ g.toLinearMap ∘ₗ (1 : WithConv (A →ₗ[R] A)).ofConv = (1 : WithConv (A →ₗ[R] B)).ofConv := by
+ ext a; simp
+
+/-- Pre-composition by a coalgebra homomorphism preserves the convolution unit. -/
+lemma _root_.LinearMap.convOne_comp_coalgHom (g : A →ₗc[R] B) :
+ (1 : WithConv (B →ₗ[R] B)).ofConv ∘ₗ g.toLinearMap = (1 : WithConv (A →ₗ[R] B)).ofConv := by
+ ext a; simp
+
+/-- Transfer the Hopf algebra axioms along a surjective bialgebra homomorphism intertwining
+the antipodes. -/
+noncomputable abbrev ofSurjective (f : A →ₐc[R] B) (hf : Function.Surjective f)
+ (hS : antipode R ∘ₗ f.toLinearMap = f.toLinearMap ∘ₗ antipode R) : HopfAlgebra R B := by
+ refine .ofConvInverse (antipode R) (ofConv_injective ?_) (ofConv_injective ?_) <;>
+ rw [← LinearMap.cancel_right (show Function.Surjective f.toLinearMap from hf)]
+ · calc (toConv (antipode R) * toConv .id : WithConv (B →ₗ[R] B)).ofConv ∘ₗ
+ f.toCoalgHom.toLinearMap
+ = (toConv (f.toLinearMap ∘ₗ antipode R) * toConv f.toLinearMap).ofConv := by
+ rw [convMul_comp_coalgHom_distrib, hS]; rfl
+ _ = (AlgHomClass.toAlgHom f).toLinearMap ∘ₗ
+ (toConv (antipode R) * toConv .id : WithConv (A →ₗ[R] A)).ofConv := by
+ rw [algHom_comp_convMul_distrib]; rfl
+ _ = (1 : WithConv (B →ₗ[R] B)).ofConv ∘ₗ f.toLinearMap := by
+ rw [antipode_mul_id, algHom_comp_convOne, ← convOne_comp_coalgHom f.toCoalgHom]
+ · calc (toConv .id * toConv (antipode R) : WithConv (B →ₗ[R] B)).ofConv ∘ₗ
+ f.toCoalgHom.toLinearMap
+ = (toConv f.toLinearMap * toConv (f.toLinearMap ∘ₗ antipode R)).ofConv := by
+ rw [convMul_comp_coalgHom_distrib, hS]; rfl
+ _ = (AlgHomClass.toAlgHom f).toLinearMap ∘ₗ
+ (toConv .id * toConv (antipode R) : WithConv (A →ₗ[R] A)).ofConv := by
+ rw [algHom_comp_convMul_distrib]; rfl
+ _ = (1 : WithConv (B →ₗ[R] B)).ofConv ∘ₗ f.toLinearMap := by
+ rw [id_mul_antipode, algHom_comp_convOne, ← convOne_comp_coalgHom f.toCoalgHom]
+
+end ofSurjective
+
+end HopfAlgebra
+
+variable {R A : Type*} [CommRing R] [Ring A]
+
+section HopfAlgebraStruct
+
+variable [HopfAlgebraStruct R A]
+
+variable (R) in
+/-- An ideal whose underlying `R`-submodule is a coideal and which is stable under the
+antipode (`S(I) ⊆ I`). Together with `I.IsTwoSided`, this makes `I` a *Hopf ideal*. -/
+@[mk_iff]
+class Ideal.IsHopfIdeal (I : Ideal A) : Prop extends (I.restrictScalars R).IsCoideal where
+ antipode_mem : ∀ ⦃x : A⦄, x ∈ I → antipode R x ∈ I
+
+end HopfAlgebraStruct
+
+namespace HopfAlgebra.Quotient
+
+section HopfAlgebraStruct
+
+variable [HopfAlgebraStruct R A] (I : Ideal A) [I.IsTwoSided] [I.IsHopfIdeal R]
+
+instance : HopfAlgebraStruct R (A ⧸ I) where
+ antipode := Submodule.mapQ (I.restrictScalars R) (I.restrictScalars R)
+ (antipode R) (Ideal.IsHopfIdeal.antipode_mem (R := R))
+
+@[simp]
+lemma antipode_mk (a : A) :
+ antipode R (Ideal.Quotient.mk I a) = Ideal.Quotient.mk I (antipode R a) := rfl
+
+lemma antipode_comp_mkₐ :
+ antipode R ∘ₗ (Ideal.Quotient.mkₐ R I).toLinearMap =
+ (Ideal.Quotient.mkₐ R I).toLinearMap ∘ₗ antipode R := by ext; simp
+
+end HopfAlgebraStruct
+
+variable [HopfAlgebra R A] (I : Ideal A) [I.IsTwoSided] [I.IsHopfIdeal R]
+
+noncomputable instance : HopfAlgebra R (A ⧸ I) :=
+ .ofSurjective (mkBialgHom I) mk_surjective (antipode_comp_mkₐ I)
+
+end HopfAlgebra.Quotient
From 05bdd16f046e16d10f628857261c596d68dc2c51 Mon Sep 17 00:00:00 2001
From: Marcelo Lynch
Date: Mon, 15 Jun 2026 19:17:28 +0000
Subject: [PATCH 0051/1300] feat(cache): restructure cache tool server-side
storage layout (#40035)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This PR significantly restructures the cache back-end storage layout to address some security issues and as a side benefit improve auditing, garbage collection, provenance.
1) Instead of setting the cache-universe boundary to the repository, we scope it to particular commit SHAs. This means a review of the changes in a PR does give you confidence in pulling from the cache at that commit.
2) Split the cache universes from master, forks, and nightly-testing at the infrastructure level, using different azure containers. This has other benefits in management, like better auditing, the possibility of targeted garbage collection, etc.
See `SECURITY.md` for the motivation and explanation of the trust model implemented here.
Also added cache tool tests, and changed a bunch of the messages output to users to surface security concerns more clearly.
Extra details:
### Trust model
- Five containers — `master`, `forks`, `nightly-testing`, `pr-toolchain-tests`,
and `legacy` (the original bare `mathlib4` bucket) — each mapped to a dedicated
Azure container. Every trust level has its own writer identity. Azure RBAC on
the OIDC token is the enforced write boundary, not the workflow logic.
- Reads use a per-repo, trust-ordered chain (most-trusted first, stop at first
hit
- URL layout is fixed per container, not per repo: `master` is flat
(`/f/{hash}`), multi-writer containers namespace by repo (`/f/{repo}/{hash}`),
`legacy` preserves its historical mixed layout for older clients.
### Per-commit scoping & discovery
- Fork uploads are scoped to their commit SHA (`/f/{repo}/{sha}/{hash}`), so a
closed/hidden PR's artifacts can't be served to a later honest PR on the same
fork. A marker blob (`/m/{repo}/{sha}`) is written after a successful upload.
- New `cache query [REF]`: walks history to the merge-base with master and HEAD-
probes markers to report the most recent cached commit (or a boolean probe for
a single ref). `cache get --scope=` then reads that commit's namespace.
- `cache get --unsafe` folds that discovery into the download itself: it
walks history for the most recent cached fork commit and reads it as a
scope automatically, instead of you copying a SHA into `--scope`.
`--unsafe-window=N` widens this to the `N` most recent (default `1`). It
always prints the security notice, since it trusts whoever built those
commits.
- `cache get` prints a security notice whenever a read leaves the repo's
default trust boundary (a scope, a widened `--cache-from`, or a `--repo` that
diverges from the git remote), plus a hint pointing uncached fork HEADs at
`cache query`.
### CLI / env surface
- New flags: `--cache-from=LIST`, `--container=NAME`, `--scope=REF`, `--unsafe`, `--unsafe-window=N`; new command
`cache query`.
- New env: `MATHLIB_CACHE_FROM`, `MATHLIB_CACHE_REPO_SCOPE`. `MATHLIB_CACHE_GET_URL`
/ `_PUT_URL` retained as single-URL escape hatches; `MATHLIB_CACHE_USE_CLOUDFLARE`
removed.
### CI wiring
- New composite action `cache-trust-dispatch` is the single source mapping
`(repo, branch) → (write container, read chain, per-commit scope)`; `build.yml`,
`bors.yml`, and `build_template.yml` consult it. Trust policy lives in YAML,
not in the Lean tool.
- During migration, master CI dual-writes to `legacy` so older cache clients
keep working; forks/nightly never write to `legacy`.
### Code organization & testing
- Backend split into focused modules: `Cache.Infra` (container model),
`Cache.Cli` (option parsing), `Cache.Marker`, `Cache.Query`, `Cache.Warning`;
`Cache.Init` folded away.
- New standalone `lake exe cache-test` (`Cache.Test`) unit-tests the pure logic —
container model, URL construction, per-repo read chains, flag parsing, and the
warning conditions — without building Mathlib.
---
.../actions/cache-trust-dispatch/action.yml | 157 +++
.github/actions/get-tools/action.yml | 26 +
.github/workflows/bors.yml | 47 +-
.github/workflows/build.yml | 40 +-
.github/workflows/build_template.yml | 165 ++-
.github/workflows/cache_test.yml | 43 +
Cache/Cli.lean | 49 +
Cache/Infra.lean | 151 +++
Cache/Init.lean | 16 -
Cache/Main.lean | 183 ++-
Cache/Marker.lean | 64 ++
Cache/Query.lean | 229 ++++
Cache/README.md | 216 ++--
Cache/Requests.lean | 609 +++++++---
Cache/SECURITY.md | 152 +++
Cache/Test.lean | 1007 +++++++++++++++++
Cache/Warning.lean | 228 ++++
lakefile.lean | 6 +
18 files changed, 3030 insertions(+), 358 deletions(-)
create mode 100644 .github/actions/cache-trust-dispatch/action.yml
create mode 100644 .github/workflows/cache_test.yml
create mode 100644 Cache/Cli.lean
create mode 100644 Cache/Infra.lean
delete mode 100644 Cache/Init.lean
create mode 100644 Cache/Marker.lean
create mode 100644 Cache/Query.lean
create mode 100644 Cache/SECURITY.md
create mode 100644 Cache/Test.lean
create mode 100644 Cache/Warning.lean
diff --git a/.github/actions/cache-trust-dispatch/action.yml b/.github/actions/cache-trust-dispatch/action.yml
new file mode 100644
index 00000000000000..7e3811a969d2d9
--- /dev/null
+++ b/.github/actions/cache-trust-dispatch/action.yml
@@ -0,0 +1,157 @@
+# Single source of truth mapping (repo, branch) → (upload container,
+# read fallback chain) for Mathlib's multi-container cache.
+#
+# Called by build, upload_cache, and post_steps in build_template.yml so
+# trust classification is decided in exactly one place. Lean-side cache
+# logic stays branch-agnostic; this composite is the seam where CI-only
+# trust policy lives.
+
+name: Cache trust dispatch
+description: Compute the cache container target and read fallback for this job.
+
+inputs:
+ repo:
+ description: GitHub repo full name (`owner/name`).
+ required: true
+ branch:
+ description: Branch name (`github.head_ref || github.ref_name`).
+ required: true
+ head-sha:
+ description: |
+ Head commit SHA for the ref being built. Used as the per-commit cache
+ namespace (`MATHLIB_CACHE_REPO_SCOPE`) for fork-trust uploads, so a
+ closed/hidden PR's poisoned artifacts cannot be served to a later
+ honest PR from the same fork.
+ required: true
+
+# Outputs are mirrored to $GITHUB_ENV inside the step, which is what
+# downstream `cache get` / `cache put-staged` calls actually read. We
+# also expose them as action outputs for callers that need them in
+# `with:` blocks (e.g. constructing further `if:` conditions).
+outputs:
+ primary:
+ description: Container name for uploads (master, forks, nightly-testing, pr-toolchain-tests).
+ value: ${{ steps.dispatch.outputs.primary }}
+ read-chain:
+ description: |
+ Comma-separated read fallback chain for `MATHLIB_CACHE_FROM`. Empty
+ when the job should use the cache tool's repo-level default.
+ value: ${{ steps.dispatch.outputs.read-chain }}
+ repo-scope:
+ description: |
+ Per-commit namespace suffix for `MATHLIB_CACHE_REPO_SCOPE`. Set to
+ the head SHA when uploading to fork-trust containers; empty for
+ master / nightly / pr-toolchain-tests uploads where scoping isn't
+ applied.
+ value: ${{ steps.dispatch.outputs.repo-scope }}
+
+runs:
+ using: composite
+ steps:
+ - name: Compute trust dispatch
+ id: dispatch
+ shell: bash
+ run: |
+ REPO="${{ inputs.repo }}"
+ BRANCH="${{ inputs.branch }}"
+ HEAD_SHA="${{ inputs.head-sha }}"
+ PRIMARY=""
+ READ_CHAIN=""
+ REPO_SCOPE=""
+
+ # Security note: this dispatch is NOT the trust boundary for writes.
+ # The real enforcement is the OIDC bearer token minted in upload_cache:
+ # the token is scoped to a specific container, so a malicious actor
+ # rewriting this case to `--container=master` from a fork build would
+ # be 403'd by Azure regardless. The dispatch exists so the workflow
+ # does the right thing in the honest case; defence in depth is RBAC.
+
+ case "$REPO" in
+ "leanprover-community/mathlib4")
+ case "$BRANCH" in
+ "master"|"staging")
+ # Master / staging are the only writers that feed `master`
+ # (`staging` is bors's merge candidate, which fast-forwards to
+ # `master`). Read `master` only, not the default [master,
+ # legacy]: files the read chain serves are skipped at stage
+ # time, so keeping `legacy` would leave legacy-only files out of
+ # `master` for good. Reading `master` alone turns them into
+ # misses that get rebuilt and uploaded, so `master` fills itself
+ # into a standalone cache. (Only PRIMARY=master does this; other
+ # runs write to `forks` and keep the wider chain.)
+ PRIMARY="master"
+ READ_CHAIN="master"
+ ;;
+ *)
+ # `bors trying`, `ci-dev/*`, maintainer dev branches on the
+ # canonical repo: trust level is fork-equivalent (the OIDC
+ # token's RBAC scopes them to `forks`). Reads must widen
+ # past the default [master, legacy] so the post-build
+ # verification finds the just-uploaded fork-trust artifacts.
+ PRIMARY="forks"
+ READ_CHAIN="master,forks,legacy"
+ ;;
+ esac
+ ;;
+ "leanprover-community/mathlib4-nightly-testing")
+ case "$BRANCH" in
+ "nightly-testing"|"nightly-testing-green"|"staging"|bump/*)
+ # Trusted nightly refs use the default [nightly-testing, legacy].
+ # It excludes `pr-toolchain-tests` so an upload from a
+ # `lean-pr-testing-*` branch never reaches a trusted-nightly
+ # consumer.
+ PRIMARY="nightly-testing"
+ ;;
+ *)
+ # `lean-pr-testing-*`, `batteries-pr-testing-*`, etc.:
+ # least-trusted (can build with arbitrary toolchains). Widen
+ # reads to recover this branch's own previously-uploaded
+ # artifacts; trusted-nightly stays preferred where hash
+ # spaces happen to align.
+ PRIMARY="pr-toolchain-tests"
+ READ_CHAIN="pr-toolchain-tests,nightly-testing,legacy"
+ ;;
+ esac
+ ;;
+ *)
+ # Foreign fork. The cache tool's default chain for a fork repo
+ # is [master, forks, legacy] (master-first): master supplies the
+ # bulk of unchanged upstream deps, forks supplies PR-specific
+ # files. No widening needed, so MATHLIB_CACHE_FROM stays unset.
+ PRIMARY="forks"
+ ;;
+ esac
+
+ # Per-commit cache namespace, only for fork-trust uploads. Closes the
+ # within-fork temporal replay attack: each commit's CI run gets its
+ # own /f/{repo}/{sha}/... namespace, so artifacts from a closed/
+ # hidden PR cannot be served to a later honest build on the same
+ # fork. Master / nightly / pr-toolchain-tests uploads stay un-scoped:
+ # master has a single writer (no replay risk), and the per-toolchain
+ # hash partitioning isolates nightly and toolchain-test classes via
+ # their root-hash inputs.
+ if [ "$PRIMARY" = "forks" ]; then
+ REPO_SCOPE="$HEAD_SHA"
+ fi
+
+ echo "primary=$PRIMARY" >> "$GITHUB_OUTPUT"
+ echo "read-chain=$READ_CHAIN" >> "$GITHUB_OUTPUT"
+ echo "repo-scope=$REPO_SCOPE" >> "$GITHUB_OUTPUT"
+ echo "MATHLIB_CACHE_PRIMARY=$PRIMARY" >> "$GITHUB_ENV"
+ if [ -n "$READ_CHAIN" ]; then
+ echo "MATHLIB_CACHE_FROM=$READ_CHAIN" >> "$GITHUB_ENV"
+ fi
+ if [ -n "$REPO_SCOPE" ]; then
+ echo "MATHLIB_CACHE_REPO_SCOPE=$REPO_SCOPE" >> "$GITHUB_ENV"
+ fi
+ # Visible in CI logs so a glance at any cache-touching step shows
+ # what trust class the job is operating under.
+ SCOPE_NOTE=""
+ if [ -n "$REPO_SCOPE" ]; then
+ SCOPE_NOTE=", MATHLIB_CACHE_REPO_SCOPE=$REPO_SCOPE"
+ fi
+ if [ -n "$READ_CHAIN" ]; then
+ echo "cache-trust-dispatch: REPO=$REPO BRANCH=$BRANCH → container=$PRIMARY, MATHLIB_CACHE_FROM=$READ_CHAIN$SCOPE_NOTE"
+ else
+ echo "cache-trust-dispatch: REPO=$REPO BRANCH=$BRANCH → container=$PRIMARY, MATHLIB_CACHE_FROM=$SCOPE_NOTE"
+ fi
diff --git a/.github/actions/get-tools/action.yml b/.github/actions/get-tools/action.yml
index 87831ca5b600e4..6e195f21ea308e 100644
--- a/.github/actions/get-tools/action.yml
+++ b/.github/actions/get-tools/action.yml
@@ -36,6 +36,15 @@ inputs:
description: Destination directory for the tools.
required: false
default: tools-branch
+ source_dir:
+ description: >
+ Optional path to a local checkout under test. When set and the artifact
+ fast path is in play, the action compares this checkout's tool sources
+ against the artifact's `master` baseline; if they differ it skips the
+ prebuilt artifact and builds from source, so a branch that changes the
+ tool is tested with its own tool. Empty disables the comparison.
+ required: false
+ default: ''
runs:
using: composite
steps:
@@ -56,6 +65,23 @@ runs:
--workflow "${{ inputs.artifact_workflow }}" \
--branch master --status success --event push \
--limit 1 --json databaseId --jq '.[0].databaseId // empty' 2>/dev/null || true)
+
+ # If a local checkout under test is provided and its tool sources differ
+ # from the artifact's master baseline, the prebuilt tool would mask the
+ # change — drop it and build from source. Local git only; no API call.
+ # These are the source paths that determine the bundled tools (the
+ # `cache` binary and the build-helper scripts in publish_tools.yml); they
+ # live here so callers don't have to duplicate the list.
+ if [[ -n "$run_id" && -n "${{ inputs.source_dir }}" ]]; then
+ git -C "${{ inputs.source_dir }}" fetch --no-tags --depth=1 \
+ "https://github.com/${{ inputs.artifact_repo }}.git" master || true
+ # Fail safe toward building: a failed fetch/diff leaves $? non-zero.
+ if ! git -C "${{ inputs.source_dir }}" diff --quiet FETCH_HEAD -- \
+ Cache scripts/lake-build-with-retry.sh scripts/lake-build-wrapper.py; then
+ echo "Tool sources in '${{ inputs.source_dir }}' differ from master; building from source."
+ run_id=""
+ fi
+ fi
fi
echo "Resolved publisher run_id: '${run_id}'"
echo "run_id=${run_id}" >> "$GITHUB_OUTPUT"
diff --git a/.github/workflows/bors.yml b/.github/workflows/bors.yml
index d1618133a6bec6..b6fc3e849b538c 100644
--- a/.github/workflows/bors.yml
+++ b/.github/workflows/bors.yml
@@ -27,13 +27,46 @@ jobs:
with:
concurrency_group: ${{ github.workflow }}-${{ github.ref }}-${{ github.run_id }}
pr_branch_ref: ${{ github.sha }}
- # Use the MASTER cache key only when merging into mathlib4 (staging branch);
- # 'bors try' runs (trying branch) and nightly-testing use NON_MASTER
- cache_application_id: ${{ github.ref_name == 'staging' && github.repository == 'leanprover-community/mathlib4' && vars.CACHE_MASTER_WRITER_AZURE_APP_ID || vars.CACHE_NON_MASTER_WRITER_AZURE_APP_ID }}
- # Track the cache_application_id choice above; the environment fixes the OIDC subject the Azure app trusts.
- # nightly-testing is intentionally left without an environment ('') so it keeps its existing ref-based trust.
- # TODO: give mathlib4-nightly-testing its own cache-upload environment + federated credential.
- cache_environment: ${{ github.repository == 'leanprover-community/mathlib4' && (github.ref_name == 'staging' && 'cache-upload-master' || 'cache-upload-forks') || '' }}
+ # Trust-level dispatch for the cache writer app (bors runs on
+ # `staging` = real merge candidate; `trying` = experimental).
+ # Each case is spelled out so the trust mapping is unambiguous:
+ # - mathlib4/staging → MASTER writer
+ # - mathlib4/trying → NON_MASTER (FORKS) writer
+ # (explicitly NOT master-trust)
+ # - mathlib4-nightly-testing/staging → NIGHTLY_TESTING writer
+ # - mathlib4-nightly-testing/trying → PR_TOOLCHAIN_TESTS writer
+ # (least-trust on the nightly repo)
+ # - any other ref on either repo → NON_MASTER (FORKS) writer
+ cache_application_id: >-
+ ${{
+ (github.repository == 'leanprover-community/mathlib4'
+ && github.ref_name == 'staging')
+ && vars.CACHE_MASTER_WRITER_AZURE_APP_ID
+ || (github.repository == 'leanprover-community/mathlib4'
+ && github.ref_name == 'trying')
+ && vars.CACHE_NON_MASTER_WRITER_AZURE_APP_ID
+ || (github.repository == 'leanprover-community/mathlib4-nightly-testing'
+ && github.ref_name == 'staging')
+ && vars.CACHE_NIGHTLY_TESTING_WRITER_AZURE_APP_ID
+ || (github.repository == 'leanprover-community/mathlib4-nightly-testing'
+ && github.ref_name == 'trying')
+ && vars.CACHE_PR_TOOLCHAIN_TESTS_WRITER_AZURE_APP_ID
+ || vars.CACHE_NON_MASTER_WRITER_AZURE_APP_ID
+ }}
+ # OIDC environment binding for the upload_cache job. MUST track the cache_application_id
+ # mapping above so the minted token's `:environment:` subject matches the writer app's
+ # federated credential. mathlib4-nightly-testing intentionally uses branch/ref-scoped
+ # trust instead of an environment: each nightly writer app is RBAC-isolated to its own
+ # container, so branch scoping is sufficient there.
+ cache_environment: >-
+ ${{
+ (github.repository == 'leanprover-community/mathlib4'
+ && github.ref_name == 'staging')
+ && 'cache-upload-master'
+ || (github.repository == 'leanprover-community/mathlib4')
+ && 'cache-upload-forks'
+ || ''
+ }}
# bors runs should build the tools from their commit-under-test: after all, we are trying to
# test 'what would happen if this was merged', so we need to use the 'would-be-post-merge' tools
tools_branch_ref: ${{ github.sha }}
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ab369c741f2c67..120bd6ae6ba196 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -36,11 +36,39 @@ jobs:
with:
concurrency_group: ${{ github.workflow }}-${{ github.ref }}-${{ (github.event_name == 'push' && github.ref == 'refs/heads/master' && github.run_id) || '' }}
pr_branch_ref: ${{ github.sha }}
- # Use the MASTER cache key only on mathlib4/master; nightly-testing and other branches use NON_MASTER
- cache_application_id: ${{ github.repository == 'leanprover-community/mathlib4' && github.ref == 'refs/heads/master' && vars.CACHE_MASTER_WRITER_AZURE_APP_ID || vars.CACHE_NON_MASTER_WRITER_AZURE_APP_ID }}
- # Track the cache_application_id choice above; the environment fixes the OIDC subject the Azure app trusts.
- # nightly-testing is intentionally left without an environment ('') so it keeps its existing ref-based trust.
- # TODO: give mathlib4-nightly-testing its own cache-upload environment + federated credential.
- cache_environment: ${{ github.repository == 'leanprover-community/mathlib4' && (github.ref == 'refs/heads/master' && 'cache-upload-master' || 'cache-upload-forks') || '' }}
+ # Trust-level dispatch for the cache writer app:
+ # - mathlib4/master → MASTER writer
+ # - mathlib4-nightly-testing/(nightly-testing | nightly-testing-green | bump/*)
+ # → NIGHTLY_TESTING writer
+ # - mathlib4-nightly-testing/(anything else) → PR_TOOLCHAIN_TESTS writer
+ # - everything else (dev branches on mathlib4, etc.) → NON_MASTER (forks) writer
+ cache_application_id: >-
+ ${{
+ (github.repository == 'leanprover-community/mathlib4'
+ && github.ref_name == 'master')
+ && vars.CACHE_MASTER_WRITER_AZURE_APP_ID
+ || (github.repository == 'leanprover-community/mathlib4-nightly-testing'
+ && (github.ref_name == 'nightly-testing'
+ || github.ref_name == 'nightly-testing-green'
+ || startsWith(github.ref_name, 'bump/')))
+ && vars.CACHE_NIGHTLY_TESTING_WRITER_AZURE_APP_ID
+ || (github.repository == 'leanprover-community/mathlib4-nightly-testing')
+ && vars.CACHE_PR_TOOLCHAIN_TESTS_WRITER_AZURE_APP_ID
+ || vars.CACHE_NON_MASTER_WRITER_AZURE_APP_ID
+ }}
+ # OIDC environment binding for the upload_cache job. MUST track the cache_application_id
+ # mapping above so the minted token's `:environment:` subject matches the writer app's
+ # federated credential. mathlib4-nightly-testing intentionally uses branch/ref-scoped
+ # trust instead of an environment: each nightly writer app is RBAC-isolated to its own
+ # container, so branch scoping is sufficient there.
+ cache_environment: >-
+ ${{
+ (github.repository == 'leanprover-community/mathlib4'
+ && github.ref_name == 'master')
+ && 'cache-upload-master'
+ || (github.repository == 'leanprover-community/mathlib4')
+ && 'cache-upload-forks'
+ || ''
+ }}
runs_on: pr
secrets: inherit
diff --git a/.github/workflows/build_template.yml b/.github/workflows/build_template.yml
index 6eea0109569af5..5b0676dd6245e7 100644
--- a/.github/workflows/build_template.yml
+++ b/.github/workflows/build_template.yml
@@ -115,6 +115,19 @@ jobs:
with:
ref: ${{ inputs.mathlib_ci_ref }}
+ # Compute the trust-classified container target and read fallback
+ # for this job. Sets MATHLIB_CACHE_FROM / MATHLIB_CACHE_PRIMARY in
+ # env so every subsequent `cache get` in this job inherits them
+ # without per-call flag plumbing. Loaded from master via the sparse
+ # `workflow-actions/` checkout above, not from the PR branch — this
+ # keeps the trust policy out of fork-controllable file paths.
+ - name: Compute cache trust dispatch
+ uses: ./workflow-actions/.github/actions/cache-trust-dispatch
+ with:
+ repo: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
+ branch: ${{ github.head_ref || github.ref_name }}
+ head-sha: ${{ github.event.pull_request.head.sha || github.sha }}
+
# Checkout the PR branch into a subdirectory
- name: Checkout PR branch
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
@@ -126,6 +139,45 @@ jobs:
# Don't leave the GITHUB_TOKEN in pr-branch/.git/config, where that code could read it.
persist-credentials: false
+ # TEMPORARY (cache storage-layout migration, PR #40035): remove once
+ # active branches have merged the new cache tool.
+ # CI writes the new container layout via the master-built cache binary,
+ # but a PR branch that predates the migration ships the old cache tool locally,
+ # so `lake exe cache get` on the contributor's machine reads the old layout and misses
+ # its own branch's freshly-built artifacts.
+ # Hard-fail here (same style as the `lake lint` predates-config gate below in 'lint mathlib')
+ # and tell the author to merge master, which swaps in the new binary.
+ # The marker is `Cache/Infra.lean`, a module that only exists post-migration (it replaced `Cache/Init.lean`).
+ #
+ # TODO: Remove this check eventually. Note that there is no 'correctness' issue if we don't check this:
+ # it's just that locally, the user will not get the artifacts this run has produced (also meaning the upload is 'waste').
+ - name: check cache tool is post-migration
+ # Canonical repo only. This nudge targets human fork-PR / dev-branch
+ # contributors who run `lake exe cache get` locally. The nightly-testing
+ # repo's branches (nightly-testing, bump/*, lean-pr-testing-*) are
+ # machine-managed and don't merge master the same way, so "merge master
+ # and push" is the wrong remedy there; never fail their CI on it.
+ if: github.repository == 'leanprover-community/mathlib4'
+ shell: bash
+ run: |
+ if [ ! -f pr-branch/Cache/Infra.lean ]; then
+ echo "::error title=Outdated cache tool::Your branch predates the cache storage-layout migration; merge master so 'lake exe cache get' can read your branch's artifacts."
+ echo ""
+ echo "=============================================================================="
+ echo "ERROR: Your branch predates the cache storage-layout migration (PR #40035)."
+ echo "Its 'lake exe cache' tool uses outdated infrastructure, so it will miss the"
+ echo "artifacts CI builds for your branch and rebuild them locally instead."
+ echo "Please merge 'master' into your PR branch and push again."
+ echo ""
+ echo "You can do this with:"
+ echo " git fetch upstream"
+ echo " git merge upstream/master"
+ echo " git push"
+ echo "=============================================================================="
+ echo ""
+ exit 1
+ fi
+
- name: Prepare DownstreamTest directory
shell: bash
run: |
@@ -198,20 +250,28 @@ jobs:
echo "LEAN_SRC_PATH=$LEAN_SRC_PATH" >> "$GITHUB_ENV"
# Populate `tools-branch/` with the trusted CI tooling (the `cache` binary and
- # the `lake-build-*` helper scripts invoked by path below). On the common path
- # this downloads the prebuilt `tools-bin` artifact published from `master` by
- # `publish_tools.yml`; otherwise (bors/ci_dev overrides, nightly-testing, or any
- # download failure) it falls back to checking out and building from source.
+ # the `lake-build-*` helper scripts invoked by path below).
#
- # Recall that on the `leanprover-community/mathlib4-nightly-testing` repository,
- # we don't maintain a `master` branch at all. For PRs and pushes to this
- # repository, we build tools from `nightly-testing-green` instead, so that even
- # when `nightly-testing` is broken, we can still build tools from a known good state.
+ # - Fast path: download the prebuilt `tools-bin` artifact published from
+ # `master` by `publish_tools.yml` — only on canonical mathlib4, and only
+ # when the branch under test doesn't change the cache tool. `get-tools`
+ # makes that comparison itself against `source_dir` (the already-checked-
+ # out `pr-branch`), so the tool-source path list lives in the action, not
+ # duplicated here.
+ # - Source build: from `tools_source_ref`, pointed at the branch *under
+ # test* (`pr_branch_ref`) — bors `staging`/`trying`, in-repo dev branches,
+ # and the nightly-testing repo (which has no `master` branch) — so the
+ # built tool matches the tree. Fork PRs keep `master` and skip the
+ # comparison (empty `source_dir`): they run master's `build_template` via
+ # `pull_request_target`, never this one, and their untrusted tool must
+ # never be built/run with cache credentials (bors exercises tool changes
+ # in-repo instead).
- name: Get CI tools
uses: ./workflow-actions/.github/actions/get-tools
with:
use_artifact: ${{ inputs.tools_branch_ref == '' && github.repository == 'leanprover-community/mathlib4' }}
- tools_source_ref: ${{ inputs.tools_branch_ref != '' && inputs.tools_branch_ref || (github.repository == 'leanprover-community/mathlib4-nightly-testing' && 'nightly-testing-green' || 'master') }}
+ tools_source_ref: ${{ inputs.tools_branch_ref != '' && inputs.tools_branch_ref || (github.event.pull_request.head.repo.fork && 'master' || inputs.pr_branch_ref) }}
+ source_dir: ${{ github.event.pull_request.head.repo.fork != true && 'pr-branch' || '' }}
github_token: ${{ github.token }}
- name: download dependencies
@@ -627,12 +687,29 @@ jobs:
# See discussion at https://leanprover.zulipchat.com/#narrow/stream/287929-mathlib4/topic/Some.20files.20not.20found.20in.20the.20cache/near/407183836
if: ${{ always() && needs.build.outputs.cache-staging-has-files == 'true' }}
steps:
+ # Build the write-side cache tool (and load the trust-dispatch action below)
+ # from the branch under test, so a PR's `--container`-aware tool is what
+ # writes the cache. This job only runs in trusted in-repo contexts (bors,
+ # dev branches, the nightly-testing repo) — fork PRs run master's
+ # `build_template` via `pull_request_target`, never this one — so
+ # `pr_branch_ref` is always a trusted ref here. Fork PRs keep `master`.
- name: Checkout tools branch
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
- ref: ${{ inputs.tools_branch_ref != '' && inputs.tools_branch_ref || (github.repository == 'leanprover-community/mathlib4-nightly-testing' && 'nightly-testing-green' || 'master') }}
+ ref: ${{ inputs.tools_branch_ref != '' && inputs.tools_branch_ref || (github.event.pull_request.head.repo.fork && 'master' || inputs.pr_branch_ref) }}
fetch-depth: 1
+ # Same trust dispatch as in the `build` and `post_steps` jobs. Loaded from
+ # the tools-branch checkout above (the branch under test, a trusted in-repo
+ # ref in the contexts where this job runs), which is what the OIDC token's
+ # container scoping is keyed to.
+ - name: Compute cache trust dispatch
+ uses: ./.github/actions/cache-trust-dispatch
+ with:
+ repo: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
+ branch: ${{ github.head_ref || github.ref_name }}
+ head-sha: ${{ github.event.pull_request.head.sha || github.sha }}
+
- name: Configure Lean
uses: leanprover/lean-action@38fbc41a8c28c4cbaec22d7f7de508ec2e7c0dd9 # v1.5.0
with:
@@ -658,40 +735,36 @@ jobs:
path: cache-staging
- name: Azure CLI OIDC login and mint storage bearer token
- id: mint_cache_bearer
- continue-on-error: true
uses: leanprover-community/mathlib-ci/.github/actions/azure-create-cache-token@17db5ff55a65df98d55cbddcc67938f70d10dab2
with:
azure-client-id: ${{ inputs.cache_application_id }}
azure-tenant-id: ${{ secrets.LPC_AZ_TENANT_ID }}
- - name: Fallback to SAS token if bearer mint fails (for ease of migration)
- if: ${{ steps.mint_cache_bearer.outcome != 'success' }}
+ - name: Upload staged cache to Azure
shell: bash
env:
- MATHLIB_CACHE_SAS_RAW: ${{ secrets.MATHLIB_CACHE_SAS }}
+ REPO: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
run: |
- if [ -z "$MATHLIB_CACHE_SAS_RAW" ]; then
- echo "Azure bearer mint failed and secrets.MATHLIB_CACHE_SAS is not set"
- exit 1
+ # $MATHLIB_CACHE_PRIMARY is set by the `Compute cache trust dispatch`
+ # step above, from the shared composite action that owns the (repo,
+ # branch) → container mapping for both this job and the read-side
+ # jobs (build, post_steps).
+ # Dual-write to the legacy `mathlib4` container first, then the
+ # primary. Only master CI dual-writes: older cache tools read only
+ # `legacy`, so it must stay fresh for them, while forks and nightly
+ # never write `legacy` (keeping low-trust artifacts out of what those
+ # readers trust). Writing `legacy` first keeps it a superset of
+ # `master` for as long as we dual-write: with `set -e`, a failed
+ # legacy write aborts the step before `master` gets artifacts that
+ # `legacy` lacks. (`put-staged` exits non-zero on real upload
+ # failures; already-present 409/412 blobs are not failures.)
+ if [ "$MATHLIB_CACHE_PRIMARY" = "master" ]; then
+ echo "Dual-writing to legacy container first (keeps legacy a superset of master)..."
+ lake env "$CACHE_BIN" put-staged --container=legacy --staging-dir="cache-staging" --repo="$REPO"
fi
- MATHLIB_CACHE_SAS="${MATHLIB_CACHE_SAS_RAW%"${MATHLIB_CACHE_SAS_RAW##*[![:space:]]}"}"
- if [ -z "$MATHLIB_CACHE_SAS" ]; then
- echo "Azure bearer mint failed and secrets.MATHLIB_CACHE_SAS is empty after trimming"
- exit 1
- fi
-
- echo "::add-mask::$MATHLIB_CACHE_SAS"
- echo "MATHLIB_CACHE_AZURE_BEARER_TOKEN=" >> "$GITHUB_ENV"
- echo "MATHLIB_CACHE_SAS=$MATHLIB_CACHE_SAS" >> "$GITHUB_ENV"
- echo "Using SAS fallback because bearer mint step failed."
-
- - name: Upload staged cache to Azure
- shell: bash
- run: |
- echo "Uploading cache to Azure..."
- MATHLIB_CACHE_USE_CLOUDFLARE=0 lake env "$CACHE_BIN" put-staged --staging-dir="cache-staging" --repo=${{ github.event.pull_request.head.repo.full_name || github.repository }}
+ echo "Uploading cache to Azure (container: $MATHLIB_CACHE_PRIMARY)..."
+ lake env "$CACHE_BIN" put-staged --container="$MATHLIB_CACHE_PRIMARY" --staging-dir="cache-staging" --repo="$REPO"
post_steps:
name: Post-Build Step
@@ -710,6 +783,30 @@ jobs:
# Untrusted (potentially fork) checkout: don't persist the GITHUB_TOKEN into its .git/config.
persist-credentials: false
+ # Sparse-checkout master's `.github/actions/` so the trust dispatch
+ # below loads from a trust-rooted source, not from PR-branch-controlled
+ # content. Mirrors the `Checkout local actions` step in the `build` job.
+ - name: Checkout local actions
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ with:
+ ref: ${{ github.workflow_sha }}
+ fetch-depth: 1
+ sparse-checkout: .github/actions
+ path: workflow-actions
+
+ # Sets MATHLIB_CACHE_FROM in env so the `cache get` calls below pick
+ # up the trust-class-appropriate read fallback automatically. Replaces
+ # the previous test-only `--cache-from=master,forks` hardcode that
+ # lived in this file — that policy is now in the shared composite
+ # action so build, upload_cache, and post_steps all consult the same
+ # source of truth.
+ - name: Compute cache trust dispatch
+ uses: ./workflow-actions/.github/actions/cache-trust-dispatch
+ with:
+ repo: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
+ branch: ${{ github.head_ref || github.ref_name }}
+ head-sha: ${{ github.event.pull_request.head.sha || github.sha }}
+
- name: Configure Lean
uses: leanprover/lean-action@38fbc41a8c28c4cbaec22d7f7de508ec2e7c0dd9 # v1.5.0
with:
diff --git a/.github/workflows/cache_test.yml b/.github/workflows/cache_test.yml
new file mode 100644
index 00000000000000..ecf1b87a9cf864
--- /dev/null
+++ b/.github/workflows/cache_test.yml
@@ -0,0 +1,43 @@
+# Runs the cache tool's unit tests (`lake exe cache-test`) on PRs that touch
+# the cache code. The suite is deliberately network-free (see the module
+# docstring in `Cache/Test.lean`), so the job only needs the toolchain and the
+# package dependencies' sources — not a Mathlib build — and finishes in a few
+# minutes on a hosted runner.
+name: cache tests
+
+on:
+ pull_request:
+ paths:
+ - 'Cache/**'
+ # The `cache-test` target and the toolchain it compiles under live here;
+ # a bump can break the tool's compilation even with `Cache/` untouched.
+ - 'lakefile.lean'
+ - 'lean-toolchain'
+
+concurrency:
+ group: cache-test-${{ github.ref }}
+ cancel-in-progress: true
+
+permissions:
+ contents: read
+
+jobs:
+ cache-test:
+ if: github.repository == 'leanprover-community/mathlib4'
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+
+ - name: install elan
+ run: |
+ set -o pipefail
+ curl -o elan-init.sh -sSfL https://elan.lean-lang.org/elan-init.sh
+ chmod +x elan-init.sh
+ ./elan-init.sh -y --default-toolchain none
+ echo "$HOME/.elan/bin" >> "${GITHUB_PATH}"
+
+ - name: build cache-test
+ run: lake build cache-test
+
+ - name: run cache tests
+ run: .lake/build/bin/cache-test
diff --git a/Cache/Cli.lean b/Cache/Cli.lean
new file mode 100644
index 00000000000000..e59a239e0122d4
--- /dev/null
+++ b/Cache/Cli.lean
@@ -0,0 +1,49 @@
+/-
+Copyright (c) 2026 Marcelo Lynch. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Marcelo Lynch
+-/
+
+/-!
+# Cache CLI option parsing
+
+Pure helpers for the cache binary's option parsing. They live here rather than
+in `Cache.Main` so the test binary can import them directly — `Cache.Main`'s
+top-level `main` would otherwise collide with the test entrypoint.
+
+The cache binary partitions its arguments into named options (`--name=value`),
+boolean flags (`--name`), and positional arguments before dispatch. These
+helpers implement that partitioning and the validation of known option names.
+-/
+
+namespace Cache.Cli
+
+/-- The named options supported by the CLI. -/
+def knownNamedOpts : List String :=
+ ["repo", "staging-dir", "cache-from", "container", "scope", "unsafe-window"]
+
+/-- The flag options supported by the CLI. -/
+def knownFlagOpts : List String := ["help", "unsafe"]
+
+/-- Parses an optional `--foo=bar` option. Returns the value for the
+last-mentioned occurrence (so a later `--foo=` overrides an earlier one). -/
+def parseNamedOpt (opt : String) (args : List String) : IO (Option String) := do
+ let pref := s!"--{opt}="
+ if let some a := args.findRev? (fun a => a.startsWith pref) then
+ let val := a.drop pref.length
+ return some val.toString
+ return none
+
+/-- Parses a boolean `--foo` flag. True iff the bare token `--foo` appears
+anywhere in `args`. -/
+def parseFlagOpt (opt : String) (args : List String) : Bool :=
+ args.elem s!"--{opt}"
+
+/-- Check whether `opt` (e.g. `"--repo=foo"` or `"--help"`) is a recognized
+option. Used to error out on unknown `--`-prefixed tokens so typos like
+`--scoop=` don't get silently ignored. -/
+def isKnownOpt (opt : String) : Bool :=
+ knownNamedOpts.any (opt.startsWith s!"--{·}=") ||
+ knownFlagOpts.any (opt == s!"--{·}")
+
+end Cache.Cli
diff --git a/Cache/Infra.lean b/Cache/Infra.lean
new file mode 100644
index 00000000000000..3b133b2d1e3bd7
--- /dev/null
+++ b/Cache/Infra.lean
@@ -0,0 +1,151 @@
+/-
+Copyright (c) 2026 Marcelo Lynch. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Marcelo Lynch, Arthur Paulino
+-/
+
+/-!
+# Cache backend infrastructure
+
+The multi-container model — trust-classified Azure containers and the per-repo
+lookup chain — together with the GitHub repo names the cache tool dispatches on.
+
+This lives apart from `Cache.Requests` so the container model and trust ordering
+stand on their own, independent of the HTTP/curl machinery that consumes them.
+-/
+
+namespace Cache.Requests
+
+open System (FilePath)
+
+/-- The full name of the main Mathlib GitHub repository. -/
+def MATHLIBREPO := "leanprover-community/mathlib4"
+
+/-- The full name of the Mathlib nightly-testing GitHub repository. -/
+def NIGHTLY_TESTING_REPO := "leanprover-community/mathlib4-nightly-testing"
+
+/--
+Trust-classified Azure storage containers for the Mathlib cache.
+
+Each variant maps to one Azure Blob Storage container on the `lakecache` storage
+account. A CI job at a given trust level may write only to its corresponding
+container, and `cache get` always tries the most trusted container first.
+-/
+inductive Container where
+ /-- Most-trusted container (`mathlib4-master`); only master CI writes here. -/
+ | master
+ /-- Container for PR builds on forks of mathlib4. -/
+ | forks
+ /-- Container for the `nightly-testing` branch and related refs. -/
+ | nightlyTesting
+ /-- Container for toolchain-PR test runs. -/
+ | prToolchainTests
+ /-- The bare `mathlib4` container that older cache clients read from. Only
+ master CI writes here (mirroring its `mathlib4-master` upload), so those
+ clients keep finding master-built artifacts; forks and nightly-testing stay
+ out to keep low-trust writes from reaching readers that predate the split. -/
+ | legacy
+ deriving DecidableEq, Repr, BEq, Inhabited
+
+namespace Container
+
+/-- Canonical short name for a container, used in CLI flags and URLs. -/
+def name : Container → String
+ | .master => "master"
+ | .forks => "forks"
+ | .nightlyTesting => "nightly-testing"
+ | .prToolchainTests => "pr-toolchain-tests"
+ | .legacy => "legacy"
+
+/-- All known containers, listed in their canonical declaration order. -/
+def all : List Container :=
+ [.master, .forks, .nightlyTesting, .prToolchainTests, .legacy]
+
+/-- Parse a short name back into a `Container`. Matching is case-insensitive. -/
+def parse? (s : String) : Option Container :=
+ match s.toLower with
+ | "master" => some .master
+ | "forks" => some .forks
+ | "nightly-testing" => some .nightlyTesting
+ | "pr-toolchain-tests" => some .prToolchainTests
+ | "legacy" => some .legacy
+ | _ => none
+
+/--
+Azure storage container name on the `lakecache` storage account.
+
+Trust-level containers follow the `mathlib4-{name}` convention; `legacy` is the
+bare `mathlib4` container.
+-/
+def azureContainerName : Container → String
+ | .legacy => "mathlib4"
+ | c => s!"mathlib4-{c.name}"
+
+/-- Public Azure Blob Storage base URL for a container. -/
+def azureURL (c : Container) : String :=
+ s!"https://lakecache.blob.core.windows.net/{c.azureContainerName}"
+
+/--
+Whether file lookups in this container use the flat `/f/` layout, or
+namespace under `/f//`.
+
+The layout is fixed per container, not per repo, because one container holds
+artifacts from several writers whose `repo` need not match the container's
+trust level, and a stable per-container layout is what keeps readers and
+writers in sync.
+
+- `master` is flat: RBAC admits only master CI, whose writes all carry
+ `repo == MATHLIBREPO`, so a single hash never collides.
+- `legacy` keys the layout on the writer: `MATHLIBREPO` writes are flat (where
+ older `mathlib4` readers look for them), fork writes are repo-namespaced.
+- `forks`, `nightly-testing`, and `pr-toolchain-tests` always namespace by
+ repo. They collect artifacts from many writers — different forks, different
+ toolchain refs, and canonical-repo builds whose trust is fork-equivalent
+ (`ci-dev/*`, `bors trying`) — so identical hashes from different writers must
+ stay on distinct paths.
+-/
+def flatPath (c : Container) (repo : String) : Bool :=
+ match c with
+ | .master => true
+ | .legacy => repo == MATHLIBREPO
+ | _ => false
+
+end Container
+
+/--
+Comma-separated list parser for `--cache-from=a,b,c`.
+
+Returns `none` if any element is unrecognized.
+-/
+def parseCacheFromList (s : String) : Option (List Container) := do
+ let parts := s.splitOn ","
+ parts.mapM (fun p => Container.parse? p.trimAscii.toString)
+
+/--
+Trust-ordered containers to try when downloading for a given GitHub repo, most
+trusted first. Each repo reads from its own trust-level container, with `legacy`
+appended so older clients' artifacts stay reachable.
+
+Fork chains lead with `master`. The layout is fixed per container
+(`Container.flatPath`), so the `master` container is read flat at `/f/{hash}`
+whatever the `repo` is, and a fork build finds the master-built deps that make
+up the bulk of its files there; the fork's own container then supplies the
+PR-specific files at `/f/{repo}/...`.
+
+Nightly-testing chains omit `master`: that repo builds under a non-release
+toolchain, so its root hash differs and a master probe never matches.
+-/
+def defaultContainersForRepo (repo : String) : List Container :=
+ if repo == MATHLIBREPO then
+ [.master, .legacy]
+ else if repo == NIGHTLY_TESTING_REPO then
+ -- Trusted-nightly consumers (`nightly-testing`, `nightly-testing-green`,
+ -- `bump/*`) read only `nightly-testing` + `legacy`; `pr-toolchain-tests` is
+ -- excluded so low-trust toolchain-PR uploads can't reach them. Toolchain-PR
+ -- branches opt into reading their own uploads with `--cache-from=...` (or,
+ -- in CI, via the `MATHLIB_CACHE_FROM` env var).
+ [.nightlyTesting, .legacy]
+ else
+ -- Forks and everything else: `master` for shared upstream deps, the fork's
+ -- own container for PR-specific files, then `legacy`.
+ [.master, .forks, .legacy]
diff --git a/Cache/Init.lean b/Cache/Init.lean
deleted file mode 100644
index 7ceceb43495b13..00000000000000
--- a/Cache/Init.lean
+++ /dev/null
@@ -1,16 +0,0 @@
-/-
-Copyright (c) 2023 Arthur Paulino. All rights reserved.
-Released under Apache 2.0 license as described in the file LICENSE.
-Authors: Arthur Paulino
--/
-
-namespace Cache.Requests
-
-open System (FilePath)
-
--- Cloudflare cache may be flaky: https://leanprover.zulipchat.com/#narrow/channel/113488-general/topic/The.20cache.20doesn't.20work/near/411058849
--- This is defined in a separate file because it is used in the definition of `URL` and `UPLOAD_URL`
--- and Lean does not allow one `initialize` to use another `initialize` defined in the same file
-initialize useCloudflareCache : Bool ← do
- let cache ← IO.getEnv "MATHLIB_CACHE_USE_CLOUDFLARE"
- return cache == some "1" || cache == some "true"
diff --git a/Cache/Main.lean b/Cache/Main.lean
index 4949ad7677f0ca..dcce94de04f8fd 100644
--- a/Cache/Main.lean
+++ b/Cache/Main.lean
@@ -1,10 +1,14 @@
/-
Copyright (c) 2023 Arthur Paulino. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
-Authors: Arthur Paulino, Jon Eugster
+Authors: Arthur Paulino, Jon Eugster, Marcelo Lynch
-/
+import Cache.Cli
import Cache.Requests
+import Cache.Marker
+import Cache.Query
+import Cache.Warning
def help : String := "Mathlib4 caching CLI
Usage: cache [OPTIONS] [COMMAND]
@@ -21,6 +25,8 @@ Commands:
clean Delete non-linked files
clean! Delete everything on the local cache
lookup [ARGS] Show information about cache files for the given Lean files
+ query [REF] Without REF: find most recent cached commit on this branch.
+ With REF (e.g. HEAD, a SHA): boolean probe; exit 0 if cached, 1 if not.
# Privilege required
put Run 'pack' then upload linked files missing on the server
@@ -39,6 +45,30 @@ Commands:
Options:
--repo=OWNER/REPO Override the repository to fetch/push cache from
--staging-dir= Required for 'stage', 'stage!', 'unstage' and 'put-staged': staging directory.
+ --cache-from=LIST Comma-separated, trust-ordered list of containers to read from
+ (e.g. `--cache-from=master,forks`). Overrides the per-repo default.
+ Known containers: master, forks, nightly-testing,
+ pr-toolchain-tests, legacy.
+ --scope=REF Read the fork SHA-scoped namespace at the given commit ref
+ (any git ref `git rev-parse` accepts: HEAD, branch, tag, SHA)
+ instead of the default, the checked-out HEAD. Use the SHA
+ reported by `cache query`. Wins over the
+ MATHLIB_CACHE_REPO_SCOPE env var. Reading another commit's
+ scope means trusting the artifacts produced at that commit;
+ `cache get` prints a security notice when the scope differs
+ from HEAD.
+ --unsafe (get only) Instead of pinning one --scope, automatically walk
+ this branch's history and try the most recent cached fork
+ commits as scopes, in order, until the cache is satisfied.
+ Trusts the artifacts of every commit it tries. Mutually
+ exclusive with --scope; always prints a security notice.
+ --unsafe-window=N Number of cached fork commits --unsafe will try (default
+ 1). Implies --unsafe.
+ --container=NAME Target container for upload commands (put/put!/put-unpacked/
+ put-staged/commit/commit!). Known containers: master, forks,
+ nightly-testing, pr-toolchain-tests, legacy. Pass this
+ explicitly; with neither it nor MATHLIB_CACHE_PUT_URL set,
+ the upload falls back to `legacy` and warns.
* Linked files refer to local cache files with corresponding Lean sources
* Commands ending with '!' should be used manually, when hot-fixes are needed
@@ -63,9 +93,11 @@ Valid arguments are:
# Environment variables
* MATHLIB_CACHE_DIR Local cache directory (default: ~/.cache/mathlib)
-* MATHLIB_CACHE_USE_CLOUDFLARE Set to '1' to use Cloudflare instead of Azure
-* MATHLIB_CACHE_GET_URL Override the download URL
-* MATHLIB_CACHE_PUT_URL Override the upload URL
+* MATHLIB_CACHE_GET_URL Download from this single URL, bypassing the containers
+* MATHLIB_CACHE_PUT_URL Upload to this single URL, bypassing the containers
+* MATHLIB_CACHE_FROM Comma-separated container list for reads, same shape as
+ --cache-from. Used by CI to widen reads per job;
+ --cache-from takes precedence when both are set.
See Cache/README.md for more details.
"
@@ -78,30 +110,7 @@ def curlArgs : List String :=
def leanTarArgs : List String :=
["get", "get!", "put", "put!", "put-unpacked", "pack", "pack!", "unpack", "lookup", "stage", "stage!"]
-/-- The named options supported by the CLI. -/
-def knownNamedOpts : List String := ["repo", "staging-dir"]
-
-/-- The flag options supported by the CLI. -/
-def knownFlagOpts : List String := ["help"]
-
-/-- Parses an optional `--foo=bar` option. -/
-def parseNamedOpt (opt : String) (args : List String) : IO (Option String) := do
- let pref := s!"--{opt}="
- if let some a := args.findRev? (fun a => a.startsWith pref) then
- let val := a.drop pref.length
- return some val.toString
- return none
-
-/-- Parses a boolean `--foo` flag. -/
-def parseFlagOpt (opt : String) (args : List String) : Bool :=
- args.elem s!"--{opt}"
-
-/-- Check whether `opt` (e.g. `"--repo=foo"` or `"--help"`) is a recognized option. -/
-def isKnownOpt (opt : String) : Bool :=
- knownNamedOpts.any (opt.startsWith s!"--{·}=") ||
- knownFlagOpts.any (opt == s!"--{·}")
-
-open Cache IO Hashing Requests System in
+open Cache Cli IO Hashing Requests System in
def main (args : List String) : IO Unit := do
if args.isEmpty || parseFlagOpt "help" args then
println help
@@ -120,6 +129,79 @@ def main (args : List String) : IO Unit := do
let repo? ← parseNamedOpt "repo" options
let stagingDir? ← parseNamedOpt "staging-dir" options
+ let cacheFromStr? ← parseNamedOpt "cache-from" options
+ let containerStr? ← parseNamedOpt "container" options
+ let scopeStr? ← parseNamedOpt "scope" options
+ let unsafeFlag := parseFlagOpt "unsafe" options
+ let unsafeWindowStr? ← parseNamedOpt "unsafe-window" options
+
+ -- Resolve `--unsafe` / `--unsafe-window=N` into an optional SHA window.
+ -- `some n` means unsafe mode is on with window `n`; `none` means off. Passing
+ -- `--unsafe-window` implies `--unsafe`.
+ let unsafeWindow? : Option Nat ← match unsafeWindowStr? with
+ | some s => match s.toNat? with
+ | some n =>
+ if n == 0 then
+ IO.eprintln "--unsafe-window must be a positive integer"
+ Process.exit 1
+ pure (some n)
+ | none =>
+ IO.eprintln s!"--unsafe-window must be a positive integer (got '{s}')"
+ Process.exit 1
+ | none => pure (if unsafeFlag then some defaultUnsafeSHAWindow else none)
+
+ -- `--unsafe` and `--scope` are mutually exclusive: `--unsafe` walks several
+ -- commit scopes automatically, `--scope` pins exactly one.
+ if unsafeWindow?.isSome && scopeStr?.isSome then
+ IO.eprintln "--unsafe and --scope are mutually exclusive: --unsafe walks several commit \
+ scopes automatically, while --scope pins exactly one."
+ Process.exit 1
+
+ -- Apply `--scope=` to the process-wide override read by `getRepoScope`.
+ -- Accepts any git ref `git rev-parse` resolves (HEAD, branch, tag, SHA);
+ -- falls through to the literal value if `git rev-parse` is unavailable
+ -- (e.g. invoked outside a git checkout with a bare SHA).
+ if let some s := scopeStr? then
+ let resolved ← try resolveGitRef s catch _ => pure s
+ scopeOverride.set (some resolved)
+
+ -- Apply `--cache-from` to the process-wide override read by `effectiveGetURLs`.
+ if let some s := cacheFromStr? then
+ match parseCacheFromList s with
+ | none =>
+ IO.eprintln s!"Unknown container name in --cache-from={s}.\n\
+ Known containers: {", ".intercalate (Container.all.map Container.name)}."
+ Process.exit 1
+ | some cs => cacheFromOverride.set (some cs)
+
+ -- Parse `--container=NAME`. Validation is unconditional; the upload commands
+ -- enforce that the flag is set (via `effectiveUploadURL`).
+ let container? ← match containerStr? with
+ | none => pure none
+ | some s => match Container.parse? s with
+ | some c => pure (some c)
+ | none =>
+ IO.eprintln s!"Unknown container name in --container={s}.\n\
+ Known containers: {", ".intercalate (Container.all.map Container.name)}."
+ Process.exit 1
+
+ -- Early dispatch for `query`: avoids running `parseArgs` (which would try to
+ -- interpret a git ref like `HEAD` as a Lean module) and skips the expensive
+ -- hash-memo build below — the query only needs git + a single HTTP probe.
+ match args with
+ | ["query"] =>
+ let repo ← resolveQueryRepo repo?
+ cacheQuery repo (cap := 50)
+ return
+ | ["query", ref] =>
+ let repo ← resolveQueryRepo repo?
+ let sha ← resolveGitRef ref
+ cacheQuerySingle repo sha
+ return
+ | "query" :: _ =>
+ IO.eprintln "Usage: cache query [REF]"
+ Process.exit 1
+ | _ => pure ()
let mut roots : Std.HashMap Lean.Name FilePath ← parseArgs args
if roots.isEmpty then do
@@ -135,12 +217,38 @@ def main (args : List String) : IO Unit := do
let goodCurl ← pure !curlArgs.contains (args.headD "") <||> validateCurl
let get (args : List String) (force := false) (decompress := true) := do
let hashMap ← if args.isEmpty then pure hashMap else hashMemo.filterByRootModules roots.keys
- getFiles repo? hashMap force force goodCurl decompress
+ -- Resolve the repo once (single git-remote probe) and thread it through the
+ -- read path, the non-default-scope warning, and the HEAD hint below.
+ let cliOverride? ← cacheFromOverride.get
+ let (detectedRepo?, resolvedRepo) ← resolveRepo repo? (← read).mathlibDepPath
+ -- Warn before reading if the scope is non-default (`--unsafe` always is).
+ warnIfNonDefaultScope repo? detectedRepo? cliOverride? resolvedRepo unsafeWindow?
+ -- In `--unsafe` mode, walk history for recent cached fork commits to try as
+ -- scopes; otherwise point an uncached fork HEAD at the per-commit workflow.
+ let unsafeScopes ← match unsafeWindow? with
+ | some window =>
+ let scopes ← discoverUnsafeScopes resolvedRepo window
+ if scopes.isEmpty then
+ IO.eprintln s!"--unsafe: no cached fork commits found in range for {resolvedRepo}; \
+ reading the default cache only."
+ else
+ IO.eprintln s!"--unsafe: trying {scopes.length} cached fork commit scope(s) for \
+ {resolvedRepo} (most recent first):"
+ for s in scopes do IO.eprintln s!" {s}"
+ pure scopes
+ | none =>
+ informIfHeadNotBuilt resolvedRepo
+ pure []
+ getFiles resolvedRepo hashMap force force goodCurl decompress (unsafeScopes := unsafeScopes)
let pack (overwrite verbose unpackedOnly := false) := do
packCache hashMap overwrite verbose unpackedOnly (← getGitCommitHash)
let put (overwrite unpackedOnly := false) := do
let repo := repo?.getD MATHLIBREPO
- putFiles repo (← pack overwrite (verbose := true) unpackedOnly) overwrite (← getUploadAuth)
+ let auth ← getUploadAuth
+ putFiles repo container? (← pack overwrite (verbose := true) unpackedOnly) overwrite auth
+ if let some sha ← getRepoScope then
+ if let some c := container? then
+ uploadMarker c repo sha auth
let stage outDir (unpackedOnly := true) := do
stageFiles outDir (← pack (verbose := true) (unpackedOnly := unpackedOnly))
let unstage (overwrite := false) := do
@@ -151,8 +259,15 @@ def main (args : List String) : IO Unit := do
if !(←stagingDir.isDir) then IO.println "--staging-dir must be a directory" return
else
let fileSet ← getFilesWithExtension stagingDir "ltar"
- putFilesAbsolute repo fileSet (tempConfigFilePath := stagingDir / "curl.config")
- (overwrite := false) (← getUploadAuth)
+ let auth ← getUploadAuth
+ putFilesAbsolute repo container? fileSet (tempConfigFilePath := stagingDir / "curl.config")
+ (overwrite := false) auth
+ -- After artifacts upload, write the per-SHA marker if the upload is
+ -- SHA-scoped. The marker lets `cache query` discover cached commits
+ -- with a cheap HEAD probe.
+ if let some sha ← getRepoScope then
+ if let some c := container? then
+ uploadMarker c repo sha auth
match args with
| "get" :: args => get args
@@ -179,10 +294,10 @@ def main (args : List String) : IO Unit := do
putStaged stagingDir?.get!
| ["commit"] =>
if !(← isGitStatusClean) then IO.println "Please commit your changes first" return else
- commit hashMap false (← getUploadAuth)
+ commit container? hashMap false (← getUploadAuth)
| ["commit!"] =>
if !(← isGitStatusClean) then IO.println "Please commit your changes first" return else
- commit hashMap true (← getUploadAuth)
+ commit container? hashMap true (← getUploadAuth)
| ["collect"] => IO.println "TODO"
| "lookup" :: _ => lookup hashMap roots.keys
| [] => println help -- unreachable: options are already partitioned out
diff --git a/Cache/Marker.lean b/Cache/Marker.lean
new file mode 100644
index 00000000000000..56a3e9d43def93
--- /dev/null
+++ b/Cache/Marker.lean
@@ -0,0 +1,64 @@
+/-
+Copyright (c) 2026 Marcelo Lynch. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Marcelo Lynch
+-/
+
+import Cache.Requests
+
+/-!
+# Per-SHA cache markers
+
+A marker is a tiny blob at `/m/{repo}/{sha}` whose existence signals that the
+full `.ltar` upload for a commit completed. `put-staged` writes it as the last
+upload step, and `cache query` probes it to discover cached commits with a
+cheap HEAD request instead of a blob listing.
+-/
+
+namespace Cache.Requests
+
+open System (FilePath)
+
+/--
+URL for the per-SHA marker blob: `{container}/m/{repo}/{sha}`.
+
+The marker is uploaded by `put-staged` as the last step when an upload is
+SHA-scoped (`MATHLIB_CACHE_REPO_SCOPE` set). Its presence at this URL
+indicates that the full `.ltar` upload completed for this commit, and lets
+`cache query` discover cached commits with a cheap HEAD probe rather than
+a blob-listing call.
+-/
+def markerURL (container : Container) (repo sha : String) : String :=
+ s!"{container.azureURL}/m/{repo}/{sha}"
+
+/--
+Upload a tiny marker blob to `/m/{repo}/{sha}` in the given container. The
+blob content is the SHA itself, as a debugging aid; existence is the
+signal.
+
+Called from `put` and `put-staged` after the `.ltar` artifact uploads
+complete. If this PUT fails the artifacts are already uploaded — the only
+loss is that `cache query` will not find this commit — so failures here
+are logged but not fatal.
+-/
+def uploadMarker (container : Container) (repo sha : String) (auth : UploadAuth) :
+ IO Unit := do
+ let url := markerURL container repo sha
+ let path := IO.CACHEDIR / s!"marker-{sha}"
+ IO.FS.createDirAll IO.CACHEDIR
+ IO.FS.writeFile path s!"{sha}\n"
+ let azureDateHeader ← getAzureDateHeader
+ try
+ match auth with
+ | .azureSas token =>
+ let params := #["-X", "PUT", "-H", "x-ms-blob-type: BlockBlob"]
+ discard <| IO.runCurl <| params ++ #["-T", path.toString, s!"{url}?{token}"]
+ | .azureBearer token =>
+ let params := #["-X", "PUT", "-H", "x-ms-blob-type: BlockBlob", "-H",
+ azureBearerApiVersionHeader, "-H", azureDateHeader, "--oauth2-bearer", token]
+ discard <| IO.runCurl <| params ++ #["-T", path.toString, url]
+ catch e =>
+ IO.eprintln s!"warning: marker upload to {url} failed: {e}"
+ IO.FS.removeFile path
+
+end Cache.Requests
diff --git a/Cache/Query.lean b/Cache/Query.lean
new file mode 100644
index 00000000000000..147f1d16723b4e
--- /dev/null
+++ b/Cache/Query.lean
@@ -0,0 +1,229 @@
+/-
+Copyright (c) 2026 Marcelo Lynch. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Marcelo Lynch
+-/
+
+import Cache.Marker
+
+/-!
+# The `cache query` subcommand
+
+Discovers the most recent commit on the current branch that has a cached CI
+build, by walking git history back to the merge base with `master` and probing
+each commit's per-SHA marker. Diagnostic only: it prints a SHA for the user to
+pass to `cache get --scope=`, and never reads or writes artifacts itself.
+-/
+
+namespace Cache.Requests
+
+open System (FilePath)
+
+/--
+Walk git log backwards from HEAD, starting from `startRef`, stopping at
+`stopRef` or after `cap` commits (whichever comes first).
+
+Returns the list of commit SHAs in reverse chronological order (most recent first).
+-/
+def gitLogWalk (startRef stopRef : String) (cap : Nat) (cwd : FilePath := ".") :
+ IO (List String) := do
+ -- Construct git log command: walk from startRef to stopRef (if provided) using first-parent.
+ -- First-parent follows the main branch across merges, which is the intended behavior.
+ let args := if stopRef.isEmpty then
+ #["log", startRef, "--first-parent", "--pretty=format:%H", s!"--max-count={cap}"]
+ else
+ #["log", s!"{startRef}...{stopRef}", "--first-parent", "--pretty=format:%H", s!"--max-count={cap}"]
+ let out ← IO.Process.output {cmd := "git", args := args, cwd := cwd}
+ unless out.exitCode == 0 do
+ throw <| IO.userError
+ s!"git log failed (exit code {out.exitCode}):\n{out.stderr.trimAscii}"
+ let shas := out.stdout.trimAscii.toString.splitOn "\n" |>.filter (· ≠ "")
+ pure shas
+
+/--
+Determine the merge base between `HEAD` and a target ref (typically `master`).
+Falls back to a cap-only walk if the ref is not reachable.
+-/
+def gitMergeBase (targetRef : String) (cwd : FilePath := ".") : IO (Option String) := do
+ let out ← IO.Process.output
+ {cmd := "git", args := #["merge-base", "HEAD", targetRef], cwd := cwd}
+ if out.exitCode == 0 then
+ pure (some out.stdout.trimAscii.toString)
+ else
+ -- merge-base failed (target ref not reachable); return none to signal cap-only walk
+ pure none
+
+/--
+Return `true` if `HEAD` is an ancestor of (or equal to) the local `master`
+branch — i.e. the current commit is already part of master's history and has no
+fork-specific divergence.
+
+Uses `git merge-base --is-ancestor HEAD master`, which exits 0 when HEAD is an
+ancestor of master and 1 when it is not. Any other outcome (e.g. `master` not
+present locally, or git unavailable) is treated as "not an ancestor", so callers
+degrade to their default behavior rather than throwing — matching the never-throw
+posture of the rest of the read path.
+-/
+def headIsAncestorOfMaster (cwd : FilePath := ".") : IO Bool := do
+ try
+ let out ← IO.Process.output
+ {cmd := "git", args := #["merge-base", "--is-ancestor", "HEAD", "master"], cwd := cwd}
+ pure (out.exitCode == 0)
+ catch _ =>
+ pure false
+
+/--
+Probe a single container for the per-SHA marker blob.
+
+Issues an anonymous HEAD against `{container}/m/{repo}/{sha}` and returns
+`true` iff the response is 200. The marker is uploaded by `put-staged`
+after a successful upload, so its existence is a reliable "this commit
+was fully cached" signal.
+
+Cheaper than blob-listing: deterministic URL, headers-only response,
+billed as a Read op.
+-/
+def probeContainerForSHA (container : Container) (repo sha : String) :
+ IO Bool := do
+ let url := markerURL container repo sha
+ let out ← IO.Process.output
+ {cmd := (← IO.getCurl),
+ args := #["-s", "-o", "/dev/null", "-w", "%{http_code}", "-I", url],
+ cwd := "."}
+ if out.exitCode != 0 then
+ -- Network error; assume no cache at this SHA
+ pure false
+ else
+ pure (out.stdout.trimAscii.toString == "200")
+
+/-- Default number of marked fork commits `cache get --unsafe` will try as SHA
+scopes: 1, namely just the latest cached SHA. Overridden by
+`--unsafe-window=N`. -/
+def defaultUnsafeSHAWindow : Nat := 1
+
+/--
+Walk a list of SHAs (most recent first) and collect up to `limit` of them whose
+per-SHA marker exists in the `forks` container. Stops early once `limit` are
+found, so at most `limit` probes succeed (and at most `shas.length` are made).
+
+`forks` is the only SHA-scoped container; master/nightly-testing/pr-toolchain-tests
+are not scoped, so probing them here would be meaningless.
+-/
+def findRecentSHAsWithCache (shas : List String) (repo : String) (limit : Nat) :
+ IO (List String) := do
+ let container := Container.forks
+ let mut found : Array String := #[]
+ for sha in shas do
+ if found.size ≥ limit then break
+ if ← probeContainerForSHA container repo sha then
+ found := found.push sha
+ pure found.toList
+
+/--
+Given a list of SHAs, find the most recent one that has cached entries in the
+forks container under the SHA-scoped namespace. Returns the first SHA the probe
+accepts, or none if none are found.
+-/
+def findMostRecentSHAWithCache (shas : List String) (repo : String) :
+ IO (Option String) :=
+ return (← findRecentSHAsWithCache shas repo 1).head?
+
+/--
+Resolve a git ref (HEAD, branch name, tag, short SHA, full SHA) to a full
+commit SHA via `git rev-parse`. Errors propagate if the ref is unknown.
+-/
+def resolveGitRef (ref : String) (cwd : FilePath := ".") : IO String := do
+ let out ← IO.Process.output {cmd := "git", args := #["rev-parse", ref], cwd := cwd}
+ unless out.exitCode == 0 do
+ throw <| IO.userError
+ s!"git rev-parse {ref} failed (exit code {out.exitCode}):\n{out.stderr.trimAscii}"
+ pure out.stdout.trimAscii.toString
+
+/--
+Resolve the repo to use for a `cache query` invocation.
+
+Precedence: the explicit `--repo=` flag (if passed) > the cwd's git remote
+> `MATHLIBREPO`. Defaulting to the git remote is intentional for `query` —
+the typical user is asking "what's cached for *my* commits", not for
+canonical mathlib's commits.
+-/
+def resolveQueryRepo (repoExplicit? : Option String) : IO String := do
+ match repoExplicit? with
+ | some r => pure r
+ | none =>
+ match ← getRemoteRepo "." with
+ | some info => pure info.repo
+ | none => pure MATHLIBREPO
+
+/--
+Boolean probe for a single commit: prints `cached` or `not cached` and exits
+with status 0 / 1 respectively. Intended for scripting.
+
+Probes the `forks` container's per-SHA marker, the only SHA-scoped container.
+-/
+def cacheQuerySingle (repo sha : String) : IO Unit := do
+ let cached ← probeContainerForSHA Container.forks repo sha
+ if cached then
+ IO.println s!"cached: {sha}"
+ else
+ IO.println s!"not cached: {sha}"
+ (← IO.getStdout).flush
+ IO.Process.exit 1
+
+/--
+Implement the `cache query` subcommand.
+
+Walks git log backwards from HEAD, stopping at the merge base with `master`
+(or a hard cap if the merge base is not reachable), and probes each commit's
+SHA-scoped namespace to find the most recent commit that has cache entries.
+
+This is a diagnostic-only command: it prints the SHA to stdout but does not
+auto-apply it. The user manually passes the result to `cache get` if desired.
+-/
+def cacheQuery (repo : String) (cap : Nat := 50) (cwd : FilePath := ".") : IO Unit := do
+ -- Determine merge base with master. If not reachable, use cap-only walk.
+ let mergeBase? ← gitMergeBase "master" cwd
+ let stopRef := mergeBase?.getD ""
+
+ -- Walk git log backwards from HEAD.
+ let shas ← gitLogWalk "HEAD" stopRef cap cwd
+ if shas.isEmpty then
+ IO.println "No commits found to walk (repository history is empty)"
+ return
+
+ -- Probe each SHA in order (most recent first).
+ let found? ← findMostRecentSHAWithCache shas repo
+ match found? with
+ | some sha =>
+ IO.println s!"Most recent cached commit on this branch for fork {repo}: {sha}"
+ IO.println s!""
+ IO.println s!"To use this cache, run:"
+ IO.println s!" lake exe cache get --scope={sha}"
+ IO.println s!""
+ IO.println s!"Note: this means trusting the artifacts built at that commit;"
+ IO.println s!"`cache get` will print a security notice when --scope is set."
+ | none =>
+ IO.println s!"No cached CI build found for fork {repo} within the last {cap} commits on this branch."
+ IO.println s!"This usually means CI hasn't built any of these commits yet."
+
+/--
+Discover the SHA scopes `cache get --unsafe` should try, most recent first.
+
+Walks git history from HEAD back to the merge base with `master` (or a hard
+`cap` if the merge base is not reachable) and returns up to `window` commit SHAs
+whose per-SHA marker exists in the `forks` container — i.e. the most recent
+`window` commits on this branch that CI has fully cached for this fork.
+
+Unlike `cacheQuery`, this is consumed automatically by `cache get` rather than
+printed for the user, and it returns several SHAs instead of one. An empty
+result means no cached commit was found in range; the caller falls back to a
+normal (unscoped) read.
+-/
+def discoverUnsafeScopes (repo : String) (window : Nat := defaultUnsafeSHAWindow)
+ (cap : Nat := 50) (cwd : FilePath := ".") : IO (List String) := do
+ let mergeBase? ← gitMergeBase "master" cwd
+ let stopRef := mergeBase?.getD ""
+ let shas ← gitLogWalk "HEAD" stopRef cap cwd
+ findRecentSHAsWithCache shas repo window
+
+end Cache.Requests
diff --git a/Cache/README.md b/Cache/README.md
index 2c25714653f41c..d027fb238a44c1 100644
--- a/Cache/README.md
+++ b/Cache/README.md
@@ -4,6 +4,9 @@ This directory contains the implementation of Mathlib's build cache system (`lak
> **Note**: A new `lake cache` command is currently being designed and implemented in Lake itself. This will eventually replace the Mathlib-specific `lake exe cache` and work for all repositories. Until then, this cache system remains the primary way to get pre-built artifacts for Mathlib.
+> **Trust model & security**: see [`SECURITY.md`](./SECURITY.md) for the
+> trust model behind the multi-container split.
+
## Quick Start
```bash
@@ -34,6 +37,7 @@ lake exe cache get Mathlib.Algebra.Group.Basic
| `clean` | Delete non-linked files |
| `clean!` | Delete everything on the local cache |
| `lookup [ARGS]` | Show information about cache files for the given Lean files |
+| `query` | Find the most recent commit with cached entries on the current branch |
### Privilege Required (CI/Maintainers)
@@ -61,37 +65,47 @@ When arguments are provided, only the specified files and their transitive impor
| Option | Description |
|---------------------|--------------------------------------------------------------------------------------------|
| `--repo=OWNER/REPO` | Override the repository to fetch cache from (e.g., `--repo=leanprover-community/mathlib4`) |
+| `--cache-from=LIST` | For `get`/`get!`/`get-`/`lookup`: trust-ordered, comma-separated list of containers to read from. Overrides the per-repo default (see [Trust-ordered containers](#trust-ordered-containers)). |
+| `--scope=REF` | For `get`/`get!`/`get-`: read from the SHA-scoped namespace for the given git ref (anything `git rev-parse` accepts: `HEAD`, branch, tag, SHA). Use the SHA reported by `cache query`. Triggers the non-default-scope security notice. |
+| `--unsafe` | For `get`/`get!`/`get-`: instead of pinning one `--scope`, automatically walk this branch's history and read the `forks` container at the most recent cached fork commit (newest first if `--unsafe-window` allows more than one), until the cache is satisfied (see [Unsafe automatic scope walk](#unsafe-automatic-scope-walk)). Mutually exclusive with `--scope`; always triggers the security notice. |
+| `--unsafe-window=N` | Number of cached fork commits `--unsafe` will try (default `1`). Implies `--unsafe`. |
+| `--container=NAME` | For `put`/`put!`/`put-unpacked`/`put-staged`/`commit`/`commit!`: target container for upload. |
-## Environment Variables
+Container names (known to both flags): `master`, `forks`, `nightly-testing`, `pr-toolchain-tests`, `legacy`.
-### Cache Location
+## Trust-ordered containers
-| Variable | Description | Default |
-|---------------------|------------------------------------|-------------------------------------------------|
-| `MATHLIB_CACHE_DIR` | Directory for cached `.ltar` files | `$XDG_CACHE_HOME/mathlib` or `~/.cache/mathlib` |
+The cache is split across multiple Azure Blob Storage containers on the
+`lakecache` storage account. Container names accepted by `--container=NAME`
+and `--cache-from=LIST`: `master`, `forks`, `nightly-testing`,
+`pr-toolchain-tests`, `legacy`.
-### Cache Backend Selection
+`cache get` resolves a file by trying a default chain of containers in
+order, depending on the repo:
-| Variable | Description | Default |
-|--------------------------------|----------------------------------------------------------|-------------|
-| `MATHLIB_CACHE_USE_CLOUDFLARE` | Set to `1` or `true` to use Cloudflare R2 instead of Azure | Azure cache |
+| GitHub repo | Container order tried |
+|-------------------------------------------------|-----------------------------|
+| `leanprover-community/mathlib4` | `master`, `legacy` |
+| `leanprover-community/mathlib4-nightly-testing` | `nightly-testing`, `legacy` |
+| any fork (PRs) | `master`, `forks`, `legacy` |
-### Custom Cache URLs
+Override the read chain with `--cache-from=LIST`:
-These allow overriding the cache endpoints, useful for mirrors or custom deployments:
+```bash
+# Read only from the master container
+lake exe cache get --cache-from=master
-| Variable | Description | Default |
-|-------------------------|---------------------------------|-------------------------------------------------------------------|
-| `MATHLIB_CACHE_GET_URL` | URL for downloading cache files | Azure or Cloudflare URL based on `MATHLIB_CACHE_USE_CLOUDFLARE` |
-| `MATHLIB_CACHE_PUT_URL` | URL for uploading cache files | Azure or Cloudflare URL based on `MATHLIB_CACHE_USE_CLOUDFLARE` |
+# Read master first, then forks
+lake exe cache get --cache-from=master,forks
+```
-### Authentication (for uploads)
+Uploads target a single container via `--container=NAME`.
-| Variable | Description |
-|--------------------------|------------------------------------------------|
-| `MATHLIB_CACHE_AZURE_BEARER_TOKEN` | Azure bearer token (preferred for Azure backend) |
-| `MATHLIB_CACHE_SAS` | Azure SAS token fallback (for Azure backend) |
-| `MATHLIB_CACHE_S3_TOKEN` | S3 credentials (when using Cloudflare backend) |
+## Environment Variables
+
+| Variable | Description | Default |
+|---------------------|------------------------------------|-------------------------------------------------|
+| `MATHLIB_CACHE_DIR` | Directory for cached `.ltar` files | `$XDG_CACHE_HOME/mathlib` or `~/.cache/mathlib` |
## How It Works
@@ -140,92 +154,134 @@ The cache covers these packages:
- `Archive`
- `Counterexamples`
-## Default Cache Backends
+## Finding Cached Commits with `query`
-### Azure Blob Storage (Default)
+For branches with per-commit SHA scoping (e.g., fork PRs), you can use
+`lake exe cache query` to discover which recent commits on your branch have
+cached entries. This is useful when your current branch has diverged from
+upstream and you want to avoid waiting for CI to build everything.
-- **Download URL**: `https://lakecache.blob.core.windows.net/mathlib4`
-- Used by default for downloads and uploads
-
-### Cloudflare R2
+```bash
+# Find the most recent cached commit on the current branch
+lake exe cache query
+
+# Example output:
+# Most recent cached commit on branch: 5a3c7e9a2f8c1d6b4e0f9a2c3d4e5f6a7b8c9d0e
+# Repository: leanprover-community/mathlib4
+# Container: forks
+#
+# To use this cache, run:
+# lake exe cache get --scope=5a3c7e9a2f8c1d6b4e0f9a2c3d4e5f6a7b8c9d0e
+```
-- **Download URL**: `https://mathlib4.lean-cache.cloud`
-- **Upload URL**: `https://a09a7664adc082e00f294ac190827820.r2.cloudflarestorage.com/mathlib4`
-- Enable with `MATHLIB_CACHE_USE_CLOUDFLARE=1`
+The `query` command walks your git log backwards from `HEAD`, stopping at the
+merge base with `master` or a hard cap of 50 commits (whichever comes first),
+and probes each commit for a completed SHA-scoped upload in the `forks`
+container. That signal is written by `put-staged` only after a successful
+upload, so its presence is a reliable "this commit was cached" signal. `query`
+prints the SHA to stdout (and does not auto-apply it) — you manually copy the
+result into your `cache get` command if desired.
-## Setting Up Your Own Cache Endpoint
+### Boolean probe on a single commit
-You can host your own cache mirror or private cache using any S3-compatible storage or HTTP server.
+`lake exe cache query [` checks a specific commit and exits with 0 (cached)
+or 1 (not cached). The ref can be `HEAD`, a branch name, a tag, or a SHA — anything
+`git rev-parse` accepts.
-### Requirements
+```bash
+# Is the current checkout's HEAD cached?
+lake exe cache query HEAD && echo "yes" || echo "no"
-Your endpoint must support:
+# Is a specific SHA cached?
+lake exe cache query 5a3c7e9a2f8c1d6b4e0f9a2c3d4e5f6a7b8c9d0e
+# prints "cached: 5a3c7e9a..." (exit 0) or "not cached: 5a3c7e9a..." (exit 1)
+```
-1. **GET requests** for downloading files at:
- - `/f/{repo}/{hash}.ltar` - for fork caches
- - `/f/{hash}.ltar` - for main mathlib cache (Azure only)
- - `/c/{commit_hash}` - for commit manifests
+By default `query` (both modes) targets the cwd's git remote — pass `--repo=`
+to override.
-2. **PUT requests** for uploading (if you need upload capability)
+### Unsafe automatic scope walk
-### Using a Custom Endpoint
+`cache get --unsafe` folds the `query` discovery into the download itself: rather
+than asking you to copy one SHA into `--scope=`, it walks your branch history
+(`HEAD` back to the merge base with `master`) for commits that have a cached fork
+build and reads the `forks` container at their scope. By
+default it uses just the single most recent such commit; `--unsafe-window=N`
+widens this to the `N` most recent, tried newest first with files fetched in one
+round dropped from the next.
```bash
-# Download from a custom mirror
-export MATHLIB_CACHE_GET_URL="https://my-mirror.example.com/mathlib4"
-lake exe cache get
-
-# Upload to a custom endpoint
-export MATHLIB_CACHE_PUT_URL="https://my-upload.example.com/mathlib4"
-export MATHLIB_CACHE_AZURE_BEARER_TOKEN="your-bearer-token" # preferred for Azure
-# export MATHLIB_CACHE_SAS="your-sas-token" # Azure fallback
-# export MATHLIB_CACHE_S3_TOKEN="ACCESS_KEY:SECRET_KEY" # for S3/Cloudflare
-lake exe cache put
+lake exe cache get --unsafe # use the most recent cached fork commit
+lake exe cache get --unsafe-window=10 # try the 10 most recent (implies --unsafe)
```
-### Example: S3-Compatible Storage
+The trust-ordered container chain is unchanged: `master` is still tried first and
+serves the bulk of every fork's files by hash; only the `forks` round is expanded
+into one round per discovered SHA. If no cached fork commit is found in range,
+`--unsafe` falls back to a plain unscoped read.
-For S3-compatible storage (MinIO, Cloudflare R2, AWS S3, etc.):
+`--unsafe` trusts the artifacts of *every* commit it tries, so it always prints
+the [non-default-scope security notice](#security-warning-non-default-scope). It
+is mutually exclusive with `--scope=` (which pins exactly one commit).
-1. Create a bucket (e.g., `mathlib-cache`)
-2. Configure public read access for downloads (or use signed URLs)
-3. Set up authentication for uploads
-4. Set the environment variables:
+### Heads-up note from `cache get`
-```bash
-export MATHLIB_CACHE_GET_URL="https://your-bucket.s3.region.amazonaws.com/mathlib-cache"
-export MATHLIB_CACHE_PUT_URL="https://your-bucket.s3.region.amazonaws.com/mathlib-cache"
-export MATHLIB_CACHE_USE_CLOUDFLARE=1 # Use S3-style auth
-export MATHLIB_CACHE_S3_TOKEN="ACCESS_KEY:SECRET_KEY"
-```
+When you run `cache get` on a fork-trust repo and HEAD has not been built and
+cached at fork-trust level, the tool prints a stderr note pointing you at
+`cache query` (and warning that picking a different commit means trusting its
+artifacts). Costs one HTTP HEAD per `cache get` invocation; only fires when the
+resolved repo's default chain includes `forks` and no `--scope=` / `--cache-from`
+override is supplied.
-### Example: Simple HTTP Mirror
+## Security Warning: Non-Default Scope
-For a read-only mirror using nginx or any static file server:
+When you read cache artifacts at a non-default scope, the cache tool prints a
+security warning to stderr. This happens when:
-1. Periodically sync files from the official cache
-2. Serve them at a public URL
-3. Point users to your mirror:
+1. **`--unsafe` is passed** — you are letting the tool walk history and trust the
+ artifacts of whichever recent fork commit(s) it finds cached.
+2. **`--scope=` is passed** — you are reading from a specific commit's
+ namespace instead of the repo's default trust chain.
+3. **`--cache-from` widens the read chain** — you are explicitly telling the tool
+ to trust containers beyond the repo default.
+4. **`--repo` overrides the detected git remote** — you are reading cache for a
+ different repository than your cwd's git remote.
+
+Example warning:
-```bash
-export MATHLIB_CACHE_GET_URL="https://mathlib-mirror.myorg.com"
-lake exe cache get
+```
+=================================================================
+SECURITY: reading cache at a non-default scope
+=================================================================
+You are reading cache artifacts at a scope outside the default trust
+boundary for this repo. The cache cannot verify the contents of these
+artifacts; you are choosing to trust whoever uploaded them.
+
+Repository: leanprover-community/mathlib4
+Reason: --scope=5a3c7e9a2f8c1d6b4e0f9a2c3d4e5f6a7b8c9d0e (explicit per-commit scope)
+=================================================================
```
-### URL Structure
+This warning is always printed — it cannot be suppressed with `--quiet`. The
+warning is purely informational; it does not prompt for confirmation (so it
+doesn't interfere with CI).
-The cache uses this URL pattern:
+## Tests
+The cache tool's pure logic (container URL construction, per-repo allowlist,
+CLI parsing) is covered by a standalone test exe:
+
+```bash
+lake exe cache-test
```
-{BASE_URL}/f/{repo}/{filename}.ltar # Fork/branch caches
-{BASE_URL}/f/{filename}.ltar # Main mathlib cache (Azure)
-{BASE_URL}/c/{commit_hash} # Commit manifests
-```
-Where:
-- `{repo}` is like `leanprover-community/mathlib4` or `username/mathlib4`
-- `{filename}` is a hash like `1234567890abcdef`
-- `{commit_hash}` is a git commit SHA
+The exe builds only `Cache.*` and its direct deps — it does not require
+Mathlib or `MathlibTest`. Exits 0 on success, non-zero on failure.
+
+> A Lake package has a single `testDriver`, which the enclosing `mathlib`
+> package already binds to `MathlibTest`. If the cache tool ever moves to
+> its own Lake project, the `cache-test` exe can be promoted to that
+> project's `testDriver` so `lake test` invokes it directly.
## Dependencies
diff --git a/Cache/Requests.lean b/Cache/Requests.lean
index cca1dc42314b1c..2b766e6fd7ba47 100644
--- a/Cache/Requests.lean
+++ b/Cache/Requests.lean
@@ -1,28 +1,23 @@
/-
Copyright (c) 2023 Arthur Paulino. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
-Authors: Arthur Paulino
+Authors: Arthur Paulino, Marcelo Lynch
-/
-import Batteries.Data.String.Matcher
import Cache.Hashing
-import Cache.Init
+import Cache.Infra
import Lake.Load.Manifest
namespace Cache.Requests
open System (FilePath)
-/-- The full name of the main Mathlib GitHub repository. -/
-def MATHLIBREPO := "leanprover-community/mathlib4"
-
/--
-Structure to hold repository information with priority ordering
+Resolved repository identity for cache lookups.
-/
structure RepoInfo where
repo : String
- useFirst : Bool
- deriving Repr
+ deriving Repr, BEq
/--
Helper function to extract repository name from a git remote URL
@@ -147,11 +142,10 @@ def isDetachedAtNightlyTesting (mathlibDepPath : FilePath) : IO Bool := do
return false
/--
-Attempts to determine the GitHub repository of a version of Mathlib from its Git remote.
-If the current commit coincides with a PR ref, it will determine the source fork
-of that PR rather than just using the origin remote.
+Inner implementation: may throw if git is unavailable or the directory has no
+git checkout. Callers should use `getRemoteRepo` instead.
-/
-def getRemoteRepo (mathlibDepPath : FilePath) : IO (Option RepoInfo) := do
+private def getRemoteRepoImpl (mathlibDepPath : FilePath) : IO (Option RepoInfo) := do
-- Since currently we need to push a PR to `leanprover-community/mathlib` build a user cache,
-- we check if we are a special branch or a branch with PR. This leaves out non-PRed fork
@@ -179,9 +173,8 @@ def getRemoteRepo (mathlibDepPath : FilePath) : IO (Option RepoInfo) := do
if shouldUseNightlyTesting then
let repo := "leanprover-community/mathlib4-nightly-testing"
- let cacheService := if useCloudflareCache then "Cloudflare" else "Azure"
- IO.println s!"Using cache ({cacheService}) from nightly-testing remote: {repo}"
- return some {repo := repo, useFirst := true}
+ IO.println s!"Using cache from nightly-testing remote: {repo}"
+ return some {repo := repo}
-- Only search for PR refs if we're not on a regular branch like master, bump/*, or nightly-testing*
-- let isSpecialBranch := branchName == "master" || branchName.startsWith "bump/" ||
@@ -241,63 +234,164 @@ def getRemoteRepo (mathlibDepPath : FilePath) : IO (Option RepoInfo) := do
let repo? ← getRepoFromRemote mathlibDepPath remoteName
s!"Ensure Git is installed and the '{remoteName}' remote points to its GitHub repository."
- let cacheService := if useCloudflareCache then "Cloudflare" else "Azure"
match repo? with
| some repo =>
- IO.println s!"Using cache ({cacheService}) from {remoteName}: {repo?}"
- return some {repo := repo, useFirst := false}
+ IO.println s!"Using cache from {remoteName}: {repo?}"
+ return some {repo := repo}
| none =>
- IO.println s!"Using cache ({cacheService}) from {MATHLIBREPO}."
+ IO.println s!"Using cache from {MATHLIBREPO}."
return none
-/-- Public URL for mathlib cache -/
-initialize URL : String ← do
- let url? ← IO.getEnv "MATHLIB_CACHE_GET_URL"
- let defaultUrl :=
- if useCloudflareCache then
- "https://mathlib4.lean-cache.cloud"
- else
- "https://lakecache.blob.core.windows.net/mathlib4"
- return url?.getD defaultUrl
+/--
+Attempts to determine the GitHub repository of a version of Mathlib from its Git remote.
+If the current commit coincides with a PR ref, it will determine the source fork
+of that PR rather than just using the origin remote.
+
+Returns `none` if git is unavailable, the path is not inside a git checkout, or
+the remote cannot be resolved. This is the expected outcome when `cache get` is
+invoked on a dependency that was fetched as an archive rather than a git clone;
+callers fall back to `MATHLIBREPO` and the master container.
+-/
+def getRemoteRepo (mathlibDepPath : FilePath) : IO (Option RepoInfo) := do
+ try
+ return (← getRemoteRepoImpl mathlibDepPath)
+ catch _ =>
+ return none
+
+/--
+Resolve the GitHub repo for cache reads from a single `getRemoteRepo` probe.
+
+Returns `(detectedRepo?, resolvedRepo)`:
+* `detectedRepo?` is what the git remote reports (`none` if it can't be
+ determined); the warning path compares it against an explicit `--repo=` to
+ tell whether the user is overriding the checkout's repo.
+* `resolvedRepo` applies the override precedence `--repo=` > git remote >
+ `MATHLIBREPO`, and is what the read path uses.
+
+`getRemoteRepo` shells out to git and prints branch/remote diagnostics;
+resolving here lets the read path, the warning, and the HEAD hint share a
+single probe keyed on `mathlibDepPath`.
+-/
+def resolveRepo (repo? : Option String) (mathlibDepPath : FilePath) :
+ IO (Option String × String) := do
+ let detected? := (← getRemoteRepo mathlibDepPath).map (·.repo)
+ return (detected?, repo?.getD (detected?.getD MATHLIBREPO))
+
+/--
+Process-wide override for the container fallback list, set by the `--cache-from`
+CLI flag. When `none`, downloads use `defaultContainersForRepo`; when `some cs`,
+all repos use `cs` instead.
+-/
+initialize cacheFromOverride : IO.Ref (Option (List Container)) ← IO.mkRef none
+
+/--
+Compute the trust-ordered list of container base URLs to try when downloading
+files for a given GitHub repo.
+
+Precedence (most specific wins):
+1. `MATHLIB_CACHE_GET_URL` env var: a single anonymous URL that bypasses the
+ container logic entirely.
+2. `--cache-from` CLI override (via `cacheFromOverride`).
+3. `MATHLIB_CACHE_FROM` env var (same comma-separated shape as `--cache-from`):
+ how CI widens the lookup chain to match its write target without touching
+ each `cache get` call. The (repo, branch) → chain mapping lives in CI config,
+ not here.
+4. `defaultContainersForRepo repo`: the repo-level fallback when nothing
+ overrides it.
+-/
+def effectiveGetURLs (repo : String) : IO (List (Option Container × String)) := do
+ if let some url ← IO.getEnv "MATHLIB_CACHE_GET_URL" then
+ return [(none, url)]
+ if let some cliOverride ← cacheFromOverride.get then
+ return cliOverride.map fun c => (some c, c.azureURL)
+ let envOverride? ← do
+ match (← IO.getEnv "MATHLIB_CACHE_FROM") with
+ | none => pure none
+ | some s =>
+ match parseCacheFromList s with
+ | some cs => pure (some cs)
+ | none =>
+ IO.eprintln s!"Warning: ignoring MATHLIB_CACHE_FROM={s} \
+ (unrecognized container name). Known containers: \
+ {", ".intercalate (Container.all.map Container.name)}."
+ pure none
+ let containers := envOverride?.getD (defaultContainersForRepo repo)
+ return containers.map fun c => (some c, c.azureURL)
/-- Authentication method used for cache upload operations. -/
inductive UploadAuth where
- | cloudflareS3 (token : String)
| azureSas (token : String)
| azureBearer (token : String)
/-- Retrieves upload credentials from the environment. -/
def getUploadAuth : IO UploadAuth := do
- if useCloudflareCache then
- let envVar := "MATHLIB_CACHE_S3_TOKEN"
- let some token ← IO.getEnv envVar
- | throw <| IO.userError s!"environment variable {envVar} must be set to upload caches"
- return .cloudflareS3 token
- else
- if let some token ← IO.getEnv "MATHLIB_CACHE_AZURE_BEARER_TOKEN" then
- let token := token.trimAscii.copy
- if !token.isEmpty then
- return .azureBearer token
- if let some token ← IO.getEnv "MATHLIB_CACHE_SAS" then
- let token := token.trimAscii.copy
- if !token.isEmpty then
- return .azureSas token
- throw <| IO.userError
- "environment variable MATHLIB_CACHE_AZURE_BEARER_TOKEN or MATHLIB_CACHE_SAS must be set to upload caches"
+ if let some token ← IO.getEnv "MATHLIB_CACHE_AZURE_BEARER_TOKEN" then
+ let token := token.trimAscii.copy
+ if !token.isEmpty then
+ return .azureBearer token
+ if let some token ← IO.getEnv "MATHLIB_CACHE_SAS" then
+ let token := token.trimAscii.copy
+ if !token.isEmpty then
+ return .azureSas token
+ throw <| IO.userError
+ "environment variable MATHLIB_CACHE_AZURE_BEARER_TOKEN or MATHLIB_CACHE_SAS must be set to upload caches"
/--
-Given a file name like `"1234.tar.gz"`, makes the URL to that file on the server.
+Construct the URL for the cache file `fileName` in repo `repo`, against the
+container reachable at `containerURL`.
+
+The `f/` prefix marks files (commits use `c/`). Whether the rest of the path is
+flat (`/f/`) or repo-namespaced (`/f//`) follows the
+container (see `Container.flatPath`), not the repo: the same hash under
+`repo = MATHLIBREPO` lands flat in `master` and prefixed in `forks`.
-The `f/` prefix means that it's a common file for caching.
+`container` is `none` for the user-supplied `MATHLIB_CACHE_GET_URL` /
+`MATHLIB_CACHE_PUT_URL` URLs, where no container policy applies; the path then
+follows the repo directly — flat for `MATHLIBREPO`, prefixed otherwise.
+-/
+def mkFileURL (container : Option Container) (repo containerURL fileName : String)
+ (repoScope : Option String := none) : String :=
+ let flat := match container with
+ | some c => c.flatPath repo
+ | none => repo == MATHLIBREPO
+ let pre := if flat then ""
+ else match repoScope with
+ | some s => s!"{repo}/{s}/"
+ | none => s!"{repo}/"
+ s!"{containerURL}/f/{pre}{fileName}"
+
+/--
+Process-wide override for the per-SHA scope, set by the `--scope=` CLI flag.
+When set, it wins over `MATHLIB_CACHE_REPO_SCOPE`.
-/
-def mkFileURL (repo URL fileName : String) : String :=
- let pre := if !useCloudflareCache && repo == MATHLIBREPO then "" else s!"{repo}/"
- s!"{URL}/f/{pre}{fileName}"
+initialize scopeOverride : IO.Ref (Option String) ← IO.mkRef none
+
+/--
+Resolved repo-scope SHA. Precedence: `--scope=` flag > `MATHLIB_CACHE_REPO_SCOPE`
+env var > `none`. Both sources mean "the user has explicitly opted into a
+SHA-scoped read"; the non-default-scope warning fires for either.
+-/
+def getRepoScope : IO (Option String) := do
+ if let some s ← scopeOverride.get then
+ return some s
+ let s? ← IO.getEnv "MATHLIB_CACHE_REPO_SCOPE"
+ match s? with
+ | some s =>
+ let trimmed := s.trimAscii.toString
+ pure (if trimmed.isEmpty then none else some trimmed)
+ | none => pure none
+
+def getGitCommitHash : IO String :=
+ return (← IO.runCmd "git" #["rev-parse", "HEAD"]).trimAsciiEnd.copy
section Get
-/-- Formats the config file for `curl`, containing the list of files to be downloaded -/
-def mkGetConfigContent (repo : String) (hashMap : IO.ModuleHashMap) : IO String := do
+/-- Formats the config file for `curl`, containing the list of files to be downloaded
+from a single container's base URL. `scope?` is the per-round SHA scope (see
+`mkFileURL`); it is the resolved `getRepoScope` for a normal read and an
+individual walked SHA for an `--unsafe` forks round. -/
+def mkGetConfigContent (container : Option Container) (repo containerURL : String)
+ (hashMap : IO.ModuleHashMap) (scope? : Option String) : IO String := do
hashMap.toArray.foldlM (init := "") fun acc ⟨_, hash⟩ => do
let fileName := hash.asLTar
-- Below we use `String.quote`, which is intended for quoting for use in Lean code
@@ -313,13 +407,16 @@ def mkGetConfigContent (repo : String) (hashMap : IO.ModuleHashMap) : IO String
-- Note we append a '.part' to the filenames here,
-- which `downloadFiles` then removes when the download is successful.
- pure <| acc ++ s!"url = {mkFileURL repo URL fileName}\n\
+ pure <| acc ++ s!"url = {mkFileURL container repo containerURL fileName scope?}\n\
-o {(IO.CACHEDIR / (fileName ++ ".part")).toString.quote}\n"
-/-- Calls `curl` to download a single file from the server to `CACHEDIR` (`.cache`) -/
-def downloadFile (repo : String) (hash : UInt64) : IO Bool := do
+/-- Calls `curl` to download a single file from a specific container to `CACHEDIR`
+(`.cache`). Returns `true` on success, `false` on any error including 404.
+`scope?` is the per-round SHA scope (see `mkGetConfigContent`). -/
+def downloadFile (container : Option Container) (repo containerURL : String)
+ (hash : UInt64) (scope? : Option String) : IO Bool := do
let fileName := hash.asLTar
- let url := mkFileURL repo URL fileName
+ let url := mkFileURL container repo containerURL fileName scope?
let path := IO.CACHEDIR / fileName
let partFileName := fileName ++ ".part"
let partPath := IO.CACHEDIR / partFileName
@@ -397,9 +494,36 @@ def dispatchDecompBatch (pending : Array (FilePath × Lean.Name)) (config : Deco
let task ← IO.asTask (decompressBatch pending config.force config.isMathlibRoot config.mathlibDepPath)
return some task
+/--
+Whether an HTTP status returned for a single-file read should be treated as a
+cache miss (fall through to the next container in the chain) rather than a
+transfer failure worth reporting.
+
+`404` is always a miss. A `403` is a miss only when `treatForbiddenAsMiss` is
+set, which callers do for the `legacy` container: when its public read access is
+revoked ahead of retirement it answers reads with `403`, and old clients whose
+chain still lists `legacy` should fall through quietly instead of printing a
+per-file transfer failure. Any other status is a real failure.
+-/
+def isCacheMissStatus (httpCode : Nat) (treatForbiddenAsMiss : Bool) : Bool :=
+ httpCode == 404 || (httpCode == 403 && treatForbiddenAsMiss)
+
+/--
+Whether an HTTP status is the one Azure returns for a blob that already exists,
+which a non-overwrite `put` (`If-None-Match: *`) hits when it declines to
+overwrite. Azure reports it as 409 (the `BlobAlreadyExists` error, what it
+returns in practice) or 412 (the conditional-header spec's code for an unmet
+`If-None-Match`), so we accept both. Whether that's benign is the caller's call:
+the upload path skips it, reads don't.
+-/
+def isAlreadyPresentStatus (httpCode : Nat) : Bool :=
+ httpCode == 409 || httpCode == 412
+
def monitorCurl (args : Array String) (size : Nat)
(caption : String) (speedVar : String) (removeOnError := false)
- (decompConfig : Option DecompConfig := none) : IO TransferState := do
+ (decompConfig : Option DecompConfig := none)
+ (treatForbiddenAsMiss : Bool := false)
+ (treatExistsAsSkip : Bool := false) : IO TransferState := do
let useAnsi := (← IO.getEnv "TERM").isSome
let mkStatus (s : TransferState) : String := Id.run do
let speedStr :=
@@ -463,26 +587,35 @@ def monitorCurl (args : Array String) (size : Nat)
currentTask ← dispatchDecompBatch pending config
pending := #[]
success := success + 1
- | .ok 404 => pure ()
+ -- A cache miss (404, or 403 from a retiring `legacy`) just falls through
+ -- to the next container; a blob already on the server (409/412 from a
+ -- non-overwrite put) is expected, not a failure; anything else fails.
| code? =>
- failed := failed + 1
- let mkFailureMsg code? fn? msg? : String := Id.run do
- let mut msg := "Transfer failed"
+ let alreadyPresent := match code? with
+ | .ok c => isAlreadyPresentStatus c
+ | .error _ => false
+ let isMiss := match code? with
+ | .ok c => isCacheMissStatus c treatForbiddenAsMiss
+ | .error _ => false
+ unless isMiss || (treatExistsAsSkip && alreadyPresent) do
+ failed := failed + 1
+ let mkFailureMsg code? fn? msg? : String := Id.run do
+ let mut msg := "Transfer failed"
+ if let .ok fn := fn? then
+ msg := s!"{fn}: {msg}"
+ if let .ok code := code? then
+ msg := s!"{msg} (error code: {code})"
+ if let .ok errMsg := msg? then
+ msg := s!"{msg}: {errMsg}"
+ return msg
+ let msg? := result.getObjValAs? String "errormsg"
+ let fn? := result.getObjValAs? String "filename_effective"
+ IO.println (mkFailureMsg code? fn? msg?)
if let .ok fn := fn? then
- msg := s!"{fn}: {msg}"
- if let .ok code := code? then
- msg := s!"{msg} (error code: {code})"
- if let .ok errMsg := msg? then
- msg := s!"{msg}: {errMsg}"
- return msg
- let msg? := result.getObjValAs? String "errormsg"
- let fn? := result.getObjValAs? String "filename_effective"
- IO.println (mkFailureMsg code? fn? msg?)
- if let .ok fn := fn? then
- if removeOnError then
- -- `curl --remove-on-error` can already do this, but only from 7.83 onwards
- if (← System.FilePath.pathExists fn) then
- IO.FS.removeFile fn
+ if removeOnError then
+ -- `curl --remove-on-error` can already do this, but only from 7.83 onwards
+ if (← System.FilePath.pathExists fn) then
+ IO.FS.removeFile fn
done := done + 1
let now ← IO.monoMsNow
if now - last ≥ 100 then -- max 10/s update rate
@@ -498,55 +631,162 @@ def monitorCurl (args : Array String) (size : Nat)
IO.eprintln (mkStatus s)
return s
+/-- Run one container's download pass for the given hash map. Returns the
+`TransferState` produced by `monitorCurl` (or a synthesized empty state in
+serial mode). Side effect: any files successfully fetched are written to
+`CACHEDIR` with their final names. -/
+private def downloadFilesFromContainer
+ (container : Option Container) (repo containerURL : String)
+ (hashMap : IO.ModuleHashMap)
+ (parallel : Bool) (decompConfig : Option DecompConfig)
+ (scope? : Option String) :
+ IO (Nat × TransferState) := do
+ let size := hashMap.size
+ if parallel then
+ IO.FS.writeFile IO.CURLCFG (← mkGetConfigContent container repo containerURL hashMap scope?)
+ let args := #["--request", "GET", "--parallel",
+ -- commented as this creates a big slowdown on curl 8.13.0: "--fail",
+ "--silent",
+ "--retry", "5", -- there seem to be some intermittent failures
+ "--write-out", "%{json}\n", "--config", IO.CURLCFG.toString]
+ -- `legacy` answers reads with 403 once its public access is revoked ahead
+ -- of retirement; treat that as a miss so the chain stays quiet for clients
+ -- whose chain still lists it.
+ let treatForbiddenAsMiss := container == some Container.legacy
+ let s ← monitorCurl args size "Downloaded" "speed_download" (removeOnError := true)
+ decompConfig (treatForbiddenAsMiss := treatForbiddenAsMiss)
+ IO.FS.removeFile IO.CURLCFG
+ return (s.failed, s)
+ else
+ let r ← hashMap.foldM (init := []) fun acc _ hash => do
+ pure <| (← IO.asTask do downloadFile container repo containerURL hash scope?) :: acc
+ let failed := r.foldl (init := 0) fun f t => if let .ok true := t.get then f else f + 1
+ let emptyState : TransferState := ⟨0, 0, 0, 0, 0, #[], none, 0, 0, 0⟩
+ return (failed, emptyState)
+
+/-- Expand the trust-ordered container list into the concrete download rounds to
+run, each carrying the SHA scope to read at. A round is
+`(container?, url, scope?)`.
+
+Without `--unsafe` (`unsafeScopes` empty) every round uses the single resolved
+`scope?`: one round per container, all at the same scope. When no explicit
+scope is given, `headScope?` (the checked-out HEAD, resolved by the caller)
+applies to the `forks` round only: fork uploads live under the per-commit
+namespace, so this is what lets a plain `cache get` retrieve what CI built for
+exactly the commit the reader has checked out. The other containers' layouts
+are not SHA-scoped, so `headScope?` must not leak into their rounds.
+
+With `--unsafe` (`unsafeScopes` non-empty) the `forks` container — the only
+SHA-scoped container, whose markers the walk probed — is expanded into one round
+per discovered SHA, most recent first. Every other container reads unscoped
+(`master` is flat and serves the bulk of files by hash; `legacy` has no walked
+markers), so the base `scope?` is intentionally dropped here. -/
+def expandDownloadRounds (containerURLs : List (Option Container × String))
+ (scope? : Option String) (unsafeScopes : List String)
+ (headScope? : Option String := none) :
+ List (Option Container × String × Option String) :=
+ if unsafeScopes.isEmpty then
+ containerURLs.map fun (c, url) =>
+ if c == some Container.forks then (c, url, scope? <|> headScope?)
+ else (c, url, scope?)
+ else
+ containerURLs.flatMap fun (c, url) =>
+ if c == some Container.forks then
+ unsafeScopes.map fun sha => (c, url, some sha)
+ else
+ [(c, url, none)]
+
/-- Call `curl` to download files from the server to `CACHEDIR` (`.cache`).
Return the number of files which failed to download.
-If `decompress` is true, decompresses files as they're downloaded (pipelined). -/
+If `decompress` is true, decompresses files as they're downloaded (pipelined).
+
+For each repo, the tool tries the trust-ordered container list returned by
+`effectiveGetURLs`. After each container round, files that were successfully
+fetched are filtered out so the next container only retries genuine misses.
+
+`unsafeScopes` is the list of SHA scopes discovered by `cache get --unsafe`
+(empty for a normal read); see `expandDownloadRounds`. -/
def downloadFiles
(repo : String) (hashMap : IO.ModuleHashMap)
(forceDownload : Bool) (parallel : Bool) (warnOnMissing : Bool)
(decompress : Bool := false) (forceUnpack : Bool := false)
- (isMathlibRoot : Bool := false) (mathlibDepPath : FilePath := ".") : IO Nat := do
+ (isMathlibRoot : Bool := false) (mathlibDepPath : FilePath := ".")
+ (unsafeScopes : List String := []) : IO Nat := do
let hashMap ← if forceDownload then pure hashMap else hashMap.filterExists false
if hashMap.isEmpty then IO.println "No files to download"; return 0
- let size := hashMap.size
IO.FS.createDirAll IO.CACHEDIR
- IO.println s!"Attempting to download {size} file(s) from {repo} cache"
- -- Set up decompression config if enabled
+ let containerURLs ← effectiveGetURLs repo
+ if containerURLs.isEmpty then
+ IO.eprintln "No container URLs configured for download"
+ return hashMap.size
+
+ -- Set up decompression config if enabled. We keep one config across all
+ -- container rounds so pipelined decompression continues across them.
let decompConfig ← if decompress then
- -- Build hash → module name mapping
let hashToMod : Std.HashMap UInt64 Lean.Name := hashMap.fold (init := ∅) fun acc mod hash =>
acc.insert hash mod
pure (some { hashToMod, force := forceUnpack, isMathlibRoot, mathlibDepPath : DecompConfig })
else
pure none
- let (downloadFailed, finalState) ← if parallel then
- IO.FS.writeFile IO.CURLCFG (← mkGetConfigContent repo hashMap)
- let args := #["--request", "GET", "--parallel",
- -- commented as this creates a big slowdown on curl 8.13.0: "--fail",
- "--silent",
- "--retry", "5", -- there seem to be some intermittent failures
- "--write-out", "%{json}\n", "--config", IO.CURLCFG.toString]
- let s ← monitorCurl args size "Downloaded" "speed_download" (removeOnError := true) decompConfig
- IO.FS.removeFile IO.CURLCFG
- if warnOnMissing && s.success + s.failed < s.done then
- IO.eprintln "Warning: some files were not found in the cache."
- IO.eprintln "This usually means that your local checkout of mathlib4 has diverged from upstream."
- IO.eprintln ""
- IO.eprintln " * If you push your commits to a PR to the mathlib4 repository"
- IO.eprintln " (use a draft PR if it is not ready for review),"
- IO.eprintln " then CI will build the oleans and they will be available later."
- IO.eprintln " * If you have already opened a PR, this may mean"
- IO.eprintln " the CI build has failed part-way through building."
- pure (s.failed, s)
- else
- let r ← hashMap.foldM (init := []) fun acc _ hash => do
- pure <| (← IO.asTask do downloadFile repo hash) :: acc
- let failed := r.foldl (init := 0) fun f t => if let .ok true := t.get then f else f + 1
- -- Non-parallel mode doesn't support pipelined decompression
- let emptyState : TransferState := ⟨0, 0, 0, 0, 0, #[], none, 0, 0, 0⟩
- pure (failed, emptyState)
+ -- Walk container URLs in trust order. After each round, drop files that
+ -- succeeded so the next round only retries genuine misses. With `--unsafe`
+ -- the `forks` container is expanded into one round per discovered SHA scope.
+ let scope? ← getRepoScope
+ -- With no explicit scope, the forks round defaults to HEAD: `cache get` on a
+ -- checked-out commit retrieves what CI built for exactly that commit, fork
+ -- included. This adds no trust over an unscoped forks read — the namespace
+ -- can only hold artifacts built from the commit the reader already has.
+ let headScope? ← if scope?.isNone && unsafeScopes.isEmpty then
+ try pure (some (← getGitCommitHash)) catch _ => pure none
+ else pure none
+ let rounds := expandDownloadRounds containerURLs scope? unsafeScopes headScope?
+ let unsafeMode := !unsafeScopes.isEmpty
+ let mut remaining := hashMap
+ let mut finalState : TransferState := ⟨0, 0, 0, 0, 0, #[], none, 0, 0, 0⟩
+ let mut downloadFailed := 0
+ -- For the `--unsafe` summary: how many files each scoped (forks) round supplied,
+ -- attributed by the drop in `remaining` across that round.
+ let mut scopeServed : Array (String × Nat) := #[]
+ for (container?, url, roundScope?) in rounds do
+ if remaining.isEmpty then break
+ let scopeNote := match roundScope? with | some s => s!" (scope {s})" | none => ""
+ IO.println s!"Attempting to download {remaining.size} file(s) from {repo} cache at {url}{scopeNote}"
+ let before := remaining.size
+ let (failed, s) ← downloadFilesFromContainer container? repo url remaining parallel decompConfig roundScope?
+ -- Carry forward the decompression-related state across container rounds.
+ -- Counter fields (success/failed/done) reflect only the last round; we
+ -- aggregate `downloadFailed` separately below.
+ finalState := s
+ downloadFailed := failed
+ remaining ← remaining.filterExists false
+ if unsafeMode then
+ if let some sha := roundScope? then
+ scopeServed := scopeServed.push (sha, before - remaining.size)
+
+ -- `--unsafe`: report which fork commits actually contributed files, so the
+ -- user knows whose artifacts they ended up trusting.
+ if unsafeMode then
+ if scopeServed.isEmpty then
+ IO.eprintln "--unsafe: no fork scopes were needed; \
+ all files were served by higher-trust containers."
+ else
+ IO.eprintln s!"--unsafe: cache served from {scopeServed.size} fork commit scope(s):"
+ for (sha, n) in scopeServed do
+ IO.eprintln s!" {sha} → {n} file(s)"
+ if remaining.size > 0 then
+ IO.eprintln s!" {remaining.size} file(s) still missing after all scopes."
+
+ if warnOnMissing && downloadFailed > 0 && parallel then
+ IO.eprintln "Warning: some files were not found in the cache."
+ IO.eprintln "This usually means that your local checkout of mathlib4 has diverged from upstream."
+ IO.eprintln ""
+ IO.eprintln " * If you push your commits to a PR to the mathlib4 repository"
+ IO.eprintln " (use a draft PR if it is not ready for review),"
+ IO.eprintln " then CI will build the oleans and they will be available later."
+ IO.eprintln " * If you have already opened a PR, this may mean"
+ IO.eprintln " the CI build has failed part-way through building."
-- Finalize decompression: wait for current task and process any remaining files
if let some config := decompConfig then
@@ -652,10 +892,17 @@ def checkForManifestMismatch : IO.CacheM Unit := do
precedence, then run `lake update`."
IO.Process.exit 1
-/-- Downloads missing files, and unpacks files. -/
+/-- Downloads missing files, and unpacks files.
+
+`repo` is the already-resolved GitHub repo (see `resolveRepo`); its
+trust-ordered container list from `defaultContainersForRepo` is the single
+source of truth for what gets tried — there's no separate outer-loop
+iteration. Master's cache reaches fork builds via `master` being in the fork
+chain (the highest-trust source, holding the bulk of any fork's deps). -/
def getFiles
- (repo? : Option String) (hashMap : IO.ModuleHashMap)
+ (repo : String) (hashMap : IO.ModuleHashMap)
(forceDownload forceUnpack parallel decompress : Bool)
+ (unsafeScopes : List String := [])
: IO.CacheM Unit := do
let isMathlibRoot ← IO.isMathlibRoot
unless isMathlibRoot do
@@ -680,38 +927,13 @@ def getFiles
else pure none
else pure none
- if let some repo := repo? then
- let failed ← downloadFiles repo hashMap forceDownload parallel (warnOnMissing := true)
- (decompress := decompress) (forceUnpack := forceUnpack)
- isMathlibRoot mathlibDepPath
- if failed > 0 then IO.Process.exit 1
- else
- let repoInfo? ← getRemoteRepo (← read).mathlibDepPath
-
- -- Build list of repositories to download from in order
- let repos : List String :=
- if let some repoInfo := repoInfo? then
- if repoInfo.repo == MATHLIBREPO then
- [MATHLIBREPO]
- else if repoInfo.useFirst then
- [repoInfo.repo, MATHLIBREPO]
- else
- [MATHLIBREPO, repoInfo.repo]
- else
- [MATHLIBREPO]
-
- let mut failed : Nat := 0
- for h : i in [0:repos.length] do
- failed ← downloadFiles repos[i] hashMap forceDownload parallel
- (warnOnMissing := i = repos.length - 1)
- (decompress := decompress) (forceUnpack := forceUnpack)
- isMathlibRoot mathlibDepPath
- if failed > 10 then
- IO.println s!"Too many downloads failed; stopping the downloading"
- IO.Process.exit 1
- if failed > 0 then
- IO.println s!"Downloading {failed} files failed"
- IO.Process.exit 1
+ let failed ← downloadFiles repo hashMap forceDownload parallel
+ (warnOnMissing := true)
+ (decompress := decompress) (forceUnpack := forceUnpack)
+ isMathlibRoot mathlibDepPath (unsafeScopes := unsafeScopes)
+ if failed > 0 then
+ IO.println s!"Downloading {failed} files failed"
+ IO.Process.exit 1
-- Wait for decompression of already-cached files to complete
if let some (task, size) := bgDecomp then
@@ -740,15 +962,31 @@ end Get
section Put
-/-- Cloudflare cache S3 URL -/
-initialize UPLOAD_URL : String ← do
- let url? ← IO.getEnv "MATHLIB_CACHE_PUT_URL"
- let defaultUrl :=
- if useCloudflareCache then
- "https://a09a7664adc082e00f294ac190827820.r2.cloudflarestorage.com/mathlib4"
- else
- "https://lakecache.blob.core.windows.net/mathlib4"
- return url?.getD defaultUrl
+/--
+Resolve the upload base URL.
+
+Precedence:
+1. `MATHLIB_CACHE_PUT_URL` env var, if set.
+2. The Azure URL for the explicitly chosen `container`.
+3. With neither set, fall back to `Container.legacy` (the bare `mathlib4`
+ container) and warn. RBAC still scopes each identity to its own container,
+ so the fallback cannot reach a trust-level container it isn't entitled to;
+ the warning steers workflows toward passing `--container=NAME`.
+-/
+def effectiveUploadURL (container : Option Container) :
+ IO (Option Container × String) := do
+ if let some url ← IO.getEnv "MATHLIB_CACHE_PUT_URL" then
+ -- A user-supplied URL carries no container policy, so signal `none` and let
+ -- `mkFileURL` choose the path from the repo alone.
+ return (none, url)
+ match container with
+ | none =>
+ IO.eprintln <|
+ "Warning: cache upload without --container=NAME; defaulting to the\n" ++
+ " `legacy` (bare `mathlib4`) container. Pass --container=NAME\n" ++
+ " explicitly to choose a trust-level container."
+ return (some Container.legacy, Container.legacy.azureURL)
+ | some c => return (some c, c.azureURL)
def azureBearerApiVersionHeader : String := "x-ms-version: 2026-02-06"
@@ -759,31 +997,37 @@ def getAzureDateHeader : IO String := do
throw <| IO.userError s!"failed to produce x-ms-date header (exit code {out.exitCode})"
return s!"x-ms-date: {out.stdout.trimAscii.copy}"
-/-- Formats the config file for `curl`, containing the list of files to be uploaded -/
-def mkPutConfigContent (repo : String) (files : Array FilePath) (auth : UploadAuth) : IO String := do
+/-- Formats the config file for `curl`, containing the list of files to be uploaded.
+The destination base URL is the explicit `uploadURL` argument. `container` is
+threaded through to `mkFileURL` so the per-container URL-shape policy applies;
+it is `none` only when `MATHLIB_CACHE_PUT_URL` is overriding the endpoint. -/
+def mkPutConfigContent (container : Option Container) (repo uploadURL : String)
+ (files : Array FilePath) (auth : UploadAuth) : IO String := do
+ let scope? ← getRepoScope
let token := match auth with
| .azureSas token => s!"?{token}"
| _ => ""
let l ← files.toList.mapM fun file : FilePath => do
- pure s!"-T {file.toString}\nurl = {mkFileURL repo UPLOAD_URL file.fileName.get!}{token}"
+ pure s!"-T {file.toString}\nurl = {mkFileURL container repo uploadURL file.fileName.get! scope?}{token}"
return "\n".intercalate l
-/-- Calls `curl` to send a set of files to the server -/
+/-- Calls `curl` to send a set of files to the server. The destination container
+is selected by `container`; pass `none` to require `MATHLIB_CACHE_PUT_URL` to
+be set instead (otherwise this errors). -/
def putFilesAbsolute
- (repo : String) (files : Array FilePath) (tempConfigFilePath : FilePath)
+ (repo : String) (container : Option Container)
+ (files : Array FilePath) (tempConfigFilePath : FilePath)
(overwrite : Bool) (auth : UploadAuth) : IO Unit := do
-- TODO: reimplement using HEAD requests?
- let _ := overwrite
let size := files.size
if size > 0 then
- IO.FS.writeFile tempConfigFilePath (← mkPutConfigContent repo files auth)
- IO.println s!"Attempting to upload {size} file(s) to {repo} cache"
+ let (urlContainer?, uploadURL) ← effectiveUploadURL container
+ IO.FS.writeFile tempConfigFilePath
+ (← mkPutConfigContent urlContainer? repo uploadURL files auth)
+ let target := container.map Container.name |>.getD "(env override)"
+ IO.println s!"Attempting to upload {size} file(s) to {repo} cache (container: {target})"
let azureDateHeader ← getAzureDateHeader
let args := match auth with
- | .cloudflareS3 token =>
- -- TODO: reimplement using HEAD requests?
- let _ := overwrite
- #["--aws-sigv4", "aws:amz:auto:s3", "--user", token]
| .azureSas _ =>
if overwrite then
#["-H", "x-ms-blob-type: BlockBlob"]
@@ -801,17 +1045,24 @@ def putFilesAbsolute
"-X", "PUT", "--parallel",
"--retry", "5", -- there seem to be some intermittent failures
"--write-out", "%{json}\n", "--config", tempConfigFilePath.toString]
- discard <| monitorCurl args size "Uploaded" "speed_upload" (removeOnError := false) (decompConfig := none)
+ let s ← monitorCurl args size "Uploaded" "speed_upload" (removeOnError := false)
+ (decompConfig := none) (treatExistsAsSkip := !overwrite)
IO.FS.removeFile tempConfigFilePath
+ -- Surface genuine upload failures. Already-present blobs (409/412 on a
+ -- non-overwrite put) are excused in `monitorCurl`, so this won't trip on a
+ -- re-upload of files the server already has.
+ if s.failed > 0 then
+ IO.eprintln s!"Uploading {s.failed} file(s) failed"
+ IO.Process.exit 1
else IO.println "No files to upload"
-/-- Calls `curl` to send a set of cached files to the server -/
+/-- Calls `curl` to send a set of cached files to the server. -/
def putFiles
- (repo : String) (fileNames : Array String)
+ (repo : String) (container : Option Container) (fileNames : Array String)
(overwrite : Bool) (auth : UploadAuth) : IO Unit := do
-- TODO: reimplement using HEAD requests?
let files : Array FilePath := fileNames.map (fun (f : String) => (IO.CACHEDIR / f))
- putFilesAbsolute repo files IO.CURLCFG overwrite auth
+ putFilesAbsolute repo container files IO.CURLCFG overwrite auth
end Put
section Stage
@@ -861,31 +1112,28 @@ section Commit
def isGitStatusClean : IO Bool :=
return (← IO.runCmd "git" #["status", "--porcelain"]).isEmpty
-def getGitCommitHash : IO String :=
- return (← IO.runCmd "git" #["rev-parse", "HEAD"]).trimAsciiEnd.copy
-
/--
Sends a commit file to the server, containing the hashes of the respective committed files.
The file name is the current Git hash and the `c/` prefix means that it's a commit file.
+The destination container follows the same rules as `putFiles`.
-/
-def commit (hashMap : IO.ModuleHashMap) (overwrite : Bool) (auth : UploadAuth) : IO Unit := do
+def commit (container : Option Container) (hashMap : IO.ModuleHashMap) (overwrite : Bool)
+ (auth : UploadAuth) : IO Unit := do
let hash ← getGitCommitHash
let path := IO.CACHEDIR / hash
IO.FS.createDirAll IO.CACHEDIR
IO.FS.writeFile path <| ("\n".intercalate <| hashMap.hashes.toList.map toString) ++ "\n"
let azureDateHeader ← getAzureDateHeader
+ -- Commit files are never namespaced by repo (they always live at `/c/`),
+ -- so we only need the URL from `effectiveUploadURL`, not the URL-shape container.
+ let (_, uploadURL) ← effectiveUploadURL container
match auth with
- | .cloudflareS3 token =>
- -- TODO: reimplement using HEAD requests?
- let _ := overwrite
- discard <| IO.runCurl #["-T", path.toString,
- "--aws-sigv4", "aws:amz:auto:s3", "--user", token, s!"{UPLOAD_URL}/c/{hash}"]
| .azureSas token =>
let params := if overwrite
then #["-X", "PUT", "-H", "x-ms-blob-type: BlockBlob"]
else #["-X", "PUT", "-H", "x-ms-blob-type: BlockBlob", "-H", "If-None-Match: *"]
- discard <| IO.runCurl <| params ++ #["-T", path.toString, s!"{URL}/c/{hash}?{token}"]
+ discard <| IO.runCurl <| params ++ #["-T", path.toString, s!"{uploadURL}/c/{hash}?{token}"]
| .azureBearer token =>
let params := if overwrite
then #["-X", "PUT", "-H", "x-ms-blob-type: BlockBlob", "-H", azureBearerApiVersionHeader,
@@ -893,7 +1141,7 @@ def commit (hashMap : IO.ModuleHashMap) (overwrite : Bool) (auth : UploadAuth) :
"--oauth2-bearer", token]
else #["-X", "PUT", "-H", "x-ms-blob-type: BlockBlob", "-H", "If-None-Match: *", "-H",
azureBearerApiVersionHeader, "-H", azureDateHeader, "--oauth2-bearer", token]
- discard <| IO.runCurl <| params ++ #["-T", path.toString, s!"{URL}/c/{hash}"]
+ discard <| IO.runCurl <| params ++ #["-T", path.toString, s!"{uploadURL}/c/{hash}"]
IO.FS.removeFile path
end Commit
@@ -922,10 +1170,9 @@ Retrieves metadata about hosted files: their names and the timestamps of last mo
Example: `["f/39476538726384726.tar.gz", "Sat, 24 Dec 2022 17:33:01 GMT"]`
-/
def getFilesInfo (q : QueryType) : IO <| List (String × String) := do
- if useCloudflareCache then
- throw <| .userError "FIXME: getFilesInfo is not adapted to Cloudflare cache yet"
IO.println s!"Downloading info list of {q.desc}"
- let ret ← IO.runCurl #["-X", "GET", s!"{URL}?comp=list&restype=container{q.prefix}"]
+ let ret ← IO.runCurl
+ #["-X", "GET", s!"{Container.master.azureURL}?comp=list&restype=container{q.prefix}"]
match ret.splitOn "" with
| [] => formatError
| [_] => return []
diff --git a/Cache/SECURITY.md b/Cache/SECURITY.md
new file mode 100644
index 00000000000000..0c93d31772981e
--- /dev/null
+++ b/Cache/SECURITY.md
@@ -0,0 +1,152 @@
+# Cache trust model & security notes
+
+## Background
+
+The mathlib build cache holds CI-built artifacts shared across every
+contributor's local checkout. A PR can run arbitrary code during its CI build
+(Lean executes user code at elaboration time), so it can write any bytes into
+the artifacts that are then packed and uploaded. The infrastructure cannot
+validate artifact content; verifying integrity would mean re-running the build,
+defeating the point of caching.
+
+The cache thus cannot prevent a malicious build from producing a poisoned
+artifact; it prevents delivery of that artifact to a higher-trust consumer.
+Artifacts produced at trust level T are only readable by consumers at level T
+or below.
+
+## Trust hierarchy and containers
+
+The model spans four storage containers, each written by a distinct class of
+CI job and assigned a trust level:
+
+| Container | Who may write | Trust |
+|-----------------------|--------------------------------------------------------|--------|
+| `master` | mathlib4 `master`/`staging` | high |
+| `forks` | mathlib4 PR builds, non-master branches, `bors try` | medium |
+| `nightly-testing` | nightly-testing's trusted branches | medium |
+| `pr-toolchain-tests` | nightly-testing's experimental toolchain branches | low |
+
+Each writer identity is granted write access to exactly one container, enforced
+by the storage backend. An upload aimed at any other container is rejected,
+regardless of what the cache binary requests.
+
+On the read side, each repo has a default lookup chain — the ordered list of
+containers a consumer reads from:
+
+| Consumer | Default lookup chain |
+|-------------------------|----------------------|
+| mathlib4 | `master` |
+| nightly-testing | `nightly-testing` |
+| forks (PRs) | `master`, `forks` |
+
+The nightly default excludes the low-trust container, so a poisoned upload from
+an experimental toolchain branch cannot reach a trusted nightly consumer.
+Branches that legitimately need to read their own prior low-trust uploads opt
+into a wider chain explicitly.
+
+## Four enforcement layers
+
+The first two enforce the trust boundary; the last two provide correctness
+guarantees and additional containment.
+
+### 1. Token-scoped uploads (server-side)
+
+Before uploading, the workflow obtains a short-lived token for the writer
+identity tied to its container. The identity provider issues the token only
+when the workflow's identity — stamped by GitHub from the repo, event type, and
+ref — matches a pre-registered credential. The token's scope is fixed when it
+is issued and cannot be widened afterward.
+
+This is the boundary's anchor: a compromised cache binary, a tampered workflow,
+or a malicious PR that captures and replays the token still cannot upload
+outside the one container the token grants.
+
+### 2. Isolation of the cache binary
+
+The cache binary is built from a trusted branch, never from the PR's checkout,
+so the PR's toolchain never reaches the compiler that produces it. The binary
+runs in two separate jobs — one that fetches and packs artifacts, and one that
+uploads them — and each builds its own copy from the trusted source. The PR's
+own build writes only its artifacts, which the trusted binary later packs.
+
+The two jobs also run on different runner pools, and the upload token is minted
+only in the upload job, so it never reaches the build host; a compromised build
+host cannot extract it.
+
+### 3. Read-only source tree during the build
+
+The PR build, where untrusted code runs, executes inside a sandbox that makes
+the source tree read-only. This keeps the inputs to the cache key honest while
+they are being hashed: without it, a malicious build could rewrite a hash input
+(such as the toolchain) between hashing and packing, aligning its keys with a
+target branch's and bypassing the partitioning below.
+
+### 4. Hash partitioning
+
+Cache keys derive from the source content, its imports, and the build's
+toolchain and configuration. Branches with different toolchains therefore live
+in disjoint key spaces, so even within one container their artifacts cannot
+collide unless an attacker aligns all of those inputs — which Layer 3 prevents.
+
+This layer is not sufficient alone: it relies on Layer 2 for an honest binary
+computing the keys, Layer 3 to keep the inputs honest, and Layer 1 to bound the
+damage if partitioning ever fails.
+
+## How CI routes each job
+
+A routing policy decides, for each CI job, which container it writes to and
+which lookup chain it reads from. The policy is loaded from the trusted branch,
+not from the PR, so a PR cannot route itself to a higher-trust container.
+
+This routing applies only in CI. User machines fall back to the strict per-repo
+default and must opt into a wider lookup chain explicitly.
+
+## Per-commit namespace for fork uploads
+
+Within the fork container, uploads are further namespaced by the PR's head
+commit. This closes a replay window: artifacts from a closed, hidden, or
+force-pushed-away PR live under a different commit, so a later honest PR from
+the same fork cannot read them. Uploads to the other containers are not
+commit-scoped — each receives uploads from a single trust level, so the
+container boundary alone isolates them.
+
+By default a `cache get` reads the fork namespace at the checked-out HEAD: it
+can only serve artifacts built from the commit the reader already has, so it
+adds no trust over the fork container itself and prints no notice. (CI pins
+the same namespace explicitly via `MATHLIB_CACHE_REPO_SCOPE`, set to the build
+SHA.) A reader opts into a *different* commit's namespace with
+`cache get --scope=SHA`, or lets `cache get --unsafe` discover the most recent
+cached fork commits automatically (`--unsafe-window=N` reads the `N` most
+recent, default `1`). Either way the reader is choosing to trust whoever
+produced those fork artifacts — the per-commit namespace bounds *replay*, not
+the trust decision itself — so both forms print the non-default-scope security
+notice before reading. Neither runs in CI; CI routing (above) is loaded from
+the trusted branch.
+
+## Explicitly out of scope
+
+The trust model does not attempt to defend against:
+
+- **Compromised upstream Lean releases** — a malicious toolchain on the trusted
+ branch builds the cache binary itself.
+- **Compromised storage tenant** — admin-level compromise defeats the access
+ grants.
+- **Sandbox escape via kernel vulnerability** — invalidates Layer 3.
+- **Maintainer trust on the trusted branches** — write access to a branch the
+ cache binary is built from can land a bad tool, workflow, or toolchain.
+- **Compromised CI platform credentials** — forged identity tokens break the
+ upload boundary.
+- **Validation of artifact byte-identity** — the cache key identifies inputs,
+ not bytes; containment is trust-bounded delivery, not fetch-time detection.
+
+## Code pointers
+
+| Concern | File(s) |
+|------------------------------------------------|------------------------------------------------------------------|
+| Container model, URL shape, per-repo defaults | [`Cache/Infra.lean`](Infra.lean) |
+| Read-fallback resolution, upload URL, dispatch | [`Cache/Requests.lean`](Requests.lean) (`effectiveGetURLs`, `effectiveUploadURL`) |
+| Trust property tests | [`Cache/Test.lean`](Test.lean) |
+| User-facing CLI surface, env vars | [`Cache/Main.lean`](Main.lean), [`Cache/README.md`](README.md) |
+| OIDC mint + per-job dispatch | [`.github/workflows/build_template.yml`](../.github/workflows/build_template.yml) (`upload_cache` job) |
+| (repo, branch) → trust class policy table | [`.github/actions/cache-trust-dispatch/action.yml`](../.github/actions/cache-trust-dispatch/action.yml) |
+| Caller `cache_application_id` ternaries | [`.github/workflows/build.yml`](../.github/workflows/build.yml), [`bors.yml`](../.github/workflows/bors.yml), [`build_fork.yml`](../.github/workflows/build_fork.yml), [`ci_dev.yml`](../.github/workflows/ci_dev.yml) |
diff --git a/Cache/Test.lean b/Cache/Test.lean
new file mode 100644
index 00000000000000..15bf2c1b701839
--- /dev/null
+++ b/Cache/Test.lean
@@ -0,0 +1,1007 @@
+/-
+Copyright (c) 2026 Marcelo Lynch. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Marcelo Lynch
+-/
+
+import Cache.Cli
+import Cache.Requests
+import Cache.Marker
+import Cache.Query
+import Cache.Warning
+import Cache.Lean
+
+/-!
+# Unit tests for the cache CLI
+
+These tests cover the pure logic of the cache system, including:
+- Container model (trust levels, URL shapes, Azure integration)
+- Trust-ordered fallback chains per repo
+- URL construction (`mkFileURL`) with support for per-SHA scoping
+- CLI flag parsing (`--cache-from`, `--scope`, `--unsafe`, `--repo`, etc.)
+- `--unsafe` download-round expansion (`expandDownloadRounds`) and the
+ non-default-scope security warning it triggers
+- Utility functions (URL extraction, filename hashing, etc.)
+
+Anything that touches `curl` or the network is left to CI, which exercises the
+`cache get`/`put` paths end-to-end on real containers.
+
+## Invariants these tests defend
+
+1. Trust boundary per container: each container has a dedicated writer (OIDC +
+ Azure RBAC) and reads follow a per-repo trust-ordered list, so a PR cannot
+ upload to a higher-trust container.
+2. Per-SHA namespace for fork uploads: fork uploads land at `/f/{repo}/{sha}/{hash}`,
+ so one commit's artifacts never serve another commit on the same fork.
+3. Flat layout for single-writer containers: `master` reads and writes flat at
+ `/f/{hash}`, the path older tools also use.
+4. Prefixed layout for multi-writer containers: `forks`, `nightly-testing`, and
+ `pr-toolchain-tests` namespace by repo so uploads from different sources don't
+ collide.
+5. `legacy` stays readable with its mixed layout (flat for the canonical repo,
+ prefixed for forks) so older clients keep working.
+
+## Running the tests
+
+Run with `lake exe cache-test`. Exits 0 on success, non-zero on failure.
+
+The tests stand alone (no dependency on `MathlibTest`). A Lake package has a
+single `testDriver`, and the enclosing `mathlib` package binds that to
+`MathlibTest` (see `lakefile.lean`); if the cache tool moves to its own Lake
+project, the `cache-test` `lean_exe` here can become that project's `testDriver`.
+-/
+
+namespace Cache.Test
+
+open Cache.Requests
+
+/-- Counter for failed assertions. -/
+initialize failures : IO.Ref Nat ← IO.mkRef 0
+
+/-- A single named assertion. On failure, prints details and bumps the counter. -/
+def assert (name : String) (cond : Bool) : IO Unit := do
+ if cond then
+ IO.println s!" ok: {name}"
+ else
+ IO.eprintln s!" FAIL: {name}"
+ failures.modify (· + 1)
+
+/-- Assert two strings are equal; show both on failure. -/
+def assertEq (name expected actual : String) : IO Unit := do
+ if expected == actual then
+ IO.println s!" ok: {name}"
+ else
+ IO.eprintln s!" FAIL: {name}\n expected: {expected}\n actual: {actual}"
+ failures.modify (· + 1)
+
+/-- Run `action` with both stdout and stderr redirected to /dev/null. Restores
+both on completion, including on exception. Apply this to every production code
+call in tests so diagnostic prints never mix with test output, regardless of
+whether the production code currently produces any. -/
+private def withSuppressedOutput (action : IO α) : IO α := do
+ let savedOut ← IO.getStdout
+ let savedErr ← IO.getStderr
+ let sink ← IO.FS.Handle.mk "/dev/null" IO.FS.Mode.append
+ let sinkStream := IO.FS.Stream.ofHandle sink
+ -- `IO.setStdout`/`IO.setStderr` return the previous stream; we already saved it,
+ -- so discard the return value here.
+ discard <| IO.setStdout sinkStream
+ discard <| IO.setStderr sinkStream
+ try
+ let r ← action
+ discard <| IO.setStdout savedOut
+ discard <| IO.setStderr savedErr
+ return r
+ catch e =>
+ discard <| IO.setStdout savedOut
+ discard <| IO.setStderr savedErr
+ throw e
+
+section ContainerModel
+
+/-- The short name is the string used on the CLI (`--container=NAME`) and to
+derive the Azure container name. These names are part of the public CLI
+contract, so they are pinned here: a rename must be a deliberate edit to this
+test, not an accident. -/
+def test_Container_name : IO Unit := do
+ IO.println "Container.name:"
+ assertEq "master" "master" Container.master.name
+ assertEq "forks" "forks" Container.forks.name
+ assertEq "nightly-testing" "nightly-testing" Container.nightlyTesting.name
+ assertEq "pr-toolchain-tests" "pr-toolchain-tests" Container.prToolchainTests.name
+ assertEq "legacy" "legacy" Container.legacy.name
+
+/-- Parser is the inverse of `Container.name` on valid inputs, and rejects everything else. -/
+def test_Container_parse : IO Unit := do
+ IO.println "Container.parse?:"
+ -- Every canonical name round-trips back to its enum case.
+ assert "master parses" (Container.parse? "master" == some .master)
+ assert "forks parses" (Container.parse? "forks" == some .forks)
+ assert "nightly-testing parses" (Container.parse? "nightly-testing" == some .nightlyTesting)
+ assert "pr-toolchain-tests parses"
+ (Container.parse? "pr-toolchain-tests" == some .prToolchainTests)
+ assert "legacy parses" (Container.parse? "legacy" == some .legacy)
+ -- Matching is case-insensitive, so `--container=Master` canonicalizes too.
+ assert "case-insensitive" (Container.parse? "Master" == some .master)
+ -- An unknown name returns `none` so `--container=bogus` errors out rather than
+ -- defaulting to some container the user didn't ask for.
+ assert "unknown rejected" (Container.parse? "bogus" == none)
+ assert "empty rejected" (Container.parse? "" == none)
+
+/-- The Azure URL each container resolves to: `mathlib4-{name}` for the
+trust-level containers, bare `mathlib4` for `legacy`. These URLs go into every
+request, and changing one means re-coordinating the Azure side with every
+consumer, so they are pinned here. -/
+def test_Container_azureURL : IO Unit := do
+ IO.println "Container.azureURL:"
+ assertEq "master URL"
+ "https://lakecache.blob.core.windows.net/mathlib4-master"
+ Container.master.azureURL
+ assertEq "forks URL"
+ "https://lakecache.blob.core.windows.net/mathlib4-forks"
+ Container.forks.azureURL
+ assertEq "nightly-testing URL"
+ "https://lakecache.blob.core.windows.net/mathlib4-nightly-testing"
+ Container.nightlyTesting.azureURL
+ assertEq "pr-toolchain-tests URL"
+ "https://lakecache.blob.core.windows.net/mathlib4-pr-toolchain-tests"
+ Container.prToolchainTests.azureURL
+ -- `legacy` is the bare `mathlib4` container, with no `-legacy` suffix.
+ assertEq "legacy URL"
+ "https://lakecache.blob.core.windows.net/mathlib4"
+ Container.legacy.azureURL
+
+/-- Whether a container lays files out flat (`/f/`) or namespaces them by
+repo (`/f//`). The layout is fixed per container so that all of a
+container's writers stay on non-colliding paths:
+- `master` is flat for every repo (one writer, no collisions possible).
+- `forks`, `nightly-testing`, and `pr-toolchain-tests` are prefixed for every
+ repo, including the canonical one, so fork-trust uploads from the canonical
+ repo coexist with fork uploads.
+- `legacy` is flat for the canonical repo and prefixed otherwise.
+-/
+def test_Container_flatPath : IO Unit := do
+ IO.println "Container.flatPath:"
+ assert "master is flat for the canonical repo"
+ (Container.master.flatPath MATHLIBREPO == true)
+ assert "master is flat for a fork repo too"
+ (Container.master.flatPath "alice/mathlib4" == true)
+ assert "legacy is flat for the canonical repo"
+ (Container.legacy.flatPath MATHLIBREPO == true)
+ assert "legacy is prefixed for a fork repo"
+ (Container.legacy.flatPath "alice/mathlib4" == false)
+ assert "forks is prefixed for the canonical repo"
+ (Container.forks.flatPath MATHLIBREPO == false)
+ assert "forks is prefixed for a fork repo"
+ (Container.forks.flatPath "alice/mathlib4" == false)
+ assert "nightly-testing is prefixed for the nightly-testing repo"
+ (Container.nightlyTesting.flatPath NIGHTLY_TESTING_REPO == false)
+ assert "nightly-testing is prefixed for the canonical repo"
+ (Container.nightlyTesting.flatPath MATHLIBREPO == false)
+ assert "pr-toolchain-tests is prefixed for the nightly-testing repo"
+ (Container.prToolchainTests.flatPath NIGHTLY_TESTING_REPO == false)
+
+end ContainerModel
+
+section PerRepoAllowlist
+
+/-- Trust-ordered read chain per GitHub repo: the tool tries containers in this
+order and stops at the first hit, so both membership and ordering are part of
+the trust boundary. Key points the tests pin:
+- The nightly-testing chain excludes `pr-toolchain-tests`, so trusted-nightly
+ consumers never fall back to low-trust toolchain-PR uploads (those branches
+ opt into the wider chain via `MATHLIB_CACHE_FROM` in CI).
+- The fork chain leads with `master` (shared upstream deps), then `forks`
+ (PR-specific files); `master` is absent from the nightly chain because that
+ repo's toolchain gives it a different root hash.
+- Every chain ends with `legacy`, so older clients' artifacts stay reachable.
+-/
+def test_defaultContainersForRepo : IO Unit := do
+ IO.println "defaultContainersForRepo:"
+ assert "canonical repo → [master, legacy]"
+ (defaultContainersForRepo MATHLIBREPO == [.master, .legacy])
+ assert "nightly-testing repo → [nightly-testing, legacy], no pr-toolchain-tests"
+ (defaultContainersForRepo NIGHTLY_TESTING_REPO == [.nightlyTesting, .legacy])
+ assert "fork repo → [master, forks, legacy]"
+ (defaultContainersForRepo "alice/mathlib4" == [.master, .forks, .legacy])
+ assert "unknown repo falls back to the fork chain"
+ (defaultContainersForRepo "some/other-repo" == [.master, .forks, .legacy])
+ -- Every chain ends with `legacy`; dropping it would quietly shrink hit rates.
+ assert "fork chain ends with legacy"
+ ((defaultContainersForRepo "alice/mathlib4").getLast? == some .legacy)
+ assert "canonical chain ends with legacy"
+ ((defaultContainersForRepo MATHLIBREPO).getLast? == some .legacy)
+ assert "nightly-testing chain ends with legacy"
+ ((defaultContainersForRepo NIGHTLY_TESTING_REPO).getLast? == some .legacy)
+
+end PerRepoAllowlist
+
+section MkFileURL
+
+/-- URL construction for a cache file. The path shape follows the container
+(`Container.flatPath`), not the repo, so the same repo lands flat in `master`
+and prefixed in `forks`. A `none` container is the user-supplied-URL case
+(`MATHLIB_CACHE_GET_URL` / `_PUT_URL`), where the shape follows the repo alone.
+
+A per-SHA scope (`MATHLIB_CACHE_REPO_SCOPE`) inserts `{sha}` between repo and
+hash on prefixed paths only — `/f/{repo}/{sha}/{hash}` — keeping each commit's
+fork uploads in their own namespace. Flat paths ignore the scope.
+-/
+def test_mkFileURL : IO Unit := do
+ IO.println "mkFileURL:"
+ assertEq "master is flat for the canonical repo"
+ "https://lakecache.blob.core.windows.net/mathlib4-master/f/abc.ltar"
+ (mkFileURL (some .master) MATHLIBREPO Container.master.azureURL "abc.ltar")
+ assertEq "master is flat for a fork repo too"
+ "https://lakecache.blob.core.windows.net/mathlib4-master/f/abc.ltar"
+ (mkFileURL (some .master) "alice/mathlib4" Container.master.azureURL "abc.ltar")
+ -- `forks` prefixes by repo even for the canonical repo, so its fork-trust
+ -- uploads don't collide with fork uploads in the same container.
+ assertEq "forks prefixes by repo for the canonical repo"
+ "https://lakecache.blob.core.windows.net/mathlib4-forks/f/leanprover-community/mathlib4/abc.ltar"
+ (mkFileURL (some .forks) MATHLIBREPO Container.forks.azureURL "abc.ltar")
+ assertEq "forks prefixes by repo for a fork repo"
+ "https://lakecache.blob.core.windows.net/mathlib4-forks/f/alice/mathlib4/abc.ltar"
+ (mkFileURL (some .forks) "alice/mathlib4" Container.forks.azureURL "abc.ltar")
+ assertEq "nightly-testing prefixes by repo"
+ "https://lakecache.blob.core.windows.net/mathlib4-nightly-testing/f/leanprover-community/mathlib4-nightly-testing/abc.ltar"
+ (mkFileURL (some .nightlyTesting) NIGHTLY_TESTING_REPO
+ Container.nightlyTesting.azureURL "abc.ltar")
+ assertEq "pr-toolchain-tests prefixes by repo"
+ "https://lakecache.blob.core.windows.net/mathlib4-pr-toolchain-tests/f/leanprover-community/mathlib4-nightly-testing/abc.ltar"
+ (mkFileURL (some .prToolchainTests) NIGHTLY_TESTING_REPO
+ Container.prToolchainTests.azureURL "abc.ltar")
+ assertEq "legacy is flat for the canonical repo"
+ "https://lakecache.blob.core.windows.net/mathlib4/f/abc.ltar"
+ (mkFileURL (some .legacy) MATHLIBREPO Container.legacy.azureURL "abc.ltar")
+ assertEq "legacy prefixes by repo for a fork repo"
+ "https://lakecache.blob.core.windows.net/mathlib4/f/alice/mathlib4/abc.ltar"
+ (mkFileURL (some .legacy) "alice/mathlib4" Container.legacy.azureURL "abc.ltar")
+ -- No container (user-supplied URL): the shape follows the repo — flat for the
+ -- canonical repo, prefixed otherwise.
+ assertEq "user URL is flat for the canonical repo"
+ "https://custom.example/cache/f/abc.ltar"
+ (mkFileURL none MATHLIBREPO "https://custom.example/cache" "abc.ltar")
+ assertEq "user URL prefixes by repo for a fork repo"
+ "https://custom.example/cache/f/alice/mathlib4/abc.ltar"
+ (mkFileURL none "alice/mathlib4" "https://custom.example/cache" "abc.ltar")
+ -- A scope adds a `{sha}` path segment on prefixed paths.
+ assertEq "scope adds a SHA segment on a fork path"
+ "https://lakecache.blob.core.windows.net/mathlib4-forks/f/alice/mathlib4/abc123def/H.ltar"
+ (mkFileURL (some .forks) "alice/mathlib4" Container.forks.azureURL "H.ltar" (some "abc123def"))
+ assertEq "scope adds a SHA segment on the canonical repo's forks path"
+ "https://lakecache.blob.core.windows.net/mathlib4-forks/f/leanprover-community/mathlib4/abc123def/H.ltar"
+ (mkFileURL (some .forks) MATHLIBREPO Container.forks.azureURL "H.ltar" (some "abc123def"))
+ -- A scope is ignored on flat paths.
+ assertEq "scope is ignored on a flat master path"
+ "https://lakecache.blob.core.windows.net/mathlib4-master/f/abc.ltar"
+ (mkFileURL (some .master) MATHLIBREPO Container.master.azureURL "abc.ltar" (some "abc123def"))
+ assertEq "scope is ignored on a flat legacy path"
+ "https://lakecache.blob.core.windows.net/mathlib4/f/abc.ltar"
+ (mkFileURL (some .legacy) MATHLIBREPO Container.legacy.azureURL "abc.ltar" (some "abc123def"))
+
+end MkFileURL
+
+section ParseCacheFromList
+
+/-- Parser for `--cache-from=a,b,c`. List order is the trust order tried at
+download time, so it is preserved exactly. The parser is strict: one bad name
+or empty input fails the whole list rather than degrading to a default, so a
+typo surfaces instead of silently changing where the cache is read. -/
+def test_parseCacheFromList : IO Unit := do
+ IO.println "parseCacheFromList:"
+ assert "single container"
+ (parseCacheFromList "master" == some [.master])
+ assert "two containers"
+ (parseCacheFromList "master,forks" == some [.master, .forks])
+ assert "all five containers"
+ (parseCacheFromList "master,forks,nightly-testing,pr-toolchain-tests,legacy" ==
+ some [.master, .forks, .nightlyTesting, .prToolchainTests, .legacy])
+ assert "master,legacy"
+ (parseCacheFromList "master,legacy" == some [.master, .legacy])
+ -- Order is preserved, not normalized: `forks,master` reverses the priority.
+ assert "preserves the given order"
+ (parseCacheFromList "forks,master" == some [.forks, .master])
+ -- Whitespace around commas is tolerated, so the flag survives shell expansion.
+ assert "whitespace around names is tolerated"
+ (parseCacheFromList " master , forks " == some [.master, .forks])
+ assert "one unknown name rejects the whole list"
+ (parseCacheFromList "master,bogus" == none)
+ assert "empty input is rejected"
+ (parseCacheFromList "" == none)
+
+end ParseCacheFromList
+
+section ExtractRepoFromUrl
+
+/-- Parses `owner/name` from a git remote URL. The result selects the per-repo
+read chain, so misreading a fork as the canonical repo would read the wrong
+chain; these cases cover every URL shape git emits via `git remote get-url` or a
+direct remote (e.g. `gh pr checkout`). Unparseable input returns `none`, and the
+caller falls back to `MATHLIBREPO`. -/
+def test_extractRepoFromUrl : IO Unit := do
+ IO.println "extractRepoFromUrl:"
+ assert "ssh URL with .git suffix"
+ (extractRepoFromUrl "git@github.com:alice/mathlib4.git" == some "alice/mathlib4")
+ assert "ssh URL without .git suffix"
+ (extractRepoFromUrl "git@github.com:alice/mathlib4" == some "alice/mathlib4")
+ assert "https URL with .git suffix"
+ (extractRepoFromUrl "https://github.com/alice/mathlib4.git" == some "alice/mathlib4")
+ assert "https URL without .git suffix"
+ (extractRepoFromUrl "https://github.com/alice/mathlib4" == some "alice/mathlib4")
+ -- A hyphenated owner is part of the repo identity and must survive intact.
+ assert "hyphenated owner is preserved"
+ (extractRepoFromUrl "https://github.com/leanprover-community/mathlib4.git" == some "leanprover-community/mathlib4")
+ assert "empty input returns none"
+ (extractRepoFromUrl "" == none)
+ assert "a token with no slash or colon returns none"
+ (extractRepoFromUrl "norepo" == none)
+
+end ExtractRepoFromUrl
+
+section ExtractPRNumber
+
+/-- Extracts a PR number from a git ref. The contract is "second-to-last
+segment must be `pr`, last must be a Nat". -/
+def test_extractPRNumber : IO Unit := do
+ IO.println "extractPRNumber:"
+ -- The shape git produces for fetched PR refs.
+ assert "standard PR ref format"
+ (extractPRNumber "refs/remotes/upstream/pr/1234" == some 1234)
+ -- Branch refs are not PR refs; must not match.
+ assert "master branch returns none"
+ (extractPRNumber "refs/heads/master" == none)
+ -- Minimal `pr/N` is also accepted — the parser only inspects the trailing two segments.
+ assert "simple pr number"
+ (extractPRNumber "pr/42" == some 42)
+ -- The tail must be a valid Nat; non-numeric tails are rejected (no partial parsing).
+ assert "non-numeric tail returns none"
+ (extractPRNumber "refs/remotes/upstream/pr/foo" == none)
+ -- `0` is a valid Nat; pin down that it isn't special-cased.
+ assert "zero PR number"
+ (extractPRNumber "refs/remotes/upstream/pr/0" == some 0)
+ -- A numeric tail without the `pr/` parent must not be mistaken for a PR ref.
+ assert "missing pr segment returns none"
+ (extractPRNumber "refs/remotes/upstream/42" == none)
+
+end ExtractPRNumber
+
+section HashFromFileName
+
+/-- Recovers the UInt64 cache hash from a cached file's path, the inverse of
+`UInt64.asLTar`. The subtle case is `.ltar.part` — the suffix curl writes during
+a download — where `.part` must be stripped before `.ltar`. A regression here
+corrupts cache lookups, so both suffixes and a non-hex stem are covered. -/
+def test_hashFromFileName : IO Unit := do
+ IO.println "hashFromFileName:"
+ assert "plain .ltar file"
+ (hashFromFileName "abc123def.ltar" == String.parseHexToUInt64? "000000abc123def")
+ assert "in-flight .ltar.part file strips both suffixes"
+ (hashFromFileName "abc123def.ltar.part" == String.parseHexToUInt64? "000000abc123def")
+ assert "full 16-digit hex stem"
+ (hashFromFileName "deadbeef00112233.ltar" == String.parseHexToUInt64? "deadbeef00112233")
+ -- A non-hex stem returns none rather than a garbage hash.
+ assert "non-hex stem returns none"
+ (hashFromFileName "nothexa.ltar" == none)
+ -- Directory components are ignored; only the basename's stem is parsed.
+ assert "leading path is ignored"
+ (hashFromFileName "/path/to/abc123def.ltar" == String.parseHexToUInt64? "000000abc123def")
+
+end HashFromFileName
+
+section IsRemoteURL
+
+/-- Discriminator: is this string a remote URL (vs a local filesystem path)?
+Used to decide whether to short-circuit `git remote get-url` lookups. -/
+def test_isRemoteURL : IO Unit := do
+ IO.println "isRemoteURL:"
+ -- The three protocols accepted by the cache tool.
+ assert "https URL is remote"
+ (isRemoteURL "https://github.com/alice/mathlib4.git" == true)
+ assert "http URL is remote"
+ (isRemoteURL "http://github.com/alice/mathlib4" == true)
+ assert "ssh URL is remote"
+ (isRemoteURL "git@github.com:alice/mathlib4.git" == true)
+ -- Absolute and relative local paths must be classified as not-remote so they
+ -- get routed through `git remote get-url`.
+ assert "local path is not remote"
+ (isRemoteURL "/local/path/to/repo" == false)
+ assert "relative path is not remote"
+ (isRemoteURL "./local/repo" == false)
+ -- Defensive — empty input shouldn't accidentally match the predicate.
+ assert "empty string is not remote"
+ (isRemoteURL "" == false)
+
+end IsRemoteURL
+
+section UInt64Formatting
+
+/-- Filename derived from a cache hash: exactly 16 lowercase hex digits plus
+`.ltar`. The fixed width makes the hash ↔ filename mapping unique and
+invertible — without it, `0x1` and `0x01` would share a stem and collide. -/
+def test_UInt64_asLTar : IO Unit := do
+ IO.println "UInt64.asLTar:"
+ assertEq "small value is left-padded to 16 digits"
+ "0000000000000001.ltar"
+ (1 : UInt64).asLTar
+ assertEq "mid-width value is left-padded"
+ "0000000000abc123.ltar"
+ (0xabc123 : UInt64).asLTar
+ assertEq "full-width value is not truncated"
+ "deadbeef00112233.ltar"
+ (0xdeadbeef00112233 : UInt64).asLTar
+ assertEq "zero is padded, not emptied"
+ "0000000000000000.ltar"
+ (0 : UInt64).asLTar
+ -- Max value is 16 lowercase `f`s; the parser elsewhere is case-sensitive.
+ assertEq "max value is lowercase hex"
+ "ffffffffffffffff.ltar"
+ (0xffffffffffffffff : UInt64).asLTar
+
+end UInt64Formatting
+
+section RoundTrip
+
+/-- `asLTar` then `hashFromFileName` must return the original hash — the property
+that lets the filename serve as the cache key. A padding or truncation bug would
+read a file back as a different hash, causing misses or collisions. -/
+def test_hash_roundtrip : IO Unit := do
+ IO.println "hash roundtrip (asLTar then hashFromFileName):"
+ let h1 : UInt64 := 0xdeadbeef00112233
+ assert "full-width hash round-trips"
+ (hashFromFileName h1.asLTar == some h1)
+ -- A short hash exercises both pad-on-write and trim-on-read.
+ let h2 : UInt64 := 0xabc123
+ assert "padded hash round-trips"
+ (hashFromFileName h2.asLTar == some h2)
+
+end RoundTrip
+
+section Marker
+
+/-- URL shape for the per-SHA marker blob written by `put-staged`. Probed by
+`cache query` with a HEAD request. The marker lives at `/m/{repo}/{sha}` in
+the chosen container; its presence is a 200 HEAD response that signals "all
+artifacts for this commit were uploaded". This shape enables cheap per-commit
+discovery via HEAD (no blob-listing). -/
+def test_markerURL : IO Unit := do
+ IO.println "markerURL:"
+ assertEq "forks marker URL"
+ "https://lakecache.blob.core.windows.net/mathlib4-forks/m/alice/mathlib4/abc123"
+ (markerURL .forks "alice/mathlib4" "abc123")
+ -- The marker lives under `/m/`, its own namespace, and is keyed by repo.
+ assertEq "marker is under /m/, keyed by repo"
+ "https://lakecache.blob.core.windows.net/mathlib4-forks/m/leanprover-community/mathlib4/deadbeef"
+ (markerURL .forks MATHLIBREPO "deadbeef")
+ assertEq "marker URL respects the container base"
+ "https://lakecache.blob.core.windows.net/mathlib4/m/someorg/mathlib4/sha9999"
+ (markerURL .legacy "someorg/mathlib4" "sha9999")
+
+end Marker
+
+section ScopeResolution
+
+/-- `getRepoScope` answers "is the user reading from a SHA-scoped namespace?".
+It reads the `--scope=` flag (the `scopeOverride` ref) first, then the
+`MATHLIB_CACHE_REPO_SCOPE` env var, so an explicit flag is never overridden by
+an inherited env var. The flag value is returned as given. The env-var branch
+needs process state, so it is exercised by the CI integration tests rather than
+here. -/
+def test_getRepoScope : IO Unit := do
+ IO.println "getRepoScope:"
+ -- Guard the IORef so a leak doesn't pollute subsequent tests.
+ let saved ← scopeOverride.get
+ try
+ scopeOverride.set none
+ assert "no scope set returns none" ((← withSuppressedOutput getRepoScope) == none)
+
+ scopeOverride.set (some "abc123")
+ assert "the flag value is returned" ((← withSuppressedOutput getRepoScope) == some "abc123")
+
+ -- The flag value is returned as-is, without trimming or normalization.
+ scopeOverride.set (some "deadbeef")
+ assert "the flag value is returned verbatim"
+ ((← withSuppressedOutput getRepoScope) == some "deadbeef")
+
+ scopeOverride.set none
+ assert "clearing the flag returns none" ((← withSuppressedOutput getRepoScope) == none)
+ finally
+ scopeOverride.set saved
+
+end ScopeResolution
+
+section NonDefaultScope
+
+/-- `shouldWarnNonDefaultScope` decides whether `cache get` prints the
+non-default-scope security warning. It warns when any of three inputs takes the
+reader off the repo's default trust boundary:
+
+1. a scope is set (`--scope=` or `MATHLIB_CACHE_REPO_SCOPE`) and differs from
+ the checked-out HEAD;
+2. `--cache-from=LIST` differs from the repo's default chain (passing the
+ default explicitly is not widening);
+3. `--repo=` is given and differs from the detected git remote.
+
+The behavior the tests pin most carefully: a plain `cache get` with no flags
+never warns, even on a fork checkout whose remote isn't the canonical repo.
+`detectedRepo?` is passed in (resolved once by `resolveRepo`), so the cases are
+deterministic without needing a real checkout. -/
+def test_shouldWarnNonDefaultScope : IO Unit := do
+ IO.println "shouldWarnNonDefaultScope:"
+ -- Sandbox the IORef for the duration of this test.
+ let saved ← scopeOverride.get
+ try
+ scopeOverride.set none
+
+ assert "plain get with no flags does not warn"
+ (!(← withSuppressedOutput (shouldWarnNonDefaultScope none none none MATHLIBREPO)))
+
+ scopeOverride.set (some "abc123")
+ assert "a set scope warns"
+ (← withSuppressedOutput (shouldWarnNonDefaultScope none none none MATHLIBREPO))
+ scopeOverride.set none
+
+ -- A scope equal to HEAD is trust-equivalent to no scope (CI's normal mode).
+ -- Skipped when HEAD can't be resolved (not in a git checkout).
+ let head? ← try some <$> withSuppressedOutput getGitCommitHash catch _ => pure none
+ if let some head := head? then
+ scopeOverride.set (some head)
+ assert "a scope equal to HEAD does not warn"
+ (!(← withSuppressedOutput (shouldWarnNonDefaultScope none none none MATHLIBREPO)))
+ scopeOverride.set none
+
+ -- --cache-from equal to the repo's default chain is not widening.
+ let mathlibDefault := defaultContainersForRepo MATHLIBREPO
+ assert "--cache-from equal to the default does not warn"
+ (!(← withSuppressedOutput
+ (shouldWarnNonDefaultScope none none (some mathlibDefault) MATHLIBREPO)))
+
+ assert "--cache-from widening the chain warns"
+ (← withSuppressedOutput
+ (shouldWarnNonDefaultScope none none (some [.master, .forks, .legacy]) MATHLIBREPO))
+
+ -- A fork checkout (remote ≠ resolved repo) stays silent without an explicit --repo.
+ assert "a fork checkout without --repo does not warn"
+ (!(← withSuppressedOutput
+ (shouldWarnNonDefaultScope none (some "alice/mathlib4") none "alice/mathlib4")))
+
+ assert "--repo differing from the remote warns"
+ (← withSuppressedOutput
+ (shouldWarnNonDefaultScope (some "bob/mathlib4") (some "alice/mathlib4") none "bob/mathlib4"))
+
+ assert "--repo matching the remote does not warn"
+ (!(← withSuppressedOutput
+ (shouldWarnNonDefaultScope (some "alice/mathlib4") (some "alice/mathlib4") none
+ "alice/mathlib4")))
+
+ -- With no detectable remote there is nothing to compare --repo against.
+ assert "--repo with no detectable remote does not warn"
+ (!(← withSuppressedOutput
+ (shouldWarnNonDefaultScope (some "bob/mathlib4") none none "bob/mathlib4")))
+
+ -- `--unsafe` (any window) always warns; it walks several untrusted scopes.
+ assert "--unsafe warns regardless of other inputs"
+ (← withSuppressedOutput
+ (shouldWarnNonDefaultScope none none none MATHLIBREPO (unsafeWindow? := some 5)))
+ assert "no --unsafe (none window) does not warn on its own"
+ (!(← withSuppressedOutput
+ (shouldWarnNonDefaultScope none none none MATHLIBREPO (unsafeWindow? := none))))
+ finally
+ scopeOverride.set saved
+
+/-- `getNonDefaultScopeReason` produces the `Reason:` line in the warning, naming
+the specific input that triggered it so the user can match it to their command
+line. When several inputs apply at once it reports the most specific first —
+scope, then `--cache-from`, then `--repo` — and that order is pinned here. -/
+def test_getNonDefaultScopeReason : IO Unit := do
+ IO.println "getNonDefaultScopeReason:"
+ let saved ← scopeOverride.get
+ try
+ scopeOverride.set none
+
+ -- A placeholder rather than a crash if nothing matches.
+ let reason ← withSuppressedOutput (getNonDefaultScopeReason none none none MATHLIBREPO)
+ assert "no trigger yields a placeholder reason" (reason == "unknown reason")
+
+ scopeOverride.set (some "abc123")
+ let reason ← withSuppressedOutput (getNonDefaultScopeReason none none none MATHLIBREPO)
+ assert "scope reason names the flag and SHA"
+ (reason == "--scope=abc123 (explicit per-commit scope)")
+
+ -- Scope outranks cache-from when both apply.
+ let reason ← withSuppressedOutput (getNonDefaultScopeReason none none (some [.forks]) MATHLIBREPO)
+ assert "scope is reported ahead of cache-from"
+ (reason == "--scope=abc123 (explicit per-commit scope)")
+ scopeOverride.set none
+
+ -- A HEAD scope is exempt from condition 1, so a simultaneous cache-from
+ -- trigger is reported instead of the scope.
+ let head? ← try some <$> withSuppressedOutput getGitCommitHash catch _ => pure none
+ if let some head := head? then
+ scopeOverride.set (some head)
+ let reason ←
+ withSuppressedOutput (getNonDefaultScopeReason none none (some [.forks, .legacy]) MATHLIBREPO)
+ assert "a HEAD scope yields the cache-from reason"
+ (reason == "--cache-from=forks, legacy (explicit container override)")
+ scopeOverride.set none
+
+ let reason ←
+ withSuppressedOutput (getNonDefaultScopeReason none none (some [.forks, .legacy]) MATHLIBREPO)
+ assert "cache-from reason names the container list"
+ (reason == "--cache-from=forks, legacy (explicit container override)")
+
+ let reason ← withSuppressedOutput
+ (getNonDefaultScopeReason (some "bob/mathlib4") (some "alice/mathlib4") none "bob/mathlib4")
+ assert "repo reason names the override and the detected remote"
+ (reason == "--repo=bob/mathlib4 (overrides detected git remote: alice/mathlib4)")
+
+ -- --cache-from equal to the default is not a trigger, so no reason applies.
+ let reason ←
+ withSuppressedOutput (getNonDefaultScopeReason none none (some [.master, .legacy]) MATHLIBREPO)
+ assert "cache-from equal to the default yields the placeholder"
+ (reason == "unknown reason")
+
+ -- `--unsafe` outranks every other trigger and names its window.
+ scopeOverride.set (some "abc123")
+ let reason ← withSuppressedOutput
+ (getNonDefaultScopeReason (some "bob/mathlib4") (some "alice/mathlib4") (some [.forks])
+ "bob/mathlib4" (unsafeWindow? := some 7))
+ assert "unsafe reason names the window and outranks scope/cache-from/repo"
+ (reason == "--unsafe (automatic walk over up to 7 fork commit(s); trusting whoever built them)")
+ scopeOverride.set none
+ finally
+ scopeOverride.set saved
+
+/-- `findMostRecentSHAWithCache` returns the first candidate SHA whose per-SHA
+marker exists in the `forks` container, used by `cache query` to find the most
+recent cached build on the branch. The non-empty cases hit the network (a marker
+HEAD probe per SHA) and aren't unit-tested; here we pin that an empty list
+returns `none` with no probe. -/
+def test_findMostRecentSHAWithCache : IO Unit := do
+ IO.println "findMostRecentSHAWithCache:"
+ let result ← withSuppressedOutput (findMostRecentSHAWithCache [] MATHLIBREPO)
+ assert "empty SHA list returns none without probing" (result == none)
+
+/-- `findRecentSHAsWithCache` collects up to `limit` marked SHAs. The non-empty
+cases hit the network (a marker HEAD probe per SHA); here we pin that an empty
+candidate list returns `[]` for any limit, with no probe. -/
+def test_findRecentSHAsWithCache : IO Unit := do
+ IO.println "findRecentSHAsWithCache:"
+ let result ← withSuppressedOutput (findRecentSHAsWithCache [] MATHLIBREPO 5)
+ assert "empty SHA list returns [] without probing" (result == [])
+ let result ← withSuppressedOutput (findRecentSHAsWithCache [] MATHLIBREPO 0)
+ assert "limit 0 returns [] without probing" (result == [])
+
+end NonDefaultScope
+
+section GitFallback
+
+/-- `getRemoteRepo` and `resolveRepo` must never throw, regardless of git's
+availability or the state of the target path. This matters for `cache get`
+invoked inside a Lake dependency update, where the Mathlib dependency may be a
+plain archive without a `.git` directory.
+
+Two distinct failure modes are tested:
+
+* **Nonexistent path** — `IO.Process.output` throws before git even starts
+ (the OS rejects the invalid cwd). The `try...catch` in `getRemoteRepo` must
+ catch the exception and return `none`.
+
+* **Non-git directory** — git runs successfully but the path is not a repo, so
+ every git command exits non-zero. The existing exit-code checks already handle
+ this path; the test pins that `none` is returned here too.
+
+In both cases `resolveRepo` must fall back to `MATHLIBREPO`, giving the
+master-only container chain (no `forks`) — exactly what a dependency build
+should read from. -/
+def test_getRemoteRepo_gitFallback : IO Unit := do
+ IO.println "getRemoteRepo git fallback:"
+ -- Case 1: nonexistent cwd causes IO.Process.output to throw.
+ -- The try...catch in getRemoteRepo must intercept it and return none.
+ let fakePath := "/tmp/surely-nonexistent-mathlib-cache-test-xyz-9999999"
+ let r1 ← withSuppressedOutput (getRemoteRepo fakePath)
+ assert "getRemoteRepo returns none when git throws (nonexistent cwd)" (r1 == none)
+
+ -- Case 2: existing directory that is not a git repo (git returns exit 128).
+ -- This exercises the exit-code fallback path that predates the try...catch.
+ let r2 ← withSuppressedOutput (getRemoteRepo "/tmp")
+ assert "getRemoteRepo returns none in a non-git directory" (r2 == none)
+
+ -- resolveRepo propagates the fallback correctly:
+ -- detected? = none, resolved = MATHLIBREPO → master-only chain.
+ let (detected?, resolved) ← withSuppressedOutput (resolveRepo none fakePath)
+ assert "resolveRepo detected? is none on git failure" (detected? == none)
+ assert "resolveRepo falls back to MATHLIBREPO on git failure" (resolved == MATHLIBREPO)
+ assert "fallback chain includes master"
+ ((defaultContainersForRepo resolved).contains .master)
+ assert "fallback chain excludes forks (no fork container for dependency builds)"
+ (!(defaultContainersForRepo resolved).contains .forks)
+
+/-- `headIsAncestorOfMaster` gates the uncached-fork-HEAD note: when HEAD is
+already part of master's history, `master` (first in the fork lookup chain)
+serves every file by hash, so the note would be a false positive and is
+suppressed.
+
+Like `getRemoteRepo`, this helper must never throw — it runs on the read path,
+including inside dependency builds where the checkout may not be a git repo (or
+may lack a local `master`). Both failure modes degrade to `false` (= "not an
+ancestor", so the caller keeps its default behavior):
+
+* **Nonexistent path** — `IO.Process.output` throws before git starts; the
+ `try...catch` must intercept it.
+* **Non-git directory** — git runs but exits non-zero; the `exitCode == 0`
+ check returns `false`.
+
+The positive topology cases (HEAD on master ⇒ `true`; diverged branch ⇒ `false`)
+exercise real git history and are covered by the CI integration tests, matching
+how the other git-walking helpers are tested. -/
+def test_headIsAncestorOfMaster_gitFallback : IO Unit := do
+ IO.println "headIsAncestorOfMaster git fallback:"
+ let fakePath := "/tmp/surely-nonexistent-mathlib-cache-test-xyz-9999999"
+ let r1 ← withSuppressedOutput (headIsAncestorOfMaster fakePath)
+ assert "headIsAncestorOfMaster returns false when git throws (nonexistent cwd)"
+ (r1 == false)
+ let r2 ← withSuppressedOutput (headIsAncestorOfMaster "/tmp")
+ assert "headIsAncestorOfMaster returns false in a non-git directory" (r2 == false)
+
+end GitFallback
+
+section CliOptions
+
+open Cache.Cli
+
+/-- `isKnownOpt` is the gatekeeper that decides whether a `--`-prefixed token
+in the command line is a recognized option or a typo. Unknown options error
+out with a help message rather than being silently ignored — important so a
+typo like `--scoop=abc` doesn't silently disable the scope flag.
+
+The recognition rule:
+- A named option matches if `--{name}=` is a prefix of the token.
+- A flag matches if the token is exactly `--{name}` (no `=`).
+
+These tests pin the contract so a future refactor can't accidentally accept
+unknown options or reject known ones. -/
+def test_isKnownOpt : IO Unit := do
+ IO.println "isKnownOpt:"
+ -- Every named option is recognized when used with `=value` form.
+ assert "--repo=foo is known" (isKnownOpt "--repo=foo")
+ assert "--cache-from=master is known" (isKnownOpt "--cache-from=master")
+ assert "--scope=HEAD is known" (isKnownOpt "--scope=HEAD")
+ assert "--container=master is known" (isKnownOpt "--container=master")
+ assert "--staging-dir=/tmp is known" (isKnownOpt "--staging-dir=/tmp")
+ assert "--unsafe-window=5 is known" (isKnownOpt "--unsafe-window=5")
+
+ -- Empty value passes recognition (parseNamedOpt returns the empty string
+ -- for these — callers decide whether to treat that as an error).
+ assert "--scope= (empty value) is known" (isKnownOpt "--scope=")
+
+ -- Flags use the bare `--name` form, no `=`.
+ assert "--help (no =) is known" (isKnownOpt "--help")
+ assert "--unsafe (no =) is known" (isKnownOpt "--unsafe")
+
+ -- `--unsafe` is a flag, not a named option: the `=value` form is a user error.
+ assert "--unsafe=5 is NOT known (flags don't take values)"
+ (!isKnownOpt "--unsafe=5")
+
+ -- A typo on a known option name should fail recognition, not be silently
+ -- accepted. This is the regression-guard: if `--scoop=` were accepted, the
+ -- user's `--scope=` would be silently dropped and reads would fall back to
+ -- the default chain with no warning.
+ assert "--scoop=foo (typo on scope) is NOT known" (!isKnownOpt "--scoop=foo")
+ assert "--bogus=foo (unknown name) is NOT known" (!isKnownOpt "--bogus=foo")
+
+ -- A named option without `=` must NOT be accepted as a flag — `--scope`
+ -- (no value) is a user error, distinct from the `--help` flag form.
+ assert "--scope (no =) is NOT known (named opts require value)"
+ (!isKnownOpt "--scope")
+
+ -- Symmetric: a flag with `=` must NOT be accepted as a named opt.
+ assert "--help=foo is NOT known (flags don't take values)"
+ (!isKnownOpt "--help=foo")
+
+ -- A bare positional doesn't even look like an option. The cache binary
+ -- splits args by `startsWith "--"` before consulting `isKnownOpt`, so this
+ -- case should never reach us, but we pin it anyway for safety.
+ assert "bare positional 'scope' is NOT known" (!isKnownOpt "scope")
+
+/-- `parseNamedOpt` extracts the value of a `--name=value` option from a
+list of args. The rules tests pin:
+
+- Missing option → `none`.
+- Single occurrence → the value after `=`.
+- Empty value (`--scope=`) → `some ""` (caller decides what to do).
+- Multiple occurrences → the *last* one wins (`findRev?`). This mirrors
+ conventional shell semantics where `--scope=a --scope=b` resolves to `b`.
+- Non-matching args are ignored, even if they look similar (e.g.,
+ `--scope-other=` is a different option name).
+-/
+def test_parseNamedOpt : IO Unit := do
+ IO.println "parseNamedOpt:"
+ -- Empty arg list.
+ let v ← parseNamedOpt "scope" []
+ assert "empty args → none" (v == none)
+
+ -- Args without the target option.
+ let v ← parseNamedOpt "scope" ["--repo=foo", "get"]
+ assert "no matching option → none" (v == none)
+
+ -- Single occurrence.
+ let v ← parseNamedOpt "scope" ["--scope=abc123"]
+ assert "single occurrence → some value" (v == some "abc123")
+
+ -- `--scope=` is recognized with the empty string as its value, distinct from
+ -- "not passed" (none).
+ let v ← parseNamedOpt "scope" ["--scope="]
+ assert "empty value → some \"\"" (v == some "")
+
+ -- Multiple occurrences: last wins, matching shell precedence.
+ let v ← parseNamedOpt "scope" ["--scope=first", "--scope=second"]
+ assert "duplicate option → last value wins" (v == some "second")
+
+ -- Surrounding positionals and other options don't interfere.
+ let v ← parseNamedOpt "scope" ["get", "--repo=foo", "--scope=mid", "Mathlib/Init.lean"]
+ assert "found among other args" (v == some "mid")
+
+ -- A longer lookalike name must not match.
+ let v ← parseNamedOpt "scope" ["--scope-other=foo"]
+ assert "--scope-other does not match --scope" (v == none)
+
+/-- `parseFlagOpt` checks whether a bare `--name` flag is present in args.
+Used for `--help` today. The contract is strict equality — `--help` matches,
+`--help=true` and `--help-me` do not. -/
+def test_parseFlagOpt : IO Unit := do
+ IO.println "parseFlagOpt:"
+ -- Empty args.
+ assert "empty args → false" (!parseFlagOpt "help" [])
+
+ -- Bare `--help` present.
+ assert "--help present → true" (parseFlagOpt "help" ["--help"])
+
+ -- `--help=` with a value is NOT a bare flag. (`isKnownOpt` would also
+ -- reject it; this is the parser-level guarantee.)
+ assert "--help=true is NOT a bare flag" (!parseFlagOpt "help" ["--help=true"])
+
+ -- Flag absent among other args.
+ assert "no flag among args → false"
+ (!parseFlagOpt "help" ["get", "--repo=foo"])
+
+ -- Lookalike: `--help-me` isn't the `--help` flag.
+ assert "lookalike prefix doesn't match" (!parseFlagOpt "help" ["--help-me"])
+
+end CliOptions
+
+section CacheMissStatus
+
+/-- `isCacheMissStatus` decides whether a read's HTTP status is a benign miss
+(fall through to the next container) or a real transfer failure. `404` is always
+a miss; `403` is a miss only for a container flagged `treatForbiddenAsMiss`
+(currently `legacy`, whose reads start returning `403` once public access is
+revoked ahead of retirement). This guards old clients — whose chain still lists
+`legacy` — against per-file failures when the container is brought down. -/
+def test_isCacheMissStatus : IO Unit := do
+ IO.println "isCacheMissStatus:"
+ -- 404 is a miss regardless of the flag.
+ assert "404 is a miss (flag off)" (isCacheMissStatus 404 false)
+ assert "404 is a miss (flag on)" (isCacheMissStatus 404 true)
+ -- 403 is a miss only when the flag is set (i.e. for `legacy`).
+ assert "403 is a failure when flag off" (!isCacheMissStatus 403 false)
+ assert "403 is a miss when flag on" (isCacheMissStatus 403 true)
+ -- Success and server errors are never misses; they must surface.
+ assert "200 is not a miss" (!isCacheMissStatus 200 true)
+ assert "500 is not a miss" (!isCacheMissStatus 500 true)
+ assert "403-as-miss is scoped to 403" (!isCacheMissStatus 401 true)
+
+end CacheMissStatus
+
+section AlreadyPresentStatus
+
+/-- A non-overwrite `put` (`If-None-Match: *`) gets a 409 or 412 back for a blob
+that already exists; both mean "present", not a failure. -/
+def test_isAlreadyPresentStatus : IO Unit := do
+ IO.println "isAlreadyPresentStatus:"
+ -- 409/412 are the codes Azure returns for a blob that already exists.
+ assert "409 is already-present" (isAlreadyPresentStatus 409)
+ assert "412 is already-present" (isAlreadyPresentStatus 412)
+ -- Successes, misses, and server errors are not.
+ assert "201 is not already-present" (!isAlreadyPresentStatus 201)
+ assert "404 is not already-present" (!isAlreadyPresentStatus 404)
+ assert "500 is not already-present" (!isAlreadyPresentStatus 500)
+
+end AlreadyPresentStatus
+
+section UnsafeRounds
+
+/-- `expandDownloadRounds` turns the trust-ordered container list into the
+concrete download rounds to run, each tagged with the SHA scope to read at.
+
+Without `--unsafe` (empty `unsafeScopes`) every round carries the single resolved
+base scope; with no base scope, `headScope?` applies to the `forks` round only,
+so a plain `cache get` reads the fork namespace of the checked-out commit while
+the other containers' non-SHA-scoped layouts stay untouched. With `--unsafe` the
+`forks` container — the only SHA-scoped container — fans out into one round per
+discovered SHA (most recent first), while every other container reads unscoped
+and the base scope is dropped. -/
+def test_expandDownloadRounds : IO Unit := do
+ IO.println "expandDownloadRounds:"
+ let chain : List (Option Container × String) :=
+ [(some .master, "U_m"), (some .forks, "U_f"), (some .legacy, "U_l")]
+
+ -- No unsafe scopes: one round per container, each carrying the base scope.
+ assert "no unsafe scopes, no base scope → scope none on every round"
+ (expandDownloadRounds chain none [] ==
+ [(some .master, "U_m", none), (some .forks, "U_f", none), (some .legacy, "U_l", none)])
+ assert "no unsafe scopes, base scope → base scope on every round"
+ (expandDownloadRounds chain (some "S") [] ==
+ [(some .master, "U_m", some "S"), (some .forks, "U_f", some "S"),
+ (some .legacy, "U_l", some "S")])
+
+ -- With no base scope the forks round defaults to the HEAD scope; the other
+ -- containers' layouts are not SHA-scoped, so it must not leak into them.
+ assert "no base scope, head scope → forks at head, others unscoped"
+ (expandDownloadRounds chain none [] (some "H") ==
+ [(some .master, "U_m", none), (some .forks, "U_f", some "H"),
+ (some .legacy, "U_l", none)])
+ assert "explicit base scope wins over head scope"
+ (expandDownloadRounds chain (some "S") [] (some "H") ==
+ [(some .master, "U_m", some "S"), (some .forks, "U_f", some "S"),
+ (some .legacy, "U_l", some "S")])
+ assert "unsafe mode ignores head scope"
+ (expandDownloadRounds chain none ["a"] (some "H") ==
+ [(some .master, "U_m", none), (some .forks, "U_f", some "a"),
+ (some .legacy, "U_l", none)])
+
+ -- Unsafe scopes: only forks fans out, in order; others unscoped, base dropped.
+ assert "unsafe scopes fan out forks (in order), others unscoped"
+ (expandDownloadRounds chain (some "ignored") ["a", "b"] ==
+ [(some .master, "U_m", none),
+ (some .forks, "U_f", some "a"), (some .forks, "U_f", some "b"),
+ (some .legacy, "U_l", none)])
+
+ -- A chain without forks admits no SHA-scoped reads, so it is left unchanged.
+ assert "no forks container → unsafe scopes have no effect"
+ (expandDownloadRounds [(some .master, "U_m"), (some .legacy, "U_l")] none ["a", "b"] ==
+ [(some .master, "U_m", none), (some .legacy, "U_l", none)])
+
+end UnsafeRounds
+
+def runAll : IO Unit := do
+ test_Container_name
+ test_Container_parse
+ test_Container_azureURL
+ test_Container_flatPath
+ test_defaultContainersForRepo
+ test_mkFileURL
+ test_parseCacheFromList
+ test_extractRepoFromUrl
+ test_extractPRNumber
+ test_hashFromFileName
+ test_isRemoteURL
+ test_UInt64_asLTar
+ test_hash_roundtrip
+ test_markerURL
+ test_getRepoScope
+ test_shouldWarnNonDefaultScope
+ test_getNonDefaultScopeReason
+ test_findMostRecentSHAWithCache
+ test_findRecentSHAsWithCache
+ test_getRemoteRepo_gitFallback
+ test_headIsAncestorOfMaster_gitFallback
+ test_isKnownOpt
+ test_parseNamedOpt
+ test_parseFlagOpt
+ test_isCacheMissStatus
+ test_isAlreadyPresentStatus
+ test_expandDownloadRounds
+
+end Cache.Test
+
+open Cache.Test in
+def main : IO UInt32 := do
+ runAll
+ let n ← failures.get
+ if n == 0 then
+ IO.println "\nAll cache tests passed."
+ return 0
+ else
+ IO.eprintln s!"\n{n} cache test(s) failed."
+ return 1
diff --git a/Cache/Warning.lean b/Cache/Warning.lean
new file mode 100644
index 00000000000000..2aba0a12428b87
--- /dev/null
+++ b/Cache/Warning.lean
@@ -0,0 +1,228 @@
+/-
+Copyright (c) 2026 Marcelo Lynch. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Marcelo Lynch
+-/
+
+import Cache.Query
+
+/-!
+# Read-time advisories
+
+Two stderr-only notices `cache get` prints before reading:
+
+* a security warning when the read is taken off the repo's default trust
+ boundary (a scope, a widened `--cache-from`, or a `--repo` that diverges
+ from the git remote), and
+* a hint pointing an uncached fork HEAD at `cache query` and the SHA-scoped
+ workflow.
+-/
+
+namespace Cache.Requests
+
+/--
+`true` iff the resolved scope (see `getRepoScope`) equals the checked-out HEAD.
+
+A HEAD scope only serves artifacts built from the commit already checked out,
+and it is what an unscoped `cache get` reads anyway — the forks round defaults
+to the HEAD namespace (see `expandDownloadRounds`). So an explicit HEAD scope
+(e.g. CI's `MATHLIB_CACHE_REPO_SCOPE`, set to the build SHA on every fork
+build) just pins the default behavior and warrants no warning.
+
+`false` when no scope is set or HEAD cannot be determined.
+-/
+def scopeIsHead : IO Bool := do
+ let some scope ← getRepoScope | return false
+ let head ← try getGitCommitHash catch _ => return false
+ return head == scope
+
+/--
+Condition to determine if a non-default scope warning should be printed.
+
+Returns `true` if any of these hold:
+0. `--unsafe` was passed (`unsafeWindow?` is `some _`): the read walks several
+ fork commits and trusts whoever built each of them
+1. `MATHLIB_CACHE_REPO_SCOPE` is set in the environment (any non-empty value)
+ and differs from the checked-out HEAD (see `scopeIsHead`)
+2. `--cache-from` was passed and widens the lookup chain beyond `defaultContainersForRepo` for the resolved repo
+3. `--repo` was passed and does not match the git remote (`detectedRepo?`)
+
+`detectedRepo?` is the repo reported by the git remote (from `resolveRepo`,
+probed once per command); it is `none` if it could not be determined.
+
+Otherwise returns `false` (default lookup chain, no warning needed).
+-/
+def shouldWarnNonDefaultScope (repoExplicit? detectedRepo? : Option String)
+ (cliCacheFromOverride? : Option (List Container)) (resolvedRepo : String)
+ (unsafeWindow? : Option Nat := none) :
+ IO Bool := do
+ -- Condition 0: `--unsafe` (with its SHA window) — the most permissive read.
+ if unsafeWindow?.isSome then return true
+
+ -- Condition 1: `--scope=` flag or `MATHLIB_CACHE_REPO_SCOPE` env var supplied.
+ -- A HEAD scope is exempt: trust-equivalent to no scope (see `scopeIsHead`).
+ if (← getRepoScope).isSome then
+ unless (← scopeIsHead) do return true
+
+ -- Condition 2: --cache-from CLI override widens the lookup chain
+ match cliCacheFromOverride? with
+ | some cliOverride =>
+ let defaultContainers := defaultContainersForRepo resolvedRepo
+ unless cliOverride == defaultContainers do
+ return true
+ | none => pure ()
+
+ -- Condition 3: --repo was explicitly passed AND does not match the git remote.
+ -- Only fires when the user explicitly overrode --repo; defaulting to MATHLIBREPO
+ -- from a fork checkout is normal and does not warn.
+ match repoExplicit?, detectedRepo? with
+ | some explicitRepo, some detected =>
+ unless explicitRepo == detected do
+ return true
+ -- No --repo override, or the remote couldn't be determined: don't warn.
+ | _, _ => pure ()
+
+ return false
+
+/--
+Print a prominent security warning to stderr when reading at a non-default scope.
+
+The warning includes:
+- A clear statement that the user is trusting artifacts at a non-default scope
+- The scope details (container, repo, SHA as applicable)
+- Why the warning is being issued (which condition triggered it)
+-/
+def printNonDefaultScopeWarning (repo : String) (triggerReason : String) : IO Unit := do
+ let lines : List String := [
+ "=================================================================",
+ "SECURITY: reading cache at a non-default scope",
+ "=================================================================",
+ "You are reading cache artifacts at a scope outside the default trust",
+ "boundary for this repo. The cache cannot verify the contents of these",
+ "artifacts; you are choosing to trust whoever uploaded them.",
+ "",
+ s!"Repository: {repo}",
+ s!"Reason: {triggerReason}",
+ "=================================================================",
+ ]
+ for line in lines do
+ IO.eprintln line
+
+/--
+Determine the reason why a non-default scope warning is being issued.
+
+Returns a human-readable string describing which condition triggered the warning.
+-/
+def getNonDefaultScopeReason (repoExplicit? detectedRepo? : Option String)
+ (cliCacheFromOverride? : Option (List Container)) (resolvedRepo : String)
+ (unsafeWindow? : Option Nat := none) :
+ IO String := do
+ -- Check conditions in order; return the first that matches.
+
+ -- Condition 0: `--unsafe` walks up to `window` fork commits, trusting each.
+ if let some window := unsafeWindow? then
+ return s!"--unsafe (automatic walk over up to {window} fork commit(s); \
+ trusting whoever built them)"
+
+ -- Condition 1: `--scope=` flag (preferred form) or `MATHLIB_CACHE_REPO_SCOPE`
+ -- env var (CI form). Reported with the source that set it. A HEAD scope is
+ -- exempt, mirroring `shouldWarnNonDefaultScope`.
+ unless (← scopeIsHead) do
+ if let some s ← scopeOverride.get then
+ return s!"--scope={s} (explicit per-commit scope)"
+ let scope? ← IO.getEnv "MATHLIB_CACHE_REPO_SCOPE"
+ if let some scope := scope? then
+ let trimmed := scope.trimAscii
+ if !trimmed.isEmpty then
+ return s!"MATHLIB_CACHE_REPO_SCOPE={trimmed} (explicit per-commit scope)"
+
+ -- Condition 2: --cache-from override
+ if let some cliOverride := cliCacheFromOverride? then
+ let defaultContainers := defaultContainersForRepo resolvedRepo
+ if cliOverride != defaultContainers then
+ let overrideStr := ", ".intercalate (cliOverride.map Container.name)
+ return s!"--cache-from={overrideStr} (explicit container override)"
+
+ -- Condition 3: --repo was explicitly passed AND doesn't match the git remote
+ match repoExplicit?, detectedRepo? with
+ | some explicitRepo, some detected =>
+ if explicitRepo != detected then
+ return s!"--repo={explicitRepo} (overrides detected git remote: {detected})"
+ | _, _ => pure ()
+
+ return "unknown reason"
+
+/--
+Print the non-default-scope warning if any of the three conditions hold.
+
+Called before a read (`cache get`). The warning is informational only — it
+never prompts, so it stays safe to run in CI.
+-/
+def warnIfNonDefaultScope (repoExplicit? detectedRepo? : Option String)
+ (cliCacheFromOverride? : Option (List Container)) (resolvedRepo : String)
+ (unsafeWindow? : Option Nat := none) :
+ IO Unit := do
+ if (← shouldWarnNonDefaultScope repoExplicit? detectedRepo? cliCacheFromOverride? resolvedRepo
+ unsafeWindow?)
+ then
+ let reason ← getNonDefaultScopeReason repoExplicit? detectedRepo? cliCacheFromOverride?
+ resolvedRepo unsafeWindow?
+ printNonDefaultScopeWarning resolvedRepo reason
+
+/--
+If the user is on a commit that hasn't been cached for this fork (no marker
+present at `forks/m/{repo}/{HEAD-sha}`), print an informational note
+explaining the new SHA-scoped behavior and pointing at `cache query`.
+
+Fires only on naive `cache get` invocations:
+- no `--scope=` / `MATHLIB_CACHE_REPO_SCOPE` set (else the user has already
+ picked a scope and the non-default-scope warning is doing the talking)
+- no `--cache-from` override (else they've already taken explicit
+ responsibility for the lookup chain)
+- the resolved repo's default lookup chain reads from `forks` (otherwise SHA
+ scoping is not relevant)
+- HEAD is not already an ancestor of `master`. From a personal-fork checkout
+ sitting on `master` (or an undiverged branch), the fork's SHA-scoped marker is
+ structurally absent, but `master` is first in the fork lookup chain and serves
+ every file by hash — there is nothing fork-specific to build, so the note would
+ be a pure false positive.
+
+One HEAD probe per invocation; the message is stderr-only so it doesn't
+mix with `cache get`'s stdout output.
+
+`repo` is the already-resolved repo (see `resolveRepo`).
+-/
+def informIfHeadNotBuilt (repo : String) : IO Unit := do
+ if (← getRepoScope).isSome then return
+ if (← cacheFromOverride.get).isSome then return
+ unless (defaultContainersForRepo repo).contains Container.forks do return
+ -- HEAD already on (an ancestor of) master: master CI builds these commits and
+ -- the master container (first in the fork lookup chain) serves their artifacts
+ -- by hash, so there is nothing fork-specific to build. The forks marker is
+ -- structurally absent here, which would otherwise trigger a misleading note.
+ if (← headIsAncestorOfMaster) then return
+ let sha ← try getGitCommitHash catch _ => return
+ let hasMarker ← probeContainerForSHA Container.forks repo sha
+ if hasMarker then return
+ let lines : List String := [
+ "",
+ s!"NOTE: no cache found for HEAD ({sha}) on fork {repo}.",
+ "This commit hasn't been built by CI for this fork yet. You'll still",
+ "get cache hits for files that match mathlib's master cache; only",
+ "files unique to this PR will need to be rebuilt.",
+ "",
+ "To use a prior CI run from this fork, find a cached commit:",
+ " lake exe cache query",
+ "",
+ "then re-run with:",
+ " lake exe cache get --scope=",
+ "",
+ "Important: using another commit's scope means trusting the artifacts",
+ "produced at that commit. `cache get` will print a security notice",
+ "when you do.",
+ "",
+ ]
+ for line in lines do
+ IO.eprintln line
+
+end Cache.Requests
diff --git a/lakefile.lean b/lakefile.lean
index 573d0222a7a789..46071184a166ee 100644
--- a/lakefile.lean
+++ b/lakefile.lean
@@ -101,6 +101,12 @@ lean_exe autolabel where
lean_exe cache where
root := `Cache.Main
+/-- `lake exe cache-test` runs the cache tool's unit tests (container URL
+construction, per-repo trust-ordered allowlist, `--cache-from` parsing).
+Runnable standalone — does not require building Mathlib or `MathlibTest`. -/
+lean_exe «cache-test» where
+ root := `Cache.Test
+
/-- `lake exe check-yaml` verifies that all declarations referred to in `docs/*.yaml` files exist. -/
lean_exe «check-yaml» where
srcDir := "scripts"
From 50b70b95b5f7070567a7de4bc2079b25776783b2 Mon Sep 17 00:00:00 2001
From: Garmelon <11077553+Garmelon@users.noreply.github.com>
Date: Mon, 15 Jun 2026 23:23:47 +0000
Subject: [PATCH 0052/1300] chore(CI): fix merge conflict resolution for
nightly-testing (#40257)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Resolving merge conflicts in favor of `nightly-testing` when merging `master` may be convenient for nightly maintainers, but it can lead to changes made on `master` accidentally being dropped again later in the bump PR. For an example case, see #40189 (see [#PR reviews > #39443 `nonempty_preimage_iff` @ 💬](https://leanprover.zulipchat.com/#narrow/channel/144837-PR-reviews/topic/.2339443.20.60nonempty_preimage_iff.60/near/599885755) for an explanation of what happened).
Resolving conflicts in favor of `master` means that `nightly-testing` will rather break than silently undoing changes made on `master`.
Co-authored-by: Joscha
---
.github/workflows/nightly_merge_master.yml | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/nightly_merge_master.yml b/.github/workflows/nightly_merge_master.yml
index 097518d25dcf5a..e371f9e9b4cd10 100644
--- a/.github/workflows/nightly_merge_master.yml
+++ b/.github/workflows/nightly_merge_master.yml
@@ -1,4 +1,4 @@
-# This job merges every commit to `master` into `nightly-testing`, resolving merge conflicts in favor of `nightly-testing`.
+# This job merges every commit to `master` into `nightly-testing`, resolving merge conflicts in favor of `master`.
name: Merge master to nightly
@@ -49,15 +49,17 @@ jobs:
git config user.name "mathlib-nightly-testing[bot]"
git config user.email "mathlib-nightly-testing[bot]@users.noreply.github.com"
- - name: Merge master to nightly favoring nightly changes
+ - name: Merge master to nightly favoring master changes
run: |
cd nightly-testing
git remote add upstream https://github.com/leanprover-community/mathlib4.git
git fetch upstream master
- # Merge master into nightly-testing, resolving conflicts in favor of nightly-testing
+ # Merge master into nightly-testing, resolving conflicts in favor of master.
+ # In the past, we've silently dropped changes made on master because
+ # conflicts were resolved in favor of nightly-testing, which is bad.
# If the merge goes badly, we proceed anyway via '|| true'.
# CI will report failures on the 'nightly-testing' branch direct to Zulip.
- git merge upstream/master --strategy-option ours --no-commit --allow-unrelated-histories || true
+ git merge upstream/master --strategy-option theirs --no-commit --allow-unrelated-histories || true
# We aggressively run `lake update`, to avoid having to do this by hand.
# When Batteries changes break Mathlib, this will likely show up on nightly-testing first.
lake update -v
From 94fba23a1390006d2c960739fec79a580e947d85 Mon Sep 17 00:00:00 2001
From: Moritz Doll <21366319+mcdoll@users.noreply.github.com>
Date: Tue, 16 Jun 2026 01:11:54 +0000
Subject: [PATCH 0053/1300] feat(MeasureTheory): use `Is*Apply` for
`VectorMeasure` (#40450)
Add `FunLike` and `IsApply` instances for `VectorMeasure`, also add a `coe_mk` `simp` lemma.
---
.../MeasureTheory/VectorMeasure/Basic.lean | 137 ++++++++++--------
.../VectorMeasure/BoundedVariation.lean | 3 +-
.../VectorMeasure/Decomposition/Hahn.lean | 6 +-
.../VectorMeasure/Decomposition/Jordan.lean | 23 ++-
.../VectorMeasure/Decomposition/Lebesgue.lean | 10 +-
.../Decomposition/RadonNikodym.lean | 2 +-
.../MeasureTheory/VectorMeasure/Integral.lean | 12 +-
.../VectorMeasure/Variation/Basic.lean | 12 +-
.../VectorMeasure/WithDensity.lean | 8 +-
9 files changed, 109 insertions(+), 104 deletions(-)
diff --git a/Mathlib/MeasureTheory/VectorMeasure/Basic.lean b/Mathlib/MeasureTheory/VectorMeasure/Basic.lean
index 89109bd96cf520..3f8ee9b91f4a05 100644
--- a/Mathlib/MeasureTheory/VectorMeasure/Basic.lean
+++ b/Mathlib/MeasureTheory/VectorMeasure/Basic.lean
@@ -82,10 +82,13 @@ section
variable {M : Type*} [AddCommMonoid M] [TopologicalSpace M]
-attribute [coe] VectorMeasure.measureOf'
+instance : FunLike (VectorMeasure α M) (Set α) M where
+ coe := VectorMeasure.measureOf'
+ coe_injective v w h := by
+ cases v; cases w; congr
-instance instCoeFun : CoeFun (VectorMeasure α M) fun _ => Set α → M :=
- ⟨VectorMeasure.measureOf'⟩
+@[simp]
+theorem coe_mk (v : Set α → M) (h₁) (h₂) (h₃) : (mk v h₁ h₂ h₃ : VectorMeasure α M) = v := rfl
initialize_simps_projections VectorMeasure (measureOf' → apply)
@@ -101,19 +104,15 @@ theorem m_iUnion (v : VectorMeasure α M) {f : ℕ → Set α} (hf₁ : ∀ i, M
(hf₂ : Pairwise (Disjoint on f)) : HasSum (fun i => v (f i)) (v (⋃ i, f i)) :=
v.m_iUnion' hf₁ hf₂
-theorem coe_injective : @Function.Injective (VectorMeasure α M) (Set α → M) (⇑) := fun v w h => by
- cases v
- cases w
- congr
+@[deprecated (since := "2026-06-10")] alias coe_injective := DFunLike.coe_injective
-theorem ext_iff' (v w : VectorMeasure α M) : v = w ↔ ∀ i : Set α, v i = w i := by
- rw [← coe_injective.eq_iff, funext_iff]
+@[deprecated (since := "2026-06-10")] alias ext_iff' := DFunLike.ext_iff
theorem ext_iff (v w : VectorMeasure α M) : v = w ↔ ∀ i : Set α, MeasurableSet i → v i = w i := by
constructor
· rintro rfl _ _
rfl
- · rw [ext_iff']
+ · rw [DFunLike.ext_iff]
intro h i
by_cases hi : MeasurableSet i
· exact h i hi
@@ -271,10 +270,12 @@ def smul (r : R) (v : VectorMeasure α M) : VectorMeasure α M where
instance instSMul : SMul R (VectorMeasure α M) :=
⟨smul⟩
-@[simp]
-theorem coe_smul (r : R) (v : VectorMeasure α M) : ⇑(r • v) = r • ⇑v := rfl
+instance : IsSMulApply R (VectorMeasure α M) (Set α) M where
+ smul_apply _ _ _ := rfl
+
+@[deprecated (since := "2026-06-10")] alias coe_smul := FunLike.coe_smul
-theorem smul_apply (r : R) (v : VectorMeasure α M) (i : Set α) : (r • v) i = r • v i := rfl
+@[deprecated (since := "2026-06-10")] protected alias smul_apply := smul_apply
end SMul
@@ -285,6 +286,9 @@ variable {M : Type*} [AddCommMonoid M] [TopologicalSpace M]
instance instZero : Zero (VectorMeasure α M) :=
⟨⟨0, rfl, fun _ _ => rfl, fun _ _ _ => hasSum_zero⟩⟩
+instance : IsZeroApply (VectorMeasure α M) (Set α) M where
+ zero_apply _ := rfl
+
instance instInhabited : Inhabited (VectorMeasure α M) :=
⟨0⟩
@@ -299,10 +303,9 @@ instance [IsEmpty α] : Subsingleton (VectorMeasure α M) :=
theorem eq_zero_of_isEmpty [IsEmpty α] (μ : VectorMeasure α M) : μ = 0 :=
Subsingleton.elim μ 0
-@[simp]
-theorem coe_zero : ⇑(0 : VectorMeasure α M) = 0 := rfl
+@[deprecated (since := "2026-06-10")] alias coe_zero := FunLike.coe_zero
-theorem zero_apply (i : Set α) : (0 : VectorMeasure α M) i = 0 := rfl
+@[deprecated (since := "2026-06-10")] protected alias zero_apply := zero_apply
variable [ContinuousAdd M]
@@ -316,24 +319,21 @@ def add (v w : VectorMeasure α M) : VectorMeasure α M where
instance instAdd : Add (VectorMeasure α M) :=
⟨add⟩
-@[simp]
-theorem coe_add (v w : VectorMeasure α M) : ⇑(v + w) = v + w := rfl
+instance : IsAddApply (VectorMeasure α M) (Set α) M where
+ add_apply _ _ _ := rfl
+
+@[deprecated (since := "2026-06-10")] alias coe_add := FunLike.coe_add
-theorem add_apply (v w : VectorMeasure α M) (i : Set α) : (v + w) i = v i + w i := rfl
+@[deprecated (since := "2026-06-10")] protected alias add_apply := add_apply
instance instAddCommMonoid : AddCommMonoid (VectorMeasure α M) :=
- Function.Injective.addCommMonoid _ coe_injective coe_zero coe_add fun _ _ => coe_smul _ _
+ fast_instance% FunLike.addCommMonoid
-/-- `(⇑)` is an `AddMonoidHom`. -/
-@[simps]
-def coeFnAddMonoidHom : VectorMeasure α M →+ Set α → M where
- toFun := (⇑)
- map_zero' := coe_zero
- map_add' := coe_add
+@[deprecated (since := "2026-06-10")] alias coeFnAddMonoidHom := FunLike.coeAddMonoidHom
-@[simp]
-theorem coe_finsetSum {ι} (I : Finset ι) (v : ι → VectorMeasure α M) :
- ⇑(∑ i ∈ I, v i) = ∑ i ∈ I, ⇑(v i) := map_sum coeFnAddMonoidHom v I
+@[deprecated (since := "2026-06-10")] alias coeFnAddMonoidHom_apply := FunLike.coeAddMonoidHom_apply
+
+@[deprecated (since := "2026-06-10")] alias coe_finsetSum := FunLike.coe_sum
end AddCommMonoid
@@ -351,10 +351,12 @@ def neg (v : VectorMeasure α M) : VectorMeasure α M where
instance instNeg : Neg (VectorMeasure α M) :=
⟨neg⟩
-@[simp]
-theorem coe_neg (v : VectorMeasure α M) : ⇑(-v) = -v := rfl
+instance : IsNegApply (VectorMeasure α M) (Set α) M where
+ neg_apply _ _ := rfl
-theorem neg_apply (v : VectorMeasure α M) (i : Set α) : (-v) i = -v i := rfl
+@[deprecated (since := "2026-06-10")] alias coe_neg := FunLike.coe_neg
+
+@[deprecated (since := "2026-06-10")] protected alias neg_apply := neg_apply
/-- The difference of two vector measure is a vector measure. -/
def sub (v w : VectorMeasure α M) : VectorMeasure α M where
@@ -366,14 +368,14 @@ def sub (v w : VectorMeasure α M) : VectorMeasure α M where
instance instSub : Sub (VectorMeasure α M) :=
⟨sub⟩
-@[simp]
-theorem coe_sub (v w : VectorMeasure α M) : ⇑(v - w) = v - w := rfl
+instance : IsSubApply (VectorMeasure α M) (Set α) M where
+ sub_apply _ _ _ := rfl
+
+@[deprecated (since := "2026-06-10")] alias coe_sub := FunLike.coe_sub
-theorem sub_apply (v w : VectorMeasure α M) (i : Set α) : (v - w) i = v i - w i := rfl
+@[deprecated (since := "2026-06-10")] protected alias sub_apply := sub_apply
-instance instAddCommGroup : AddCommGroup (VectorMeasure α M) :=
- Function.Injective.addCommGroup _ coe_injective coe_zero coe_add coe_neg coe_sub
- (fun _ _ => coe_smul _ _) fun _ _ => coe_smul _ _
+instance instAddCommGroup : AddCommGroup (VectorMeasure α M) := fast_instance% FunLike.addCommGroup
end AddCommGroup
@@ -383,7 +385,7 @@ variable {M : Type*} [AddCommMonoid M] [TopologicalSpace M]
variable {R : Type*} [Semiring R] [DistribMulAction R M] [ContinuousConstSMul R M]
instance instDistribMulAction [ContinuousAdd M] : DistribMulAction R (VectorMeasure α M) :=
- Function.Injective.distribMulAction coeFnAddMonoidHom coe_injective coe_smul
+ fast_instance% FunLike.distribMulAction
end DistribMulAction
@@ -393,7 +395,7 @@ variable {M : Type*} [AddCommMonoid M] [TopologicalSpace M]
variable {R : Type*} [Semiring R] [Module R M] [ContinuousConstSMul R M]
instance instModule [ContinuousAdd M] : Module R (VectorMeasure α M) :=
- Function.Injective.module R coeFnAddMonoidHom coe_injective coe_smul
+ fast_instance% FunLike.module
end Module
@@ -423,8 +425,8 @@ def dirac (x : β) (v : M) : VectorMeasure β M where
have : Disjoint (f i) (f j) := f_disj hi
grind
-@[simp] lemma dirac_apply_of_mem (hs : MeasurableSet s) (hx : x ∈ s) : dirac x v s = v := by
- simp [dirac, hs, hx]
+@[simp] lemma dirac_apply_of_mem (hs : MeasurableSet s) (hx : x ∈ s) : dirac x v s = v :=
+ if_pos (And.intro hs hx)
@[simp] lemma dirac_apply_of_notMem (hx : x ∉ s) : dirac x v s = 0 := by
simp [dirac, hx]
@@ -439,11 +441,10 @@ end VectorMeasure
namespace Measure
-open Classical in
+open scoped Classical in
/-- A finite measure coerced into a real function is a signed measure. -/
-@[simps]
def toSignedMeasure (μ : Measure α) [hμ : IsFiniteMeasure μ] : SignedMeasure α where
- measureOf' := fun s : Set α => if MeasurableSet s then μ.real s else 0
+ measureOf' s := if MeasurableSet s then μ.real s else 0
empty' := by simp
not_measurable' _ hi := if_neg hi
m_iUnion' f hf₁ hf₂ := by
@@ -451,6 +452,11 @@ def toSignedMeasure (μ : Measure α) [hμ : IsFiniteMeasure μ] : SignedMeasure
rw [ENNReal.tsum_toReal_eq]
exacts [(summable_measure_toReal hf₁ hf₂).hasSum, fun _ ↦ measure_ne_top _ _]
+open scoped Classical in
+@[simp]
+theorem toSignedMeasure_apply (μ : Measure α) [hμ : IsFiniteMeasure μ] (i : Set α) :
+ μ.toSignedMeasure i = if MeasurableSet i then μ.real i else 0 := rfl
+
theorem toSignedMeasure_apply_measurable {μ : Measure α} [IsFiniteMeasure μ] {i : Set α}
(hi : MeasurableSet i) : μ.toSignedMeasure i = μ.real i :=
if_pos hi
@@ -472,30 +478,29 @@ theorem toSignedMeasure_eq_toSignedMeasure_iff {μ ν : Measure α} [IsFiniteMea
@[simp]
theorem toSignedMeasure_zero : (0 : Measure α).toSignedMeasure = 0 := by
- ext i
- simp
+ ext i hi
+ simp [hi]
@[simp]
theorem toSignedMeasure_add (μ ν : Measure α) [IsFiniteMeasure μ] [IsFiniteMeasure ν] :
(μ + ν).toSignedMeasure = μ.toSignedMeasure + ν.toSignedMeasure := by
ext i hi
rw [toSignedMeasure_apply_measurable hi, measureReal_add_apply,
- VectorMeasure.add_apply, toSignedMeasure_apply_measurable hi,
+ _root_.add_apply, toSignedMeasure_apply_measurable hi,
toSignedMeasure_apply_measurable hi]
@[simp]
theorem toSignedMeasure_smul (μ : Measure α) [IsFiniteMeasure μ] (r : ℝ≥0) :
(r • μ).toSignedMeasure = r • μ.toSignedMeasure := by
ext i hi
- rw [toSignedMeasure_apply_measurable hi, VectorMeasure.smul_apply,
+ rw [toSignedMeasure_apply_measurable hi, _root_.smul_apply,
toSignedMeasure_apply_measurable hi, measureReal_nnreal_smul_apply]
rfl
-open Classical in
+open scoped Classical in
/-- A measure is a vector measure over `ℝ≥0∞`. -/
-@[simps]
def toENNRealVectorMeasure (μ : Measure α) : VectorMeasure α ℝ≥0∞ where
- measureOf' := fun i : Set α => if MeasurableSet i then μ i else 0
+ measureOf' i := if MeasurableSet i then μ i else 0
empty' := by simp
not_measurable' _ hi := if_neg hi
m_iUnion' _ hf₁ hf₂ := by
@@ -503,6 +508,11 @@ def toENNRealVectorMeasure (μ : Measure α) : VectorMeasure α ℝ≥0∞ where
MeasureTheory.measure_iUnion hf₂ hf₁]
exact tsum_congr fun n => if_pos (hf₁ n)
+open scoped Classical in
+@[simp]
+theorem toENNRealVectorMeasure_apply (μ : Measure α) (i : Set α) :
+ μ.toENNRealVectorMeasure i = if MeasurableSet i then μ i else 0 := rfl
+
theorem toENNRealVectorMeasure_apply_measurable {μ : Measure α} {i : Set α} (hi : MeasurableSet i) :
μ.toENNRealVectorMeasure i = μ i :=
if_pos hi
@@ -516,13 +526,13 @@ theorem toENNRealVectorMeasure_zero : (0 : Measure α).toENNRealVectorMeasure =
theorem toENNRealVectorMeasure_add (μ ν : Measure α) :
(μ + ν).toENNRealVectorMeasure = μ.toENNRealVectorMeasure + ν.toENNRealVectorMeasure := by
refine MeasureTheory.VectorMeasure.ext fun i hi => ?_
- rw [toENNRealVectorMeasure_apply_measurable hi, add_apply, VectorMeasure.add_apply,
+ rw [toENNRealVectorMeasure_apply_measurable hi, add_apply, _root_.add_apply,
toENNRealVectorMeasure_apply_measurable hi, toENNRealVectorMeasure_apply_measurable hi]
theorem toSignedMeasure_sub_apply {μ ν : Measure α} [IsFiniteMeasure μ] [IsFiniteMeasure ν]
{i : Set α} (hi : MeasurableSet i) :
(μ.toSignedMeasure - ν.toSignedMeasure) i = μ.real i - ν.real i := by
- rw [VectorMeasure.sub_apply, toSignedMeasure_apply_measurable hi,
+ rw [_root_.sub_apply, toSignedMeasure_apply_measurable hi,
Measure.toSignedMeasure_apply_measurable hi]
end Measure
@@ -812,7 +822,7 @@ variable {v : VectorMeasure α M} {i s t : Set α}
theorem restrict_add_restrict_compl (hi : MeasurableSet i) :
v.restrict i + v.restrict iᶜ = v := by
ext A hA
- rw [add_apply, restrict_apply _ hi hA, restrict_apply _ hi.compl hA,
+ rw [_root_.add_apply, restrict_apply _ hi hA, restrict_apply _ hi.compl hA,
← of_union _ (hA.inter hi) (hA.inter hi.compl)]
· simp
· exact disjoint_compl_right.inter_right' A |>.inter_left' A
@@ -820,7 +830,7 @@ theorem restrict_add_restrict_compl (hi : MeasurableSet i) :
theorem restrict_inter_add_sdiff (hs : MeasurableSet s) (ht : MeasurableSet t) :
v.restrict (s ∩ t) + v.restrict (s \ t) = v.restrict s := by
ext u hu
- simp only [add_apply, restrict_apply, hs, hu, hs.inter ht, hs.diff ht]
+ simp only [_root_.add_apply, restrict_apply, hs, hu, hs.inter ht, hs.diff ht]
rw [← of_union (by grind) (hu.inter (hs.inter ht)) (hu.inter (hs.diff ht))]
congr
grind
@@ -1100,7 +1110,7 @@ variable {M : Type*} [TopologicalSpace M] [AddCommMonoid M] [PartialOrder M]
[AddLeftMono M] [ContinuousAdd M]
instance instAddLeftMono : AddLeftMono (VectorMeasure α M) :=
- ⟨fun _ _ _ h i hi => by dsimp; grw [h i hi]⟩
+ ⟨fun _ _ _ h i hi => by simp only [_root_.add_apply]; grw [h i hi]⟩
end
@@ -1159,7 +1169,7 @@ theorem neg_right {N : Type*} [AddCommGroup N] [TopologicalSpace N] [IsTopologic
theorem add [ContinuousAdd M] {v₁ v₂ : VectorMeasure α M} {w : VectorMeasure α N} (hv₁ : v₁ ≪ᵥ w)
(hv₂ : v₂ ≪ᵥ w) : v₁ + v₂ ≪ᵥ w := by
intro s hs
- rw [add_apply, hv₁ hs, hv₂ hs, zero_add]
+ rw [_root_.add_apply, hv₁ hs, hv₂ hs, zero_add]
theorem sub {M : Type*} [AddCommGroup M] [TopologicalSpace M] [IsTopologicalAddGroup M]
{v₁ v₂ : VectorMeasure α M} {w : VectorMeasure α N} (hv₁ : v₁ ≪ᵥ w) (hv₂ : v₂ ≪ᵥ w) :
@@ -1170,7 +1180,7 @@ theorem sub {M : Type*} [AddCommGroup M] [TopologicalSpace M] [IsTopologicalAddG
theorem smul {R : Type*} [Semiring R] [DistribMulAction R M] [ContinuousConstSMul R M] {r : R}
{v : VectorMeasure α M} {w : VectorMeasure α N} (h : v ≪ᵥ w) : r • v ≪ᵥ w := by
intro s hs
- rw [smul_apply, h hs, smul_zero]
+ rw [_root_.smul_apply, h hs, smul_zero]
theorem map [MeasureSpace β] (h : v ≪ᵥ w) (f : α → β) : v.map f ≪ᵥ w.map f := by
by_cases hf : Measurable f
@@ -1233,7 +1243,7 @@ theorem add_left [T2Space N] [ContinuousAdd M] (h₁ : v₁ ⟂ᵥ w) (h₂ : v
obtain ⟨u, hmu, hu₁, hu₂⟩ := h₁
obtain ⟨v, hmv, hv₁, hv₂⟩ := h₂
refine mk (u ∩ v) (hmu.inter hmv) (fun t ht _ => ?_) fun t ht hmt => ?_
- · rw [add_apply, hu₁ _ (Set.subset_inter_iff.1 ht).1, hv₁ _ (Set.subset_inter_iff.1 ht).2,
+ · rw [_root_.add_apply, hu₁ _ (Set.subset_inter_iff.1 ht).1, hv₁ _ (Set.subset_inter_iff.1 ht).2,
zero_add]
· rw [Set.compl_inter] at ht
rw [(_ : t = uᶜ ∩ t ∪ vᶜ \ uᶜ ∩ t),
@@ -1254,7 +1264,7 @@ theorem add_right [T2Space M] [ContinuousAdd N] (h₁ : v ⟂ᵥ w₁) (h₂ : v
theorem smul_right {R : Type*} [Semiring R] [DistribMulAction R N] [ContinuousConstSMul R N]
(r : R) (h : v ⟂ᵥ w) : v ⟂ᵥ r • w :=
let ⟨s, hmeas, hs₁, hs₂⟩ := h
- ⟨s, hmeas, hs₁, fun t ht => by simp only [coe_smul, Pi.smul_apply, hs₂ t ht, smul_zero]⟩
+ ⟨s, hmeas, hs₁, fun t ht => by simp only [_root_.smul_apply, hs₂ t ht, smul_zero]⟩
theorem smul_left {R : Type*} [Semiring R] [DistribMulAction R M] [ContinuousConstSMul R M] (r : R)
(h : v ⟂ᵥ w) : r • v ⟂ᵥ w :=
@@ -1427,8 +1437,7 @@ variable (μ ν : Measure α) [IsFiniteMeasure μ] [IsFiniteMeasure ν] (s : Set
theorem zero_le_toSignedMeasure : 0 ≤ μ.toSignedMeasure := by
rw [← le_restrict_univ_iff_le]
refine restrict_le_restrict_of_subset_le _ _ fun j hj₁ _ => ?_
- simp only [VectorMeasure.coe_zero, Pi.zero_apply, Measure.toSignedMeasure_apply_measurable hj₁,
- measureReal_nonneg]
+ simp [hj₁]
theorem toSignedMeasure_toMeasureOfZeroLE :
μ.toSignedMeasure.toMeasureOfZeroLE Set.univ MeasurableSet.univ
@@ -1442,7 +1451,7 @@ theorem toSignedMeasure_toMeasureOfZeroLE :
theorem toSignedMeasure_restrict_eq_restrict_toSignedMeasure (hs : MeasurableSet s) :
μ.toSignedMeasure.restrict s = (μ.restrict s).toSignedMeasure := by
ext A hA
- simp [VectorMeasure.restrict_apply, toSignedMeasure_apply, hA, hs]
+ simp [VectorMeasure.restrict_apply, hA, hs]
theorem toSignedMeasure_le_toSignedMeasure_iff :
μ.toSignedMeasure ≤ ν.toSignedMeasure ↔ μ ≤ ν := by
diff --git a/Mathlib/MeasureTheory/VectorMeasure/BoundedVariation.lean b/Mathlib/MeasureTheory/VectorMeasure/BoundedVariation.lean
index 6cfb1d2441bbdf..c053ad9ee7f9c9 100644
--- a/Mathlib/MeasureTheory/VectorMeasure/BoundedVariation.lean
+++ b/Mathlib/MeasureTheory/VectorMeasure/BoundedVariation.lean
@@ -139,8 +139,7 @@ lemma vectorMeasure_singleton (hf : BoundedVariationOn f univ) :
have A : hf.exists_vectorMeasure_le_measureAux.choose {a} = 0 := by
rw [← botSet_eq_singleton_of_isBot ha]
exact hf.exists_vectorMeasure_le_measureAux.choose_spec.2.1
- simp only [vectorMeasure, h, ↓reduceDIte, VectorMeasure.coe_add, Pi.add_apply, A,
- zero_add]
+ simp only [vectorMeasure, h, ↓reduceDIte, add_apply, A, zero_add]
rw [VectorMeasure.dirac_apply_of_mem (MeasurableSet.singleton a)]
· simpa only [heqa, sub_right_inj] using (leftLim_eq_of_isBot ha).symm
· simp [heqa]
diff --git a/Mathlib/MeasureTheory/VectorMeasure/Decomposition/Hahn.lean b/Mathlib/MeasureTheory/VectorMeasure/Decomposition/Hahn.lean
index cac5d70445e547..79ff56ab497813 100644
--- a/Mathlib/MeasureTheory/VectorMeasure/Decomposition/Hahn.lean
+++ b/Mathlib/MeasureTheory/VectorMeasure/Decomposition/Hahn.lean
@@ -423,8 +423,7 @@ theorem of_symmDiff_compl_positive_negative {s : SignedMeasure α} {i j : Set α
le_antisymm (hi'.2 (hi.compl.inter hj) Set.inter_subset_left)
(hj'.1 (hi.compl.inter hj) Set.inter_subset_right),
le_antisymm (hj'.2 (hj.compl.inter hi) Set.inter_subset_left)
- (hi'.1 (hj.compl.inter hi) Set.inter_subset_right),
- VectorMeasure.zero_apply, VectorMeasure.zero_apply, zero_add]
+ (hi'.1 (hj.compl.inter hi) Set.inter_subset_right), zero_apply, zero_apply, zero_add]
· exact
Set.disjoint_of_subset_left Set.inter_subset_left
(Set.disjoint_of_subset_right Set.inter_subset_right
@@ -436,8 +435,7 @@ theorem of_symmDiff_compl_positive_negative {s : SignedMeasure α} {i j : Set α
le_antisymm (hi'.2 (hj.inter hi.compl) Set.inter_subset_right)
(hj'.1 (hj.inter hi.compl) Set.inter_subset_left),
le_antisymm (hj'.2 (hi.inter hj.compl) Set.inter_subset_right)
- (hi'.1 (hi.inter hj.compl) Set.inter_subset_left),
- VectorMeasure.zero_apply, VectorMeasure.zero_apply, zero_add]
+ (hi'.1 (hi.inter hj.compl) Set.inter_subset_left), zero_apply, zero_apply, zero_add]
· exact
Set.disjoint_of_subset_left Set.inter_subset_left
(Set.disjoint_of_subset_right Set.inter_subset_right
diff --git a/Mathlib/MeasureTheory/VectorMeasure/Decomposition/Jordan.lean b/Mathlib/MeasureTheory/VectorMeasure/Decomposition/Jordan.lean
index 262cf40971bb08..417bfddffebd7e 100644
--- a/Mathlib/MeasureTheory/VectorMeasure/Decomposition/Jordan.lean
+++ b/Mathlib/MeasureTheory/VectorMeasure/Decomposition/Jordan.lean
@@ -152,16 +152,16 @@ def toSignedMeasure : SignedMeasure α :=
theorem toSignedMeasure_zero : (0 : JordanDecomposition α).toSignedMeasure = 0 := by
ext1 i hi
rw [toSignedMeasure, toSignedMeasure_sub_apply hi, zero_posPart, zero_negPart, sub_self,
- VectorMeasure.coe_zero, Pi.zero_apply]
+ FunLike.coe_zero, Pi.zero_apply]
theorem toSignedMeasure_neg : (-j).toSignedMeasure = -j.toSignedMeasure := by
ext1 i hi
- rw [VectorMeasure.neg_apply, toSignedMeasure, toSignedMeasure, toSignedMeasure_sub_apply hi,
+ rw [neg_apply, toSignedMeasure, toSignedMeasure, toSignedMeasure_sub_apply hi,
toSignedMeasure_sub_apply hi, neg_sub, neg_posPart, neg_negPart]
theorem toSignedMeasure_smul (r : ℝ≥0) : (r • j).toSignedMeasure = r • j.toSignedMeasure := by
ext1 i hi
- rw [VectorMeasure.smul_apply, toSignedMeasure, toSignedMeasure,
+ rw [_root_.smul_apply, toSignedMeasure, toSignedMeasure,
toSignedMeasure_sub_apply hi, toSignedMeasure_sub_apply hi, smul_sub, smul_posPart,
smul_negPart, measureReal_nnreal_smul_apply, measureReal_nnreal_smul_apply]
rfl
@@ -177,7 +177,7 @@ theorem exists_compl_positive_negative :
· refine restrict_le_restrict_of_subset_le _ _ fun A hA hA₁ => ?_
rw [toSignedMeasure, toSignedMeasure_sub_apply hA, measureReal_def,
show j.posPart A = 0 from nonpos_iff_eq_zero.1 (hS₂ ▸ measure_mono hA₁), ENNReal.toReal_zero,
- zero_sub, neg_le, VectorMeasure.zero_apply, neg_zero]
+ zero_sub, neg_le, zero_apply, neg_zero]
exact ENNReal.toReal_nonneg
· refine restrict_le_restrict_of_subset_le _ _ fun A hA hA₁ => ?_
rw [toSignedMeasure, toSignedMeasure_sub_apply hA, measureReal_def (μ := j.negPart),
@@ -261,7 +261,7 @@ theorem subset_negative_null_set (hu : MeasurableSet u) (hv : MeasurableSet v)
s v = 0 := by
rw [← s.neg_le_neg_iff _ hu, neg_zero] at hsu
have := subset_positive_null_set hu hv hw hsu
- simp only [Pi.neg_apply, neg_eq_zero, coe_neg] at this
+ simp only [neg_apply, neg_eq_zero] at this
exact this hw₁ hw₂ hwt
open scoped symmDiff
@@ -277,7 +277,7 @@ theorem of_sdiff_eq_zero_of_symmDiff_eq_zero_positive (hu : MeasurableSet u) (hv
rw [Set.symmDiff_def,
of_union (v := s) (Set.disjoint_of_subset_left sdiff_subset disjoint_sdiff_self_right)
(hu.diff hv) (hv.diff hu)] at hs
- rw [VectorMeasure.zero_apply] at a b
+ rw [zero_apply] at a b
constructor
· linarith
· linarith
@@ -294,7 +294,7 @@ theorem of_sdiff_eq_zero_of_symmDiff_eq_zero_negative (hu : MeasurableSet u) (hv
rw [← s.neg_le_neg_iff _ hu, neg_zero] at hsu
rw [← s.neg_le_neg_iff _ hv, neg_zero] at hsv
have := of_sdiff_eq_zero_of_symmDiff_eq_zero_positive hu hv hsu hsv
- simp only [Pi.neg_apply, neg_eq_zero, coe_neg] at this
+ simp only [neg_apply, neg_eq_zero] at this
exact this hs
@[deprecated (since := "2026-06-03")]
@@ -322,7 +322,7 @@ theorem of_inter_eq_of_symmDiff_eq_zero_negative (hu : MeasurableSet u) (hv : Me
rw [← s.neg_le_neg_iff _ hu, neg_zero] at hsu
rw [← s.neg_le_neg_iff _ hv, neg_zero] at hsv
have := of_inter_eq_of_symmDiff_eq_zero_positive hu hv hw hsu hsv
- simp only [Pi.neg_apply, neg_inj, neg_eq_zero, coe_neg] at this
+ simp only [neg_apply, neg_inj, neg_eq_zero] at this
exact this hs
end
@@ -469,11 +469,10 @@ theorem totalVariation_neg (s : SignedMeasure α) : (-s).totalVariation = s.tota
theorem null_of_totalVariation_zero (s : SignedMeasure α) {i : Set α}
(hs : s.totalVariation i = 0) : s i = 0 := by
rw [totalVariation, Measure.coe_add, Pi.add_apply, add_eq_zero] at hs
- rw [← toSignedMeasure_toJordanDecomposition s, toSignedMeasure, VectorMeasure.coe_sub,
- Pi.sub_apply, Measure.toSignedMeasure_apply, Measure.toSignedMeasure_apply]
by_cases hi : MeasurableSet i
- · simp [hs.1, hs.2, measureReal_def]
- · simp [if_neg hi]
+ · rw [← toSignedMeasure_toJordanDecomposition s, toSignedMeasure]
+ simp [hi, measureReal_def, hs.1, hs.2]
+ · simp [hi]
theorem absolutelyContinuous_ennreal_iff (s : SignedMeasure α) (μ : VectorMeasure α ℝ≥0∞) :
s ≪ᵥ μ ↔ s.totalVariation ≪ μ.ennrealToMeasure := by
diff --git a/Mathlib/MeasureTheory/VectorMeasure/Decomposition/Lebesgue.lean b/Mathlib/MeasureTheory/VectorMeasure/Decomposition/Lebesgue.lean
index e86f35d52639ce..11c223168aeaf3 100644
--- a/Mathlib/MeasureTheory/VectorMeasure/Decomposition/Lebesgue.lean
+++ b/Mathlib/MeasureTheory/VectorMeasure/Decomposition/Lebesgue.lean
@@ -234,15 +234,15 @@ theorem toJordanDecomposition_eq_of_eq_add_withDensity {f : α → ℝ} (hf : Me
refine toJordanDecomposition_eq ?_
simp_rw [JordanDecomposition.toSignedMeasure, hadd]
ext i hi
- rw [VectorMeasure.sub_apply, toSignedMeasure_apply_measurable hi,
+ rw [_root_.sub_apply, toSignedMeasure_apply_measurable hi,
toSignedMeasure_apply_measurable hi, measureReal_add_apply, measureReal_add_apply,
add_sub_add_comm, ← toSignedMeasure_apply_measurable hi,
- ← toSignedMeasure_apply_measurable hi, ← VectorMeasure.sub_apply,
+ ← toSignedMeasure_apply_measurable hi, ← _root_.sub_apply,
← JordanDecomposition.toSignedMeasure, toSignedMeasure_toJordanDecomposition,
- VectorMeasure.add_apply, ← toSignedMeasure_apply_measurable hi,
+ _root_.add_apply, ← toSignedMeasure_apply_measurable hi,
← toSignedMeasure_apply_measurable hi,
withDensityᵥ_eq_withDensity_pos_part_sub_withDensity_neg_part hfi,
- VectorMeasure.sub_apply]
+ _root_.sub_apply]
private theorem haveLebesgueDecomposition_mk' (μ : Measure α) {f : α → ℝ} (hf : Measurable f)
(hfi : Integrable f μ) (htμ : t ⟂ᵥ μ.toENNRealVectorMeasure) (hadd : s = t + μ.withDensityᵥ f) :
@@ -441,7 +441,7 @@ theorem singularPart_add_withDensity_rnDeriv_eq [c.HaveLebesgueDecomposition μ]
c.singularPart μ + μ.withDensityᵥ (c.rnDeriv μ) = c := by
conv_rhs => rw [← c.toComplexMeasure_to_signedMeasure]
ext i hi : 1
- rw [VectorMeasure.add_apply, SignedMeasure.toComplexMeasure_apply]
+ rw [add_apply, SignedMeasure.toComplexMeasure_apply]
apply Complex.ext
· rw [Complex.add_re, withDensityᵥ_apply (c.integrable_rnDeriv μ) hi, ← RCLike.re_eq_complex_re,
← integral_re (c.integrable_rnDeriv μ).integrableOn, RCLike.re_eq_complex_re,
diff --git a/Mathlib/MeasureTheory/VectorMeasure/Decomposition/RadonNikodym.lean b/Mathlib/MeasureTheory/VectorMeasure/Decomposition/RadonNikodym.lean
index e000659ce34903..e61eedeb921bc4 100644
--- a/Mathlib/MeasureTheory/VectorMeasure/Decomposition/RadonNikodym.lean
+++ b/Mathlib/MeasureTheory/VectorMeasure/Decomposition/RadonNikodym.lean
@@ -34,7 +34,7 @@ theorem withDensityᵥ_rnDeriv_eq (s : SignedMeasure α) (μ : Measure α) [Sigm
rw [withDensityᵥ_apply (integrable_rnDeriv _ _) hi, rnDeriv_def, integral_sub,
setIntegral_toReal_rnDeriv h.1 i, setIntegral_toReal_rnDeriv h.2 i]
· conv_rhs => rw [← s.toSignedMeasure_toJordanDecomposition]
- rw [JordanDecomposition.toSignedMeasure, VectorMeasure.sub_apply,
+ rw [JordanDecomposition.toSignedMeasure, _root_.sub_apply,
toSignedMeasure_apply_measurable hi, toSignedMeasure_apply_measurable hi, measureReal_def,
measureReal_def]
all_goals
diff --git a/Mathlib/MeasureTheory/VectorMeasure/Integral.lean b/Mathlib/MeasureTheory/VectorMeasure/Integral.lean
index 227c9783e8f9a8..9c96314769d4c9 100644
--- a/Mathlib/MeasureTheory/VectorMeasure/Integral.lean
+++ b/Mathlib/MeasureTheory/VectorMeasure/Integral.lean
@@ -479,7 +479,7 @@ lemma Integrable.restrict (hf : μ.Integrable f B) {s : Set X} :
@[simp]
theorem integral_zero_vectorMeasure :
- ∫ᵛ x, f x ∂[B; (0 : VectorMeasure X F)] = 0 := by simp [integral]
+ ∫ᵛ x, f x ∂[B; (0 : VectorMeasure X F)] = 0 := by simp [integral, FunLike.coe_zero]
lemma integral_of_isEmpty [IsEmpty X] : ∫ᵛ x, f x ∂[B; μ] = 0 := by simp [eq_zero_of_isEmpty]
@@ -493,7 +493,7 @@ theorem integral_smul_vectorMeasure (f : X → E) (c : ℝ) :
simp [transpose, mapRange_smul, variation_smul]
simp only [this, mul_one]
have : DominatedFinMeasAdditive (μ.transpose B).variation ((c • μ).transpose B) ‖c‖ := by
- simp only [transpose_smul, coe_smul, Real.norm_eq_abs]
+ simp only [transpose_smul, FunLike.coe_smul, Real.norm_eq_abs]
simpa using! (dominatedFinMeasAdditive_cbmApplyMeasure μ B).smul c
rw! [← setToFun_congr_smul_measure' _ this, transpose_smul]
rfl
@@ -522,7 +522,7 @@ theorem integral_finsetSum_vectorMeasure {μ : ι → VectorMeasure X F}
@[integral_simps]
theorem integral_neg_vectorMeasure :
∫ᵛ x, f x ∂[B; -μ] = -∫ᵛ x, f x ∂[B; μ] := by
- simp [integral, ← setToFun_neg']
+ simp [integral, ← setToFun_neg', FunLike.coe_neg]
theorem integral_sub_vectorMeasure (hμ : μ.Integrable f B) (hν : ν.Integrable f B) :
∫ᵛ x, f x ∂[B; μ - ν] = ∫ᵛ x, f x ∂[B; μ] - ∫ᵛ x, f x ∂[B; ν] := by
@@ -568,7 +568,7 @@ variable (f μ) in
@[simp]
theorem integral_zero_cbm :
∫ᵛ x, f x ∂[(0 : E →L[ℝ] F →L[ℝ] G); μ] = 0 := by
- simp [integral]
+ simp [integral, FunLike.coe_zero]
theorem integral_add_cbm (hB : μ.Integrable f B) (hC : μ.Integrable f C) :
∫ᵛ x, f x ∂[B + C; μ] = ∫ᵛ x, f x ∂[B; μ] + ∫ᵛ x, f x ∂[C; μ] :=
@@ -589,7 +589,7 @@ theorem integral_finsetSum_cbm {B : ι → E →L[ℝ] F →L[ℝ] G}
@[integral_simps]
theorem integral_neg_cbm :
∫ᵛ x, f x ∂[-B; μ] = -∫ᵛ x, f x ∂[B; μ] := by
- simp [integral, ← setToFun_neg']
+ simp [integral, ← setToFun_neg', FunLike.coe_neg]
theorem integral_sub_cbm (hB : μ.Integrable f B) (hC : μ.Integrable f C) :
∫ᵛ x, f x ∂[B - C; μ] = ∫ᵛ x, f x ∂[B; μ] - ∫ᵛ x, f x ∂[C; μ] := by
@@ -646,7 +646,7 @@ theorem exists_ne_zero_of_integral_ne_zero
simp only [variation_transpose_lsmul_flip, variation_toSignedMeasure]
apply setToFun_congr_left' _ _ (fun s hs h's ↦ ?_)
simp only [transpose, ContinuousLinearMap.flip_flip, mapRange_apply,
- Measure.toSignedMeasure_apply, hs, ↓reduceIte, LinearMap.toAddMonoidHom_coe,
+ Measure.toSignedMeasure_apply_measurable hs, LinearMap.toAddMonoidHom_coe,
ContinuousLinearMap.coe_coe, weightedSMul]
rfl
diff --git a/Mathlib/MeasureTheory/VectorMeasure/Variation/Basic.lean b/Mathlib/MeasureTheory/VectorMeasure/Variation/Basic.lean
index 90022637bbd5a5..2c0ba7a93d9b1e 100644
--- a/Mathlib/MeasureTheory/VectorMeasure/Variation/Basic.lean
+++ b/Mathlib/MeasureTheory/VectorMeasure/Variation/Basic.lean
@@ -110,7 +110,7 @@ lemma exists_variation_le_add (μ : VectorMeasure X V) {s : Set X} (hs : Measura
theorem enorm_measure_le_variation (μ : VectorMeasure X V) (E : Set X) :
‖μ E‖ₑ ≤ variation μ E := by
by_cases hE : MeasurableSet E
- swap; · simp [μ.not_measurable' hE]
+ swap; · simp [hE]
by_cases hE' : (⟨E, hE⟩ : Subtype MeasurableSet) = ⊥
· simp_all
simp only [variation_apply, preVariation, ennrealToMeasure_apply hE, ennrealPreVariation_apply]
@@ -120,7 +120,7 @@ theorem enorm_measure_le_variation (μ : VectorMeasure X V) (E : Set X) :
@[simp]
lemma variation_zero : (0 : VectorMeasure X V).variation = 0 := by
- simp only [variation, coe_zero, Pi.zero_apply, enorm_zero]
+ simp only [variation, zero_apply, enorm_zero]
exact preVariation_zero
lemma absolutelyContinuous (μ : VectorMeasure X V) : μ ≪ᵥ μ.ennrealVariation := by
@@ -128,7 +128,7 @@ lemma absolutelyContinuous (μ : VectorMeasure X V) : μ ≪ᵥ μ.ennrealVariat
by_cases hsm : MeasurableSet s
· suffices ‖μ s‖ₑ ≤ 0 by simp_all
grw [enorm_measure_le_variation, ← ennrealVariation_apply _ hsm, hs]
- · exact μ.not_measurable' hsm
+ · exact μ.not_measurable hsm
lemma variation_apply_le_of_forall_enorm_le {m : Measure X} (hs : MeasurableSet s)
(h : ∀ E, MeasurableSet E → E ⊆ s → ‖μ E‖ₑ ≤ m E) :
@@ -273,7 +273,7 @@ lemma variation_sub_le : (μ - ν).variation ≤ μ.variation + ν.variation :=
private lemma variation_smul_le {𝕜 : Type*} [NormedField 𝕜] [NormedSpace 𝕜 V] {c : 𝕜} :
(c • μ).variation ≤ ‖c‖₊ • μ.variation := by
apply variation_le_of_forall_enorm_le (fun s hs ↦ ?_)
- simp only [coe_smul, Pi.smul_apply, enorm_smul, Measure.smul_apply, Measure.nnreal_smul_coe_apply]
+ simp only [smul_apply, enorm_smul, Measure.smul_apply, Measure.nnreal_smul_coe_apply]
grw [enorm_measure_le_variation, enorm_eq_nnnorm]
lemma variation_smul {𝕜 : Type*} [NormedField 𝕜] [NormedSpace 𝕜 V] {c : 𝕜} :
@@ -310,10 +310,10 @@ instance {x : X} {v : V} : IsFiniteMeasure (VectorMeasure.dirac x v).variation :
μ.toSignedMeasure.variation = μ := by
apply le_antisymm
· apply variation_le_of_forall_enorm_le (fun s hs ↦ ?_)
- simp [Measure.toSignedMeasure_apply, hs, Measure.real, Real.enorm_eq_ofReal]
+ simp [hs, Measure.real, Real.enorm_eq_ofReal]
· apply Measure.le_iff.2 (fun s hs ↦ ?_)
apply le_trans ?_ (enorm_measure_le_variation _ _)
- simp [Measure.toSignedMeasure_apply, hs, Measure.real, Real.enorm_eq_ofReal]
+ simp [hs, Measure.real, Real.enorm_eq_ofReal]
end NormedAddCommGroup
diff --git a/Mathlib/MeasureTheory/VectorMeasure/WithDensity.lean b/Mathlib/MeasureTheory/VectorMeasure/WithDensity.lean
index 595c05309b37e4..a6fa2b886b0531 100644
--- a/Mathlib/MeasureTheory/VectorMeasure/WithDensity.lean
+++ b/Mathlib/MeasureTheory/VectorMeasure/WithDensity.lean
@@ -70,7 +70,7 @@ theorem withDensityᵥ_zero : μ.withDensityᵥ (0 : α → E) = 0 := by
theorem withDensityᵥ_neg : μ.withDensityᵥ (-f) = -μ.withDensityᵥ f := by
by_cases hf : Integrable f μ
· ext1 i hi
- rw [VectorMeasure.neg_apply, withDensityᵥ_apply hf hi, ← integral_neg,
+ rw [_root_.neg_apply, withDensityᵥ_apply hf hi, ← integral_neg,
withDensityᵥ_apply hf.neg hi]
simp only [Pi.neg_apply]
· rw [withDensityᵥ, withDensityᵥ, dif_neg hf, dif_neg, neg_zero]
@@ -83,7 +83,7 @@ theorem withDensityᵥ_neg' : (μ.withDensityᵥ fun x => -f x) = -μ.withDensit
theorem withDensityᵥ_add (hf : Integrable f μ) (hg : Integrable g μ) :
μ.withDensityᵥ (f + g) = μ.withDensityᵥ f + μ.withDensityᵥ g := by
ext1 i hi
- rw [withDensityᵥ_apply (hf.add hg) hi, VectorMeasure.add_apply, withDensityᵥ_apply hf hi,
+ rw [withDensityᵥ_apply (hf.add hg) hi, _root_.add_apply, withDensityᵥ_apply hf hi,
withDensityᵥ_apply hg hi]
simp_rw [Pi.add_apply]
rw [integral_add]
@@ -108,7 +108,7 @@ theorem withDensityᵥ_smul {𝕜 : Type*} [NontriviallyNormedField 𝕜] [Norme
[SMulCommClass ℝ 𝕜 E] (f : α → E) (r : 𝕜) : μ.withDensityᵥ (r • f) = r • μ.withDensityᵥ f := by
by_cases hf : Integrable f μ
· ext1 i hi
- rw [withDensityᵥ_apply (hf.smul r) hi, VectorMeasure.smul_apply, withDensityᵥ_apply hf hi, ←
+ rw [withDensityᵥ_apply (hf.smul r) hi, _root_.smul_apply, withDensityᵥ_apply hf hi, ←
integral_smul r f]
simp only [Pi.smul_apply]
· by_cases hr : r = 0
@@ -195,7 +195,7 @@ theorem withDensityᵥ_eq_withDensity_pos_part_sub_withDensity_neg_part {f : α
ext i hi
rw [withDensityᵥ_apply hfi hi,
integral_eq_lintegral_pos_part_sub_lintegral_neg_part hfi.integrableOn,
- VectorMeasure.sub_apply, toSignedMeasure_apply_measurable hi,
+ _root_.sub_apply, toSignedMeasure_apply_measurable hi,
toSignedMeasure_apply_measurable hi, measureReal_def, measureReal_def,
withDensity_apply _ hi, withDensity_apply _ hi]
From 71a914d3d98f2e1720b90f38818c0b5687272e9a Mon Sep 17 00:00:00 2001
From: Whysoserioushah <109107491+Whysoserioushah@users.noreply.github.com>
Date: Tue, 16 Jun 2026 07:15:39 +0000
Subject: [PATCH 0054/1300]
feat(LinearAlgebra/Matrix/GeneralLinear/Projective): add iso between psl and
pgl when algebraically closed (#40605)
This PR is split and generalised from [FLT#1020](https://github.com/ImperialCollegeLondon/FLT/pull/1020)
---
.../Matrix/GeneralLinearGroup/Defs.lean | 8 ++
.../Matrix/GeneralLinearGroup/Projective.lean | 102 +++++++++++++++++-
2 files changed, 107 insertions(+), 3 deletions(-)
diff --git a/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/Defs.lean b/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/Defs.lean
index 6f87c1ce89d19a..5572c3144c2f05 100644
--- a/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/Defs.lean
+++ b/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/Defs.lean
@@ -88,6 +88,14 @@ theorem det_scalar (u : Rˣ) : det (scalar n u) = u ^ Fintype.card n := by
ext
simp
+lemma det_surjective [Nonempty n] : Function.Surjective (det : GL n R → Rˣ) := fun r ↦ by
+ obtain ⟨i⟩ := ‹Nonempty n›
+ refine ⟨⟨diagonal fun j ↦ if j = i then r else 1, diagonal fun j ↦ if j = i then r⁻¹.1 else 1,
+ ?_, ?_⟩, by simp [det]⟩
+ <;> simp only [diagonal_mul_diagonal, mul_ite, ite_mul, Units.mul_inv, one_mul, mul_one,
+ diagonal_eq_one]
+ <;> funext j <;> split_ifs <;> simp
+
/-- The groups `GL n R` (notation for `Matrix.GeneralLinearGroup n R`) and
`LinearMap.GeneralLinearGroup R (n → R)` are multiplicatively equivalent -/
def toLin : GL n R ≃* LinearMap.GeneralLinearGroup R (n → R) :=
diff --git a/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/Projective.lean b/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/Projective.lean
index 5127ad2bb5304d..e71b01a2ac3414 100644
--- a/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/Projective.lean
+++ b/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/Projective.lean
@@ -5,9 +5,10 @@ Authors: Yury G. Kudryashov
-/
module
-public import Mathlib.LinearAlgebra.Matrix.GeneralLinearGroup.Defs
public import Mathlib.Data.Sign.Basic
-import Mathlib.LinearAlgebra.Matrix.GeneralLinearGroup.Basic
+public import Mathlib.FieldTheory.IsAlgClosed.Basic
+public import Mathlib.LinearAlgebra.Matrix.GeneralLinearGroup.Basic
+public import Mathlib.LinearAlgebra.Matrix.ProjectiveSpecialLinearGroup
/-!
# Projective general linear group
@@ -40,11 +41,19 @@ namespace ProjGenLinGroup
variable {n R : Type*} [Fintype n] [DecidableEq n] [CommRing R]
/-- The natural projection from `GL n R` to `PGL n R`. -/
-def mk : GL n R →* PGL(n, R) := QuotientGroup.mk' _
+def mk : GL n R →* PGL(n, R) := QuotientGroup.mk' (Subgroup.center (GL n R))
theorem mk_surjective : Function.Surjective (mk : GL n R → PGL(n, R)) :=
Quotient.mk_surjective
+lemma mk_eq_mk_iff' {g₁ g₂ : GL n R} :
+ mk g₁ = mk g₂ ↔ ∃ z ∈ Subgroup.center (GL n R), g₁ * z = g₂ :=
+ QuotientGroup.mk'_eq_mk' (Subgroup.center (GL n R))
+
+lemma mk_eq_mk_iff {g₁ g₂ : GL n R} :
+ mk g₁ = mk g₂ ↔ ∃ u : Rˣ, g₁ * .scalar n u = g₂ := by
+ simp [mk_eq_mk_iff', Matrix.GeneralLinearGroup.center_eq_range_scalar]
+
@[simp]
theorem ker_mk : mk.ker = Subgroup.center (GL n R) := QuotientGroup.ker_mk' _
@@ -62,6 +71,93 @@ theorem induction_on {motive : PGL(n, R) → Prop} (g : PGL(n, R))
(mk : ∀ g : GL n R, motive (ProjGenLinGroup.mk g)) : motive g :=
Quotient.inductionOn g mk
+section isoPSL
+
+open Matrix.SpecialLinearGroup
+
+/-- The natural inclusion map from `PSL(n, R)` to `PGL(n, R)` induced by the inclusion
+ map from `SL(n, R)` to `GL(n, R)`. -/
+@[expose]
+def _root_.Matrix.ProjectiveSpecialLinearGroup.toPGL :
+ ProjectiveSpecialLinearGroup n R →* PGL(n, R) :=
+ QuotientGroup.lift _ (mk.comp toGL) fun x hx ↦ by
+ simp only [mem_center_iff, scalar_apply, MonoidHom.mem_ker, MonoidHom.coe_comp,
+ Function.comp_apply, mk_eq_one, GeneralLinearGroup.mem_center_iff_val_mem_range_scalar,
+ coe_GL_coe_matrix, Set.mem_range] at hx ⊢
+ exact ⟨hx.choose, hx.choose_spec.2⟩
+
+@[simp]
+lemma _root_.Matrix.ProjectiveSpecialLinearGroup.toPGL_mk (g : SpecialLinearGroup n R) :
+ ProjectiveSpecialLinearGroup.toPGL g = mk (toGL g) := rfl
+
+lemma _root_.Matrix.ProjectiveSpecialLinearGroup.toPGL_injective :
+ Function.Injective (ProjectiveSpecialLinearGroup.toPGL (n := n) (R := R)) := fun x y h ↦ by
+ induction x using QuotientGroup.induction_on with | H x =>
+ induction y using QuotientGroup.induction_on with | H y =>
+ simp only [ProjectiveSpecialLinearGroup.toPGL_mk, mk_eq_mk_iff] at h
+ rw [← QuotientGroup.mk'_apply, ← QuotientGroup.mk'_apply]
+ simp only [QuotientGroup.mk'_eq_mk', mem_center_iff]
+ obtain ⟨u, hu'⟩ := h
+ have hu : u.1 ^ Fintype.card n = 1 := by
+ simpa [Units.ext_iff] using congr(Matrix.GeneralLinearGroup.det $hu')
+ set z : SpecialLinearGroup n R := ⟨scalar n u.1, by simpa using hu⟩ with hz_eq
+ have hz : (GeneralLinearGroup.scalar n) u = toGL z := by ext; simp [hz_eq]
+ refine ⟨z, ⟨u.1, hu, by simp [hz_eq]⟩, ?_⟩
+ rwa [hz, ← map_mul, toGL_inj] at hu'
+
+lemma _root_.Matrix.ProjectiveSpecialLinearGroup.toPGL_surj_of_roots
+ (hR : ∀ r : Rˣ, ∃ k : Rˣ, k ^ Fintype.card n = r) :
+ Function.Surjective (ProjectiveSpecialLinearGroup.toPGL (n := n) (R := R)) := fun g ↦ by
+ induction g using Matrix.ProjGenLinGroup.induction_on with | mk g =>
+ obtain ⟨r, hr⟩ : ∃ r : Rˣ, r ^ Fintype.card n * g.det = 1 := by
+ obtain ⟨r, hr⟩ := hR g.det⁻¹
+ exact ⟨r, by simpa [mul_eq_one_iff_eq_inv] using hr⟩
+ simp only [Units.ext_iff, Units.val_mul, Units.val_pow_eq_pow_val,
+ GeneralLinearGroup.val_det_apply, ← Matrix.det_smul g.1 r.1, Units.val_one] at hr
+ use QuotientGroup.mk ⟨r.1 • g.1, hr⟩
+ simp only [ProjectiveSpecialLinearGroup.toPGL_mk, mk_eq_mk_iff]
+ refine ⟨r⁻¹, Units.ext ?_⟩
+ simp only [Units.val_mul, coe_GL_coe_matrix,GeneralLinearGroup.val_scalar_apply]
+ simp [← Matrix.mul_smul, ← Matrix.diagonal_smul, Pi.smul_def, smul_eq_mul]
+
+lemma _root_.Matrix.ProjectiveSpecialLinearGroup.toPGL_surj_iff [Nonempty n] :
+ Function.Surjective (ProjectiveSpecialLinearGroup.toPGL (n := n) (R := R)) ↔
+ ∀ r : Rˣ, ∃ k : Rˣ, k ^ Fintype.card n = r := by
+ refine ⟨fun h r ↦ ?_, ProjectiveSpecialLinearGroup.toPGL_surj_of_roots⟩
+ obtain ⟨A, hA⟩ := GeneralLinearGroup.det_surjective (n := n) r
+ obtain ⟨X, hX⟩ := h (.mk A)
+ induction X using QuotientGroup.induction_on with | H X =>
+ obtain ⟨u, hu⟩ : ∃ u, toGL X * (GeneralLinearGroup.scalar n) u = A := by
+ simpa [mk_eq_mk_iff] using hX
+ exact ⟨u, by simpa [hA] using congr(Matrix.GeneralLinearGroup.det $hu)⟩
+
+open Polynomial in
+/-- An isomorphism between `PGL(n, F)` and `PSL(n, F)` in the case of an algebraically closed field
+ induced from the natural inclusion map. -/
+noncomputable def isoPSLOfAlgClosedOfNonempty [Nonempty n] {F : Type*} [Field F] [IsAlgClosed F] :
+ PGL(n, F) ≃* ProjectiveSpecialLinearGroup n F :=
+ MulEquiv.symm (MulEquiv.ofBijective Matrix.ProjectiveSpecialLinearGroup.toPGL
+ ⟨Matrix.ProjectiveSpecialLinearGroup.toPGL_injective,
+ Matrix.ProjectiveSpecialLinearGroup.toPGL_surj_of_roots fun r => by
+ obtain ⟨x, hx⟩ := IsAlgClosed.exists_root (X ^ Fintype.card n - C r.1 : F[X]) (by
+ simp [Polynomial.degree_X_pow_sub_C Fintype.card_pos])
+ have hx' : x ≠ 0 := by aesop
+ exact ⟨⟨x, x⁻¹, mul_inv_cancel₀ hx', inv_mul_cancel₀ hx'⟩, by
+ simpa [Units.ext_iff, sub_eq_zero] using hx⟩⟩)
+
+/-- An isomorphism between `PGL(n, F)` and `PSL(n, F)` in the case of an algebraically closed field
+ induced from the natural inclusion map where when `n` is empty it gives a junk isomorphism. -/
+noncomputable def isoPSLOfAlgClosed {F : Type*} [Field F] [IsAlgClosed F] :
+ PGL(n, F) ≃* ProjectiveSpecialLinearGroup n F :=
+ open scoped Classical in
+ if h : Nonempty n then isoPSLOfAlgClosedOfNonempty else
+ have : IsEmpty n := by simpa using h
+ have : Subsingleton (PGL(n, F)) := mk_surjective.subsingleton
+ MulEquiv.symm (MulEquiv.ofBijective Matrix.ProjectiveSpecialLinearGroup.toPGL
+ ⟨Matrix.ProjectiveSpecialLinearGroup.toPGL_injective, Function.surjective_to_subsingleton _⟩)
+
+end isoPSL
+
variable {M : Type*} [Monoid M]
/-- Lift a monoid homomorphism `f : GL n R →* M` that vanishes on all scalar matrices
From cbeca4d743ba17b79fd585e31f4cd035b555da98 Mon Sep 17 00:00:00 2001
From: Eric Wieser <425260+eric-wieser@users.noreply.github.com>
Date: Tue, 16 Jun 2026 07:39:23 +0000
Subject: [PATCH 0055/1300] doc: adjust references to ring quotients (#40640)
The previous references weren't linked. Linking to `RingCon` rather than `RingQuot` is arguably the better choice, since the former references the latter but not vice versa.
In the longer term I'd like to drop `RingQuot` entirely.
---
Mathlib/RingTheory/Ideal/Quotient/Basic.lean | 2 +-
Mathlib/RingTheory/Ideal/Quotient/Defs.lean | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/Mathlib/RingTheory/Ideal/Quotient/Basic.lean b/Mathlib/RingTheory/Ideal/Quotient/Basic.lean
index 6d3fa90bb8b0d5..ab3b8fc6869fd3 100644
--- a/Mathlib/RingTheory/Ideal/Quotient/Basic.lean
+++ b/Mathlib/RingTheory/Ideal/Quotient/Basic.lean
@@ -18,7 +18,7 @@ public import Mathlib.Tactic.FinCases
This file defines ideal quotients as a special case of submodule quotients and proves some basic
results about these quotients.
-See `Algebra.RingQuot` for quotients of semirings.
+See `RingCon.Quotient` for quotients of (possibly non-commutative) semirings.
## Main definitions
diff --git a/Mathlib/RingTheory/Ideal/Quotient/Defs.lean b/Mathlib/RingTheory/Ideal/Quotient/Defs.lean
index 8a05330c3e763b..352e9b336639a9 100644
--- a/Mathlib/RingTheory/Ideal/Quotient/Defs.lean
+++ b/Mathlib/RingTheory/Ideal/Quotient/Defs.lean
@@ -15,7 +15,7 @@ public import Mathlib.RingTheory.Ideal.Defs
This file defines ideal quotients as a special case of submodule quotients and proves some basic
results about these quotients.
-See `Algebra.RingQuot` for quotients of non-commutative rings.
+See `RingCon.Quotient` for quotients of (possibly non-commutative) semirings.
## Main definitions
From c1f268b3370e727834bc31ca014d3c212eb91b42 Mon Sep 17 00:00:00 2001
From: "mathlib-update-dependencies[bot]"
<258990618+mathlib-update-dependencies[bot]@users.noreply.github.com>
Date: Tue, 16 Jun 2026 08:48:36 +0000
Subject: [PATCH 0056/1300] chore: update Mathlib dependencies 2026-06-16
(#40659)
This PR updates the Mathlib dependencies.
---
lake-manifest.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lake-manifest.json b/lake-manifest.json
index 1db86ab034fd85..aff8d1dac87c1c 100644
--- a/lake-manifest.json
+++ b/lake-manifest.json
@@ -65,7 +65,7 @@
"type": "git",
"subDir": null,
"scope": "leanprover-community",
- "rev": "fa08db58b30eb033edcdab331bba000827f9f785",
+ "rev": "fc38104235ab6cf8a448a74405aa258804ef4e36",
"name": "batteries",
"manifestFile": "lake-manifest.json",
"inputRev": "main",
From 45042665a65650329c9d2db223405af67c227d77 Mon Sep 17 00:00:00 2001
From: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com>
Date: Tue, 16 Jun 2026 09:46:34 +0000
Subject: [PATCH 0057/1300] feat(Data/Sym/Sym2): `fromRel` equivalence with
`Sigma` over a `Quotient` (#34909)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add a non-dependent recursor on members of a `fromRel` set, and the following `Equiv`s:
- The `fromRel` set of a symmetric relation `r` is equivalent to summing that set restricted to fibers of `f`.
- For a relation homomorphism `r →r r'` where `r` is symmetric, the `fromRel` set of `r` is equivalent to summing that set restricted to equivalence classes of `r'` using a `Subtype`.
---
.../Combinatorics/SimpleGraph/Bipartite.lean | 6 +--
Mathlib/Data/Sym/Sym2.lean | 51 +++++++++++++++++++
2 files changed, 54 insertions(+), 3 deletions(-)
diff --git a/Mathlib/Combinatorics/SimpleGraph/Bipartite.lean b/Mathlib/Combinatorics/SimpleGraph/Bipartite.lean
index 04a8ab8e959625..db6aebddef53e0 100644
--- a/Mathlib/Combinatorics/SimpleGraph/Bipartite.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/Bipartite.lean
@@ -487,13 +487,13 @@ theorem encard_edgeSet_completeBipartiteGraph :
def IsBipartiteWith.edgeSetEmbeddingCompleteBipartiteGraph [DecidableRel (· ∈ · : V → Set V → _)]
(hG : G.IsBipartiteWith s t) : G.edgeSet ↪ (completeBipartiteGraph s t).edgeSet where
toFun := fun ⟨e, he⟩ ↦
- e.hrec (fun u v h ↦ hG.mem_of_adj h |>.by_cases
+ e.fromRelNdrec he (sym := G.symm) (fun u v h ↦ hG.mem_of_adj h |>.by_cases
(fun h ↦ ⟨s(.inl ⟨u, h.left⟩, .inr ⟨v, h.right⟩), .inl ⟨rfl, rfl⟩⟩)
(fun h ↦ ⟨s(.inl ⟨v, h.right⟩, .inr ⟨u, h.left⟩), .inl ⟨rfl, rfl⟩⟩)
- ) (fun _ _ ↦ Function.hfunext (by grind) <| by grind [Or.by_cases, hG.disjoint]) he
+ ) <| by grind [Or.by_cases, hG.disjoint]
inj' := by
rintro ⟨⟨⟩⟩ ⟨⟨⟩⟩
- change (if _ : _ then _ else _) = (if _ : _ then _ else _) → _
+ change (dite ..) = (dite ..) → _
grind
end completeBipartiteGraph
diff --git a/Mathlib/Data/Sym/Sym2.lean b/Mathlib/Data/Sym/Sym2.lean
index 77435725873bc5..a70c5c2b5cff24 100644
--- a/Mathlib/Data/Sym/Sym2.lean
+++ b/Mathlib/Data/Sym/Sym2.lean
@@ -606,6 +606,11 @@ theorem fromRel_mono_iff (sym₁ : Std.Symm r₁) (sym₂ : Std.Symm r₂) :
@[gcongr]
alias ⟨_, fromRel_mono⟩ := fromRel_mono_iff
+theorem mem_fromRel_comap {r : β → β → Prop} (sym : Std.Symm r) (f : α → β) (z : Sym2 α) :
+ z ∈ fromRel (sym.comap f) ↔ z.map f ∈ fromRel sym := by
+ cases z
+ simp
+
theorem fromRel_bot : fromRel (α := α) (r := ⊥) inferInstance = ∅ :=
Set.eq_empty_of_forall_notMem <| Sym2.ind <| by simp
@@ -672,6 +677,52 @@ lemma fromRel_relationMap {r : α → α → Prop} (hr : Std.Symm r) (f : α →
forall_exists_index, and_imp]
exact fun c d hcd hc hd ↦ ⟨d, c, symm hcd, hd, hc⟩
+/-- Non-dependent recursor on members of a `fromRel` set -/
+def fromRelNdrec {motive : Sort*} {sym : Std.Symm r} (z : Sym2 α) (hz : z ∈ fromRel sym)
+ (f : (a b : α) → r a b → motive) (h : ∀ (a b : α) (h : r a b), f a b h = f b a (symm h)) :
+ motive :=
+ z.hrec f (fun _ _ ↦ Function.hfunext (sym.iff .. |>.eq) fun _ _ _ ↦ heq_of_eq <| h ..) hz
+
+@[simp]
+theorem fromRelNdrec_mk {motive : Sort*} {sym : Std.Symm r} {a b : α} (hz : r a b)
+ (f : (a b : α) → r a b → motive) (h : ∀ (a b : α) (h : r a b), f a b h = f b a (symm h)) :
+ fromRelNdrec (sym := sym) s(a, b) hz f h = f a b hz :=
+ rfl
+
+/-- The `fromRel` set of a symmetric relation `r` is equivalent to summing that set restricted to
+fibers of a function `f`, given that `f` agrees on elements related by `r`. -/
+@[simps]
+def _root_.Equiv.sigmaFiberFromRel (sym : Std.Symm r) {f : α → β} (hf : r ≤ Setoid.ker f) :
+ fromRel sym ≃ Σ b : β, fromRel (α := { a // f a = b }) <| sym.comap (↑) where
+ toFun z := z.val.fromRelNdrec z.prop
+ (fun a₁ a₂ h ↦ ⟨f a₁, s(⟨a₁, rfl⟩, ⟨a₂, hf a₁ a₂ h |>.symm⟩), h⟩)
+ fun a₁ a₂ h ↦ by
+ rw! [hf a₁ a₂ h, eq_swap]
+ rfl
+ invFun z := ⟨z.snd.val.map (↑), mem_fromRel_comap sym .. |>.mp z.snd.prop⟩
+ left_inv z := by
+ rcases z with ⟨⟨a₁, a₂⟩, h⟩
+ rfl
+ right_inv z := by
+ rcases z with ⟨b, ⟨⟨a₁, rfl⟩, ⟨a₂, ha₂⟩⟩, h⟩
+ rfl
+
+/-- For a relation homomorphism `r →r r'` where `r` is symmetric, the `fromRel` set of `r` is
+equivalent to summing that set restricted to equivalence classes of `r'` using a `Subtype`,
+`Quot` version -/
+@[simps!]
+def _root_.Equiv.sigmaQuotFromRel (sym : Std.Symm r) {r' : β → β → Prop} (f : r →r r') :
+ fromRel sym ≃ Σ q : Quot r', fromRel (α := { x // .mk r' (f x) = q }) <| sym.comap (↑) :=
+ .sigmaFiberFromRel sym fun _ _ h ↦ Quot.sound <| f.map_rel h
+
+/-- For a relation homomorphism `r →r r'` where `r` is symmetric, the `fromRel` set of `r` is
+equivalent to summing that set restricted to equivalence classes of `r'` using a `Subtype`,
+`Quotient` version -/
+@[simps!]
+def _root_.Equiv.sigmaQuotientFromRel (sym : Std.Symm r) {r' : Setoid β} (f : r →r r') :
+ fromRel sym ≃ Σ q : Quotient r', fromRel (α := { x // ⟦f x⟧ = q }) <| sym.comap (↑) :=
+ .sigmaFiberFromRel sym fun _ _ h ↦ Quotient.sound <| f.map_rel h
+
/-- The inverse to `Sym2.fromRel`. Given a set on `Sym2 α`, give a symmetric relation on `α`
(see `Sym2.toRel_symm`). -/
def ToRel (s : Set (Sym2 α)) (x y : α) : Prop :=
From f469a458bcdb9e2d5d58594871deb33644477e62 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ya=C3=ABl=20Dillies?=
Date: Tue, 16 Jun 2026 09:46:37 +0000
Subject: [PATCH 0058/1300] chore(Algebra): use `Even.pow_of_ne_zero` (#40456)
Also use `Even.pow_of_ne_zero` wherever possible.
From APAP
---
Mathlib/NumberTheory/Fermat.lean | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Mathlib/NumberTheory/Fermat.lean b/Mathlib/NumberTheory/Fermat.lean
index 088c6444ea0f2f..c5b50d9a5d15ab 100644
--- a/Mathlib/NumberTheory/Fermat.lean
+++ b/Mathlib/NumberTheory/Fermat.lean
@@ -51,7 +51,7 @@ lemma two_lt_fermatNumber (n : ℕ) : 2 < fermatNumber n := three_le_fermatNumbe
lemma fermatNumber_ne_one (n : ℕ) : fermatNumber n ≠ 1 := by have := three_le_fermatNumber n; lia
theorem odd_fermatNumber (n : ℕ) : Odd (fermatNumber n) :=
- (even_pow.mpr ⟨even_two, (pow_pos two_pos n).ne'⟩).add_one
+ (even_two.pow_of_ne_zero (pow_pos two_pos n).ne').add_one
theorem prod_fermatNumber (n : ℕ) : ∏ k ∈ range n, fermatNumber k = fermatNumber n - 2 := by
induction n with | zero => rfl | succ n hn =>
@@ -175,7 +175,7 @@ lemma fermat_primeFactors_one_lt (n p : ℕ) (hn : 1 < n) (hp : p.Prime)
∃ k, p = k * 2 ^ (n + 2) + 1 := by
have : Fact p.Prime := Fact.mk hp
have hp2 : p ≠ 2 := by
- exact ((even_pow.mpr ⟨even_two, pow_ne_zero n two_ne_zero⟩).add_one).ne_two_of_dvd_nat hpdvd
+ exact (even_two.pow_of_ne_zero <| pow_ne_zero n two_ne_zero).add_one.ne_two_of_dvd_nat hpdvd
have hp8 : p % 8 = 1 := by
obtain ⟨k, rfl⟩ := pow_pow_add_primeFactors_one_lt hp hp2 hpdvd
obtain ⟨n, rfl⟩ := Nat.exists_eq_add_of_le' hn
From d292472f13fc10eb063e6efd93601f976a387055 Mon Sep 17 00:00:00 2001
From: Jireh Loreaux
Date: Tue, 16 Jun 2026 09:46:39 +0000
Subject: [PATCH 0059/1300] feat: expand API for `StarAlgEquiv` (#40517)
This PR adds API for `StarAlgEquiv`. It provides:
+ `StarAlgEquiv.toNonUnitalStarAlgHom`
+ `StarAlgEquiv.toStarAlgHom`
+ `StarAlgEquiv.arrowCongr'` (non-unital morphisms)
+ `StarAlgEquiv.arrowCongr` (unital morphisms)
+ `StarAlgEquiv.ofNonUnitalStarAlgHom` (non-unital morphisms)
+ `StarAlgEquiv.ofStarAlgHom` (unital morphisms); this was pre-existing, but used morphism classes instead of actual morphisms. This PR fixes that.
It also generalizes the type class hypothesis in `StarAlgEquiv.restrictScalars` so that it applies to non-unital algebras.
In addition, `StarAlgEquiv.refl` is protected and its type arguments made explicit.
---
Mathlib/Algebra/Star/StarAlgHom.lean | 210 ++++++++++++++++--
Mathlib/Algebra/Star/Subalgebra.lean | 40 +++-
.../Analysis/InnerProductSpace/Adjoint.lean | 2 +-
3 files changed, 223 insertions(+), 29 deletions(-)
diff --git a/Mathlib/Algebra/Star/StarAlgHom.lean b/Mathlib/Algebra/Star/StarAlgHom.lean
index 4d83cb05d2e607..badc8dfcf88451 100644
--- a/Mathlib/Algebra/Star/StarAlgHom.lean
+++ b/Mathlib/Algebra/Star/StarAlgHom.lean
@@ -723,17 +723,18 @@ theorem toRingEquiv_eq_coe (e : A ≃⋆ₐ[R] B) : e.toRingEquiv = e :=
theorem ext {f g : A ≃⋆ₐ[R] B} (h : ∀ a, f a = g a) : f = g :=
DFunLike.ext f g h
+variable (R A) in
/-- The identity map is a star algebra isomorphism. -/
@[refl]
-def refl : A ≃⋆ₐ[R] A :=
+protected def refl : A ≃⋆ₐ[R] A :=
{ StarRingEquiv.refl (A := A) with
map_smul' := fun _ _ => rfl }
instance : Inhabited (A ≃⋆ₐ[R] A) :=
- ⟨refl⟩
+ ⟨.refl R A⟩
@[simp]
-theorem coe_refl : ⇑(refl : A ≃⋆ₐ[R] A) = id :=
+theorem coe_refl : ⇑(StarAlgEquiv.refl R A) = id :=
rfl
/-- The inverse of a star algebra isomorphism is a star algebra isomorphism. -/
@@ -775,7 +776,7 @@ theorem symm_mk (e : A ≃⋆+* B) (h₁) : dsimp%
rfl
@[simp]
-theorem refl_symm : (StarAlgEquiv.refl : A ≃⋆ₐ[R] A).symm = StarAlgEquiv.refl :=
+theorem refl_symm : (StarAlgEquiv.refl R A).symm = .refl R A :=
rfl
@[simp]
@@ -845,7 +846,7 @@ theorem toAlgEquiv_injective : Function.Injective (toAlgEquiv (R := R) (A := A)
fun _ _ h => ext <| AlgEquiv.congr_fun h
@[simp]
-theorem toAlgEquiv_refl : (refl : A ≃⋆ₐ[R] A).toAlgEquiv = AlgEquiv.refl := rfl
+theorem toAlgEquiv_refl : (StarAlgEquiv.refl R A).toAlgEquiv = AlgEquiv.refl := rfl
/-- Upgrade an algebra equivalence to a ⋆-algebra equivalence given that it preserves the
`star` operation. -/
@@ -876,6 +877,190 @@ end AlgEquiv
end Basic
+section NonUnitalArrowCongr
+
+variable {R A₁ A₂ A₃ A₁' A₂' A₃' : Type*} [Monoid R]
+ [NonUnitalNonAssocSemiring A₁] [DistribMulAction R A₁] [Star A₁]
+ [NonUnitalNonAssocSemiring A₂] [DistribMulAction R A₂] [Star A₂]
+ [NonUnitalNonAssocSemiring A₃] [DistribMulAction R A₃] [Star A₃]
+ [NonUnitalNonAssocSemiring A₁'] [DistribMulAction R A₁'] [Star A₁']
+ [NonUnitalNonAssocSemiring A₂'] [DistribMulAction R A₂'] [Star A₂']
+ [NonUnitalNonAssocSemiring A₃'] [DistribMulAction R A₃'] [Star A₃']
+ (e : A₁ ≃⋆ₐ[R] A₂)
+
+/-- Reintrepret a star algebra equivalence as a non-unital star algebra homomorphism. -/
+@[simps]
+def toNonUnitalStarAlgHom : A₁ →⋆ₙₐ[R] A₂ :=
+ { e with
+ toFun := e
+ map_zero' := map_zero e }
+
+@[simp]
+lemma toNonUnitalStarAlgHom_refl : (StarAlgEquiv.refl R A₁).toNonUnitalStarAlgHom = .id R A₁ :=
+ rfl
+
+@[simp]
+lemma toNonUnitalStarAlgHom_comp (e₁ : A₁ ≃⋆ₐ[R] A₂) (e₂ : A₂ ≃⋆ₐ[R] A₃) :
+ e₂.toNonUnitalStarAlgHom.comp e₁.toNonUnitalStarAlgHom =
+ (e₁.trans e₂).toNonUnitalStarAlgHom := rfl
+
+/-- If `A₁` is equivalent to `A₁'` and `A₂` is equivalent to `A₂'` as star algebras, then the type
+of maps `A₁ →⋆ₙₐ[R] A₂` is equivalent to the type of maps `A₁' →⋆ₙₐ[R] A₂'`.
+
+For unital star algebra homomorphisms, see `StarAlgEquiv.arrowCongr`. -/
+@[simps apply]
+def arrowCongr' (e₁ : A₁ ≃⋆ₐ[R] A₁') (e₂ : A₂ ≃⋆ₐ[R] A₂') :
+ (A₁ →⋆ₙₐ[R] A₂) ≃ (A₁' →⋆ₙₐ[R] A₂') where
+ toFun f := (e₂.toNonUnitalStarAlgHom.comp f).comp e₁.symm.toNonUnitalStarAlgHom
+ invFun f := (e₂.symm.toNonUnitalStarAlgHom.comp f).comp e₁.toNonUnitalStarAlgHom
+ left_inv f := by ext; simp
+ right_inv f := by ext; simp
+
+theorem arrowCongr'_comp (e₁ : A₁ ≃⋆ₐ[R] A₁') (e₂ : A₂ ≃⋆ₐ[R] A₂')
+ (e₃ : A₃ ≃⋆ₐ[R] A₃') (f : A₁ →⋆ₙₐ[R] A₂) (g : A₂ →⋆ₙₐ[R] A₃) :
+ arrowCongr' e₁ e₃ (g.comp f) = (arrowCongr' e₂ e₃ g).comp (arrowCongr' e₁ e₂ f) := by
+ ext
+ simp
+
+@[simp]
+theorem arrowCongr'_refl : arrowCongr' (.refl _ _) (.refl _ _) = Equiv.refl (A₁ →⋆ₙₐ[R] A₂) :=
+ rfl
+
+@[simp]
+theorem arrowCongr'_trans (e₁ : A₁ ≃⋆ₐ[R] A₂) (e₁' : A₁' ≃⋆ₐ[R] A₂')
+ (e₂ : A₂ ≃⋆ₐ[R] A₃) (e₂' : A₂' ≃⋆ₐ[R] A₃') :
+ arrowCongr' (e₁.trans e₂) (e₁'.trans e₂') = (arrowCongr' e₁ e₁').trans (arrowCongr' e₂ e₂') :=
+ rfl
+
+@[simp]
+theorem symm_arrowCongr' (e₁ : A₁ ≃⋆ₐ[R] A₁') (e₂ : A₂ ≃⋆ₐ[R] A₂') :
+ (arrowCongr' e₁ e₂).symm = arrowCongr' e₁.symm e₂.symm :=
+ rfl
+
+/-- Construct a star algebra equivalence from a pair of non-unital star algebra homomorphisms. -/
+@[simps]
+def ofNonUnitalStarAlgHom (f : A₁ →⋆ₙₐ[R] A₂) (g : A₂ →⋆ₙₐ[R] A₁) (h₁ : g.comp f = .id R A₁)
+ (h₂ : f.comp g = .id R A₂) : A₁ ≃⋆ₐ[R] A₂ :=
+ { f with
+ toFun := f
+ invFun := g
+ left_inv x := congr($h₁ x)
+ right_inv x := congr($h₂ x) }
+
+@[simp]
+lemma toNonUnitalStarAlgHom_ofNonUnitalStarAlgHom (f : A₁ →⋆ₙₐ[R] A₂) (g : A₂ →⋆ₙₐ[R] A₁)
+ (h₁ : g.comp f = .id R A₁) (h₂ : f.comp g = .id R A₂) :
+ (ofNonUnitalStarAlgHom f g h₁ h₂).toNonUnitalStarAlgHom = f :=
+ rfl
+
+lemma symm_ofNonUnitalStarAlgHom (f : A₁ →⋆ₙₐ[R] A₂) (g : A₂ →⋆ₙₐ[R] A₁)
+ (h₁ : g.comp f = .id R A₁) (h₂ : f.comp g = .id R A₂) :
+ (ofNonUnitalStarAlgHom f g h₁ h₂).symm = ofNonUnitalStarAlgHom g f h₂ h₁ :=
+ rfl
+
+@[simp]
+lemma toNonUnitalStarAlgHom_symm_ofNonUnitalStarAlgHom (f : A₁ →⋆ₙₐ[R] A₂) (g : A₂ →⋆ₙₐ[R] A₁)
+ (h₁ : g.comp f = .id R A₁) (h₂ : f.comp g = .id R A₂) :
+ (ofNonUnitalStarAlgHom f g h₁ h₂).symm.toNonUnitalStarAlgHom = g :=
+ rfl
+
+end NonUnitalArrowCongr
+
+section Unital
+
+variable {R A₁ A₂ A₃ A₁' A₂' A₃' : Type*}
+ [CommSemiring R] [Semiring A₁] [Semiring A₂] [Semiring A₃]
+ [Semiring A₁'] [Semiring A₂'] [Semiring A₃']
+ [Algebra R A₁] [Algebra R A₂] [Algebra R A₃]
+ [Algebra R A₁'] [Algebra R A₂'] [Algebra R A₃']
+ [Star A₁] [Star A₂] [Star A₃]
+ [Star A₁'] [Star A₂'] [Star A₃']
+ (e : A₁ ≃⋆ₐ[R] A₂)
+
+/-- Reintrepret a star algebra equivalence as a star algebra homomorphism. -/
+@[simps]
+def toStarAlgHom : A₁ →⋆ₐ[R] A₂ :=
+ { e with
+ toFun := e
+ __ := e.toAlgEquiv.toAlgHom }
+
+@[simp]
+lemma toNonUnitalStarAlgHom_toStarAlgHom (e : A₁ ≃⋆ₐ[R] A₂) :
+ e.toStarAlgHom.toNonUnitalStarAlgHom = e.toNonUnitalStarAlgHom :=
+ rfl
+
+@[simp]
+lemma toStarAlgHom_refl : (StarAlgEquiv.refl R A₁).toStarAlgHom = .id R A₁ :=
+ rfl
+
+@[simp]
+lemma toStarAlgHom_comp (e₁ : A₁ ≃⋆ₐ[R] A₂) (e₂ : A₂ ≃⋆ₐ[R] A₃) :
+ e₂.toStarAlgHom.comp e₁.toStarAlgHom = (e₁.trans e₂).toStarAlgHom := rfl
+
+/-- If `A₁` is equivalent to `A₁'` and `A₂` is equivalent to `A₂'` as star algebras, then the type
+of maps `A₁ →⋆ₐ[R] A₂` is equivalent to the type of maps `A₁' →⋆ₐ[R] A₂'`.
+
+For non-unital star algebra homomorphisms, see `StarAlgEquiv.arrowCongr'`. -/
+@[simps apply]
+def arrowCongr (e₁ : A₁ ≃⋆ₐ[R] A₁') (e₂ : A₂ ≃⋆ₐ[R] A₂') : (A₁ →⋆ₐ[R] A₂) ≃ (A₁' →⋆ₐ[R] A₂') where
+ toFun f := (e₂.toStarAlgHom.comp f).comp e₁.symm.toStarAlgHom
+ invFun f := (e₂.symm.toStarAlgHom.comp f).comp e₁.toStarAlgHom
+ left_inv f := by ext; simp
+ right_inv f := by ext; simp
+
+theorem arrowCongr_comp (e₁ : A₁ ≃⋆ₐ[R] A₁') (e₂ : A₂ ≃⋆ₐ[R] A₂')
+ (e₃ : A₃ ≃⋆ₐ[R] A₃') (f : A₁ →⋆ₐ[R] A₂) (g : A₂ →⋆ₐ[R] A₃) :
+ arrowCongr e₁ e₃ (g.comp f) = (arrowCongr e₂ e₃ g).comp (arrowCongr e₁ e₂ f) := by
+ ext
+ simp
+
+@[simp]
+theorem arrowCongr_refl : arrowCongr (.refl _ _) (.refl _ _) = Equiv.refl (A₁ →⋆ₐ[R] A₂) :=
+ rfl
+
+@[simp]
+theorem arrowCongr_trans (e₁ : A₁ ≃⋆ₐ[R] A₂) (e₁' : A₁' ≃⋆ₐ[R] A₂')
+ (e₂ : A₂ ≃⋆ₐ[R] A₃) (e₂' : A₂' ≃⋆ₐ[R] A₃') :
+ arrowCongr (e₁.trans e₂) (e₁'.trans e₂') = (arrowCongr e₁ e₁').trans (arrowCongr e₂ e₂') :=
+ rfl
+
+@[simp]
+theorem symm_arrowCongr (e₁ : A₁ ≃⋆ₐ[R] A₁') (e₂ : A₂ ≃⋆ₐ[R] A₂') :
+ (arrowCongr e₁ e₂).symm = arrowCongr e₁.symm e₂.symm :=
+ rfl
+
+/-- Construct a star algebra equivalence from a pair of star algebra homomorphisms. -/
+@[simps]
+def ofStarAlgHom {R A B : Type*} [CommSemiring R]
+ [Semiring A] [Algebra R A] [Star A] [Semiring B] [Algebra R B] [Star B]
+ (f : A →⋆ₐ[R] B) (g : B →⋆ₐ[R] A) (h₁ : g.comp f = .id R A) (h₂ : f.comp g = .id R B) :
+ A ≃⋆ₐ[R] B :=
+ { f with
+ toFun := f
+ invFun := g
+ left_inv x := congr($h₁ x)
+ right_inv x := congr($h₂ x)
+ map_smul' := map_smul f }
+
+@[simp]
+lemma toStarAlgHom_ofStarAlgHom (f : A₁ →⋆ₐ[R] A₂) (g : A₂ →⋆ₐ[R] A₁)
+ (h₁ : g.comp f = .id R A₁) (h₂ : f.comp g = .id R A₂) :
+ (ofStarAlgHom f g h₁ h₂).toStarAlgHom = f :=
+ rfl
+
+lemma symm_ofStarAlgHom (f : A₁ →⋆ₐ[R] A₂) (g : A₂ →⋆ₐ[R] A₁)
+ (h₁ : g.comp f = .id R A₁) (h₂ : f.comp g = .id R A₂) :
+ (ofStarAlgHom f g h₁ h₂).symm = ofStarAlgHom g f h₂ h₁ :=
+ rfl
+
+@[simp]
+lemma toStarAlgHom_symm_ofStarAlgHom (f : A₁ →⋆ₐ[R] A₂) (g : A₂ →⋆ₐ[R] A₁)
+ (h₁ : g.comp f = .id R A₁) (h₂ : f.comp g = .id R A₂) :
+ (ofStarAlgHom f g h₁ h₂).symm.toStarAlgHom = g :=
+ rfl
+
+end Unital
+
section Bijective
variable {F G R A B : Type*} [Monoid R]
@@ -884,19 +1069,6 @@ variable [NonUnitalNonAssocSemiring B] [DistribMulAction R B] [Star B]
variable [FunLike F A B] [NonUnitalAlgHomClass F R A B] [StarHomClass F A B]
variable [FunLike G B A] [NonUnitalAlgHomClass G R B A] [StarHomClass G B A]
-/-- If a (unital or non-unital) star algebra morphism has an inverse, it is an isomorphism of
-star algebras. -/
-@[simps]
-def ofStarAlgHom (f : F) (g : G) (h₁ : ∀ x, g (f x) = x) (h₂ : ∀ x, f (g x) = x) : A ≃⋆ₐ[R] B where
- toFun := f
- invFun := g
- left_inv := h₁
- right_inv := h₂
- map_add' := map_add f
- map_mul' := map_mul f
- map_smul' := map_smul f
- map_star' := map_star f
-
/-- Promote a bijective star algebra homomorphism to a star algebra equivalence. -/
noncomputable def ofBijective (f : F) (hf : Function.Bijective f) : A ≃⋆ₐ[R] B :=
{
@@ -922,7 +1094,7 @@ variable {S R : Type*} [Mul R] [Add R] [Star R] [SMul S R]
@[simps -isSimp one mul]
instance aut : Group (R ≃⋆ₐ[S] R) where
- one := refl
+ one := .refl _ _
mul a b := b.trans a
one_mul _ := rfl
mul_one _ := rfl
diff --git a/Mathlib/Algebra/Star/Subalgebra.lean b/Mathlib/Algebra/Star/Subalgebra.lean
index 4ca55c7aca647f..993ecdcd7aaba0 100644
--- a/Mathlib/Algebra/Star/Subalgebra.lean
+++ b/Mathlib/Algebra/Star/Subalgebra.lean
@@ -869,6 +869,32 @@ end StarAlgHom
section RestrictScalars
+section Equiv
+
+variable (R : Type*) {S A B : Type*} [CommSemiring R] [CommSemiring S]
+ [NonUnitalNonAssocSemiring A] [NonUnitalNonAssocSemiring B] [MulAction R S] [Module S A]
+ [Module S B] [Module R A] [Module R B] [IsScalarTower R S A] [IsScalarTower R S B]
+ [Star A] [Star B]
+
+/-- Restrict the scalar ring of a star algebra equivalence. -/
+@[simps]
+def StarAlgEquiv.restrictScalars (f : A ≃⋆ₐ[S] B) : A ≃⋆ₐ[R] B :=
+ { (f : A →ₗ[S] B).restrictScalars R, f with
+ toFun := f }
+
+theorem StarAlgEquiv.restrictScalars_injective :
+ Function.Injective (StarAlgEquiv.restrictScalars R : (A ≃⋆ₐ[S] B) → A ≃⋆ₐ[R] B) :=
+ fun _ _ h => ext (DFunLike.congr_fun h ·)
+
+@[simp]
+theorem StarAlgEquiv.toNonUnitalStarAlgHom_restrictScalars (e : A ≃⋆ₐ[S] B) :
+ (e.restrictScalars R).toNonUnitalStarAlgHom = e.toNonUnitalStarAlgHom.restrictScalars R :=
+ rfl
+
+end Equiv
+
+section Unital
+
variable (R : Type*) {S A B : Type*} [CommSemiring R]
[CommSemiring S] [Semiring A] [Semiring B] [Algebra R S] [Algebra S A] [Algebra S B]
[Algebra R A] [Algebra R B] [IsScalarTower R S A] [IsScalarTower R S B] [Star A] [Star B]
@@ -883,16 +909,12 @@ theorem StarAlgHom.restrictScalars_injective :
fun f g h => StarAlgHom.ext fun x =>
show f.restrictScalars R x = g.restrictScalars R x from DFunLike.congr_fun h x
-@[simps]
-def StarAlgEquiv.restrictScalars (f : A ≃⋆ₐ[S] B) : A ≃⋆ₐ[R] B :=
- { (f : A →⋆ₐ[S] B).restrictScalars R, f with
- toFun := f
- map_smul' := map_smul ((f : A →⋆ₐ[S] B).restrictScalars R) }
+@[simp]
+theorem StarAlgEquiv.toStarAlgHom_restrictScalars (e : A ≃⋆ₐ[S] B) :
+ (e.restrictScalars R).toStarAlgHom = e.toStarAlgHom.restrictScalars R :=
+ rfl
-theorem StarAlgEquiv.restrictScalars_injective :
- Function.Injective (StarAlgEquiv.restrictScalars R : (A ≃⋆ₐ[S] B) → A ≃⋆ₐ[R] B) :=
- fun f g h => StarAlgEquiv.ext fun x =>
- show f.restrictScalars R x = g.restrictScalars R x from DFunLike.congr_fun h x
+end Unital
end RestrictScalars
diff --git a/Mathlib/Analysis/InnerProductSpace/Adjoint.lean b/Mathlib/Analysis/InnerProductSpace/Adjoint.lean
index 2f9e018b3e211f..61d64cedb022f6 100644
--- a/Mathlib/Analysis/InnerProductSpace/Adjoint.lean
+++ b/Mathlib/Analysis/InnerProductSpace/Adjoint.lean
@@ -894,7 +894,7 @@ lemma conjStarAlgEquiv_apply (e : H ≃ₗᵢ[𝕜] K) (x : H →L[𝕜] H) :
@[simp] lemma symm_conjStarAlgEquiv (e : H ≃ₗᵢ[𝕜] K) :
e.conjStarAlgEquiv.symm = e.symm.conjStarAlgEquiv := rfl
-@[simp] theorem conjStarAlgEquiv_refl : conjStarAlgEquiv (.refl 𝕜 H) = .refl := rfl
+@[simp] theorem conjStarAlgEquiv_refl : conjStarAlgEquiv (.refl 𝕜 H) = .refl _ _ := rfl
theorem conjStarAlgEquiv_trans {G : Type*} [NormedAddCommGroup G] [InnerProductSpace 𝕜 G]
[CompleteSpace G] (e : H ≃ₗᵢ[𝕜] K) (f : K ≃ₗᵢ[𝕜] G) :
From 95eb1704eebcd56eafb16e5c01969bba5f2597cf Mon Sep 17 00:00:00 2001
From: Christian Merten <136261474+chrisflav@users.noreply.github.com>
Date: Tue, 16 Jun 2026 09:46:42 +0000
Subject: [PATCH 0060/1300] feat(CategoryTheory/Sites): morphism property
induced by precoverage (#40529)
We define the weakest morphism property satisfied by all morphisms in covering families of a given precoverage. This provides a left-adjoint to the existing `Precoverage.morphismProperty`. This is useful when talking about morphism properties local on the source.
---
.../Sites/MorphismProperty.lean | 59 ++++++++++++++++++-
1 file changed, 58 insertions(+), 1 deletion(-)
diff --git a/Mathlib/CategoryTheory/Sites/MorphismProperty.lean b/Mathlib/CategoryTheory/Sites/MorphismProperty.lean
index 270662f5f73331..7d6f1c72ace666 100644
--- a/Mathlib/CategoryTheory/Sites/MorphismProperty.lean
+++ b/Mathlib/CategoryTheory/Sites/MorphismProperty.lean
@@ -46,6 +46,11 @@ lemma ofArrows_mem_precoverage {X : C} {ι : Type*} {Y : ι → C} {f : ∀ i, Y
.ofArrows Y f ∈ precoverage P X ↔ ∀ i, P (f i) :=
⟨fun h i ↦ h ⟨i⟩, fun h _ g ⟨i⟩ ↦ h i⟩
+@[simp, grind =]
+lemma singleton_mem_precoverage {X Y : C} (f : X ⟶ Y) :
+ .singleton f ∈ precoverage P Y ↔ P f := by
+ simp [← Presieve.ofArrows_pUnit.{_, _, 0}]
+
instance [P.ContainsIdentities] [P.RespectsIso] : P.precoverage.HasIsos where
mem_coverings_of_isIso f _ _ _ := fun ⟨⟩ ↦ P.of_isIso f
@@ -145,4 +150,56 @@ end
end HasPullbacks
-end CategoryTheory.MorphismProperty
+end MorphismProperty
+
+/-- The weakest morphism property satisfied by all morphisms in covering families. -/
+def Precoverage.morphismProperty (K : Precoverage C) : MorphismProperty C :=
+ fun _ Y f ↦ ∃ R ∈ K Y, R f
+
+@[simp]
+lemma MorphismProperty.morphismProperty_precoverage (P : MorphismProperty C) :
+ P.precoverage.morphismProperty = P := by
+ ext X Y f
+ exact ⟨fun ⟨R, hR, hf⟩ ↦ hR hf, fun hf ↦ ⟨.singleton f, by simpa⟩⟩
+
+namespace Precoverage
+
+variable {K L : Precoverage C} {P : MorphismProperty C}
+
+lemma morphismProperty_le_iff_le_precoverage :
+ K.morphismProperty ≤ P ↔ K ≤ P.precoverage :=
+ ⟨fun hle _ R hR _ _ hf ↦ hle _ ⟨R, hR, hf⟩, fun hle _ _ _ ⟨_, hR, hf⟩ ↦ hle _ hR hf⟩
+
+lemma galoisConnection_morphismProperty_precoverage :
+ GaloisConnection (Precoverage.morphismProperty (C := C)) MorphismProperty.precoverage :=
+ @Precoverage.morphismProperty_le_iff_le_precoverage _ _
+
+lemma monotone_morphismProperty : Monotone (Precoverage.morphismProperty (C := C)) :=
+ Precoverage.galoisConnection_morphismProperty_precoverage.monotone_l
+
+lemma le_precoverage_morphismProperty : K ≤ K.morphismProperty.precoverage :=
+ galoisConnection_morphismProperty_precoverage.le_u_l _
+
+@[simp]
+lemma morphismProperty_bot : (⊥ : Precoverage C).morphismProperty = ⊥ :=
+ Precoverage.galoisConnection_morphismProperty_precoverage.l_bot
+
+@[simp]
+lemma morphismProperty_sup : (K ⊔ L).morphismProperty = K.morphismProperty ⊔ L.morphismProperty :=
+ Precoverage.galoisConnection_morphismProperty_precoverage.l_sup
+
+instance [K.HasIsos] : K.morphismProperty.ContainsIdentities where
+ id_mem X := ⟨.singleton (𝟙 X), K.mem_coverings_of_isIso _, by simp⟩
+
+@[simp, grind .]
+lemma ZeroHypercover.morphismProperty {X : C} {E : ZeroHypercover.{w} K X} (i : E.I₀) :
+ K.morphismProperty (E.f i) :=
+ ⟨_, E.mem₀, ⟨i⟩⟩
+
+end Precoverage
+
+@[simp]
+lemma MorphismProperty.precoverage_top : (⊤ : MorphismProperty C).precoverage = ⊤ :=
+ Precoverage.galoisConnection_morphismProperty_precoverage.u_top
+
+end CategoryTheory
From a35aeb07d4cd240c7f9bfe98606c27fcf6ad28a5 Mon Sep 17 00:00:00 2001
From: Kamille Bidan <25210160030@m.fudan.edu.cn>
Date: Tue, 16 Jun 2026 10:21:33 +0000
Subject: [PATCH 0061/1300] feat(Topology/DerivedSet): add relative derived set
lemmas (#37374)
Add `relDerivedSet`, `relDerivedSet_subset`, and `IsClosed.relDerivedSet_eq`.
Co-authored-by: NoneMore
---
Mathlib/Topology/DerivedSet.lean | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/Mathlib/Topology/DerivedSet.lean b/Mathlib/Topology/DerivedSet.lean
index aa560743e11750..e63908ffb209c4 100644
--- a/Mathlib/Topology/DerivedSet.lean
+++ b/Mathlib/Topology/DerivedSet.lean
@@ -46,6 +46,16 @@ lemma derivedSet_union (A B : Set X) : derivedSet (A ∪ B) = derivedSet A ∪ d
lemma derivedSet_mono (A B : Set X) (h : A ⊆ B) : derivedSet A ⊆ derivedSet B :=
fun _ hx ↦ hx.mono <| le_principal_iff.mpr <| mem_principal.mpr h
+/-- The relative derived set operator viewed as a monotone self-map of `Set X`. -/
+def relDerivedSet : Set X →o Set X where
+ toFun s := derivedSet s ∩ s
+ monotone' s t h := Set.inter_subset_inter (derivedSet_mono s t h) h
+
+@[simp] lemma relDerivedSet_apply (A : Set X) : relDerivedSet A = derivedSet A ∩ A := rfl
+
+lemma relDerivedSet_subset {A : Set X} : relDerivedSet A ⊆ A :=
+ Set.inter_subset_right
+
theorem Continuous.image_derivedSet {β : Type*} [TopologicalSpace β] {A : Set X} {f : X → β}
(hf1 : Continuous f) (hf2 : Function.Injective f) :
f '' derivedSet A ⊆ derivedSet (f '' A) := by
@@ -68,6 +78,10 @@ lemma isClosed_iff_derivedSet_subset (A : Set X) : IsClosed A ↔ derivedSet A
rw [this, ← accPt_principal_iff_clusterPt] at ha
exact nh (h ha)
+lemma IsClosed.relDerivedSet_eq {A : Set X} (hA : IsClosed A) :
+ relDerivedSet A = derivedSet A := by
+ simpa using (isClosed_iff_derivedSet_subset A).mp hA
+
lemma closure_eq_self_union_derivedSet (A : Set X) : closure A = A ∪ derivedSet A := by
ext
simp [closure_eq_cluster_pts, clusterPt_principal]
@@ -95,6 +109,9 @@ lemma isClosed_derivedSet [T1Space X] (A : Set X) : IsClosed (derivedSet A) := b
lemma preperfect_iff_subset_derivedSet {U : Set X} : Preperfect U ↔ U ⊆ derivedSet U :=
Iff.rfl
+lemma preperfect_iff_eq_relDerivedSet {U : Set X} : Preperfect U ↔ U = relDerivedSet U := by
+ simp [preperfect_iff_subset_derivedSet]
+
lemma perfect_iff_eq_derivedSet {U : Set X} : Perfect U ↔ U = derivedSet U := by
rw [perfect_def, isClosed_iff_derivedSet_subset, preperfect_iff_subset_derivedSet,
← subset_antisymm_iff, eq_comm]
From 3eb2bc7febbb9767d38bb83758c4aee453264c83 Mon Sep 17 00:00:00 2001
From: Suzuka Yu <109365723+Yu-Misaka@users.noreply.github.com>
Date: Tue, 16 Jun 2026 10:35:36 +0000
Subject: [PATCH 0062/1300] feat: a Cartan matrix of a reduced crystallographic
root system cannot have eigenvalue 4 (#39491)
This proves the TODO that a Cartan matrix of a reduced crystallographic root system cannot have eigenvalue 4.
Co-authored-by: Oliver Nash
---
.../LinearAlgebra/RootSystem/CartanMatrix.lean | 17 +++++++++++++++++
.../RootSystem/GeckConstruction/Basis.lean | 3 ---
.../RootSystem/GeckConstruction/Semisimple.lean | 7 +++----
3 files changed, 20 insertions(+), 7 deletions(-)
diff --git a/Mathlib/LinearAlgebra/RootSystem/CartanMatrix.lean b/Mathlib/LinearAlgebra/RootSystem/CartanMatrix.lean
index 29897030287b16..8b913425fb324e 100644
--- a/Mathlib/LinearAlgebra/RootSystem/CartanMatrix.lean
+++ b/Mathlib/LinearAlgebra/RootSystem/CartanMatrix.lean
@@ -8,8 +8,10 @@ module
public import Mathlib.Algebra.CharZero.Infinite
public import Mathlib.Algebra.Module.Submodule.Union
public import Mathlib.Data.Int.Star
+public import Mathlib.LinearAlgebra.Determinant
public import Mathlib.LinearAlgebra.Matrix.BilinearForm
public import Mathlib.LinearAlgebra.Matrix.PosDef
+public import Mathlib.LinearAlgebra.Matrix.ZMatrix
public import Mathlib.LinearAlgebra.RootSystem.Base
public import Mathlib.LinearAlgebra.RootSystem.Finite.Lemmas
public import Mathlib.LinearAlgebra.RootSystem.Finite.Nondegenerate
@@ -208,6 +210,21 @@ lemma exists_cartanMatrix_diagaonal_mul_posDef [DecidableEq ι] [P.IsRootSystem]
rw [← PosDef.transpose_iff] at hd'
aesop
+open LinearMap Module.End in
+lemma det_four_sub_cartanMatrix_ne_zero [DecidableEq ι] [P.IsRootSystem] :
+ (4 - b.cartanMatrix).det ≠ 0 := by
+ suffices ¬ HasEigenvalue b.cartanMatrix.toLin' 4 by
+ have aux : (4 - b.cartanMatrix).toLin' = - (b.cartanMatrix.toLin' - (4 : ℤ) • 1) := by ext; simp
+ rwa [ne_eq, ← det_toLin', det_eq_zero_iff_ker_ne_bot, aux, ker_neg, ← eigenspace_def,
+ ← hasEigenvalue_iff]
+ obtain ⟨d, hd, hdS⟩ := b.exists_cartanMatrix_diagaonal_mul_posDef
+ have aux (i j : b.support) : b.cartanMatrix i j ≤ if i = j then 2 else 0 := by
+ rcases eq_or_ne i j with rfl | hij
+ · simp
+ · simpa [hij] using cartanMatrix_le_zero_of_ne b i j hij
+ have := b.cartanMatrix.lt_two_mul_of_mul_diagonal_posDef_of_for_le_of_hasEigen d hdS hd 2 4 aux
+ aesop
+
/-- A characterisation of the connectedness of the Dynkin diagram for irreducible root pairings. -/
lemma induction_on_cartanMatrix [P.IsReduced] [P.IsIrreducible]
(p : b.support → Prop) {i j : b.support} (hi : p i)
diff --git a/Mathlib/LinearAlgebra/RootSystem/GeckConstruction/Basis.lean b/Mathlib/LinearAlgebra/RootSystem/GeckConstruction/Basis.lean
index d863aa78d52771..aefcf38fd038cd 100644
--- a/Mathlib/LinearAlgebra/RootSystem/GeckConstruction/Basis.lean
+++ b/Mathlib/LinearAlgebra/RootSystem/GeckConstruction/Basis.lean
@@ -91,9 +91,6 @@ def basis :
instance : (cartanSubalgebra' b).IsCartanSubalgebra :=
inferInstanceAs (basis b).cartan.IsCartanSubalgebra
--- TODO drop this after: https://github.com/leanprover-community/mathlib4/issues/28713
-variable [Fact ((4 - b.cartanMatrix).det ≠ 0)]
-
open LieAlgebra.IsKilling in
/-- Up to equivalence, `LieAlgebra.IsKilling.rootSystem` is left inverse to
`RootPairing.GeckConstruction.lieAlgebra`. -/
diff --git a/Mathlib/LinearAlgebra/RootSystem/GeckConstruction/Semisimple.lean b/Mathlib/LinearAlgebra/RootSystem/GeckConstruction/Semisimple.lean
index 9bef0c271679aa..80afc55a86c57b 100644
--- a/Mathlib/LinearAlgebra/RootSystem/GeckConstruction/Semisimple.lean
+++ b/Mathlib/LinearAlgebra/RootSystem/GeckConstruction/Semisimple.lean
@@ -352,9 +352,7 @@ lemma coe_genWeightSpace_zero_eq_span_range_u :
rintro ⟨⟨x, -⟩, hx⟩
exact ⟨1, funext fun j ↦ by simpa using apply_sum_inl_eq_zero_of_mem_span_h i j hx⟩
--- TODO Turn this `Fact` into a lemma: it is always true and may be proved via Perron-Frobenius
--- See https://leanprover.zulipchat.com/#narrow/channel/116395-maths/topic/Eigenvalues.20of.20Cartan.20matrices/near/516844801
-variable [Fact ((4 - b.cartanMatrix).det ≠ 0)] [P.IsReduced] [P.IsIrreducible]
+variable [P.IsReduced] [P.IsIrreducible]
/-- Lemma 4.2 from [Geck](Geck2017). -/
instance instIsIrreducible [Nonempty ι] :
@@ -369,7 +367,8 @@ instance instIsIrreducible [Nonempty ι] :
obtain ⟨c, hc⟩ : ∃ c : b.support → K, ∑ i, c i • u i = x :=
(mem_span_range_iff_exists_fun K).mp <| hU hx
suffices c = 0 by simp [this, ← hc]
- have hCM : (4 - b.cartanMatrix).det ≠ 0 := Fact.out
+ have hCM : (4 - b.cartanMatrix).det ≠ 0 :=
+ RootPairing.Base.det_four_sub_cartanMatrix_ne_zero b
contrapose! hCM
suffices ((Int.castRingHom K).mapMatrix (4 - b.cartanMatrix)).det = 0 by
simpa only [← RingHom.map_det, eq_intCast, Int.cast_eq_zero] using this
From 28c4b7f064ee955494c783d17322da87a4cb5b9f Mon Sep 17 00:00:00 2001
From: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com>
Date: Tue, 16 Jun 2026 11:54:43 +0000
Subject: [PATCH 0063/1300] feat(Combinatorics/SimpleGraph/Operations): a graph
is the supremum of edge graphs (#39561)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- `G = ⨆ e ∈ G.edgeSet, fromEdgeSet {e}`
- `G = sSup { edge u v | (u : V) (v : V) (_ : G.Adj u v) }`
and `edgeSet` & `fromEdgeSet` preserve sup & inf.
---
Mathlib/Combinatorics/SimpleGraph/Basic.lean | 42 +++++++++++++++++++
.../Combinatorics/SimpleGraph/Operations.lean | 9 ++++
2 files changed, 51 insertions(+)
diff --git a/Mathlib/Combinatorics/SimpleGraph/Basic.lean b/Mathlib/Combinatorics/SimpleGraph/Basic.lean
index 8ca659a86b0a21..6d2f1c5de950af 100644
--- a/Mathlib/Combinatorics/SimpleGraph/Basic.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/Basic.lean
@@ -526,6 +526,27 @@ theorem edgeSet_inf : (G₁ ⊓ G₂).edgeSet = G₁.edgeSet ∩ G₂.edgeSet :=
ext ⟨x, y⟩
rfl
+theorem edgeSet_sSup {s : Set (SimpleGraph V)} : (sSup s).edgeSet = ⋃₀ (edgeSet '' s) := by
+ ext ⟨x, y⟩
+ simp
+
+theorem edgeSet_sInf {s : Set (SimpleGraph V)} (h : s.Nonempty) :
+ (sInf s).edgeSet = ⋂₀ (edgeSet '' s) := by
+ ext ⟨x, y⟩
+ have ⟨G, hG⟩ := h
+ simpa using (· G hG |>.ne)
+
+theorem edgeSet_iSup {ι : Sort*} {f : ι → SimpleGraph V} :
+ (⨆ i, f i).edgeSet = ⋃ i, (f i).edgeSet := by
+ ext ⟨x, y⟩
+ simp
+
+theorem edgeSet_iInf {ι : Sort*} [Nonempty ι] {f : ι → SimpleGraph V} :
+ (⨅ i, f i).edgeSet = ⋂ i, (f i).edgeSet := by
+ ext ⟨x, y⟩
+ have ⟨i⟩ := ‹Nonempty ι›
+ simpa using (· i |>.ne)
+
@[simp]
theorem edgeSet_sdiff : (G₁ \ G₂).edgeSet = G₁.edgeSet \ G₂.edgeSet := by
ext ⟨x, y⟩
@@ -670,6 +691,27 @@ theorem fromEdgeSet_union (s t : Set (Sym2 V)) :
ext v w
simp [Set.mem_union, or_and_right]
+theorem fromEdgeSet_sUnion {s : Set (Set (Sym2 V))} :
+ fromEdgeSet (⋃₀ s) = sSup (fromEdgeSet '' s) := by
+ ext u v
+ simp
+ grind
+
+theorem fromEdgeSet_iUnion {ι : Sort*} {f : ι → Set (Sym2 V)} :
+ fromEdgeSet (⋃ i, f i) = ⨆ i, fromEdgeSet (f i) := by
+ ext u v
+ simp
+
+theorem fromEdgeSet_sInter {s : Set (Set (Sym2 V))} :
+ fromEdgeSet (⋂₀ s) = sInf (fromEdgeSet '' s) := by
+ ext u v
+ simp_all
+
+theorem fromEdgeSet_iInter {ι : Sort*} {f : ι → Set (Sym2 V)} :
+ fromEdgeSet (⋂ i, f i) = ⨅ i, fromEdgeSet (f i) := by
+ ext u v
+ simp_all
+
@[simp]
theorem fromEdgeSet_sdiff (s t : Set (Sym2 V)) :
fromEdgeSet (s \ t) = fromEdgeSet s \ fromEdgeSet t := by
diff --git a/Mathlib/Combinatorics/SimpleGraph/Operations.lean b/Mathlib/Combinatorics/SimpleGraph/Operations.lean
index d8fa90b4dfa818..43932a8732e0b1 100644
--- a/Mathlib/Combinatorics/SimpleGraph/Operations.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/Operations.lean
@@ -195,6 +195,15 @@ lemma disjoint_edge {u v : V} : Disjoint G (edge u v) ↔ ¬G.Adj u v := by
lemma sdiff_edge {u v : V} (h : ¬G.Adj u v) : G \ edge u v = G := by
simp [disjoint_edge, h]
+theorem biSup_fromEdgeSet_singleton_eq : ⨆ e ∈ G.edgeSet, fromEdgeSet {e} = G := by
+ simp_rw [← edgeSet_inj, ← iSup_subtype'', edgeSet_iSup, edgeSet_fromEdgeSet, ← Set.iUnion_sdiff,
+ Set.iUnion_coe_set, Set.biUnion_of_singleton]
+ exact Set.disjoint_left.mpr G.edgeSet_subset_compl_diagSet |>.sdiff_eq_left
+
+theorem sSup_edge_eq : sSup { edge u v | (u : V) (v : V) (_ : G.Adj u v) } = G := by
+ refine .trans ?_ G.biSup_fromEdgeSet_singleton_eq
+ simp_rw [edge, ← iSup_subtype'', iSup, Set.range, Subtype.exists, Sym2.exists, mem_edgeSet]
+
theorem Subgraph.spanningCoe_sup_edge_le {H : Subgraph (G ⊔ edge s t)} (h : ¬ H.Adj s t) :
H.spanningCoe ≤ G := by
intro v w hvw
From 2c3e688dffed1b1e5ca4749b9e4732fbf2ac0879 Mon Sep 17 00:00:00 2001
From: Jovan Gerbscheid <56355248+JovanGerb@users.noreply.github.com>
Date: Tue, 16 Jun 2026 12:08:55 +0000
Subject: [PATCH 0064/1300] feat(CategoryTheory/Comma/Basic): use `to_dual`
more (#40355)
This PR continues tagging things about `Comma` with `to_dual`.
This PR also expands the `to_dual_name_hint` syntax so that you can give multiple name hints with a single command, instead of having to repeat the command.
This PR adds `CategoryTheory.Comma.map_obj_hom'`, the dual of `CategoryTheory.Comma.map_obj_hom`.
---
Mathlib/CategoryTheory/Comma/Basic.lean | 198 +++++++++--------------
Mathlib/Order/Interval/Set/Disjoint.lean | 3 +-
Mathlib/Tactic/Translate/ToDual.lean | 11 +-
MathlibTest/Attribute/ToDual.lean | 3 +-
4 files changed, 81 insertions(+), 134 deletions(-)
diff --git a/Mathlib/CategoryTheory/Comma/Basic.lean b/Mathlib/CategoryTheory/Comma/Basic.lean
index eb82d3564b36c4..84918905be1fe7 100644
--- a/Mathlib/CategoryTheory/Comma/Basic.lean
+++ b/Mathlib/CategoryTheory/Comma/Basic.lean
@@ -100,6 +100,8 @@ structure CommaMorphism (X Y : Comma L R) where
attribute [to_dual existing right] CommaMorphism.left
+to_dual_name_hint Left Right, Fst Snd, L R, L₁ R₁, L₂ R₂, A B, F₁ F₂
+
@[to_dual existing w]
theorem CommaMorphism.w' {X Y : Comma R L} (self : CommaMorphism Y X) :
Y.hom ≫ L.map self.right = R.map self.left ≫ X.hom :=
@@ -141,11 +143,11 @@ variable {X Y Z : Comma L R} {f : X ⟶ Y} {g : Y ⟶ Z}
lemma hom_ext (f g : X ⟶ Y) (h₁ : f.left = g.left) (h₂ : f.right = g.right) : f = g :=
CommaMorphism.ext h₁ h₂
-@[to_dual (attr := simp) id_right]
+@[to_dual (attr := simp)]
theorem id_left : (𝟙 X : CommaMorphism X X).left = 𝟙 X.left :=
rfl
-@[to_dual (attr := simp) comp_right]
+@[to_dual (attr := simp)]
theorem comp_left : (f ≫ g).left = f.left ≫ g.left :=
rfl
@@ -154,23 +156,21 @@ end
variable (L) (R)
/-- The functor sending an object `X` in the comma category to `X.left`. -/
-@[simps]
+@[to_dual (reorder := L R) (attr := simps)
+/-- The functor sending an object `X` in the comma category to `X.right`. -/]
def fst : Comma L R ⥤ A where
obj X := X.left
map f := f.left
-/-- The functor sending an object `X` in the comma category to `X.right`. -/
-@[simps]
-def snd : Comma L R ⥤ B where
- obj X := X.right
- map f := f.right
+set_option linter.existingAttributeWarning false in
+attribute [to_dual existing] fst_map
set_option backward.defeqAttrib.useBackward true in
/-- We can interpret the commutative square constituting a morphism in the comma category as a
natural transformation between the functors `fst ⋙ L` and `snd ⋙ R` from the comma category
to `T`, where the components are given by the morphism that constitutes an object of the comma
category. -/
-@[simps]
+@[simps, to_dual self]
def natTrans : fst L R ⋙ L ⟶ snd L R ⋙ R where app X := X.hom
@[simp]
@@ -189,28 +189,19 @@ section
variable {L R} {X Y : Comma L R} (e : X ⟶ Y)
+@[to_dual]
instance [IsIso e] : IsIso e.left :=
(Comma.fst L R).map_isIso e
-instance [IsIso e] : IsIso e.right :=
- (Comma.snd L R).map_isIso e
-
-@[simp, push ←]
+@[to_dual (attr := simp, push ←)]
lemma inv_left [IsIso e] : (inv e).left = inv e.left := by
apply IsIso.eq_inv_of_hom_inv_id
rw [← Comma.comp_left, IsIso.hom_inv_id, id_left]
-@[simp, push ←]
-lemma inv_right [IsIso e] : (inv e).right = inv e.right := by
- apply IsIso.eq_inv_of_hom_inv_id
- rw [← Comma.comp_right, IsIso.hom_inv_id, id_right]
-
+@[to_dual inv_left_hom_right]
lemma left_hom_inv_right [IsIso e] : L.map (e.left) ≫ Y.hom ≫ R.map (inv e.right) = X.hom := by
simp
-lemma inv_left_hom_right [IsIso e] : L.map (inv e.left) ≫ X.hom ≫ R.map e.right = Y.hom := by
- simp
-
end
section
@@ -218,17 +209,14 @@ section
variable {L₁ L₂ L₃ : A ⥤ T} {R₁ R₂ R₃ : B ⥤ T}
/-- Extract the isomorphism between the left objects from an isomorphism in the comma category. -/
-@[simps!]
+@[to_dual (attr := simps!)
+/-- Extract the isomorphism between the right objects from an isomorphism in the comma category. -/]
def leftIso {X Y : Comma L₁ R₁} (α : X ≅ Y) : X.left ≅ Y.left := (fst L₁ R₁).mapIso α
-/-- Extract the isomorphism between the right objects from an isomorphism in the comma category. -/
-@[simps!]
-def rightIso {X Y : Comma L₁ R₁} (α : X ≅ Y) : X.right ≅ Y.right := (snd L₁ R₁).mapIso α
-
/-- Construct an isomorphism in the comma category given isomorphisms of the objects whose forward
directions give a commutative square.
-/
-@[simps]
+@[to_dual none, simps (attr := to_dual none)]
def isoMk {X Y : Comma L₁ R₁} (l : X.left ≅ Y.left) (r : X.right ≅ Y.right)
(h : L₁.map l.hom ≫ Y.hom = X.hom ≫ R₁.map r.hom := by cat_disch) : X ≅ Y where
hom :=
@@ -253,7 +241,7 @@ variable {L' : A' ⥤ T'} {R' : B' ⥤ T'}
set_option backward.isDefEq.respectTransparency false in
/-- The functor `Comma L R ⥤ Comma L' R'` induced by three functors `F₁`, `F₂`, `F`
and two natural transformations `F₁ ⋙ L' ⟶ L ⋙ F` and `R ⋙ F ⟶ F₂ ⋙ R'`. -/
-@[simps]
+@[simps, to_dual self (reorder := A B, 2 4, A' B', 8 10, L R, L' R', F₁ F₂, α β)]
def map : Comma L R ⥤ Comma L' R' where
obj X :=
{ left := F₁.obj X.left
@@ -269,6 +257,15 @@ def map : Comma L R ⥤ Comma L' R' where
dsimp
rw [← F.map_comp_assoc, ← F.map_comp_assoc, φ.w] }
+set_option linter.existingAttributeWarning false in
+attribute [to_dual existing] map_obj_left
+
+set_option backward.isDefEq.respectTransparency false in
+@[to_dual existing (reorder := A B, 2 4, A' B', 8 10, L R, L' R', F₁ F₂, α β) map_obj_hom]
+theorem map_obj_hom' (X : Comma L R) :
+ ((map α β).obj X).hom = (α.app X.left ≫ F.map X.hom) ≫ β.app X.right := by simp
+
+@[to_dual self (reorder := A B, 2 4, A' B', 8 10, L R, L' R', F₁ F₂, α β, 22 23)]
instance faithful_map [F₁.Faithful] [F₂.Faithful] : (map α β).Faithful where
map_injective {X Y} f g h := by
ext
@@ -276,6 +273,7 @@ instance faithful_map [F₁.Faithful] [F₂.Faithful] : (map α β).Faithful whe
· exact F₂.map_injective (congr_arg CommaMorphism.right h)
set_option backward.isDefEq.respectTransparency false in
+@[to_dual self (reorder := A B, 2 4, A' B', 8 10, L R, L' R', F₁ F₂, α β, 23 24, 25 26)]
instance full_map [F.Faithful] [F₁.Full] [F₂.Full] [IsIso α] [IsIso β] : (map α β).Full where
map_surjective {X Y} φ :=
⟨{left := F₁.preimage φ.left
@@ -292,6 +290,7 @@ instance full_map [F.Faithful] [F₁.Full] [F₂.Full] [IsIso α] [IsIso β] : (
by cat_disch⟩
set_option backward.defeqAttrib.useBackward true in
+@[to_dual self (reorder := A B, 2 4, A' B', 8 10, L R, L' R', F₁ F₂, α β, 22 23, 25 26)]
instance essSurj_map [F₁.EssSurj] [F₂.EssSurj] [F.Full] [IsIso α] [IsIso β] :
(map α β).EssSurj where
mem_essImage X :=
@@ -305,38 +304,32 @@ instance essSurj_map [F₁.EssSurj] [F₂.EssSurj] [F.Full] [IsIso α] [IsIso β
IsIso.inv_hom_id, comp_id, IsIso.hom_inv_id_assoc]
rw [← R'.map_comp, Iso.inv_hom_id, R'.map_id, comp_id])⟩⟩
+@[to_dual self (reorder := A B, 2 4, A' B', 8 10, L R, L' R', F₁ F₂, α β, 22 23, 26 27)]
noncomputable instance isEquivalenceMap
[F₁.IsEquivalence] [F₂.IsEquivalence] [F.Faithful] [F.Full] [IsIso α] [IsIso β] :
(map α β).IsEquivalence where
/-- The equality between `map α β ⋙ fst L' R'` and `fst L R ⋙ F₁`,
where `α : F₁ ⋙ L' ⟶ L ⋙ F`. -/
-@[simp]
+@[to_dual (attr := simp) (reorder := α β)
+/-- The equality between `map α β ⋙ snd L' R'` and `snd L R ⋙ F₂`,
+where `β : R ⋙ F ⟶ F₂ ⋙ R'`. -/]
theorem map_fst : map α β ⋙ fst L' R' = fst L R ⋙ F₁ :=
rfl
/-- The isomorphism between `map α β ⋙ fst L' R'` and `fst L R ⋙ F₁`,
where `α : F₁ ⋙ L' ⟶ L ⋙ F`. -/
-@[simps!]
-def mapFst : map α β ⋙ fst L' R' ≅ fst L R ⋙ F₁ :=
- NatIso.ofComponents (fun _ => Iso.refl _) (by simp)
-
-/-- The equality between `map α β ⋙ snd L' R'` and `snd L R ⋙ F₂`,
-where `β : R ⋙ F ⟶ F₂ ⋙ R'`. -/
-@[simp]
-theorem map_snd : map α β ⋙ snd L' R' = snd L R ⋙ F₂ :=
- rfl
-
+@[to_dual (attr := simps!) (reorder := α β)
/-- The isomorphism between `map α β ⋙ snd L' R'` and `snd L R ⋙ F₂`,
-where `β : R ⋙ F ⟶ F₂ ⋙ R'`. -/
-@[simps!]
-def mapSnd : map α β ⋙ snd L' R' ≅ snd L R ⋙ F₂ :=
+where `β : R ⋙ F ⟶ F₂ ⋙ R'`. -/]
+def mapFst : map α β ⋙ fst L' R' ≅ fst L R ⋙ F₁ :=
NatIso.ofComponents (fun _ => Iso.refl _) (by simp)
end
/-- A natural transformation `L₁ ⟶ L₂` induces a functor `Comma L₂ R ⥤ Comma L₁ R`. -/
-@[simps]
+@[to_dual (attr := simps)
+/-- A natural transformation `R₁ ⟶ R₂` induces a functor `Comma L R₁ ⥤ Comma L R₂`. -/]
def mapLeft (l : L₁ ⟶ L₂) : Comma L₂ R ⥤ Comma L₁ R where
obj X :=
{ left := X.left
@@ -346,10 +339,17 @@ def mapLeft (l : L₁ ⟶ L₂) : Comma L₂ R ⥤ Comma L₁ R where
{ left := f.left
right := f.right }
+set_option linter.existingAttributeWarning false
+set_option linter.translateGenerateName false
+attribute [to_dual existing mapRight_map_right] mapLeft_map_left
+attribute [to_dual existing mapRight_map_left] mapLeft_map_right
+
set_option backward.defeqAttrib.useBackward true in
/-- The functor `Comma L R ⥤ Comma L R` induced by the identity natural transformation on `L` is
naturally isomorphic to the identity functor. -/
-@[simps!]
+@[to_dual (attr := simps!)
+/-- The functor `Comma L R ⥤ Comma L R` induced by the identity natural transformation on `R` is
+naturally isomorphic to the identity functor. -/]
def mapLeftId : mapLeft R (𝟙 L) ≅ 𝟭 _ :=
NatIso.ofComponents (fun X => isoMk (Iso.refl _) (Iso.refl _))
@@ -357,70 +357,34 @@ set_option backward.defeqAttrib.useBackward true in
/-- The functor `Comma L₁ R ⥤ Comma L₃ R` induced by the composition of two natural transformations
`l : L₁ ⟶ L₂` and `l' : L₂ ⟶ L₃` is naturally isomorphic to the composition of the two functors
induced by these natural transformations. -/
-@[simps!]
+@[to_dual (attr := simps!)
+/-- The functor `Comma L R₁ ⥤ Comma L R₃` induced by the composition of the natural transformations
+`r : R₁ ⟶ R₂` and `r' : R₂ ⟶ R₃` is naturally isomorphic to the composition of the functors
+induced by these natural transformations. -/]
def mapLeftComp (l : L₁ ⟶ L₂) (l' : L₂ ⟶ L₃) :
mapLeft R (l ≫ l') ≅ mapLeft R l' ⋙ mapLeft R l :=
NatIso.ofComponents (fun X => isoMk (Iso.refl _) (Iso.refl _))
/-- Two equal natural transformations `L₁ ⟶ L₂` yield naturally isomorphic functors
`Comma L₁ R ⥤ Comma L₂ R`. -/
-@[simps!]
+@[to_dual (attr := simps!)
+/-- Two equal natural transformations `R₁ ⟶ R₂` yield naturally isomorphic functors
+`Comma L R₁ ⥤ Comma L R₂`. -/]
def mapLeftEq (l l' : L₁ ⟶ L₂) (h : l = l') : mapLeft R l ≅ mapLeft R l' :=
NatIso.ofComponents (fun X => isoMk (Iso.refl _) (Iso.refl _))
set_option backward.defeqAttrib.useBackward true in
/-- A natural isomorphism `L₁ ≅ L₂` induces an equivalence of categories
`Comma L₁ R ≌ Comma L₂ R`. -/
-@[simps!]
+@[to_dual (attr := simps!)
+/-- A natural isomorphism `R₁ ≅ R₂` induces an equivalence of categories
+`Comma L R₁ ≌ Comma L R₂`. -/]
def mapLeftIso (i : L₁ ≅ L₂) : Comma L₁ R ≌ Comma L₂ R where
functor := mapLeft _ i.inv
inverse := mapLeft _ i.hom
unitIso := (mapLeftId _ _).symm ≪≫ mapLeftEq _ _ _ i.hom_inv_id.symm ≪≫ mapLeftComp _ _ _
counitIso := (mapLeftComp _ _ _).symm ≪≫ mapLeftEq _ _ _ i.inv_hom_id ≪≫ mapLeftId _ _
-/-- A natural transformation `R₁ ⟶ R₂` induces a functor `Comma L R₁ ⥤ Comma L R₂`. -/
-@[simps]
-def mapRight (r : R₁ ⟶ R₂) : Comma L R₁ ⥤ Comma L R₂ where
- obj X :=
- { left := X.left
- right := X.right
- hom := X.hom ≫ r.app X.right }
- map f :=
- { left := f.left
- right := f.right }
-
-set_option backward.defeqAttrib.useBackward true in
-/-- The functor `Comma L R ⥤ Comma L R` induced by the identity natural transformation on `R` is
-naturally isomorphic to the identity functor. -/
-@[simps!]
-def mapRightId : mapRight L (𝟙 R) ≅ 𝟭 _ :=
- NatIso.ofComponents (fun X => isoMk (Iso.refl _) (Iso.refl _))
-
-set_option backward.defeqAttrib.useBackward true in
-/-- The functor `Comma L R₁ ⥤ Comma L R₃` induced by the composition of the natural transformations
-`r : R₁ ⟶ R₂` and `r' : R₂ ⟶ R₃` is naturally isomorphic to the composition of the functors
-induced by these natural transformations. -/
-@[simps!]
-def mapRightComp (r : R₁ ⟶ R₂) (r' : R₂ ⟶ R₃) :
- mapRight L (r ≫ r') ≅ mapRight L r ⋙ mapRight L r' :=
- NatIso.ofComponents (fun X => isoMk (Iso.refl _) (Iso.refl _))
-
-/-- Two equal natural transformations `R₁ ⟶ R₂` yield naturally isomorphic functors
-`Comma L R₁ ⥤ Comma L R₂`. -/
-@[simps!]
-def mapRightEq (r r' : R₁ ⟶ R₂) (h : r = r') : mapRight L r ≅ mapRight L r' :=
- NatIso.ofComponents (fun X => isoMk (Iso.refl _) (Iso.refl _))
-
-set_option backward.defeqAttrib.useBackward true in
-/-- A natural isomorphism `R₁ ≅ R₂` induces an equivalence of categories
-`Comma L R₁ ≌ Comma L R₂`. -/
-@[simps!]
-def mapRightIso (i : R₁ ≅ R₂) : Comma L R₁ ≌ Comma L R₂ where
- functor := mapRight _ i.hom
- inverse := mapRight _ i.inv
- unitIso := (mapRightId _ _).symm ≪≫ mapRightEq _ _ _ i.hom_inv_id.symm ≪≫ mapRightComp _ _ _
- counitIso := (mapRightComp _ _ _).symm ≪≫ mapRightEq _ _ _ i.inv_hom_id ≪≫ mapRightId _ _
-
end
section
@@ -428,7 +392,7 @@ section
variable {C : Type u₄} [Category.{v₄} C]
/-- The functor `(F ⋙ L, R) ⥤ (L, R)` -/
-@[simps]
+@[to_dual (attr := simps) (reorder := F L R) /-- The functor `(L, F ⋙ R) ⥤ (L, R)` -/]
def preLeft (F : C ⥤ A) (L : A ⥤ T) (R : B ⥤ T) : Comma (F ⋙ L) R ⥤ Comma L R where
obj X :=
{ left := F.obj X.left
@@ -442,58 +406,33 @@ def preLeft (F : C ⥤ A) (L : A ⥤ T) (R : B ⥤ T) : Comma (F ⋙ L) R ⥤ Co
set_option backward.defeqAttrib.useBackward true in
/-- `Comma.preLeft` is a particular case of `Comma.map`,
but with better definitional properties. -/
+@[to_dual (reorder := F L R)
+/-- `Comma.preRight` is a particular case of `Comma.map`,
+but with better definitional properties. -/]
def preLeftIso (F : C ⥤ A) (L : A ⥤ T) (R : B ⥤ T) :
preLeft F L R ≅ map (F ⋙ L).rightUnitor.inv (R.rightUnitor.hom ≫ R.leftUnitor.inv) :=
- NatIso.ofComponents (fun X => isoMk (Iso.refl _) (Iso.refl _))
+ NatIso.ofComponents (fun X => isoMk (Iso.refl _) (Iso.refl _) (by simp -implicitDefEqProofs))
+@[to_dual]
instance (F : C ⥤ A) (L : A ⥤ T) (R : B ⥤ T) [F.Faithful] : (preLeft F L R).Faithful :=
Functor.Faithful.of_iso (preLeftIso F L R).symm
+@[to_dual]
instance (F : C ⥤ A) (L : A ⥤ T) (R : B ⥤ T) [F.Full] : (preLeft F L R).Full :=
Functor.Full.of_iso (preLeftIso F L R).symm
+@[to_dual]
instance (F : C ⥤ A) (L : A ⥤ T) (R : B ⥤ T) [F.EssSurj] : (preLeft F L R).EssSurj :=
Functor.essSurj_of_iso (preLeftIso F L R).symm
/-- If `F` is an equivalence, then so is `preLeft F L R`. -/
+@[to_dual /-- If `F` is an equivalence, then so is `preRight L F R`. -/]
instance isEquivalence_preLeft (F : C ⥤ A) (L : A ⥤ T) (R : B ⥤ T) [F.IsEquivalence] :
(preLeft F L R).IsEquivalence where
-set_option backward.isDefEq.respectTransparency false in
-/-- The functor `(L, F ⋙ R) ⥤ (L, R)` -/
-@[simps]
-def preRight (L : A ⥤ T) (F : C ⥤ B) (R : B ⥤ T) : Comma L (F ⋙ R) ⥤ Comma L R where
- obj X :=
- { left := X.left
- right := F.obj X.right
- hom := X.hom }
- map f :=
- { left := f.left
- right := F.map f.right }
-
-set_option backward.defeqAttrib.useBackward true in
-/-- `Comma.preRight` is a particular case of `Comma.map`,
-but with better definitional properties. -/
-def preRightIso (L : A ⥤ T) (F : C ⥤ B) (R : B ⥤ T) :
- preRight L F R ≅ map (L.leftUnitor.hom ≫ L.rightUnitor.inv) (F ⋙ R).rightUnitor.hom :=
- NatIso.ofComponents (fun X => isoMk (Iso.refl _) (Iso.refl _))
-
-instance (L : A ⥤ T) (F : C ⥤ B) (R : B ⥤ T) [F.Faithful] : (preRight L F R).Faithful :=
- Functor.Faithful.of_iso (preRightIso L F R).symm
-
-instance (L : A ⥤ T) (F : C ⥤ B) (R : B ⥤ T) [F.Full] : (preRight L F R).Full :=
- Functor.Full.of_iso (preRightIso L F R).symm
-
-instance (L : A ⥤ T) (F : C ⥤ B) (R : B ⥤ T) [F.EssSurj] : (preRight L F R).EssSurj :=
- Functor.essSurj_of_iso (preRightIso L F R).symm
-
-/-- If `F` is an equivalence, then so is `preRight L F R`. -/
-instance isEquivalence_preRight (L : A ⥤ T) (F : C ⥤ B) (R : B ⥤ T) [F.IsEquivalence] :
- (preRight L F R).IsEquivalence where
-
set_option backward.isDefEq.respectTransparency false in
/-- The functor `(L, R) ⥤ (L ⋙ F, R ⋙ F)` -/
-@[simps]
+@[to_dual self, simps]
def post (L : A ⥤ T) (R : B ⥤ T) (F : T ⥤ C) : Comma L R ⥤ Comma (L ⋙ F) (R ⋙ F) where
obj X :=
{ left := X.left
@@ -504,22 +443,31 @@ def post (L : A ⥤ T) (R : B ⥤ T) (F : T ⥤ C) : Comma L R ⥤ Comma (L ⋙
right := f.right
w := by simp only [Functor.comp_map, ← F.map_comp, f.w] }
+set_option linter.existingAttributeWarning false in
+attribute [to_dual existing] post_obj_left
+attribute [to_dual self] post_obj_hom
+
set_option backward.defeqAttrib.useBackward true in
/-- `Comma.post` is a particular case of `Comma.map`, but with better definitional properties. -/
+@[to_dual self]
def postIso (L : A ⥤ T) (R : B ⥤ T) (F : T ⥤ C) :
post L R F ≅ map (F₁ := 𝟭 _) (F₂ := 𝟭 _) (L ⋙ F).leftUnitor.hom (R ⋙ F).leftUnitor.inv :=
NatIso.ofComponents (fun X => isoMk (Iso.refl _) (Iso.refl _))
+@[to_dual self]
instance (L : A ⥤ T) (R : B ⥤ T) (F : T ⥤ C) : (post L R F).Faithful :=
Functor.Faithful.of_iso (postIso L R F).symm
+@[to_dual self]
instance (L : A ⥤ T) (R : B ⥤ T) (F : T ⥤ C) [F.Faithful] : (post L R F).Full :=
Functor.Full.of_iso (postIso L R F).symm
+@[to_dual self]
instance (L : A ⥤ T) (R : B ⥤ T) (F : T ⥤ C) [F.Full] : (post L R F).EssSurj :=
Functor.essSurj_of_iso (postIso L R F).symm
/-- If `F` is an equivalence, then so is `post L R F`. -/
+@[to_dual self]
instance isEquivalence_post (L : A ⥤ T) (R : B ⥤ T) (F : T ⥤ C) [F.IsEquivalence] :
(post L R F).IsEquivalence where
diff --git a/Mathlib/Order/Interval/Set/Disjoint.lean b/Mathlib/Order/Interval/Set/Disjoint.lean
index aff5f999922bcc..f42c0a674c0c52 100644
--- a/Mathlib/Order/Interval/Set/Disjoint.lean
+++ b/Mathlib/Order/Interval/Set/Disjoint.lean
@@ -36,8 +36,7 @@ section Preorder
variable [Preorder α] {a b c : α}
-to_dual_name_hint Disjoint Disjoint
-to_dual_name_hint Left Right
+to_dual_name_hint Disjoint Disjoint, Left Right
@[to_dual (attr := simp)]
theorem Iic_disjoint_Ioi (h : a ≤ b) : Disjoint (Iic a) (Ioi b) :=
diff --git a/Mathlib/Tactic/Translate/ToDual.lean b/Mathlib/Tactic/Translate/ToDual.lean
index b46222f703695c..da8dcb078ff371 100644
--- a/Mathlib/Tactic/Translate/ToDual.lean
+++ b/Mathlib/Tactic/Translate/ToDual.lean
@@ -295,10 +295,11 @@ 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
+/-- `to_dual_name_hint src₁ tgt₁, ..., srcₙ tgtₙ` lets `to_dual` translate between the name segments
+`srcᵢ` and `tgtᵢ` for the rest of the file current. The name segments should be capitalized. -/
+elab "to_dual_name_hint" hints:(ident ident),* : command => do
+ for ⟨hint⟩ in hints.getElems do
+ guessNameExt.addTranslation ⟨hint[0]⟩ ⟨hint[1]⟩
+ guessNameExt.addTranslation ⟨hint[1]⟩ ⟨hint[0]⟩
end Mathlib.Tactic.ToDual
diff --git a/MathlibTest/Attribute/ToDual.lean b/MathlibTest/Attribute/ToDual.lean
index 8113c3ac6fe7fc..890c9c996640e3 100644
--- a/MathlibTest/Attribute/ToDual.lean
+++ b/MathlibTest/Attribute/ToDual.lean
@@ -431,8 +431,7 @@ to_dual_name_hint LeftMono 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
+to_dual_name_hint Left Right, Epi Mono
/-- info: "right_epi" -/
#guard_msgs in
From bf81df9aa4ee509ba0247f7d0aa9fe24d3f45d7a Mon Sep 17 00:00:00 2001
From: Michael Rothgang <10105016+grunweg@users.noreply.github.com>
Date: Tue, 16 Jun 2026 13:03:38 +0000
Subject: [PATCH 0065/1300] feat: an explicit criterion for membership in the
maximal atlas of a `C^n` manifold (#40632)
Add an explicit characterisation, that an `OpenPartialHomeomorph` phi lies in the maximal atlas of a C^n manifold if both phi and phi.symm are C^n on their source.
This can be useful to prove that certain explicit maps are in the maximal atlas, so prove some maps are immersions. Motivating examples are #29077 (for the inclusion of a closed interval [a, b] into the real numbers) and for a manual proof that diffeomorphisms are immersions.
Along the way, rename `contMDiff_model` and its inverse cousin `contMDiffOn_model_symm` to match the naming convention, and golf their proofs.
---
.../Geometry/Manifold/ContMDiff/Atlas.lean | 43 +++++++++++++------
.../Geometry/Manifold/PartitionOfUnity.lean | 2 +-
2 files changed, 32 insertions(+), 13 deletions(-)
diff --git a/Mathlib/Geometry/Manifold/ContMDiff/Atlas.lean b/Mathlib/Geometry/Manifold/ContMDiff/Atlas.lean
index f52989501c5754..78a63c9fcaf56b 100644
--- a/Mathlib/Geometry/Manifold/ContMDiff/Atlas.lean
+++ b/Mathlib/Geometry/Manifold/ContMDiff/Atlas.lean
@@ -45,19 +45,20 @@ variable {𝕜 : Type*} [NontriviallyNormedField 𝕜]
section Atlas
-theorem contMDiff_model : ContMDiff I 𝓘(𝕜, E) n I := by
+variable (I) in
+theorem ModelWithCorners.contMDiff : ContMDiff I 𝓘(𝕜, E) n I := by
intro x
refine contMDiffAt_iff.mpr ⟨I.continuousAt, ?_⟩
- simp only [mfld_simps]
- refine contDiffWithinAt_id.congr_of_eventuallyEq ?_ ?_
- · exact Filter.eventuallyEq_of_mem self_mem_nhdsWithin fun x₂ => I.right_inv
- simp_rw [Function.comp_apply, I.left_inv, Function.id_def]
+ simpa using contDiffWithinAt_id.congr (fun y hy ↦ by simp [hy]) (by simp)
+@[deprecated (since := "2026-06-16")] alias contMDiff_model := ModelWithCorners.contMDiff
-theorem contMDiffOn_model_symm : ContMDiffOn 𝓘(𝕜, E) I n I.symm (range I) := by
- rw [contMDiffOn_iff]
- refine ⟨I.continuousOn_symm, fun x y => ?_⟩
- simp only [mfld_simps]
- exact contDiffOn_id.congr fun x' => I.right_inv
+variable (I) in
+theorem ModelWithCorners.contMDiffOn_symm : ContMDiffOn 𝓘(𝕜, E) I n I.symm (range I) := by
+ intro x hx
+ apply contMDiffWithinAt_iff.mpr ⟨by fun_prop, ?_⟩
+ simpa using contDiffWithinAt_id.congr (fun y hy ↦ by simp [hy]) (by simp [hx])
+@[deprecated (since := "2026-06-16")]
+alias contMDiffOn_model_symm := ModelWithCorners.contMDiffOn_symm
/-- An atlas member is `C^n` for any `n`. -/
theorem contMDiffOn_of_mem_maximalAtlas (h : e ∈ maximalAtlas I n M) :
@@ -89,7 +90,7 @@ theorem contMDiffOn_chart_symm [IsManifold I n M] :
theorem contMDiffAt_extend {x : M} (he : e ∈ maximalAtlas I n M) (hx : x ∈ e.source) :
ContMDiffAt I 𝓘(𝕜, E) n (e.extend I) x :=
- (contMDiff_model _).comp x <| contMDiffAt_of_mem_maximalAtlas he hx
+ (I.contMDiff _).comp x <| contMDiffAt_of_mem_maximalAtlas he hx
theorem contMDiffOn_extend (he : e ∈ maximalAtlas I n M) :
ContMDiffOn I 𝓘(𝕜, E) n (e.extend I) e.source :=
@@ -112,7 +113,7 @@ theorem contMDiffOn_extChartAt [IsManifold I n M] :
theorem contMDiffOn_extend_symm (he : e ∈ maximalAtlas I n M) :
ContMDiffOn 𝓘(𝕜, E) I n (e.extend I).symm (I '' e.target) := by
refine (contMDiffOn_symm_of_mem_maximalAtlas he).comp
- (contMDiffOn_model_symm.mono <| image_subset_range _ _) ?_
+ (I.contMDiffOn_symm.mono <| image_subset_range _ _) ?_
simp_rw [image_subset_iff, PartialEquiv.restr_coe_symm, I.toPartialEquiv_coe_symm,
preimage_preimage, I.left_inv, preimage_id']; rfl
@@ -155,6 +156,24 @@ theorem contMDiffOn_of_mem_contDiffGroupoid {e' : OpenPartialHomeomorph H H}
(h : e' ∈ contDiffGroupoid n I) : ContMDiffOn I I n e' e'.source :=
(contDiffWithinAt_localInvariantProp n).liftPropOn_of_mem_groupoid contDiffWithinAtProp_id h
+lemma OpenPartialHomeomorph.mem_maximalAtlas_of_contMDiffOn (φ : OpenPartialHomeomorph H H)
+ (hφ : ContMDiffOn I I n φ φ.source) (hφ' : ContMDiffOn I I n φ.symm φ.target) :
+ φ ∈ maximalAtlas I n H := by
+ simp only [mfld_simps, IsManifold.mem_maximalAtlas_iff, StructureGroupoid.maximalAtlas, forall_eq,
+ contDiffGroupoid, mem_groupoid_of_pregroupoid, contDiffPregroupoid,
+ ← contMDiffOn_iff_contDiffOn]
+ refine ⟨⟨?_, ?_⟩, ?_, ?_⟩
+ all_goals apply I.contMDiff.comp_contMDiffOn
+ · exact hφ'.comp (I.contMDiffOn_symm.mono (by simp)) (by simp)
+ · exact hφ.comp (I.contMDiffOn_symm.mono (by simp)) (by simp)
+ · exact hφ.comp (I.contMDiffOn_symm.mono (by simp)) (by simp)
+ · exact hφ'.comp (I.contMDiffOn_symm.mono (by simp)) (by simp)
+
+lemma IsManifold.mem_maximalAtlas_iff_contMDiffOn (φ : OpenPartialHomeomorph H H) :
+ φ ∈ maximalAtlas I n H ↔ ContMDiffOn I I n φ φ.source ∧ ContMDiffOn I I n φ.symm φ.target :=
+ ⟨fun h ↦ ⟨contMDiffOn_of_mem_maximalAtlas h, contMDiffOn_symm_of_mem_maximalAtlas h⟩,
+ fun ⟨hφ, hφ'⟩ ↦ φ.mem_maximalAtlas_of_contMDiffOn hφ hφ'⟩
+
end Atlas
/-! ### (local) structomorphisms are `C^n` -/
diff --git a/Mathlib/Geometry/Manifold/PartitionOfUnity.lean b/Mathlib/Geometry/Manifold/PartitionOfUnity.lean
index 2fa99c7ca2695d..db07e504d28b3d 100644
--- a/Mathlib/Geometry/Manifold/PartitionOfUnity.lean
+++ b/Mathlib/Geometry/Manifold/PartitionOfUnity.lean
@@ -736,7 +736,7 @@ lemma IsOpen.exists_contMDiff_support_eq_aux {s : Set H} (hs : IsOpen s) :
refine ⟨f ∘ I, ?_, ?_, ?_⟩
· rw [support_comp_eq_preimage, f_supp, ← preimage_comp]
simp only [ModelWithCorners.symm_comp_self, preimage_id_eq, id_eq]
- · exact f_diff.comp_contMDiff contMDiff_model
+ · exact f_diff.comp_contMDiff I.contMDiff
· exact Subset.trans (range_comp_subset_range _ _) f_range
@[deprecated (since := "2025-12-17")]
From d18bb636bbd4f3f19fa9a70b7f11e3bf4b501e97 Mon Sep 17 00:00:00 2001
From: Oliver Nash <7734364+ocfnash@users.noreply.github.com>
Date: Tue, 16 Jun 2026 13:17:59 +0000
Subject: [PATCH 0066/1300] chore: make `_root_.toContinuousMap` reducible
(#40477)
If a structure extends `ContinuousMap` and also carries an instance of `ContinuousMapClass` then it will have two `toContinuousMap` functions available to it. Without this change, at non-reducible transparency Lean is unable to see that these two are defeq (assuming we have written a sane API and they are!).
The motivating example is the `Path` structure where the lack of this reducibility was responsible for some
`backward.isDefEq.respectTransparency false` in #33108.
---
.../CStarAlgebra/ContinuousFunctionalCalculus/Basic.lean | 2 +-
.../CStarAlgebra/ContinuousFunctionalCalculus/Isometric.lean | 2 +-
.../CStarAlgebra/ContinuousFunctionalCalculus/NonUnital.lean | 2 +-
.../CStarAlgebra/ContinuousFunctionalCalculus/Unique.lean | 4 ++--
Mathlib/Analysis/RCLike/BoundedContinuous.lean | 2 +-
Mathlib/Topology/ContinuousMap/ContinuousMapZero.lean | 2 +-
Mathlib/Topology/ContinuousMap/Defs.lean | 2 +-
Mathlib/Topology/ContinuousMap/Ideals.lean | 2 +-
Mathlib/Topology/ContinuousMap/StoneWeierstrass.lean | 4 ++--
Mathlib/Topology/Homotopy/HomotopyGroup.lean | 2 +-
Mathlib/Topology/Homotopy/TopCat/ZerothHomotopy.lean | 4 ++--
11 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/Basic.lean b/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/Basic.lean
index 88bf4a6749cf38..bdce04f278e2d3 100644
--- a/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/Basic.lean
+++ b/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/Basic.lean
@@ -212,7 +212,7 @@ instance IsStarNormal.instNonUnitalIsometricContinuousFunctionalCalculus :
rw [← norm_inr (𝕜 := ℂ), ← inrNonUnitalStarAlgHom_apply, ← NonUnitalStarAlgHom.comp_apply,
inr_comp_cfcₙHom_eq_cfcₙAux a, cfcₙAux]
simp only [NonUnitalStarAlgHom.comp_assoc, NonUnitalStarAlgHom.comp_apply,
- toContinuousMapHom_apply, NonUnitalStarAlgHom.coe_coe]
+ NonUnitalStarAlgHom.coe_coe]
rw [norm_cfcHom (a : Unitization ℂ A), StarAlgEquiv.norm_map]
rfl
diff --git a/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/Isometric.lean b/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/Isometric.lean
index 3143700d96afb5..d720af78d8eec5 100644
--- a/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/Isometric.lean
+++ b/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/Isometric.lean
@@ -387,7 +387,7 @@ protected theorem isometric_cfc (f : C(S, R)) (halg : Isometry (algebraMap R S))
· simpa [halg.dist_eq] using! ContinuousMap.dist_apply_le_dist _
· let x' : σₙ S a := Subtype.map (algebraMap R S) (fun _ ↦ quasispectrum.algebraMap_mem S) x
apply le_of_eq_of_le ?_ <| ContinuousMap.dist_apply_le_dist x'
- simp only [ContinuousMap.coe_coe, ContinuousMapZero.comp_apply, ContinuousMapZero.coe_mk,
+ simp only [ContinuousMapZero.comp_apply, ContinuousMapZero.coe_mk,
ContinuousMap.coe_mk, StarAlgHom.ofId_apply, halg.dist_eq, x']
congr! 2
all_goals ext; exact haf.left_inv _ |>.symm
diff --git a/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/NonUnital.lean b/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/NonUnital.lean
index ff14e3c5dd92ea..be142dd60e974d 100644
--- a/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/NonUnital.lean
+++ b/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/NonUnital.lean
@@ -873,7 +873,7 @@ lemma cfcₙ_eq_cfc [ContinuousFunctionalCalculus R A p] [ContinuousMapZero.Uniq
by_cases ha : p a
· have hf' := hf.mono <| spectrum_subset_quasispectrum R a
rw [cfc_apply f a ha hf', cfcₙ_apply f a hf, cfcₙHom_eq_cfcₙHom_of_cfcHom, cfcₙHom_of_cfcHom]
- dsimp only [NonUnitalStarAlgHom.comp_apply, toContinuousMapHom_apply,
+ dsimp only [NonUnitalStarAlgHom.comp_apply,
NonUnitalStarAlgHom.coe_coe, compStarAlgHom'_apply]
congr
· simp [cfc_apply_of_not_predicate a ha, cfcₙ_apply_of_not_predicate (R := R) a ha]
diff --git a/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/Unique.lean b/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/Unique.lean
index 99805af4c52965..035cce82a04c39 100644
--- a/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/Unique.lean
+++ b/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/Unique.lean
@@ -262,14 +262,14 @@ lemma toNNReal_mul_add_neg_mul_add_mul_neg_eq (f g : C(X, ℝ)₀) :
((f * g).toNNReal + (-f).toNNReal * g.toNNReal + f.toNNReal * (-g).toNNReal) =
((-(f * g)).toNNReal + f.toNNReal * g.toNNReal + (-f).toNNReal * (-g).toNNReal) := by
apply toContinuousMap_injective
- simpa only [← toContinuousMapHom_apply, map_add, map_mul, map_neg, toContinuousMapHom_toNNReal]
+ simpa only [map_add, map_mul, map_neg, toContinuousMapHom_toNNReal]
using! (f : C(X, ℝ)).toNNReal_mul_add_neg_mul_add_mul_neg_eq g
lemma toNNReal_add_add_neg_add_neg_eq (f g : C(X, ℝ)₀) :
((f + g).toNNReal + (-f).toNNReal + (-g).toNNReal) =
((-(f + g)).toNNReal + f.toNNReal + g.toNNReal) := by
apply toContinuousMap_injective
- simpa only [← toContinuousMapHom_apply, map_add, map_mul, map_neg, toContinuousMapHom_toNNReal]
+ simpa only [map_add, map_mul, map_neg, toContinuousMapHom_toNNReal]
using! (f : C(X, ℝ)).toNNReal_add_add_neg_add_neg_eq g
end ContinuousMapZero
diff --git a/Mathlib/Analysis/RCLike/BoundedContinuous.lean b/Mathlib/Analysis/RCLike/BoundedContinuous.lean
index 4570eefe1e7e14..1dc8f44d0cbef7 100644
--- a/Mathlib/Analysis/RCLike/BoundedContinuous.lean
+++ b/Mathlib/Analysis/RCLike/BoundedContinuous.lean
@@ -32,7 +32,7 @@ theorem restrict_toContinuousMap_eq_toContinuousMapStar_restrict
(ofRealAm.compLeftContinuous ℝ continuous_ofReal) := by
ext g
simp only [Subalgebra.mem_map, Subalgebra.mem_comap, Subalgebra.mem_restrictScalars,
- StarSubalgebra.mem_toSubalgebra, toContinuousMapₐ_apply, StarSubalgebra.mem_map]
+ StarSubalgebra.mem_toSubalgebra, StarSubalgebra.mem_map]
constructor
· intro ⟨x, hxA, hxg⟩
use (@ofRealAm 𝕜 _).compLeftContinuousBounded ℝ lipschitzWith_ofReal x, hxA
diff --git a/Mathlib/Topology/ContinuousMap/ContinuousMapZero.lean b/Mathlib/Topology/ContinuousMap/ContinuousMapZero.lean
index 9225e1d9bdca95..69d07a5fe3f10b 100644
--- a/Mathlib/Topology/ContinuousMap/ContinuousMapZero.lean
+++ b/Mathlib/Topology/ContinuousMap/ContinuousMapZero.lean
@@ -329,7 +329,7 @@ def toContinuousMapHom [StarRing R] [ContinuousStar R] : C(X, R)₀ →⋆ₙₐ
map_mul' _ _ := rfl
map_star' _ := rfl
-lemma coe_toContinuousMapHom [StarRing R] [ContinuousStar R] :
+@[simp] lemma coe_toContinuousMapHom [StarRing R] [ContinuousStar R] :
⇑(toContinuousMapHom (X := X) (R := R)) = (↑) :=
rfl
diff --git a/Mathlib/Topology/ContinuousMap/Defs.lean b/Mathlib/Topology/ContinuousMap/Defs.lean
index e8d9692c28c740..f3a961ad2e07de 100644
--- a/Mathlib/Topology/ContinuousMap/Defs.lean
+++ b/Mathlib/Topology/ContinuousMap/Defs.lean
@@ -61,7 +61,7 @@ variable {F X Y : Type*} [TopologicalSpace X] [TopologicalSpace Y] [FunLike F X
variable [ContinuousMapClass F X Y]
/-- Coerce a bundled morphism with a `ContinuousMapClass` instance to a `ContinuousMap`. -/
-@[coe] def toContinuousMap (f : F) : C(X, Y) := ⟨f, map_continuous f⟩
+@[coe, reducible] def toContinuousMap (f : F) : C(X, Y) := ⟨f, map_continuous f⟩
instance : CoeTC F C(X, Y) := ⟨toContinuousMap⟩
diff --git a/Mathlib/Topology/ContinuousMap/Ideals.lean b/Mathlib/Topology/ContinuousMap/Ideals.lean
index 3cb9dec9560162..b13c306a8ecc46 100644
--- a/Mathlib/Topology/ContinuousMap/Ideals.lean
+++ b/Mathlib/Topology/ContinuousMap/Ideals.lean
@@ -268,7 +268,7 @@ theorem idealOfSet_ofIdeal_eq_closure (I : Ideal C(X, 𝕜)) :
pow_pos (norm_pos_iff.mpr hx.1) 2⟩⟩
convert! I.mul_mem_left (star g) hI
ext
- simp only [comp_apply, ContinuousMap.coe_coe, coe_mk, algebraMapCLM_apply, map_pow,
+ simp only [comp_apply, coe_mk, algebraMapCLM_apply, map_pow,
mul_apply, star_apply, star_def]
simp only [RCLike.conj_mul]
rfl
diff --git a/Mathlib/Topology/ContinuousMap/StoneWeierstrass.lean b/Mathlib/Topology/ContinuousMap/StoneWeierstrass.lean
index d3a0f9317126f0..2b8c165c02034c 100644
--- a/Mathlib/Topology/ContinuousMap/StoneWeierstrass.lean
+++ b/Mathlib/Topology/ContinuousMap/StoneWeierstrass.lean
@@ -632,10 +632,10 @@ lemma ContinuousMapZero.adjoin_id_dense (s : Set 𝕜) [Fact (0 ∈ s)]
← isClosedEmbedding_toContinuousMap.injective.preimage_image (closure _),
← isClosedEmbedding_toContinuousMap.closure_image_eq, ← coe_toContinuousMapHom,
← NonUnitalStarSubalgebra.coe_map, NonUnitalStarAlgHom.map_adjoin_singleton,
- toContinuousMapHom_apply, toContinuousMap_id,
+ coe_toContinuousMapHom, toContinuousMap_id,
← ContinuousMap.ker_evalStarAlgHom_eq_closure_adjoin_id s h0']
apply Set.eq_univ_of_forall fun f ↦ ?_
- simp only [Set.mem_preimage, toContinuousMapHom_apply, SetLike.mem_coe, RingHom.mem_ker,
+ simp only [Set.mem_preimage, SetLike.mem_coe, RingHom.mem_ker,
ContinuousMap.evalStarAlgHom_apply, ContinuousMap.coe_coe]
exact map_zero f
diff --git a/Mathlib/Topology/Homotopy/HomotopyGroup.lean b/Mathlib/Topology/Homotopy/HomotopyGroup.lean
index 9f469ee3cf1744..3c720fbf834efe 100644
--- a/Mathlib/Topology/Homotopy/HomotopyGroup.lean
+++ b/Mathlib/Topology/Homotopy/HomotopyGroup.lean
@@ -285,7 +285,7 @@ def fromLoop (i : N) (p : Ω (Ω^ { j // j ≠ i } X x) const) : Ω^ N X x :=
(Cube.splitAt i),
by
rintro y ⟨j, Hj⟩
- simp only [ContinuousMap.comp_apply, ContinuousMap.coe_coe,
+ simp only [ContinuousMap.comp_apply,
funSplitAt_apply, ContinuousMap.uncurry_apply, ContinuousMap.coe_mk,
Function.uncurry_apply_pair]
obtain rfl | Hne := eq_or_ne j i
diff --git a/Mathlib/Topology/Homotopy/TopCat/ZerothHomotopy.lean b/Mathlib/Topology/Homotopy/TopCat/ZerothHomotopy.lean
index bdf2b1c36414d2..5e833eaa0a3ae7 100644
--- a/Mathlib/Topology/Homotopy/TopCat/ZerothHomotopy.lean
+++ b/Mathlib/Topology/Homotopy/TopCat/ZerothHomotopy.lean
@@ -43,14 +43,14 @@ set_option backward.isDefEq.respectTransparency false in
@[simp]
lemma toSSetObj₁Equiv_apply_zero (s : toSSet.obj X _⦋1⦌) :
X.toSSetObj₁Equiv s 0 = toSSetObj₀Equiv ((toSSet.obj X).δ 1 s) := by
- simp [toSSetObj₀Equiv, toSSetObj₁Equiv,
+ simp [toSSetObj₀Equiv, toSSetObj₁Equiv, -ContinuousMap.coe_mk,
Subsingleton.elim (default : stdSimplex ℝ (Fin 1)) (stdSimplex.vertex 0)]
set_option backward.isDefEq.respectTransparency false in
@[simp]
lemma toSSetObj₁Equiv_apply_one (s : toSSet.obj X _⦋1⦌) :
X.toSSetObj₁Equiv s 1 = toSSetObj₀Equiv ((toSSet.obj X).δ 0 s) := by
- simp [toSSetObj₀Equiv, toSSetObj₁Equiv,
+ simp [toSSetObj₀Equiv, toSSetObj₁Equiv, -ContinuousMap.coe_mk,
Subsingleton.elim (default : stdSimplex ℝ (Fin 1)) (stdSimplex.vertex 0)]
@[simp]
From 3eb2cbf91c0e2ce5fddf184f5d5b0457a210585e Mon Sep 17 00:00:00 2001
From: Oliver Nash <7734364+ocfnash@users.noreply.github.com>
Date: Tue, 16 Jun 2026 13:55:01 +0000
Subject: [PATCH 0067/1300] feat: define the closed unit disc in the complex
numbers (#40511)
The API is a minimally-edited copy-paste of that which already exists for `Complex.UnitDisc`.
Upstreaming this from https://alexkontorovich.github.io/CoveringSpacesProject/web/ where it is called `Complex.disk`.
Co-authored-by: AlexKontorovich <58564076+AlexKontorovich@users.noreply.github.com>
---
Mathlib/Analysis/Complex/UnitDisc/Basic.lean | 271 +++++++++++++++++--
Mathlib/Analysis/Normed/Field/UnitBall.lean | 4 +
2 files changed, 246 insertions(+), 29 deletions(-)
diff --git a/Mathlib/Analysis/Complex/UnitDisc/Basic.lean b/Mathlib/Analysis/Complex/UnitDisc/Basic.lean
index 94f936987ebc4f..e6ce3a7c5e48fb 100644
--- a/Mathlib/Analysis/Complex/UnitDisc/Basic.lean
+++ b/Mathlib/Analysis/Complex/UnitDisc/Basic.lean
@@ -7,6 +7,7 @@ module
public import Mathlib.Analysis.Complex.Circle
public import Mathlib.Analysis.Normed.Module.Ball.Action
+public import Mathlib.Algebra.Group.NatPowAssoc
public import Mathlib.Algebra.Group.PNatPowAssoc
/-!
@@ -29,7 +30,13 @@ namespace Complex
def UnitDisc : Type :=
Subsemigroup.unitBall ℂ deriving TopologicalSpace
+/-- The complex closed unit disc, denoted as `𝕔𝔻` within the Complex namespace -/
+def UnitClosedDisc : Type :=
+ Submonoid.unitClosedBall ℂ deriving TopologicalSpace
+
@[inherit_doc] scoped[Complex.UnitDisc] notation "𝔻" => Complex.UnitDisc
+@[inherit_doc] scoped[Complex.UnitDisc] notation "𝕔𝔻" => Complex.UnitClosedDisc
+
open UnitDisc
namespace UnitDisc
@@ -171,35 +178,6 @@ theorem coe_circle_smul (z : Circle) (w : 𝔻) : ↑(z • w) = (z * w : ℂ) :
@[deprecated (since := "2026-01-06")]
alias coe_smul_circle := coe_circle_smul
-instance instMulActionClosedBall : MulAction (closedBall (0 : ℂ) 1) 𝔻 :=
- inferInstanceAs <| MulAction (closedBall _ _) (ball _ _)
-
-instance instIsScalarTower_closedBall_closedBall :
- IsScalarTower (closedBall (0 : ℂ) 1) (closedBall (0 : ℂ) 1) 𝔻 :=
- inferInstanceAs <| IsScalarTower (closedBall _ _) (closedBall _ _) (ball _ _)
-
-instance instIsScalarTower_closedBall : IsScalarTower (closedBall (0 : ℂ) 1) 𝔻 𝔻 :=
- inferInstanceAs <| IsScalarTower (closedBall _ _) (ball _ _) (ball _ _)
-
-instance instSMulCommClass_closedBall_left : SMulCommClass (closedBall (0 : ℂ) 1) 𝔻 𝔻 :=
- ⟨fun _ _ _ => Subtype.ext <| mul_left_comm _ _ _⟩
-
-instance instSMulCommClass_closedBall_right : SMulCommClass 𝔻 (closedBall (0 : ℂ) 1) 𝔻 :=
- SMulCommClass.symm _ _ _
-
-instance instSMulCommClass_circle_closedBall : SMulCommClass Circle (closedBall (0 : ℂ) 1) 𝔻 :=
- inferInstanceAs <| SMulCommClass (sphere _ _) (closedBall _ _) (ball _ _)
-
-instance instSMulCommClass_closedBall_circle : SMulCommClass (closedBall (0 : ℂ) 1) Circle 𝔻 :=
- SMulCommClass.symm _ _ _
-
-@[simp, norm_cast]
-theorem coe_closedBall_smul (z : closedBall (0 : ℂ) 1) (w : 𝔻) : ↑(z • w) = (z * w : ℂ) :=
- rfl
-
-@[deprecated (since := "2026-01-06")]
-alias coe_smul_closedBall := coe_closedBall_smul
-
instance : Pow UnitDisc ℕ+ where
pow z n := ⟨z ^ (n : ℕ), by simp [pow_lt_one_iff_of_nonneg, z.norm_lt_one]⟩
@@ -302,4 +280,239 @@ theorem conj_mul (z w : 𝔻) : star (z * w) = star z * star w :=
end UnitDisc
+namespace UnitClosedDisc
+
+/-- Coercion to `ℂ`. -/
+@[coe] protected def coe : 𝕔𝔻 → ℂ := Subtype.val
+
+instance : MonoidWithZero 𝕔𝔻 := inferInstanceAs <| MonoidWithZero (closedBall _ _)
+
+instance : IsCancelMulZero 𝕔𝔻 :=
+ inferInstanceAs <| IsCancelMulZero (closedBall _ _)
+
+instance : HasDistribNeg 𝕔𝔻 :=
+ inferInstanceAs <| HasDistribNeg (closedBall _ _)
+
+instance : Coe 𝕔𝔻 ℂ := ⟨UnitClosedDisc.coe⟩
+
+@[ext]
+theorem coe_injective : Injective ((↑) : 𝕔𝔻 → ℂ) :=
+ Subtype.coe_injective
+
+@[simp, norm_cast]
+theorem coe_inj {z w : 𝕔𝔻} : (z : ℂ) = w ↔ z = w := Subtype.val_inj
+
+@[fun_prop]
+theorem isEmbedding_coe : Topology.IsEmbedding ((↑) : 𝕔𝔻 → ℂ) := .subtypeVal
+
+@[fun_prop]
+theorem continuous_coe : Continuous ((↑) : 𝕔𝔻 → ℂ) := isEmbedding_coe.continuous
+
+theorem norm_le_one (z : 𝕔𝔻) : ‖(z : ℂ)‖ ≤ 1 :=
+ mem_closedBall_zero_iff.1 z.2
+
+theorem sq_norm_lt_one (z : 𝕔𝔻) : ‖(z : ℂ)‖ ^ 2 ≤ 1 := by
+ rw [sq_le_one_iff_abs_le_one, abs_norm]
+ exact z.norm_le_one
+
+theorem normSq_lt_one (z : 𝕔𝔻) : normSq z ≤ 1 := by
+ rw [← Complex.norm_mul_self_eq_normSq, ← sq]
+ exact z.sq_norm_lt_one
+
+@[simp, norm_cast]
+theorem coe_mul (z w : 𝕔𝔻) : ↑(z * w) = (z * w : ℂ) :=
+ rfl
+
+@[simp, norm_cast]
+theorem coe_neg (z : 𝕔𝔻) : ↑(-z) = (-z : ℂ) := rfl
+
+/-- A constructor that assumes `‖z‖ < 1` instead of `dist z 0 < 1` and returns an element
+of `𝕔𝔻` instead of `↥Metric.ball (0 : ℂ) 1`. -/
+def mk (z : ℂ) (hz : ‖z‖ ≤ 1) : 𝕔𝔻 :=
+ ⟨z, mem_closedBall_zero_iff.2 hz⟩
+
+instance : CanLift ℂ 𝕔𝔻 (↑) (‖·‖ ≤ 1) where
+ prf z hz := ⟨mk z hz, rfl⟩
+
+/-- A cases eliminator that makes `cases z` use `UnitClosedDisc.mk` instead of `Subtype.mk`. -/
+@[elab_as_elim, cases_eliminator]
+protected def casesOn {motive : 𝕔𝔻 → Sort*} (mk : ∀ z hz, motive (.mk z hz)) (z : 𝕔𝔻) :
+ motive z :=
+ mk z z.norm_le_one
+
+@[simp]
+theorem casesOn_mk {motive : 𝕔𝔻 → Sort*} (mk' : ∀ z hz, motive (.mk z hz)) {z : ℂ} (hz : ‖z‖ ≤ 1) :
+ (mk z hz).casesOn mk' = mk' z hz :=
+ rfl
+
+@[simp]
+theorem coe_mk (z : ℂ) (hz : ‖z‖ ≤ 1) : (mk z hz : ℂ) = z :=
+ rfl
+
+@[simp]
+theorem mk_coe (z : 𝕔𝔻) (hz : ‖(z : ℂ)‖ ≤ 1 := z.norm_le_one) : mk z hz = z :=
+ Subtype.eta _ _
+
+@[simp]
+theorem mk_inj {z w : ℂ} (hz : ‖z‖ ≤ 1) (hw : ‖w‖ ≤ 1) : mk z hz = mk w hw ↔ z = w :=
+ Subtype.mk_eq_mk
+
+protected theorem «forall» {p : 𝕔𝔻 → Prop} : (∀ z, p z) ↔ ∀ z hz, p (mk z hz) :=
+ ⟨fun h z hz ↦ h (mk z hz), fun h z ↦ h z z.norm_le_one⟩
+
+protected theorem «exists» {p : 𝕔𝔻 → Prop} : (∃ z, p z) ↔ ∃ z hz, p (mk z hz) :=
+ ⟨fun ⟨z, hz⟩ ↦ ⟨z, z.norm_le_one, hz⟩, fun ⟨z, hz, h⟩ ↦ ⟨mk z hz, h⟩⟩
+
+@[simp]
+theorem mk_neg (z : ℂ) (hz : ‖-z‖ ≤ 1) : mk (-z) hz = -mk z (norm_neg z ▸ hz) :=
+ rfl
+
+@[simp]
+theorem coe_zero : ((0 : 𝕔𝔻) : ℂ) = 0 :=
+ rfl
+
+@[simp]
+theorem coe_eq_zero {z : 𝕔𝔻} : (z : ℂ) = 0 ↔ z = 0 :=
+ coe_injective.eq_iff' coe_zero
+
+@[simp] theorem mk_zero : mk 0 (by simp) = 0 := rfl
+
+@[simp] theorem mk_eq_zero {z : ℂ} (hz : ‖z‖ ≤ 1) : mk z hz = 0 ↔ z = 0 := by simp [← coe_inj]
+
+@[simp]
+theorem coe_one : ((1 : 𝕔𝔻) : ℂ) = 1 :=
+ rfl
+
+@[simp]
+theorem coe_eq_one {z : 𝕔𝔻} : (z : ℂ) = 1 ↔ z = 1 :=
+ coe_injective.eq_iff' coe_one
+
+@[simp] theorem mk_one : mk 1 (by simp) = 1 := rfl
+
+@[simp] theorem mk_eq_one {z : ℂ} (hz : ‖z‖ ≤ 1) : mk z hz = 1 ↔ z = 1 := by simp [← coe_inj]
+
+instance : Inhabited 𝕔𝔻 :=
+ ⟨0⟩
+
+instance : MulAction Circle 𝕔𝔻 :=
+ inferInstanceAs <| MulAction (sphere _ _) (closedBall _ _)
+
+instance : IsScalarTower Circle Circle 𝕔𝔻 :=
+ inferInstanceAs <| IsScalarTower (sphere _ _) (sphere _ _) (closedBall _ _)
+
+instance : IsScalarTower Circle 𝕔𝔻 𝕔𝔻 :=
+ isScalarTower_sphere_closedBall_closedBall
+
+instance : SMulCommClass Circle 𝕔𝔻 𝕔𝔻 :=
+ instSMulCommClass_sphere_closedBall_closedBall
+
+instance : SMulCommClass 𝕔𝔻 Circle 𝕔𝔻 :=
+ SMulCommClass.symm _ _ _
+
+instance instMulActionClosedBall : MulAction 𝕔𝔻 𝔻 :=
+ inferInstanceAs <| MulAction (closedBall _ _) (ball _ _)
+
+instance instIsScalarTower_closedBall_closedBall :
+ IsScalarTower 𝕔𝔻 𝕔𝔻 𝔻 :=
+ inferInstanceAs <| IsScalarTower (closedBall _ _) (closedBall _ _) (ball _ _)
+
+instance instIsScalarTower_closedBall : IsScalarTower 𝕔𝔻 𝔻 𝔻 :=
+ inferInstanceAs <| IsScalarTower (closedBall _ _) (ball _ _) (ball _ _)
+
+instance instSMulCommClass_closedBall_left : SMulCommClass 𝕔𝔻 𝔻 𝔻 :=
+ ⟨fun _ _ _ => Subtype.ext <| mul_left_comm _ _ _⟩
+
+instance instSMulCommClass_closedBall_right : SMulCommClass 𝔻 𝕔𝔻 𝔻 :=
+ SMulCommClass.symm _ _ _
+
+instance instSMulCommClass_circle_closedBall : SMulCommClass Circle 𝕔𝔻 𝔻 :=
+ inferInstanceAs <| SMulCommClass (sphere _ _) (closedBall _ _) (ball _ _)
+
+instance instSMulCommClass_closedBall_circle : SMulCommClass 𝕔𝔻 Circle 𝔻 :=
+ SMulCommClass.symm _ _ _
+
+@[simp, norm_cast]
+theorem coe_closedBall_smul (z : 𝕔𝔻) (w : 𝔻) : ↑(z • w) = (z * w : ℂ) :=
+ rfl
+
+@[deprecated (since := "2026-01-06")]
+alias coe_smul_closedBall := coe_closedBall_smul
+
+@[simp, norm_cast]
+theorem coe_circle_smul (z : Circle) (w : 𝕔𝔻) : ↑(z • w) = (z * w : ℂ) :=
+ rfl
+
+instance : SMulCommClass 𝕔𝔻 Circle 𝕔𝔻 :=
+ SMulCommClass.symm _ _ _
+
+instance : Pow 𝕔𝔻 ℕ where
+ pow z n := ⟨z ^ n, by simp [pow_le_one₀ (norm_nonneg _) z.norm_le_one]⟩
+
+@[simp, norm_cast]
+theorem coe_pow (z : 𝕔𝔻) (n : ℕ) : ((z ^ n : 𝕔𝔻) : ℂ) = z ^ (n : ℕ) := rfl
+
+@[fun_prop]
+theorem continuous_pow (n : ℕ) : Continuous (· ^ n : 𝕔𝔻 → 𝕔𝔻) := by
+ simp only [isEmbedding_coe.continuous_iff, Function.comp_def, coe_pow]
+ fun_prop
+
+instance : NatPowAssoc 𝕔𝔻 where
+ npow_add m n z := mod_cast pow_add (z : ℂ) m n
+ npow_one z := by simp [← coe_inj]
+ npow_zero z := by simp [← coe_inj]
+
+/-- Real part of a point of the unit disc. -/
+def re (z : 𝕔𝔻) : ℝ :=
+ Complex.re z
+
+/-- Imaginary part of a point of the unit disc. -/
+def im (z : 𝕔𝔻) : ℝ :=
+ Complex.im z
+
+@[simp, norm_cast]
+theorem re_coe (z : 𝕔𝔻) : (z : ℂ).re = z.re :=
+ rfl
+
+@[simp, norm_cast]
+theorem im_coe (z : 𝕔𝔻) : (z : ℂ).im = z.im :=
+ rfl
+
+@[simp]
+theorem re_neg (z : 𝕔𝔻) : (-z).re = -z.re :=
+ rfl
+
+@[simp]
+theorem im_neg (z : 𝕔𝔻) : (-z).im = -z.im :=
+ rfl
+
+@[simp] theorem re_zero : re 0 = 0 := rfl
+@[simp] theorem im_zero : im 0 = 0 := rfl
+
+/-- Conjugate point of the unit disc. -/
+instance : Star 𝕔𝔻 where
+ star z := mk (conj z) <| (norm_conj z).symm ▸ z.norm_le_one
+
+@[simp] theorem coe_star (z : 𝕔𝔻) : (↑(star z) : ℂ) = conj ↑z := rfl
+
+@[simp]
+protected theorem star_eq_zero {z : 𝕔𝔻} : star z = 0 ↔ z = 0 := by
+ simp [← coe_eq_zero]
+
+@[simp]
+protected theorem star_zero : star (0 : 𝕔𝔻) = 0 := by simp
+
+instance : InvolutiveStar 𝕔𝔻 where
+ star_involutive z := by ext; simp
+
+@[simp] protected theorem star_neg (z : 𝕔𝔻) : star (-z) = -(star z) := rfl
+
+@[simp] protected theorem re_star (z : 𝕔𝔻) : (star z).re = z.re := rfl
+
+@[simp] protected theorem im_star (z : 𝕔𝔻) : (star z).im = -z.im := rfl
+
+instance : StarMul 𝕔𝔻 where
+ star_mul z w := coe_injective <| by simp [mul_comm]
+
+end UnitClosedDisc
+
end Complex
diff --git a/Mathlib/Analysis/Normed/Field/UnitBall.lean b/Mathlib/Analysis/Normed/Field/UnitBall.lean
index 99252ea7420fb0..16d2e409f1cf80 100644
--- a/Mathlib/Analysis/Normed/Field/UnitBall.lean
+++ b/Mathlib/Analysis/Normed/Field/UnitBall.lean
@@ -141,6 +141,10 @@ def Submonoid.unitClosedBall (𝕜 : Type*) [SeminormedRing 𝕜] [NormOneClass
carrier := closedBall 0 1
one_mem' := mem_closedBall_zero_iff.2 norm_one.le }
+@[simp] lemma Submonoid.mem_unitClosedBall (𝕜 : Type*) [SeminormedRing 𝕜] [NormOneClass 𝕜] {x : 𝕜} :
+ x ∈ Submonoid.unitClosedBall 𝕜 ↔ ‖x‖ ≤ 1 := by
+ simp [Submonoid.unitClosedBall]
+
instance Metric.unitClosedBall.instMonoid [SeminormedRing 𝕜] [NormOneClass 𝕜] :
Monoid (closedBall (0 : 𝕜) 1) :=
inferInstanceAs <| Monoid (Submonoid.unitClosedBall 𝕜)
From b1013c671ddf090973ec6cf555d9941c21a979bf Mon Sep 17 00:00:00 2001
From: Eric Wieser <425260+eric-wieser@users.noreply.github.com>
Date: Tue, 16 Jun 2026 16:06:19 +0000
Subject: [PATCH 0068/1300] feat: tag map_mono and comap_mono with gcongr
(#40560)
---
Mathlib/Algebra/Algebra/NonUnitalSubalgebra.lean | 1 +
Mathlib/Algebra/Algebra/Subalgebra/Basic.lean | 1 +
Mathlib/Algebra/Group/Subgroup/Map.lean | 2 +-
Mathlib/Algebra/Star/NonUnitalSubalgebra.lean | 1 +
Mathlib/Algebra/Star/Subalgebra.lean | 1 +
Mathlib/CategoryTheory/Groupoid/Subgroupoid.lean | 1 +
Mathlib/Combinatorics/SimpleGraph/Maps.lean | 2 ++
Mathlib/Data/Multiset/MapFold.lean | 2 ++
Mathlib/FieldTheory/IntermediateField/Basic.lean | 1 +
Mathlib/GroupTheory/FiniteIndexNormalSubgroup.lean | 2 +-
Mathlib/LinearAlgebra/AffineSpace/AffineSubspace/Basic.lean | 1 +
Mathlib/MeasureTheory/MeasurableSpace/Basic.lean | 2 ++
Mathlib/RingTheory/Congruence/Basic.lean | 1 +
Mathlib/RingTheory/Ideal/Maps.lean | 2 ++
Mathlib/Topology/Sets/Opens.lean | 1 +
15 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/Mathlib/Algebra/Algebra/NonUnitalSubalgebra.lean b/Mathlib/Algebra/Algebra/NonUnitalSubalgebra.lean
index 6ba167e51d4ba3..49483234759347 100644
--- a/Mathlib/Algebra/Algebra/NonUnitalSubalgebra.lean
+++ b/Mathlib/Algebra/Algebra/NonUnitalSubalgebra.lean
@@ -344,6 +344,7 @@ def map (f : F) (S : NonUnitalSubalgebra R A) : NonUnitalSubalgebra R B :=
rcases hb with ⟨a, ha, rfl⟩
exact map_smulₛₗ f r a ▸ Set.mem_image_of_mem f (S.smul_mem' r ha) }
+@[gcongr]
theorem map_mono {S₁ S₂ : NonUnitalSubalgebra R A} {f : F} :
S₁ ≤ S₂ → (map f S₁ : NonUnitalSubalgebra R B) ≤ map f S₂ :=
Set.image_mono
diff --git a/Mathlib/Algebra/Algebra/Subalgebra/Basic.lean b/Mathlib/Algebra/Algebra/Subalgebra/Basic.lean
index f78f5e49160742..a34f06fcba9763 100644
--- a/Mathlib/Algebra/Algebra/Subalgebra/Basic.lean
+++ b/Mathlib/Algebra/Algebra/Subalgebra/Basic.lean
@@ -417,6 +417,7 @@ def map (f : A →ₐ[R] B) (S : Subalgebra R A) : Subalgebra R B :=
{ S.toSubsemiring.map (f : A →+* B) with
algebraMap_mem' := fun r => f.commutes r ▸ Set.mem_image_of_mem _ (S.algebraMap_mem r) }
+@[gcongr]
theorem map_mono {S₁ S₂ : Subalgebra R A} {f : A →ₐ[R] B} : S₁ ≤ S₂ → S₁.map f ≤ S₂.map f :=
Set.image_mono
diff --git a/Mathlib/Algebra/Group/Subgroup/Map.lean b/Mathlib/Algebra/Group/Subgroup/Map.lean
index 2d8825b6258ce9..d05286dc6eca13 100644
--- a/Mathlib/Algebra/Group/Subgroup/Map.lean
+++ b/Mathlib/Algebra/Group/Subgroup/Map.lean
@@ -82,7 +82,7 @@ theorem coe_comap (K : Subgroup N) (f : G →* N) : (K.comap f : Set G) = f ⁻
theorem mem_comap {K : Subgroup N} {f : G →* N} {x : G} : x ∈ K.comap f ↔ f x ∈ K :=
Iff.rfl
-@[to_additive]
+@[to_additive (attr := gcongr)]
theorem comap_mono {f : G →* N} {K K' : Subgroup N} : K ≤ K' → comap f K ≤ comap f K' :=
preimage_mono
diff --git a/Mathlib/Algebra/Star/NonUnitalSubalgebra.lean b/Mathlib/Algebra/Star/NonUnitalSubalgebra.lean
index 00eb073f8ede15..4ff88552ac4834 100644
--- a/Mathlib/Algebra/Star/NonUnitalSubalgebra.lean
+++ b/Mathlib/Algebra/Star/NonUnitalSubalgebra.lean
@@ -343,6 +343,7 @@ def map (f : F) (S : NonUnitalStarSubalgebra R A) : NonUnitalStarSubalgebra R B
toNonUnitalSubalgebra := S.toNonUnitalSubalgebra.map (f : A →ₙₐ[R] B)
star_mem' := by rintro _ ⟨a, ha, rfl⟩; exact ⟨star a, star_mem (s := S) ha, map_star f a⟩
+@[gcongr]
theorem map_mono {S₁ S₂ : NonUnitalStarSubalgebra R A} {f : F} :
S₁ ≤ S₂ → (map f S₁ : NonUnitalStarSubalgebra R B) ≤ map f S₂ :=
Set.image_mono
diff --git a/Mathlib/Algebra/Star/Subalgebra.lean b/Mathlib/Algebra/Star/Subalgebra.lean
index 993ecdcd7aaba0..d729e0d61d2a4f 100644
--- a/Mathlib/Algebra/Star/Subalgebra.lean
+++ b/Mathlib/Algebra/Star/Subalgebra.lean
@@ -273,6 +273,7 @@ theorem map_le_iff_le_comap {S : StarSubalgebra R A} {f : A →⋆ₐ[R] B} {U :
theorem gc_map_comap (f : A →⋆ₐ[R] B) : GaloisConnection (map f) (comap f) := fun _S _U =>
map_le_iff_le_comap
+@[gcongr]
theorem comap_mono {S₁ S₂ : StarSubalgebra R B} {f : A →⋆ₐ[R] B} :
S₁ ≤ S₂ → S₁.comap f ≤ S₂.comap f :=
Set.preimage_mono
diff --git a/Mathlib/CategoryTheory/Groupoid/Subgroupoid.lean b/Mathlib/CategoryTheory/Groupoid/Subgroupoid.lean
index f9d148751510b9..d27f71fc9cc941 100644
--- a/Mathlib/CategoryTheory/Groupoid/Subgroupoid.lean
+++ b/Mathlib/CategoryTheory/Groupoid/Subgroupoid.lean
@@ -387,6 +387,7 @@ def comap (S : Subgroupoid D) : Subgroupoid C where
simp only [mem_setOf, Functor.map_comp]
apply S.mul <;> assumption
+@[gcongr]
theorem comap_mono (S T : Subgroupoid D) : S ≤ T → comap φ S ≤ comap φ T := fun ST _ =>
@ST ⟨_, _, _⟩
diff --git a/Mathlib/Combinatorics/SimpleGraph/Maps.lean b/Mathlib/Combinatorics/SimpleGraph/Maps.lean
index e2aec80aab3607..07a8cc9ab92df0 100644
--- a/Mathlib/Combinatorics/SimpleGraph/Maps.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/Maps.lean
@@ -101,6 +101,7 @@ theorem map_adj_apply' {f : V → W} (hadj : G.Adj u v) (hne : f u ≠ f v) :
(G.map f).Adj (f u) (f v) :=
⟨hne, u, v, hadj, rfl, rfl⟩
+@[gcongr]
theorem map_monotone (f : V → W) : Monotone (SimpleGraph.map f) := by
rintro G G' h z1 z2 ⟨huv, u, v, ha, rfl, rfl⟩
exact ⟨huv, _, _, h ha, rfl, rfl⟩
@@ -151,6 +152,7 @@ lemma comap_symm (G : SimpleGraph V) (e : V ≃ W) :
lemma map_symm (G : SimpleGraph W) (e : V ≃ W) :
G.map e.symm.toEmbedding = G.comap e.toEmbedding := by rw [← comap_symm, e.symm_symm]
+@[gcongr]
theorem comap_monotone (f : V ↪ W) : Monotone (SimpleGraph.comap f) :=
fun _ _ h _ _ ha ↦ h ha
diff --git a/Mathlib/Data/Multiset/MapFold.lean b/Mathlib/Data/Multiset/MapFold.lean
index f343e6abbee27c..78ddf10a56632d 100644
--- a/Mathlib/Data/Multiset/MapFold.lean
+++ b/Mathlib/Data/Multiset/MapFold.lean
@@ -183,8 +183,10 @@ theorem map_lt_map {f : α → β} {s t : Multiset α} (h : s < t) : s.map f < t
rw [← s.card_map f, ← t.card_map f]
exact card_le_card H
+@[gcongr]
theorem map_mono (f : α → β) : Monotone (map f) := fun _ _ => map_le_map
+@[gcongr]
theorem map_strictMono (f : α → β) : StrictMono (map f) := fun _ _ => map_lt_map
@[simp, gcongr]
diff --git a/Mathlib/FieldTheory/IntermediateField/Basic.lean b/Mathlib/FieldTheory/IntermediateField/Basic.lean
index 6867c9c2e5e58a..ca54cf81abab2a 100644
--- a/Mathlib/FieldTheory/IntermediateField/Basic.lean
+++ b/Mathlib/FieldTheory/IntermediateField/Basic.lean
@@ -495,6 +495,7 @@ theorem map_map {K L₁ L₂ L₃ : Type*} [Field K] [Field L₁] [Algebra K L
(E.map f).map g = E.map (g.comp f) :=
SetLike.coe_injective <| Set.image_image _ _ _
+@[gcongr]
theorem map_mono (f : L →ₐ[K] L') {S T : IntermediateField K L} (h : S ≤ T) :
S.map f ≤ T.map f :=
SetLike.coe_mono (Set.image_mono h)
diff --git a/Mathlib/GroupTheory/FiniteIndexNormalSubgroup.lean b/Mathlib/GroupTheory/FiniteIndexNormalSubgroup.lean
index 1330053ba514bd..1376102a527800 100644
--- a/Mathlib/GroupTheory/FiniteIndexNormalSubgroup.lean
+++ b/Mathlib/GroupTheory/FiniteIndexNormalSubgroup.lean
@@ -138,7 +138,7 @@ theorem toSubgroup_comap (f : G →* H) (K : FiniteIndexNormalSubgroup H) :
((comap f K : FiniteIndexNormalSubgroup G) : Subgroup G) = (K : Subgroup H).comap f :=
rfl
-@[to_additive]
+@[to_additive (attr := gcongr)]
theorem comap_mono (f : G →* H) {K L : FiniteIndexNormalSubgroup H} (h : K ≤ L) :
comap f K ≤ comap f L :=
fun _ hx ↦ h hx
diff --git a/Mathlib/LinearAlgebra/AffineSpace/AffineSubspace/Basic.lean b/Mathlib/LinearAlgebra/AffineSpace/AffineSubspace/Basic.lean
index 3ee18f671c5e45..b4f59a92546e88 100644
--- a/Mathlib/LinearAlgebra/AffineSpace/AffineSubspace/Basic.lean
+++ b/Mathlib/LinearAlgebra/AffineSpace/AffineSubspace/Basic.lean
@@ -736,6 +736,7 @@ theorem coe_comap (f : P₁ →ᵃ[k] P₂) (s : AffineSubspace k P₂) : (s.com
theorem mem_comap {f : P₁ →ᵃ[k] P₂} {x : P₁} {s : AffineSubspace k P₂} : x ∈ s.comap f ↔ f x ∈ s :=
Iff.rfl
+@[gcongr]
theorem comap_mono {f : P₁ →ᵃ[k] P₂} {s t : AffineSubspace k P₂} : s ≤ t → s.comap f ≤ t.comap f :=
preimage_mono
diff --git a/Mathlib/MeasureTheory/MeasurableSpace/Basic.lean b/Mathlib/MeasureTheory/MeasurableSpace/Basic.lean
index dd4131f6d51f18..f092dbf255e704 100644
--- a/Mathlib/MeasureTheory/MeasurableSpace/Basic.lean
+++ b/Mathlib/MeasureTheory/MeasurableSpace/Basic.lean
@@ -113,11 +113,13 @@ theorem gc_comap_map (f : α → β) :
theorem map_mono (h : m₁ ≤ m₂) : m₁.map f ≤ m₂.map f :=
(gc_comap_map f).monotone_u h
+@[gcongr]
theorem monotone_map : Monotone (MeasurableSpace.map f) := fun _ _ => map_mono
theorem comap_mono (h : m₁ ≤ m₂) : m₁.comap g ≤ m₂.comap g :=
(gc_comap_map g).monotone_l h
+@[gcongr]
theorem monotone_comap : Monotone (MeasurableSpace.comap g) := fun _ _ h => comap_mono h
@[simp]
diff --git a/Mathlib/RingTheory/Congruence/Basic.lean b/Mathlib/RingTheory/Congruence/Basic.lean
index 10c01c3f5100f1..ed6041e7d612d6 100644
--- a/Mathlib/RingTheory/Congruence/Basic.lean
+++ b/Mathlib/RingTheory/Congruence/Basic.lean
@@ -142,6 +142,7 @@ instance : LE (RingCon R) where
/-- Definition of `≤` for congruence relations. -/
theorem le_def : c ≤ d ↔ ∀ {x y}, c x y → d x y := .rfl
+@[gcongr]
theorem comap_mono {R' : Type*} [Add R'] [Mul R']
{F : Type*} [FunLike F R R'] [AddHomClass F R R'] [MulHomClass F R R']
{J J' : RingCon R'} {f : F} (h : J ≤ J') :
diff --git a/Mathlib/RingTheory/Ideal/Maps.lean b/Mathlib/RingTheory/Ideal/Maps.lean
index 8b2013443e57fb..523c5a0fdc457d 100644
--- a/Mathlib/RingTheory/Ideal/Maps.lean
+++ b/Mathlib/RingTheory/Ideal/Maps.lean
@@ -62,6 +62,7 @@ lemma map_coe [RingHomClass F R S] (I : Ideal R) : I.map (f : R →+* S) = I.map
variable {f}
+@[gcongr]
theorem map_mono (h : I ≤ J) : map f I ≤ map f J :=
span_mono <| Set.image_mono h
@@ -78,6 +79,7 @@ theorem map_le_iff_le_comap [RingHomClass F R S] : map f I ≤ K ↔ I ≤ comap
theorem mem_comap [RingHomClass F R S] {x} : x ∈ comap f K ↔ f x ∈ K :=
Iff.rfl
+@[gcongr]
theorem comap_mono [RingHomClass F R S] (h : K ≤ L) : comap f K ≤ comap f L :=
Set.preimage_mono fun _ hx => h hx
diff --git a/Mathlib/Topology/Sets/Opens.lean b/Mathlib/Topology/Sets/Opens.lean
index f15b486678b9f6..13e02d1439fc4c 100644
--- a/Mathlib/Topology/Sets/Opens.lean
+++ b/Mathlib/Topology/Sets/Opens.lean
@@ -411,6 +411,7 @@ def comap (f : C(α, β)) : FrameHom (Opens β) (Opens α) where
theorem comap_id : comap (ContinuousMap.id α) = FrameHom.id _ :=
FrameHom.ext fun _ => ext rfl
+@[gcongr]
theorem comap_mono (f : C(α, β)) {s t : Opens β} (h : s ≤ t) : comap f s ≤ comap f t :=
OrderHomClass.mono (comap f) h
From c53555075e0c49c3963699412f35799e7e6a43bd Mon Sep 17 00:00:00 2001
From: Yifan Bai <110049727+TTony2019@users.noreply.github.com>
Date: Tue, 16 Jun 2026 16:57:08 +0000
Subject: [PATCH 0069/1300] feat: Add `AffineEquiv.image_intrinsicInterior`
(#38275)
## Summary
This PR generalizes the existing lemma `AffineIsometry.image_intrinsicInterior` to the setting of affine equivalences, yielding the corresponding result `AffineEquiv.image_intrinsicInterior`.
Since affine equivalences are more general than affine isometries, the proof requires an additional finite-dimensionality assumption. The key extra input is that in finite-dimensional spaces, an affine equivalence induces a homeomorphism via `AffineEquiv.toHomeomorphOfFiniteDimensional`, so the assumptions are adjusted accordingly.
Apart from these extra assumptions, the argument is essentially the same as for `AffineIsometry.image_intrinsicInterior`.
## Collaboration
This PR was developed together with @imathwy.
Co-authored-by: imathwy <148067791+imathwy@users.noreply.github.com>
Co-authored-by: PrinChern <148067791+imathwy@users.noreply.github.com>
---
Mathlib/Analysis/Convex/Intrinsic.lean | 183 ++++++++++++++----
.../Normed/Module/FiniteDimension.lean | 32 ++-
.../LinearAlgebra/AffineSpace/Restrict.lean | 20 ++
Mathlib/Topology/Algebra/AffineSubspace.lean | 51 ++++-
Mathlib/Topology/Homeomorph/Defs.lean | 38 ++++
Mathlib/Topology/Homeomorph/Lemmas.lean | 11 --
6 files changed, 282 insertions(+), 53 deletions(-)
diff --git a/Mathlib/Analysis/Convex/Intrinsic.lean b/Mathlib/Analysis/Convex/Intrinsic.lean
index 9f0d2177026522..9479339d6c71c4 100644
--- a/Mathlib/Analysis/Convex/Intrinsic.lean
+++ b/Mathlib/Analysis/Convex/Intrinsic.lean
@@ -29,8 +29,8 @@ same as the topological closure.
## Results
The main results are:
-* `AffineIsometry.image_intrinsicInterior`/`AffineIsometry.image_intrinsicFrontier`/
- `AffineIsometry.image_intrinsicClosure`: Intrinsic interiors/frontiers/closures commute with
+* `AffineIsometry.intrinsicInterior_image`/`AffineIsometry.intrinsicFrontier_image`/
+ `AffineIsometry.intrinsicClosure_image`: Intrinsic interiors/frontiers/closures commute with
taking the image under an affine isometry.
* `Set.Nonempty.intrinsicInterior`: The intrinsic interior of a nonempty convex set is nonempty.
@@ -217,8 +217,99 @@ theorem intrinsicClosure_eq_closure_inter_affineSpan (s : Set P) :
rw [Subtype.range_coe]
apply subset_affineSpan
+section ImageOfHomeomorphAffineSpan
+
+variable [AddCommGroup W] [Module 𝕜 W] [TopologicalSpace Q] [AddTorsor W Q]
+ {f : P → Q} {s : Set P}
+
+/-- If `f` agrees with a homeomorphism between the affine spans of `s` and `f '' s`, then pulling
+`f '' s` back to the affine span of `s` recovers `s` itself. -/
+private theorem preimage_image_eq_of_homeomorph_affineSpan
+ (e : affineSpan 𝕜 s → affineSpan 𝕜 (f '' s)) (he_homeo : IsHomeomorph e)
+ (he : ∀ x, (e x : Q) = f x) :
+ (f ∘ (↑)) ⁻¹' (f '' s) = ((↑) ⁻¹' s : Set <| affineSpan 𝕜 s) := by
+ ext x
+ refine ⟨fun ⟨_, hy, hfy⟩ ↦ ?_ , fun hx ↦ ⟨_, hx, rfl⟩⟩
+ change (x : P) ∈ s
+ rwa [exists_eq_subtype_mk_iff.mp ⟨subset_affineSpan 𝕜 s hy, he_homeo.injective <| Subtype.ext <|
+ by simpa [he] using hfy.symm⟩]
+
+variable (e : [Nonempty s] → affineSpan 𝕜 s → affineSpan 𝕜 (f '' s))
+ (he_homeo : [Nonempty s] → IsHomeomorph e) (he : [Nonempty s] → ∀ x, e x = f x)
+
+include e he_homeo he
+
+/-- Naturality of intrinsic interior under a map whose induced map on affine spans is a
+homeomorphism. It is introduced here to share the proof of the affine equivalence and affine
+isometry versions below. -/
+private theorem intrinsicInterior_image_of_homeomorph_affineSpan :
+ intrinsicInterior 𝕜 (f '' s) = f '' intrinsicInterior 𝕜 s := by
+ rcases s.eq_empty_or_nonempty with rfl | hs
+ · simp
+ · haveI : Nonempty s := hs.to_subtype
+ rw [intrinsicInterior, ← image_interior_preimage_comp e he_homeo,
+ (funext he : (↑) ∘ e = f ∘ (↑)),
+ preimage_image_eq_of_homeomorph_affineSpan e he_homeo he, image_comp]; rfl
+
+/-- Naturality of intrinsic frontier under a map whose induced map on affine spans is a
+homeomorphism. It is introduced here to share the proof of the affine equivalence and affine
+isometry versions below. -/
+private theorem intrinsicFrontier_image_of_homeomorph_affineSpan :
+ intrinsicFrontier 𝕜 (f '' s) = f '' intrinsicFrontier 𝕜 s := by
+ rcases s.eq_empty_or_nonempty with rfl | hs
+ · simp
+ · haveI : Nonempty s := hs.to_subtype
+ rw [intrinsicFrontier, ← image_frontier_preimage_comp e he_homeo,
+ (funext he : (↑) ∘ e = f ∘ (↑)),
+ preimage_image_eq_of_homeomorph_affineSpan e he_homeo he, image_comp]; rfl
+
+/-- Naturality of intrinsic closure under a map whose induced map on affine spans is a
+homeomorphism. It is introduced here to share the proof of the affine equivalence and affine
+isometry versions below. -/
+private theorem intrinsicClosure_image_of_homeomorph_affineSpan :
+ intrinsicClosure 𝕜 (f '' s) = f '' intrinsicClosure 𝕜 s := by
+ rcases s.eq_empty_or_nonempty with rfl | hs
+ · simp
+ · haveI : Nonempty s := hs.to_subtype
+ rw [intrinsicClosure, ← image_closure_preimage_comp e he_homeo,
+ (funext he : (↑) ∘ e = f ∘ (↑)),
+ preimage_image_eq_of_homeomorph_affineSpan e he_homeo he, image_comp]; rfl
+
+end ImageOfHomeomorphAffineSpan
+
end AddTorsor
+namespace ContinuousAffineEquiv
+
+variable [Ring 𝕜] [AddCommGroup V] [AddCommGroup W] [Module 𝕜 V] [Module 𝕜 W]
+ [TopologicalSpace P] [TopologicalSpace Q] [AddTorsor V P] [AddTorsor W Q]
+
+@[simp]
+theorem intrinsicInterior_image (φ : P ≃ᴬ[𝕜] Q) (s : Set P) :
+ intrinsicInterior 𝕜 (φ '' s) = φ '' intrinsicInterior 𝕜 s :=
+ let e : [Nonempty s] → (affineSpan 𝕜 s) ≃ᴬ[𝕜] (affineSpan 𝕜 (φ '' s)) := fun [_] =>
+ (φ.affineSubspaceMap (affineSpan 𝕜 s)).trans <| ofEq (map_span φ.toAffineMap s)
+ intrinsicInterior_image_of_homeomorph_affineSpan
+ (fun [_] => e.toHomeomorph) (fun [_] => e.toHomeomorph.isHomeomorph) (fun [_] _ => rfl)
+
+@[simp]
+theorem intrinsicFrontier_image (φ : P ≃ᴬ[𝕜] Q) (s : Set P) :
+ intrinsicFrontier 𝕜 (φ '' s) = φ '' intrinsicFrontier 𝕜 s :=
+ let e : [Nonempty s] → (affineSpan 𝕜 s) ≃ᴬ[𝕜] (affineSpan 𝕜 (φ '' s)) := fun [_] =>
+ (φ.affineSubspaceMap (affineSpan 𝕜 s)).trans <| ofEq (map_span φ.toAffineMap s)
+ intrinsicFrontier_image_of_homeomorph_affineSpan
+ (fun [_] => e.toHomeomorph) (fun [_] => e.toHomeomorph.isHomeomorph) (fun [_] _ => rfl)
+
+@[simp]
+theorem intrinsicClosure_image (φ : P ≃ᴬ[𝕜] Q) (s : Set P) :
+ intrinsicClosure 𝕜 (φ '' s) = φ '' intrinsicClosure 𝕜 s :=
+ let e : [Nonempty s] → (affineSpan 𝕜 s) ≃ᴬ[𝕜] (affineSpan 𝕜 (φ '' s)) := fun [_] =>
+ (φ.affineSubspaceMap (affineSpan 𝕜 s)).trans <| ofEq (map_span φ.toAffineMap s)
+ intrinsicClosure_image_of_homeomorph_affineSpan
+ (fun [_] => e.toHomeomorph) (fun [_] => e.toHomeomorph.isHomeomorph) (fun [_] _ => rfl)
+
+end ContinuousAffineEquiv
+
namespace AffineIsometry
variable [NormedField 𝕜] [SeminormedAddCommGroup V] [SeminormedAddCommGroup W] [NormedSpace 𝕜 V]
@@ -226,46 +317,68 @@ variable [NormedField 𝕜] [SeminormedAddCommGroup V] [SeminormedAddCommGroup W
[NormedAddTorsor W Q]
@[simp]
-theorem image_intrinsicInterior (φ : P →ᵃⁱ[𝕜] Q) (s : Set P) :
- intrinsicInterior 𝕜 (φ '' s) = φ '' intrinsicInterior 𝕜 s := by
- obtain rfl | hs := s.eq_empty_or_nonempty
- · simp only [intrinsicInterior_empty, image_empty]
- haveI : Nonempty s := hs.to_subtype
- let f := ((affineSpan 𝕜 s).isometryEquivMap φ).toHomeomorph
- have : φ.toAffineMap ∘ (↑) ∘ f.symm = (↑) := funext isometryEquivMap.apply_symm_apply
- rw [intrinsicInterior, intrinsicInterior, ← φ.coe_toAffineMap, ← map_span φ.toAffineMap s, ← this,
- ← Function.comp_assoc, image_comp, image_comp, f.symm.image_interior, f.image_symm,
- ← preimage_comp, Function.comp_assoc, f.symm_comp_self, AffineIsometry.coe_toAffineMap,
- Function.comp_id, preimage_comp, φ.injective.preimage_image]
+theorem intrinsicInterior_image (φ : P →ᵃⁱ[𝕜] Q) (s : Set P) :
+ intrinsicInterior 𝕜 (φ '' s) = φ '' intrinsicInterior 𝕜 s :=
+ let e : [Nonempty s] → (affineSpan 𝕜 s) ≃ᴬ[𝕜] (affineSpan 𝕜 (φ '' s)) := fun [_] =>
+ ((affineSpan 𝕜 s).isometryEquivMap φ).toContinuousAffineEquiv.trans <| ofEq <|
+ (map_span φ.toAffineMap s).trans <| congrArg _ <| congrArg (· '' s) φ.coe_toAffineMap
+ intrinsicInterior_image_of_homeomorph_affineSpan
+ (fun [_] => e.toHomeomorph) (fun [_] => e.toHomeomorph.isHomeomorph) (fun [_] _ => rfl)
@[simp]
-theorem image_intrinsicFrontier (φ : P →ᵃⁱ[𝕜] Q) (s : Set P) :
- intrinsicFrontier 𝕜 (φ '' s) = φ '' intrinsicFrontier 𝕜 s := by
- obtain rfl | hs := s.eq_empty_or_nonempty
- · simp
- haveI : Nonempty s := hs.to_subtype
- let f := ((affineSpan 𝕜 s).isometryEquivMap φ).toHomeomorph
- have : φ.toAffineMap ∘ (↑) ∘ f.symm = (↑) := funext isometryEquivMap.apply_symm_apply
- rw [intrinsicFrontier, intrinsicFrontier, ← φ.coe_toAffineMap, ← map_span φ.toAffineMap s, ← this,
- ← Function.comp_assoc, image_comp, image_comp, f.symm.image_frontier, f.image_symm,
- ← preimage_comp, Function.comp_assoc, f.symm_comp_self, AffineIsometry.coe_toAffineMap,
- Function.comp_id, preimage_comp, φ.injective.preimage_image]
+theorem intrinsicFrontier_image (φ : P →ᵃⁱ[𝕜] Q) (s : Set P) :
+ intrinsicFrontier 𝕜 (φ '' s) = φ '' intrinsicFrontier 𝕜 s :=
+ let e : [Nonempty s] → (affineSpan 𝕜 s) ≃ᴬ[𝕜] (affineSpan 𝕜 (φ '' s)) := fun [_] =>
+ ((affineSpan 𝕜 s).isometryEquivMap φ).toContinuousAffineEquiv.trans <| ofEq <|
+ (map_span φ.toAffineMap s).trans <| congrArg _ <| congrArg (· '' s) φ.coe_toAffineMap
+ intrinsicFrontier_image_of_homeomorph_affineSpan
+ (fun [_] => e.toHomeomorph) (fun [_] => e.toHomeomorph.isHomeomorph) (fun [_] _ => rfl)
@[simp]
-theorem image_intrinsicClosure (φ : P →ᵃⁱ[𝕜] Q) (s : Set P) :
- intrinsicClosure 𝕜 (φ '' s) = φ '' intrinsicClosure 𝕜 s := by
- obtain rfl | hs := s.eq_empty_or_nonempty
- · simp
- haveI : Nonempty s := hs.to_subtype
- let f := ((affineSpan 𝕜 s).isometryEquivMap φ).toHomeomorph
- have : φ.toAffineMap ∘ (↑) ∘ f.symm = (↑) := funext isometryEquivMap.apply_symm_apply
- rw [intrinsicClosure, intrinsicClosure, ← φ.coe_toAffineMap, ← map_span φ.toAffineMap s, ← this,
- ← Function.comp_assoc, image_comp, image_comp, f.symm.image_closure, f.image_symm,
- ← preimage_comp, Function.comp_assoc, f.symm_comp_self, AffineIsometry.coe_toAffineMap,
- Function.comp_id, preimage_comp, φ.injective.preimage_image]
+theorem intrinsicClosure_image (φ : P →ᵃⁱ[𝕜] Q) (s : Set P) :
+ intrinsicClosure 𝕜 (φ '' s) = φ '' intrinsicClosure 𝕜 s :=
+ let e : [Nonempty s] → (affineSpan 𝕜 s) ≃ᴬ[𝕜] (affineSpan 𝕜 (φ '' s)) := fun [_] =>
+ ((affineSpan 𝕜 s).isometryEquivMap φ).toContinuousAffineEquiv.trans <| ofEq <|
+ (map_span φ.toAffineMap s).trans <| congrArg _ <| congrArg (· '' s) φ.coe_toAffineMap
+ intrinsicClosure_image_of_homeomorph_affineSpan
+ (fun [_] => e.toHomeomorph) (fun [_] => e.toHomeomorph.isHomeomorph) (fun [_] _ => rfl)
+
+@[deprecated intrinsicInterior_image (since := "2026-05-08")]
+alias image_intrinsicInterior := intrinsicInterior_image
+
+@[deprecated intrinsicFrontier_image (since := "2026-05-08")]
+alias image_intrinsicFrontier := intrinsicFrontier_image
+
+@[deprecated intrinsicClosure_image (since := "2026-05-08")]
+alias image_intrinsicClosure := intrinsicClosure_image
end AffineIsometry
+namespace AffineEquiv
+
+variable [NontriviallyNormedField 𝕜] [CompleteSpace 𝕜]
+ [NormedAddCommGroup V] [NormedSpace 𝕜 V] [FiniteDimensional 𝕜 V]
+ [NormedAddCommGroup W] [NormedSpace 𝕜 W]
+ [MetricSpace P] [NormedAddTorsor V P]
+ [MetricSpace Q] [NormedAddTorsor W Q]
+
+@[simp]
+theorem intrinsicInterior_image (φ : P ≃ᵃ[𝕜] Q) (s : Set P) :
+ intrinsicInterior 𝕜 (φ '' s) = φ '' intrinsicInterior 𝕜 s :=
+ φ.toContinuousAffineEquiv.intrinsicInterior_image s
+
+@[simp]
+theorem intrinsicFrontier_image (φ : P ≃ᵃ[𝕜] Q) (s : Set P) :
+ intrinsicFrontier 𝕜 (φ '' s) = φ '' intrinsicFrontier 𝕜 s :=
+ φ.toContinuousAffineEquiv.intrinsicFrontier_image s
+
+@[simp]
+theorem intrinsicClosure_image (φ : P ≃ᵃ[𝕜] Q) (s : Set P) :
+ intrinsicClosure 𝕜 (φ '' s) = φ '' intrinsicClosure 𝕜 s :=
+ φ.toContinuousAffineEquiv.intrinsicClosure_image s
+
+end AffineEquiv
+
section NormedAddTorsor
variable (𝕜) [NontriviallyNormedField 𝕜] [CompleteSpace 𝕜] [NormedAddCommGroup V] [NormedSpace 𝕜 V]
diff --git a/Mathlib/Analysis/Normed/Module/FiniteDimension.lean b/Mathlib/Analysis/Normed/Module/FiniteDimension.lean
index a4d06ac9733d96..efa1834193c4dc 100644
--- a/Mathlib/Analysis/Normed/Module/FiniteDimension.lean
+++ b/Mathlib/Analysis/Normed/Module/FiniteDimension.lean
@@ -131,13 +131,30 @@ theorem AffineMap.continuous_of_finiteDimensional (f : PE →ᵃ[𝕜] PF) : Con
theorem AffineEquiv.continuous_of_finiteDimensional (f : PE ≃ᵃ[𝕜] PF) : Continuous f :=
f.toAffineMap.continuous_of_finiteDimensional
+/-- Reinterpret an affine equivalence as a continuous affine equivalence in finite dimension. -/
+def AffineEquiv.toContinuousAffineEquiv : (PE ≃ᵃ[𝕜] PF) ≃ (PE ≃ᴬ[𝕜] PF) where
+ toFun f :=
+ haveI := f.linear.finiteDimensional
+ ⟨f, f.continuous_of_finiteDimensional, f.symm.continuous_of_finiteDimensional⟩
+ invFun f := f.toAffineEquiv
+ left_inv _ := rfl
+ right_inv _ := ContinuousAffineEquiv.toAffineEquiv_injective rfl
+
+@[simp]
+theorem AffineEquiv.coe_toContinuousAffineEquiv (f : PE ≃ᵃ[𝕜] PF) :
+ ⇑(toContinuousAffineEquiv f) = f := rfl
+
+@[simp]
+theorem AffineEquiv.toAffineEquiv_toContinuousAffineEquiv (f : PE ≃ᵃ[𝕜] PF) :
+ (toContinuousAffineEquiv f).toAffineEquiv = f := rfl
+
+@[simp]
+theorem AffineEquiv.toContinuousAffineEquiv_symm_apply (f : PE ≃ᴬ[𝕜] PF) :
+ toContinuousAffineEquiv.symm f = f.toAffineEquiv := rfl
+
/-- Reinterpret an affine equivalence as a homeomorphism. -/
-def AffineEquiv.toHomeomorphOfFiniteDimensional (f : PE ≃ᵃ[𝕜] PF) : PE ≃ₜ PF where
- toEquiv := f.toEquiv
- continuous_toFun := f.continuous_of_finiteDimensional
- continuous_invFun :=
- haveI : FiniteDimensional 𝕜 F := f.linear.finiteDimensional
- f.symm.continuous_of_finiteDimensional
+def AffineEquiv.toHomeomorphOfFiniteDimensional (f : PE ≃ᵃ[𝕜] PF) : PE ≃ₜ PF :=
+ (toContinuousAffineEquiv f).toHomeomorph
@[simp]
theorem AffineEquiv.coe_toHomeomorphOfFiniteDimensional (f : PE ≃ᵃ[𝕜] PF) :
@@ -149,6 +166,9 @@ theorem AffineEquiv.coe_toHomeomorphOfFiniteDimensional_symm (f : PE ≃ᵃ[𝕜
⇑f.toHomeomorphOfFiniteDimensional.symm = f.symm :=
rfl
+attribute [deprecated AffineEquiv.toContinuousAffineEquiv (since := "2026-05-11")]
+ AffineEquiv.toHomeomorphOfFiniteDimensional
+
/-- An affine map from a finite-dimensional space is automatically Lipschitz. -/
theorem AffineMap.lipschitzWith_of_finiteDimensional (f : PE →ᵃ[𝕜] PF) :
∃ K : ℝ≥0, LipschitzWith K f := by
diff --git a/Mathlib/LinearAlgebra/AffineSpace/Restrict.lean b/Mathlib/LinearAlgebra/AffineSpace/Restrict.lean
index 95f5a7340452e5..4a16f9c79064a2 100644
--- a/Mathlib/LinearAlgebra/AffineSpace/Restrict.lean
+++ b/Mathlib/LinearAlgebra/AffineSpace/Restrict.lean
@@ -81,3 +81,23 @@ theorem AffineMap.restrict.surjective (φ : P₁ →ᵃ[k] P₂) {E : AffineSubs
theorem AffineMap.restrict.bijective {E : AffineSubspace k P₁} [Nonempty E] {φ : P₁ →ᵃ[k] P₂}
(hφ : Function.Injective φ) : Function.Bijective (φ.restrict (le_refl (E.map φ))) :=
⟨AffineMap.restrict.injective hφ _, AffineMap.restrict.surjective _ rfl⟩
+
+namespace AffineEquiv
+
+/-- An affine equivalence restricts to an affine equivalence between an affine subspace and its
+image. -/
+noncomputable def affineSubspaceMap (e : P₁ ≃ᵃ[k] P₂) (s : AffineSubspace k P₁)
+ [Nonempty s] : s ≃ᵃ[k] s.map e.toAffineMap :=
+ .ofBijective (AffineMap.restrict.bijective e.injective)
+
+@[simp]
+theorem affineSubspaceMap_apply (e : P₁ ≃ᵃ[k] P₂) (s : AffineSubspace k P₁)
+ [Nonempty s] (x : s) : e.affineSubspaceMap s x = e x :=
+ rfl
+
+@[simp]
+theorem affineSubspaceMap_apply_symm_apply (e : P₁ ≃ᵃ[k] P₂) (s : AffineSubspace k P₁)
+ [Nonempty s] (x : s.map e.toAffineMap) : e ((e.affineSubspaceMap s).symm x) = x :=
+ congrArg Subtype.val <| (e.affineSubspaceMap s).apply_symm_apply x
+
+end AffineEquiv
diff --git a/Mathlib/Topology/Algebra/AffineSubspace.lean b/Mathlib/Topology/Algebra/AffineSubspace.lean
index 0896280ff814f5..dc38b016cd6fdb 100644
--- a/Mathlib/Topology/Algebra/AffineSubspace.lean
+++ b/Mathlib/Topology/Algebra/AffineSubspace.lean
@@ -6,8 +6,9 @@ Authors: Joseph Myers
module
public import Mathlib.LinearAlgebra.AffineSpace.AffineSubspace.Basic
+public import Mathlib.LinearAlgebra.AffineSpace.Restrict
public import Mathlib.Topology.Algebra.ContinuousAffineMap
-public import Mathlib.Topology.Algebra.Group.Torsor
+public import Mathlib.Topology.Algebra.ContinuousAffineEquiv
/-!
# Topology of affine subspaces.
@@ -41,6 +42,54 @@ def subtypeA (s : AffineSubspace R P) [Nonempty s] : s →ᴬ[R] P where
s.subtypeA.toAffineMap = s.subtype :=
rfl
+/-- `AffineEquiv.ofEq` as a continuous affine equivalence. -/
+noncomputable def ofEq {s t : AffineSubspace R P} [Nonempty s] [Nonempty t]
+ (h : s = t) : s ≃ᴬ[R] t where
+ toAffineEquiv := .ofEq s t h
+ continuous_toFun := by subst h; exact continuous_id
+ continuous_invFun := by subst h; exact continuous_id
+
+@[simp]
+theorem coe_ofEq_apply {s t : AffineSubspace R P} [Nonempty s] [Nonempty t]
+ (h : s = t) (x : s) : (ofEq h x : P) = x := AffineEquiv.coe_ofEq_apply s t h x
+
+end AffineSubspace
+
+namespace ContinuousAffineEquiv
+
+variable {R V P W Q : Type*} [Ring R] [AddCommGroup V] [Module R V] [TopologicalSpace P]
+ [AddTorsor V P] [AddCommGroup W] [Module R W] [TopologicalSpace Q] [AddTorsor W Q]
+
+/-- A continuous affine equivalence restricts to a continuous affine equivalence between an affine
+subspace and its image.
+
+This is the continuous affine version of `AffineEquiv.affineSubspaceMap`. -/
+noncomputable def affineSubspaceMap (e : P ≃ᴬ[R] Q) (s : AffineSubspace R P) [Nonempty s] :
+ s ≃ᴬ[R] s.map e.toAffineMap :=
+ { e.toAffineEquiv.affineSubspaceMap s with
+ continuous_toFun := by simpa [Topology.IsEmbedding.subtypeVal.continuous_iff] using!
+ (e.continuous.comp continuous_subtype_val).congr fun _ => rfl
+ continuous_invFun := by simpa [Topology.IsEmbedding.subtypeVal.continuous_iff] using!
+ (e.continuous_invFun.comp continuous_subtype_val).congr fun x ↦
+ (e.apply_eq_iff_eq_symm_apply.mp
+ (AffineEquiv.affineSubspaceMap_apply_symm_apply e.toAffineEquiv s x)).symm }
+
+@[simp]
+theorem affineSubspaceMap_apply (e : P ≃ᴬ[R] Q) (s : AffineSubspace R P) [Nonempty s]
+ (x : s) : e.affineSubspaceMap s x = e x := rfl
+
+@[simp]
+theorem affineSubspaceMap_apply_symm_apply (e : P ≃ᴬ[R] Q) (s : AffineSubspace R P)
+ [Nonempty s] (x : s.map e.toAffineMap) : e ((e.affineSubspaceMap s).symm x) = x :=
+ AffineEquiv.affineSubspaceMap_apply_symm_apply e.toAffineEquiv s x
+
+end ContinuousAffineEquiv
+
+namespace AffineSubspace
+
+variable {R V P : Type*} [Ring R] [AddCommGroup V] [Module R V] [TopologicalSpace P]
+ [AddTorsor V P]
+
variable [TopologicalSpace V] [IsTopologicalAddTorsor P]
instance {s : AffineSubspace R P} [Nonempty s] : IsTopologicalAddTorsor s where
diff --git a/Mathlib/Topology/Homeomorph/Defs.lean b/Mathlib/Topology/Homeomorph/Defs.lean
index 102f58751b96a6..79ccff73ceae30 100644
--- a/Mathlib/Topology/Homeomorph/Defs.lean
+++ b/Mathlib/Topology/Homeomorph/Defs.lean
@@ -499,14 +499,52 @@ protected theorem Homeomorph.isHomeomorph (h : X ≃ₜ Y) : IsHomeomorph h :=
namespace IsHomeomorph
+/-- Bundled homeomorphism constructed from a map that is a homeomorphism. -/
+@[simps! toEquiv apply symm_apply]
+noncomputable def homeomorph (f : X → Y) (hf : IsHomeomorph f) : X ≃ₜ Y where
+ continuous_toFun := hf.1
+ continuous_invFun :=
+ Equiv.ofBijective f hf.bijective |>.continuous_symm_iff.2 hf.isOpenMap
+ toEquiv := Equiv.ofBijective f hf.bijective
+
protected lemma injective (hf : IsHomeomorph f) : Function.Injective f := hf.bijective.injective
protected lemma surjective (hf : IsHomeomorph f) : Function.Surjective f := hf.bijective.surjective
protected lemma id : IsHomeomorph (@id X) := ⟨continuous_id, .id, Function.bijective_id⟩
+theorem image_interior (hf : IsHomeomorph f) (s : Set X) :
+ f '' interior s = interior (f '' s) := hf.homeomorph.image_interior s
+
+theorem image_closure (hf : IsHomeomorph f) (s : Set X) :
+ f '' closure s = closure (f '' s) := hf.homeomorph.image_closure s
+
+theorem image_frontier (hf : IsHomeomorph f) (s : Set X) :
+ f '' frontier s = frontier (f '' s) := hf.homeomorph.image_frontier s
+
lemma comp {g : Y → Z} (hg : IsHomeomorph g) (hf : IsHomeomorph f) : IsHomeomorph (g ∘ f) :=
⟨hg.1.comp hf.1, hg.2.comp hf.2, hg.3.comp hf.3⟩
end IsHomeomorph
end IsHomeomorph
+
+variable {X Y Z : Type*} [TopologicalSpace X] [TopologicalSpace Y] in
+/-- Precomposing by a homeomorphism does not change the image of the interior of a preimage. -/
+theorem image_interior_preimage_comp (e : X → Y) (he : IsHomeomorph e) (f : Y → Z) (s : Set Z) :
+ (f ∘ e) '' interior ((f ∘ e) ⁻¹' s) = f '' interior (f ⁻¹' s) := by
+ simp only [Set.preimage_comp, Set.image_comp, he.image_interior,
+ Set.image_preimage_eq _ he.surjective]
+
+variable {X Y Z : Type*} [TopologicalSpace X] [TopologicalSpace Y] in
+/-- Precomposing by a homeomorphism does not change the image of the frontier of a preimage. -/
+theorem image_frontier_preimage_comp (e : X → Y) (he : IsHomeomorph e) (f : Y → Z) (s : Set Z) :
+ (f ∘ e) '' frontier ((f ∘ e) ⁻¹' s) = f '' frontier (f ⁻¹' s) := by
+ simp only [Set.preimage_comp, Set.image_comp, he.image_frontier,
+ Set.image_preimage_eq _ he.surjective]
+
+variable {X Y Z : Type*} [TopologicalSpace X] [TopologicalSpace Y] in
+/-- Precomposing by a homeomorphism does not change the image of the closure of a preimage. -/
+theorem image_closure_preimage_comp (e : X → Y) (he : IsHomeomorph e) (f : Y → Z) (s : Set Z) :
+ (f ∘ e) '' closure ((f ∘ e) ⁻¹' s) = f '' closure (f ⁻¹' s) := by
+ simp only [Set.preimage_comp, Set.image_comp, he.image_closure,
+ Set.image_preimage_eq _ he.surjective]
diff --git a/Mathlib/Topology/Homeomorph/Lemmas.lean b/Mathlib/Topology/Homeomorph/Lemmas.lean
index 4ac8614c83e476..9fc328a54765e0 100644
--- a/Mathlib/Topology/Homeomorph/Lemmas.lean
+++ b/Mathlib/Topology/Homeomorph/Lemmas.lean
@@ -484,17 +484,6 @@ namespace IsHomeomorph
variable (hf : IsHomeomorph f)
include hf
-variable (f) in
-/-- Bundled homeomorphism constructed from a map that is a homeomorphism. -/
-@[simps! toEquiv apply symm_apply]
-noncomputable def homeomorph : X ≃ₜ Y where
- continuous_toFun := hf.1
- continuous_invFun := by
- rw [← continuousOn_univ, ← hf.bijective.2.range_eq]
- exact hf.isOpenMap.continuousOn_range_of_leftInverse
- (Equiv.ofBijective f hf.bijective).left_inv
- toEquiv := Equiv.ofBijective f hf.bijective
-
protected lemma isClosedMap : IsClosedMap f := (hf.homeomorph f).isClosedMap
lemma isInducing : IsInducing f := (hf.homeomorph f).isInducing
lemma isQuotientMap : IsQuotientMap f := (hf.homeomorph f).isQuotientMap
From 6891e4fdd8ba77c691b4a89ebe3715622cd30d4c Mon Sep 17 00:00:00 2001
From: Michael Rothgang <10105016+grunweg@users.noreply.github.com>
Date: Tue, 16 Jun 2026 16:57:11 +0000
Subject: [PATCH 0070/1300] chore: move Data.Set.{Accumulate,Dissipate} to
Order.Set{Accumulate,Dissipate} (#40003)
Both files have only order-theoretic imports, and their contents are arguably also order-theoretic.
---
Mathlib.lean | 4 ++--
Mathlib/MeasureTheory/PiSystem.lean | 2 +-
Mathlib/MeasureTheory/SetSemiring.lean | 2 +-
.../{Data/Set/Accumulate.lean => Order/SetAccumulate.lean} | 0
Mathlib/{Data/Set/Dissipate.lean => Order/SetDissipate.lean} | 2 +-
Mathlib/Topology/Compactness/Compact.lean | 2 +-
6 files changed, 6 insertions(+), 6 deletions(-)
rename Mathlib/{Data/Set/Accumulate.lean => Order/SetAccumulate.lean} (100%)
rename Mathlib/{Data/Set/Dissipate.lean => Order/SetDissipate.lean} (98%)
diff --git a/Mathlib.lean b/Mathlib.lean
index cbf1ae94070768..e6c0e093bd8d15 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -4312,7 +4312,6 @@ public import Mathlib.Data.Seq.Basic
public import Mathlib.Data.Seq.Computation
public import Mathlib.Data.Seq.Defs
public import Mathlib.Data.Seq.Parallel
-public import Mathlib.Data.Set.Accumulate
public import Mathlib.Data.Set.Basic
public import Mathlib.Data.Set.BoolIndicator
public import Mathlib.Data.Set.BooleanAlgebra
@@ -4323,7 +4322,6 @@ public import Mathlib.Data.Set.Constructions
public import Mathlib.Data.Set.Countable
public import Mathlib.Data.Set.Defs
public import Mathlib.Data.Set.Disjoint
-public import Mathlib.Data.Set.Dissipate
public import Mathlib.Data.Set.Enumerate
public import Mathlib.Data.Set.Equitable
public import Mathlib.Data.Set.Finite.Basic
@@ -6152,6 +6150,8 @@ public import Mathlib.Order.ScottContinuity.Complete
public import Mathlib.Order.ScottContinuity.Prod
public import Mathlib.Order.SemiconjSup
public import Mathlib.Order.Set
+public import Mathlib.Order.SetAccumulate
+public import Mathlib.Order.SetDissipate
public import Mathlib.Order.SetIsMax
public import Mathlib.Order.SetNotation
public import Mathlib.Order.Shrink
diff --git a/Mathlib/MeasureTheory/PiSystem.lean b/Mathlib/MeasureTheory/PiSystem.lean
index 323e653bf0d44c..06bfac122aa2f9 100644
--- a/Mathlib/MeasureTheory/PiSystem.lean
+++ b/Mathlib/MeasureTheory/PiSystem.lean
@@ -5,10 +5,10 @@ Authors: Johannes Hölzl, Martin Zinkevich, Rémy Degenne
-/
module
-public import Mathlib.Data.Set.Dissipate
public import Mathlib.Logic.Encodable.Lattice
public import Mathlib.MeasureTheory.MeasurableSpace.Defs
public import Mathlib.Order.Disjointed
+public import Mathlib.Order.SetDissipate
/-!
# Induction principles for measurable sets, related to π-systems and λ-systems.
diff --git a/Mathlib/MeasureTheory/SetSemiring.lean b/Mathlib/MeasureTheory/SetSemiring.lean
index 04be1bbd849335..55d406b20306b8 100644
--- a/Mathlib/MeasureTheory/SetSemiring.lean
+++ b/Mathlib/MeasureTheory/SetSemiring.lean
@@ -5,11 +5,11 @@ Authors: Rémy Degenne, Peter Pfaffelhuber
-/
module
-public import Mathlib.Data.Set.Accumulate
public import Mathlib.Data.Set.Pairwise.Lattice
public import Mathlib.MeasureTheory.PiSystem
public import Mathlib.Order.Lattice.Nat
public import Mathlib.Order.Partition.Finpartition
+public import Mathlib.Order.SetAccumulate
public import Mathlib.Order.SupClosed
/-! # Semirings and rings of sets
diff --git a/Mathlib/Data/Set/Accumulate.lean b/Mathlib/Order/SetAccumulate.lean
similarity index 100%
rename from Mathlib/Data/Set/Accumulate.lean
rename to Mathlib/Order/SetAccumulate.lean
diff --git a/Mathlib/Data/Set/Dissipate.lean b/Mathlib/Order/SetDissipate.lean
similarity index 98%
rename from Mathlib/Data/Set/Dissipate.lean
rename to Mathlib/Order/SetDissipate.lean
index f0d281c104912e..e163c53144fcf5 100644
--- a/Mathlib/Data/Set/Dissipate.lean
+++ b/Mathlib/Order/SetDissipate.lean
@@ -6,7 +6,7 @@ Authors: Peter Pfaffelhuber
module
-public import Mathlib.Data.Set.Accumulate
+public import Mathlib.Order.SetAccumulate
/-!
# Dissipate
diff --git a/Mathlib/Topology/Compactness/Compact.lean b/Mathlib/Topology/Compactness/Compact.lean
index fb6b6d7acff7d6..4df746261b2037 100644
--- a/Mathlib/Topology/Compactness/Compact.lean
+++ b/Mathlib/Topology/Compactness/Compact.lean
@@ -6,7 +6,7 @@ Authors: Johannes Hölzl, Mario Carneiro, Yury Kudryashov
module
public import Mathlib.Order.Filter.Tendsto
-public import Mathlib.Data.Set.Accumulate
+public import Mathlib.Order.SetAccumulate
public import Mathlib.Topology.Bornology.Basic
public import Mathlib.Topology.ContinuousOn
public import Mathlib.Topology.Ultrafilter
From 483ba09b2edd4d422fa9e502f1c6df6b382483e8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ya=C3=ABl=20Dillies?=
Date: Tue, 16 Jun 2026 16:57:14 +0000
Subject: [PATCH 0071/1300] chore: deprecate `Rat.coe_int_inj` (#40670)
---
Mathlib/Algebra/Module/ZLattice/Basic.lean | 2 +-
Mathlib/Data/Rat/Defs.lean | 5 +----
Mathlib/Data/Rat/Lemmas.lean | 2 +-
Mathlib/NumberTheory/DiophantineApproximation/Basic.lean | 2 +-
Mathlib/NumberTheory/PythagoreanTriples.lean | 2 +-
5 files changed, 5 insertions(+), 8 deletions(-)
diff --git a/Mathlib/Algebra/Module/ZLattice/Basic.lean b/Mathlib/Algebra/Module/ZLattice/Basic.lean
index ae95cf8def950d..a7546316ed9cbe 100644
--- a/Mathlib/Algebra/Module/ZLattice/Basic.lean
+++ b/Mathlib/Algebra/Module/ZLattice/Basic.lean
@@ -594,7 +594,7 @@ theorem ZLattice.rank [hs : IsZLattice K L] : finrank ℤ L = finrank K E := by
obtain ⟨n, -, m, -, h_ne, h_eq⟩ := Set.Infinite.exists_ne_map_eq_of_mapsTo
Set.infinite_univ h_mapsto h_finite
have h_nz : (-n + m : ℚ) ≠ 0 := by
- rwa [Ne, add_eq_zero_iff_eq_neg.not, neg_inj, Rat.coe_int_inj, ← Ne]
+ rwa [Ne, add_eq_zero_iff_eq_neg.not, neg_inj, Rat.intCast_inj, ← Ne]
apply (smul_mem_iff _ h_nz).mp
refine span_subset_span ℤ ℚ _ ?_
rwa [add_smul, neg_smul, SetLike.mem_coe, ← fract_eq_fract, Int.cast_smul_eq_zsmul ℚ,
diff --git a/Mathlib/Data/Rat/Defs.lean b/Mathlib/Data/Rat/Defs.lean
index ac76129b7fcc11..1b3f964e3a1f79 100644
--- a/Mathlib/Data/Rat/Defs.lean
+++ b/Mathlib/Data/Rat/Defs.lean
@@ -263,10 +263,7 @@ theorem den_eq_one_iff (r : ℚ) : r.den = 1 ↔ ↑r.num = r :=
instance canLift : CanLift ℚ ℤ (↑) fun q => q.den = 1 :=
⟨fun q hq => ⟨q.num, coe_int_num_of_den_eq_one hq⟩⟩
--- Will be subsumed by `Int.coe_inj` after we have defined
--- `LinearOrderedField ℚ` (which implies characteristic zero).
-theorem coe_int_inj (m n : ℤ) : (m : ℚ) = n ↔ m = n :=
- ⟨congr_arg num, congr_arg _⟩
+@[deprecated (since := "2026-06-06")] alias coe_int_inj := intCast_inj
end Casts
diff --git a/Mathlib/Data/Rat/Lemmas.lean b/Mathlib/Data/Rat/Lemmas.lean
index 5e7016ec9f7af6..fe43e95dfecb29 100644
--- a/Mathlib/Data/Rat/Lemmas.lean
+++ b/Mathlib/Data/Rat/Lemmas.lean
@@ -262,7 +262,7 @@ theorem intCast_div_self (n : ℤ) : ((n / n : ℤ) : ℚ) = n / n := by
by_cases hn : n = 0
· subst hn
simp
- · have : (n : ℚ) ≠ 0 := by rwa [← coe_int_inj] at hn
+ · have : (n : ℚ) ≠ 0 := by rwa [← intCast_inj] at hn
simp only [Int.ediv_self hn, Int.cast_one, div_self this]
@[norm_cast]
diff --git a/Mathlib/NumberTheory/DiophantineApproximation/Basic.lean b/Mathlib/NumberTheory/DiophantineApproximation/Basic.lean
index c71bef7bddf592..704a720427df65 100644
--- a/Mathlib/NumberTheory/DiophantineApproximation/Basic.lean
+++ b/Mathlib/NumberTheory/DiophantineApproximation/Basic.lean
@@ -501,7 +501,7 @@ theorem exists_rat_eq_convergent' {v : ℕ} (h : ContfracLegendre.Ass ξ u v) :
obtain ⟨_, h₁, h₂⟩ := h
rcases le_or_gt (u : ℝ) ξ with ht | ht
· use 0
- rw [convergent_zero, Rat.coe_int_inj, eq_comm, floor_eq_iff]
+ rw [convergent_zero, Rat.intCast_inj, eq_comm, floor_eq_iff]
convert! And.intro ht (sub_lt_iff_lt_add'.mp (abs_lt.mp h₂).2) <;> norm_num
· replace h₁ := lt_sub_iff_add_lt'.mp (h₁ rfl)
have hξ₁ : ⌊ξ⌋ = u - 1 := by
diff --git a/Mathlib/NumberTheory/PythagoreanTriples.lean b/Mathlib/NumberTheory/PythagoreanTriples.lean
index 062bb6e1941f47..4a3ccc22d77dc2 100644
--- a/Mathlib/NumberTheory/PythagoreanTriples.lean
+++ b/Mathlib/NumberTheory/PythagoreanTriples.lean
@@ -413,7 +413,7 @@ theorem isPrimitiveClassified_aux (hc : x.gcd y = 1) (hzpos : 0 < z) {m n : ℤ}
apply And.intro _ (And.intro co pp)
right
refine ⟨?_, h2.left⟩
- rw [← Rat.coe_int_inj _ _, ← div_left_inj' ((mt (Rat.coe_int_inj z 0).mp) hz), hv2, h2.right]
+ rw [← Rat.intCast_inj, ← div_left_inj' (mt Rat.intCast_inj.mp hz), hv2, h2.right]
norm_cast
theorem isPrimitiveClassified_of_coprime_of_odd_of_pos (hc : Int.gcd x y = 1) (hyo : y % 2 = 1)
From 3d21c42b6e1f377f299956fed4d8351bdbc52deb Mon Sep 17 00:00:00 2001
From: "Yi.Yuan"
Date: Tue, 16 Jun 2026 17:40:55 +0000
Subject: [PATCH 0072/1300] refactor(Analysis): golf
`Mathlib/Analysis/Normed/Unbundled/FiniteExtension` (#39899)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- refactors `Normed/Unbundled/FiniteExtension` by proving `Basis.norm_smul` via `Finset.mul₀_sup'`
Extracted from #37968
[](https://gitpod.io/from-referrer/)
---
.../Normed/Unbundled/FiniteExtension.lean | 19 ++++---------------
1 file changed, 4 insertions(+), 15 deletions(-)
diff --git a/Mathlib/Analysis/Normed/Unbundled/FiniteExtension.lean b/Mathlib/Analysis/Normed/Unbundled/FiniteExtension.lean
index 1236db2da757f6..028dffb4ec2f00 100644
--- a/Mathlib/Analysis/Normed/Unbundled/FiniteExtension.lean
+++ b/Mathlib/Analysis/Normed/Unbundled/FiniteExtension.lean
@@ -153,21 +153,10 @@ theorem norm_mul_le_const_mul_norm {i : ι} (hBi : B i = (1 : L))
theorem norm_smul {ι : Type*} [Fintype ι] [Nonempty ι] {B : Basis ι K L} {i : ι}
(hBi : B i = (1 : L)) (k : K) (y : L) :
B.norm ((algebraMap K L) k * y) = B.norm ((algebraMap K L) k) * B.norm y := by
- by_cases hk : k = 0
- · rw [hk, map_zero, zero_mul, B.norm_zero, zero_mul]
- · rw [norm_extends hBi]
- obtain ⟨i, _, hi⟩ := exists_mem_eq_sup' univ_nonempty (fun i ↦ ‖B.repr y i‖)
- obtain ⟨j, _, hj⟩ := exists_mem_eq_sup' univ_nonempty
- (fun i ↦ ‖B.repr ((algebraMap K L) k * y) i‖)
- have hij : ‖B.repr y i‖ = ‖B.repr y j‖ := by
- rw [← hi]
- apply le_antisymm _ (norm_repr_le_norm B j)
- have hj' := Finset.le_sup' (fun i ↦ ‖B.repr ((algebraMap K L) k * y) i‖) (mem_univ i)
- simp only [repr_smul', norm_mul, ← hi] at hj hj'
- exact (mul_le_mul_iff_right₀ (lt_of_le_of_ne (norm_nonneg _)
- (Ne.symm (norm_ne_zero_iff.mpr hk)))).mp (hj ▸ hj')
- simp only [norm, hj]
- rw [repr_smul', norm_mul, hi, hij]
+ rw [norm_extends hBi, Basis.norm, Basis.norm,
+ Finset.mul₀_sup' (norm_nonneg _) (fun j : ι ↦ ‖B.repr y j‖) univ univ_nonempty]
+ congr with j
+ rw [repr_smul', norm_mul]
end Module.Basis
From 8e7aadcd06bec331b15f9c7b0b7c2fc01e37a884 Mon Sep 17 00:00:00 2001
From: "Yi.Yuan"
Date: Tue, 16 Jun 2026 17:40:58 +0000
Subject: [PATCH 0073/1300] refactor(Analysis): golf
`Mathlib/Analysis/SpecialFunctions/Gaussian/GaussianIntegral` (#39900)
- refactors `Gaussian/GaussianIntegral` by proving `integral_gaussian_complex_Ioi` using `integral_comp_neg_Ioi`
Extracted from #37968
[](https://gitpod.io/from-referrer/)
---
.../Gaussian/GaussianIntegral.lean | 29 ++++++-------------
1 file changed, 9 insertions(+), 20 deletions(-)
diff --git a/Mathlib/Analysis/SpecialFunctions/Gaussian/GaussianIntegral.lean b/Mathlib/Analysis/SpecialFunctions/Gaussian/GaussianIntegral.lean
index df5ccca8e69926..d5530157c7cded 100644
--- a/Mathlib/Analysis/SpecialFunctions/Gaussian/GaussianIntegral.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Gaussian/GaussianIntegral.lean
@@ -292,27 +292,16 @@ theorem integral_gaussian_complex {b : ℂ} (hb : 0 < re b) :
-- The Gaussian integral on the half-line, `∫ x in Ioi 0, exp (-b * x^2)`, for complex `b`.
theorem integral_gaussian_complex_Ioi {b : ℂ} (hb : 0 < re b) :
∫ x : ℝ in Ioi 0, cexp (-b * (x : ℂ) ^ 2) = (π / b) ^ (1 / 2 : ℂ) / 2 := by
+ let f : ℝ → ℂ := fun x => cexp (-b * (x : ℂ) ^ 2)
have full_integral := integral_gaussian_complex hb
- have : MeasurableSet (Ioi (0 : ℝ)) := measurableSet_Ioi
- rw [← integral_add_compl this (integrable_cexp_neg_mul_sq hb), compl_Ioi] at full_integral
- suffices ∫ x : ℝ in Iic 0, cexp (-b * (x : ℂ) ^ 2) = ∫ x : ℝ in Ioi 0, cexp (-b * (x : ℂ) ^ 2) by
- rw [this, ← mul_two] at full_integral
- rwa [eq_div_iff]; exact two_ne_zero
- have : ∀ c : ℝ, ∫ x in (0 : ℝ)..c, cexp (-b * (x : ℂ) ^ 2) =
- ∫ x in -c..0, cexp (-b * (x : ℂ) ^ 2) := by
- intro c
- have := intervalIntegral.integral_comp_sub_left (a := 0) (b := c)
- (fun x => cexp (-b * (x : ℂ) ^ 2)) 0
- simpa [zero_sub, neg_sq, neg_zero] using this
- have t1 :=
- intervalIntegral_tendsto_integral_Ioi 0 (integrable_cexp_neg_mul_sq hb).integrableOn tendsto_id
- have t2 :
- Tendsto (fun c : ℝ => ∫ x : ℝ in (0 : ℝ)..c, cexp (-b * (x : ℂ) ^ 2)) atTop
- (𝓝 (∫ x : ℝ in Iic 0, cexp (-b * (x : ℂ) ^ 2))) := by
- simp_rw [this]
- refine intervalIntegral_tendsto_integral_Iic _ ?_ tendsto_neg_atTop_atBot
- apply (integrable_cexp_neg_mul_sq hb).integrableOn
- exact tendsto_nhds_unique t2 t1
+ have h_eq := calc
+ ∫ x : ℝ in Iic 0, f x = ∫ x : ℝ in Ioi 0, f (-x) := by
+ simpa [f] using (integral_comp_neg_Ioi 0 f).symm
+ _ = ∫ x : ℝ in Ioi 0, f x :=
+ setIntegral_congr_fun measurableSet_Ioi fun _ _ ↦ (by simp [f])
+ rw [← integral_add_compl (s := Ioi 0) (by simp) (integrable_cexp_neg_mul_sq hb), compl_Ioi, h_eq,
+ ← mul_two] at full_integral
+ exact (eq_div_iff two_ne_zero).2 (by simpa using full_integral)
-- The Gaussian integral on the half-line, `∫ x in Ioi 0, exp (-b * x^2)`, for real `b`.
theorem integral_gaussian_Ioi (b : ℝ) :
From ce43383a24924c84cbc2223c3649080a15e6aa84 Mon Sep 17 00:00:00 2001
From: "Yi.Yuan"
Date: Tue, 16 Jun 2026 17:41:03 +0000
Subject: [PATCH 0074/1300] refactor(Analysis): golf
`Mathlib/Analysis/Analytic/Order` (#40067)
- refactors `AnalyticOnNhd.preimage_zero_mem_codiscreteWithin` to use `eqOn_zero_or_eventually_ne_zero_of_preconnected` directly
Extracted from #37968
[](https://gitpod.io/from-referrer/)
---
Mathlib/Analysis/Analytic/Order.lean | 11 +++--------
1 file changed, 3 insertions(+), 8 deletions(-)
diff --git a/Mathlib/Analysis/Analytic/Order.lean b/Mathlib/Analysis/Analytic/Order.lean
index 018825449dd978..5f8cb7ed88f349 100644
--- a/Mathlib/Analysis/Analytic/Order.lean
+++ b/Mathlib/Analysis/Analytic/Order.lean
@@ -664,14 +664,9 @@ codiscrete sets.
theorem preimage_zero_mem_codiscreteWithin {x : 𝕜} (h₁f : AnalyticOnNhd 𝕜 f U) (h₂f : f x ≠ 0)
(hx : x ∈ U) (hU : IsConnected U) :
f ⁻¹' {0}ᶜ ∈ codiscreteWithin U := by
- filter_upwards [h₁f.codiscreteWithin_setOf_analyticOrderAt_eq_zero_or_top,
- self_mem_codiscreteWithin U] with a ha h₂a
- rw [← (h₁f x hx).analyticOrderAt_eq_zero] at h₂f
- have {u : U} : analyticOrderAt f u ≠ ⊤ := by
- apply (h₁f.exists_analyticOrderAt_ne_top_iff_forall hU).1
- use ⟨x, hx⟩
- simp_all
- simp_all [(h₁f a h₂a).analyticOrderAt_eq_zero]
+ rcases h₁f.eqOn_zero_or_eventually_ne_zero_of_preconnected hU.isPreconnected with hzero | hne
+ · exact (h₂f (hzero hx)).elim
+ · exact hne
/--
If an analytic function `f` is not constantly zero on `𝕜`, then its set of zeros is codiscrete.
From 6071266be2fa9ec5032c9dfc574bb560f53db509 Mon Sep 17 00:00:00 2001
From: "Yi.Yuan"
Date: Tue, 16 Jun 2026 17:41:05 +0000
Subject: [PATCH 0075/1300] refactor(Analysis): golf
`Mathlib/Analysis/Complex/UpperHalfPlane/MoebiusAction` (#40069)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- refactors `σ_mul` by simplifying the determinant sign split with `mul_pos_iff`
- rewrites `denom_cocycle'` as a direct consequence of `denom_cocycle`
Extracted from #37968
[](https://gitpod.io/from-referrer/)
---
.../Analysis/Complex/UpperHalfPlane/MoebiusAction.lean | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/Mathlib/Analysis/Complex/UpperHalfPlane/MoebiusAction.lean b/Mathlib/Analysis/Complex/UpperHalfPlane/MoebiusAction.lean
index 6b66a5771136f2..91ed0eb715979c 100644
--- a/Mathlib/Analysis/Complex/UpperHalfPlane/MoebiusAction.lean
+++ b/Mathlib/Analysis/Complex/UpperHalfPlane/MoebiusAction.lean
@@ -156,13 +156,7 @@ def smulAux (g : GL (Fin 2) ℝ) (z : ℍ) : ℍ :=
lemma denom_cocycle' (g h : GL (Fin 2) ℝ) (z : ℍ) :
denom (g * h) z = σ h (denom g (smulAux h z)) * denom h z := by
- simp only [smulAux, smulAux', coe_mk, map_div₀, σ_num, σ_denom, σ_sq]
- change _ = (_ * (_ / _) + _) * _
- field_simp [denom_ne_zero h z]
- simp only [denom, Units.val_mul, mul_apply, Fin.sum_univ_succ, Finset.univ_unique,
- Fin.default_eq_zero, Finset.sum_singleton, Fin.succ_zero_eq_one, Complex.ofReal_add,
- Complex.ofReal_mul, num]
- ring
+ simpa [smulAux, smulAux', denom, σ_sq] using denom_cocycle g h z.im_ne_zero
theorem mul_smul' (g h : GL (Fin 2) ℝ) (z : ℍ) :
smulAux (g * h) z = smulAux g (smulAux h z) := by
From 9b785f98836c80b97bd069719ab54aa29c197200 Mon Sep 17 00:00:00 2001
From: "Thomas R. Murrills" <68410468+thorimur@users.noreply.github.com>
Date: Tue, 16 Jun 2026 17:41:08 +0000
Subject: [PATCH 0076/1300] chore: clean up `Mathlib.Init` imports after #32419
(#40674)
Now that #32419 is merged, we can remove this import from Mathlib.Init.
---
Mathlib/Init.lean | 4 ----
1 file changed, 4 deletions(-)
diff --git a/Mathlib/Init.lean b/Mathlib/Init.lean
index 5c6e0b48acc622..81b05422e07d29 100644
--- a/Mathlib/Init.lean
+++ b/Mathlib/Init.lean
@@ -28,10 +28,6 @@ public import Mathlib.Tactic.Linter.Style
public import Mathlib.Tactic.Linter.Whitespace
public import Mathlib.Tactic.TacticAnalysis.Declarations
public import Mathlib.Tactic.TypeStar
--- This is a redundant import, but it is needed so that
--- the linter doesn't complain about `ParseCommand` not importing `Header`.
--- This can be removed after https://github.com/leanprover-community/mathlib4/pull/32419
-public import Mathlib.Util.ParseCommand
-- This import makes the `#help` command available globally.
public import Batteries.Tactic.HelpCmd
-- This import makes the `proof_wanted` command available globally.
From 545bd043025d40d8f925d89d3f4ba7a8976530ee Mon Sep 17 00:00:00 2001
From: Dennj
Date: Tue, 16 Jun 2026 18:37:30 +0000
Subject: [PATCH 0077/1300] feat(LinearAlgebra/Matrix): add Hadamard matrices
(#38582)
Define Hadamard matrices (over any star ring) and add basic API, including the fact that a Hadamard matrix with integer entries must have rank 1 or 2 or divisible by 4.
Co-authored-by: Eric Wieser
Co-authored-by: Oliver Nash
---
Mathlib.lean | 1 +
Mathlib/Algebra/Regular/Basic.lean | 10 +
.../LinearAlgebra/Matrix/ConjTranspose.lean | 4 +
.../LinearAlgebra/Matrix/HadamardMatrix.lean | 261 ++++++++++++++++++
docs/references.bib | 11 +
5 files changed, 287 insertions(+)
create mode 100644 Mathlib/LinearAlgebra/Matrix/HadamardMatrix.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index e6c0e093bd8d15..8a4fbc6a4795c7 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -5085,6 +5085,7 @@ public import Mathlib.LinearAlgebra.Matrix.GeneralLinearGroup.MvPolynomial
public import Mathlib.LinearAlgebra.Matrix.GeneralLinearGroup.Projective
public import Mathlib.LinearAlgebra.Matrix.Gershgorin
public import Mathlib.LinearAlgebra.Matrix.Hadamard
+public import Mathlib.LinearAlgebra.Matrix.HadamardMatrix
public import Mathlib.LinearAlgebra.Matrix.Hermitian
public import Mathlib.LinearAlgebra.Matrix.Ideal
public import Mathlib.LinearAlgebra.Matrix.Integer
diff --git a/Mathlib/Algebra/Regular/Basic.lean b/Mathlib/Algebra/Regular/Basic.lean
index 14d95772970905..8f567ab997558f 100644
--- a/Mathlib/Algebra/Regular/Basic.lean
+++ b/Mathlib/Algebra/Regular/Basic.lean
@@ -155,6 +155,16 @@ theorem isRegular_mul_iff : IsRegular (a * b) ↔ IsRegular a ∧ IsRegular b :=
refine Iff.trans ?_ isRegular_mul_and_mul_iff
exact ⟨fun ab => ⟨ab, by rwa [mul_comm]⟩, fun rab => rab.1⟩
+/-- If a product is regular, so is its left factor. -/
+@[to_additive /-- If a sum is add-regular, so is its left summand. -/]
+theorem IsRegular.of_mul_left (h : IsRegular (a * b)) : IsRegular a :=
+ (isRegular_mul_iff.mp h).1
+
+/-- If a product is regular, so is its right factor. -/
+@[to_additive /-- If a sum is add-regular, so is its right summand. -/]
+theorem IsRegular.of_mul_right (h : IsRegular (a * b)) : IsRegular b :=
+ (isRegular_mul_iff.mp h).2
+
end CommSemigroup
section Monoid
diff --git a/Mathlib/LinearAlgebra/Matrix/ConjTranspose.lean b/Mathlib/LinearAlgebra/Matrix/ConjTranspose.lean
index 6acd1c31f97c1d..ff316045d3c17e 100644
--- a/Mathlib/LinearAlgebra/Matrix/ConjTranspose.lean
+++ b/Mathlib/LinearAlgebra/Matrix/ConjTranspose.lean
@@ -151,6 +151,10 @@ theorem transpose_conjTranspose [Star α] (M : Matrix m n α) :
Mᵀᴴ = M.map star :=
rfl
+theorem conjTranspose_transpose_eq_transpose_conjTranspose [Star α] (M : Matrix m n α) :
+ Mᵀᴴ = Mᴴᵀ :=
+ rfl
+
theorem conjTranspose_injective [InvolutiveStar α] :
Function.Injective (conjTranspose : Matrix m n α → Matrix n m α) :=
(map_injective star_injective).comp transpose_injective
diff --git a/Mathlib/LinearAlgebra/Matrix/HadamardMatrix.lean b/Mathlib/LinearAlgebra/Matrix/HadamardMatrix.lean
new file mode 100644
index 00000000000000..4ace07110c3435
--- /dev/null
+++ b/Mathlib/LinearAlgebra/Matrix/HadamardMatrix.lean
@@ -0,0 +1,261 @@
+/-
+Copyright (c) 2026 Dennj Osele. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Dennj Osele
+-/
+module
+
+public import Mathlib.LinearAlgebra.Matrix.Kronecker
+public import Mathlib.LinearAlgebra.Matrix.Adjugate
+public import Mathlib.Data.Matrix.Basic
+public import Mathlib.Algebra.Star.Unitary
+
+/-!
+# Hadamard matrices
+
+This file defines `Matrix.IsHadamard`, a unified notion that specializes to the classical real
+Hadamard matrices over `ℝ`/`ℤ` (where `star` is trivial and entries are `±1`) and to the complex
+Hadamard matrices over `ℂ` (where entries have unit norm). Basic results: conjugate-transpose
+closure, the order identity `n = s * star s` from constant row or column sums, the Sylvester
+(Kronecker) construction, and the divisibility obstruction `4 ∣ n`.
+
+## References
+
+* [W. de Launey and D. L. Flannery, *Algebraic Design Theory*][deLauneyFlannery2011]
+-/
+
+@[expose] public section
+
+
+variable {m n R : Type*}
+
+namespace Matrix
+
+open scoped Kronecker
+
+variable [Fintype m] [Fintype n] [DecidableEq m] [DecidableEq n]
+
+section Semiring
+variable [Semiring R] [StarRing R]
+
+/-- A square matrix over a `*`-semiring whose entries are unitary and whose rows and columns are
+orthogonal with respect to the conjugate transpose:
+`A * Aᴴ = n • 1` and `Aᴴ * A = n • 1`.
+
+Over a commutative ring in which the order is regular, the one-sided condition from
+[Definition 2.3.1][deLauneyFlannery2011] implies this predicate by
+`IsHadamard.of_mul_conjTranspose`; over a ring with trivial star (e.g. `ℝ`, `ℤ`), the entry
+condition becomes `A i j = 1 ∨ A i j = -1`. Over `ℂ`, the entry condition becomes `‖A i j‖ = 1`,
+generalizing the fourth-root complex Hadamard matrices of
+[Definition 2.7.1][deLauneyFlannery2011]. -/
+@[mk_iff] structure IsHadamard (A : Matrix n n R) : Prop where
+ apply_mem (i j : n) : A i j ∈ unitary R
+ mul_conjTranspose : A * Aᴴ = (Fintype.card n : R) • (1 : Matrix n n R)
+ conjTranspose_mul : Aᴴ * A = (Fintype.card n : R) • (1 : Matrix n n R)
+
+variable {A : Matrix n n R}
+
+theorem IsHadamard.isStarNormal (hA : A.IsHadamard) : IsStarNormal A where
+ star_comm_self := by
+ rw [commute_iff_eq, star_eq_conjTranspose, hA.conjTranspose_mul, hA.mul_conjTranspose]
+
+/-- The conjugate transpose of a Hadamard matrix is Hadamard. -/
+theorem IsHadamard.conjTranspose (hA : A.IsHadamard) : Aᴴ.IsHadamard := by
+ exact ⟨fun i j => Unitary.star_mem (hA.apply_mem j i),
+ by simpa using hA.conjTranspose_mul,
+ by simpa using hA.mul_conjTranspose⟩
+
+@[simp]
+theorem isHadamard_conjTranspose_iff : Aᴴ.IsHadamard ↔ A.IsHadamard :=
+ ⟨fun hA => by simpa using hA.conjTranspose, (·.conjTranspose)⟩
+
+/-- Permuting the rows and columns of a Hadamard matrix gives a Hadamard matrix. -/
+theorem IsHadamard.reindex (e₁ e₂ : n ≃ m) (hA : A.IsHadamard) :
+ (reindex e₁ e₂ A).IsHadamard := by
+ refine ⟨fun i j => hA.apply_mem _ _, ?_, ?_⟩ <;>
+ simp [reindex_apply, submatrix_mul_equiv, hA.mul_conjTranspose, hA.conjTranspose_mul,
+ Fintype.card_congr e₁, submatrix_smul, Pi.smul_apply]
+
+@[simp]
+theorem isHadamard_submatrix_equiv_iff (e₁ e₂ : m ≃ n) :
+ (A.submatrix e₁ e₂).IsHadamard ↔ A.IsHadamard :=
+ ⟨fun h => by simpa using h.reindex e₁ e₂,
+ fun h => by simpa [reindex_apply] using h.reindex e₁.symm e₂.symm⟩
+
+/-- The Kronecker product of two Hadamard matrices is Hadamard. -/
+theorem IsHadamard.kronecker {A : Matrix m m R} {B : Matrix n n R}
+ (hA : A.IsHadamard) (hB : B.IsHadamard) : (A ⊗ₖ B).IsHadamard := by
+ refine ⟨fun _ _ ↦ mul_mem (hA.apply_mem _ _) (hB.apply_mem _ _), ?_, ?_⟩ <;> ext ⟨i, i'⟩ ⟨j, j'⟩
+ · calc
+ _ = ∑ x₁, ∑ x₂, A i x₁ * (B i' x₂ * Bᴴ x₂ j') * Aᴴ x₁ j := by
+ simp [conjTranspose_kronecker', mul_apply, mul_assoc, ← Finset.sum_product']
+ _ = if i' = j' then ∑ x, A i x * (Fintype.card n • Aᴴ) x j else 0 := by
+ simp [← Finset.sum_mul, ← Finset.mul_sum, ← mul_apply, hB.mul_conjTranspose,
+ one_apply, mul_assoc _ (Fintype.card n : R), -conjTranspose_apply]
+ _ = _ := by
+ simp only [← mul_apply, mul_smul_comm, hA.mul_conjTranspose]
+ simp [one_apply, ← Nat.cast_mul, mul_comm, ← ite_and, and_comm]
+ · calc
+ _ = ∑ x₁, ∑ x₂, Bᴴ i' x₂ * (Aᴴ i x₁ * A x₁ j) * B x₂ j' := by
+ simp [conjTranspose_kronecker', mul_apply, mul_assoc, ← Finset.sum_product']
+ _ = if i = j then ∑ x, Bᴴ i' x * (Fintype.card m • B) x j' else 0 := by
+ rw [Finset.sum_comm]
+ simp [← Finset.sum_mul, ← Finset.mul_sum, ← mul_apply, hA.conjTranspose_mul,
+ one_apply, mul_assoc _ (Fintype.card m : R), -conjTranspose_apply]
+ _ = _ := by
+ simp only [← mul_apply, mul_smul_comm, hB.conjTranspose_mul]
+ simp [one_apply, ← Nat.cast_mul, ← ite_and]
+
+/-- A Hadamard matrix with constant column sum `s` has order `s * star s`, provided the order
+is regular in `R`.
+
+The row-sum form is `IsHadamard.card_eq_star_mul_of_const_row_sum`; over a ring with trivial
+star the conclusion becomes `(Fintype.card n : R) = s ^ 2`, a slightly stronger form of
+[Theorem 2.3.7][deLauneyFlannery2011]: only a constant sum hypothesis on one side is needed
+under the two-sided orthogonality condition. -/
+theorem IsHadamard.card_eq_mul_star_of_const_col_sum {s : R}
+ (hA : A.IsHadamard) (hcard : IsRegular (Fintype.card n : R))
+ (hcol : ∀ j, ∑ i, A i j = s) : (Fintype.card n : R) = s * star s := by
+ have hvcol : (1 : n → R) ᵥ* A = s • 1 := by
+ ext j
+ simpa [Matrix.vecMul, dotProduct] using hcol j
+ have hconjcol : Aᴴ *ᵥ (1 : n → R) = star s • 1 := by
+ ext i
+ simp [Matrix.mulVec, dotProduct, ← star_sum, hcol i]
+ have hleft : (1 : n → R) ᵥ* (A * Aᴴ) ⬝ᵥ 1 = (Fintype.card n : R) ^ 2 := by
+ rw [hA.mul_conjTranspose, Nat.cast_smul_eq_nsmul, vecMul_smul, smul_dotProduct]
+ simp [dotProduct, pow_two]
+ have hright : (1 : n → R) ᵥ* (A * Aᴴ) ⬝ᵥ 1 = (Fintype.card n : R) * (s * star s) := by
+ rw [← vecMul_vecMul, ← dotProduct_mulVec, hvcol, hconjcol]
+ simp [dotProduct]
+ exact hcard.left <| show (Fintype.card n : R) * (Fintype.card n : R) =
+ (Fintype.card n : R) * (s * star s) by
+ simpa [pow_two] using hleft.symm.trans hright
+
+/-- A Hadamard matrix with constant row sum `s` has order `star s * s`, provided the order
+is regular in `R`. This generalizes [Theorem 2.3.7][deLauneyFlannery2011]. -/
+theorem IsHadamard.card_eq_star_mul_of_const_row_sum {s : R}
+ (hA : A.IsHadamard) (hcard : IsRegular (Fintype.card n : R))
+ (hrow : ∀ i, ∑ j, A i j = s) : (Fintype.card n : R) = star s * s := by
+ have hcol : ∀ j, ∑ i, Aᴴ i j = star s := fun j => by
+ simp [conjTranspose_apply, ← star_sum, hrow j]
+ simpa using hA.conjTranspose.card_eq_mul_star_of_const_col_sum hcard hcol
+
+end Semiring
+
+section CommSemiring
+variable [CommSemiring R] [StarRing R] {A : Matrix n n R}
+
+/-- The transpose of a Hadamard matrix is Hadamard.
+
+Unlike `IsHadamard.conjTranspose` this requires commutativity: over a noncommutative ring the
+transpose of a Hadamard matrix need not be Hadamard. -/
+theorem IsHadamard.transpose (hA : A.IsHadamard) : Aᵀ.IsHadamard where
+ apply_mem i j := hA.apply_mem j i
+ mul_conjTranspose := by
+ rw [conjTranspose_transpose_eq_transpose_conjTranspose, ← transpose_mul, hA.conjTranspose_mul,
+ transpose_smul, transpose_one]
+ conjTranspose_mul := by
+ rw [conjTranspose_transpose_eq_transpose_conjTranspose, ← transpose_mul, hA.mul_conjTranspose,
+ transpose_smul, transpose_one]
+
+@[simp]
+theorem isHadamard_transpose_iff : Aᵀ.IsHadamard ↔ A.IsHadamard :=
+ ⟨fun hA => by simpa using hA.transpose, (·.transpose)⟩
+
+end CommSemiring
+
+section Ring
+variable [Ring R] [StarRing R] {A : Matrix n n R}
+
+/-- Negating a Hadamard matrix gives a Hadamard matrix. -/
+theorem IsHadamard.neg (hA : A.IsHadamard) : (-A).IsHadamard := by
+ simpa [isHadamard_iff, Unitary.mem_iff] using hA
+
+/-- A matrix is Hadamard iff its negation is. -/
+@[simp]
+theorem IsHadamard.neg_iff : (-A).IsHadamard ↔ A.IsHadamard :=
+ ⟨fun hA => by simpa using hA.neg, (·.neg)⟩
+
+end Ring
+
+section CommRing
+variable [CommRing R] [StarRing R] {A : Matrix n n R}
+
+/-- The Hadamard determinant identity: `det A * star (det A) = (card n)^(card n)`. -/
+theorem IsHadamard.det_mul_star_det (hA : A.IsHadamard) :
+ A.det * star A.det = (Fintype.card n : R) ^ Fintype.card n := by
+ have := congr_arg det hA.mul_conjTranspose
+ rwa [det_mul, det_conjTranspose, det_smul, det_one, mul_one] at this
+
+/-- The Hadamard determinant identity: `star (det A) * det A = (card n)^(card n)`. -/
+theorem IsHadamard.star_det_mul_det (hA : A.IsHadamard) :
+ star A.det * A.det = (Fintype.card n : R) ^ Fintype.card n := by
+ rw [mul_comm, hA.det_mul_star_det]
+
+/-- A Hadamard matrix over a reduced commutative ring has nonzero determinant, provided the order
+is nonzero in `R`. -/
+theorem IsHadamard.det_ne_zero [IsReduced R] (hA : A.IsHadamard)
+ (hcard : (Fintype.card n : R) ≠ 0) : A.det ≠ 0 := fun h =>
+ pow_ne_zero _ hcard <| by rw [← hA.det_mul_star_det, h, star_zero, zero_mul]
+
+/-- The determinant of a Hadamard matrix is regular, provided the order is regular in `R`. -/
+theorem IsHadamard.isRegular_det (hA : A.IsHadamard)
+ (hcard : IsRegular (Fintype.card n : R)) : IsRegular A.det := by
+ have : IsRegular (A.det * star A.det) := by
+ rw [hA.det_mul_star_det]
+ exact hcard.pow _
+ exact this.of_mul_left
+
+/-- Build a Hadamard matrix from the one-sided row-orthogonality condition, provided the order is
+regular in `R`.
+
+This is the matrix form of [Theorem 2.3.6][deLauneyFlannery2011]. -/
+theorem IsHadamard.of_mul_conjTranspose
+ (hentry : ∀ i j, A i j ∈ unitary R)
+ (hmul : A * Aᴴ = (Fintype.card n : R) • (1 : Matrix n n R))
+ (hcard : IsRegular (Fintype.card n : R)) : A.IsHadamard := by
+ refine ⟨hentry, hmul, ?_⟩
+ have hdet : IsRegular (A.det * star A.det) := by
+ have := congr_arg det hmul
+ rw [det_mul, det_conjTranspose, det_smul, det_one, mul_one] at this
+ rw [this]
+ exact hcard.pow _
+ have hreg : IsLeftRegular A :=
+ (isRegular_of_isLeftRegular_det hdet.of_mul_left.left).left
+ exact hreg <| show A * (Aᴴ * A) = A * ((Fintype.card n : R) • 1) by
+ rw [← mul_assoc, hmul, smul_mul_assoc, one_mul, mul_smul_comm, mul_one]
+
+theorem isHadamard_iff_mul_conjTranspose
+ (hcard : IsRegular (Fintype.card n : R)) :
+ A.IsHadamard ↔
+ (∀ i j, A i j ∈ unitary R) ∧
+ A * Aᴴ = (Fintype.card n : R) • (1 : Matrix n n R) :=
+ ⟨fun hA => ⟨hA.apply_mem, hA.mul_conjTranspose⟩,
+ fun hA => IsHadamard.of_mul_conjTranspose hA.1 hA.2 hcard⟩
+
+end CommRing
+
+/-- An integer Hadamard matrix of order greater than two has order divisible by four.
+
+This is the standard divisibility obstruction in [Section 2.3][deLauneyFlannery2011]. -/
+theorem IsHadamard.four_dvd_card {A : Matrix n n ℤ}
+ (hA : A.IsHadamard) (hcard : 2 < Fintype.card n) : 4 ∣ Fintype.card n := by
+ have hpm : ∀ i j, A i j = 1 ∨ A i j = -1 := fun i j =>
+ Unitary.mem_iff_eq_one_or_eq_neg_one.mp (hA.apply_mem i j)
+ obtain ⟨r, s, t, hrs, hrt, hst⟩ := Fintype.two_lt_card_iff.mp hcard
+ have horth ⦃i k : n⦄ (hik : i ≠ k) : ∑ j, A i j * A k j = 0 := by
+ simpa [Matrix.mul_apply, hik] using congr_fun (congr_fun hA.mul_conjTranspose i) k
+ have hexpand : ∀ j, (1 + A s j * A r j) * (1 + A t j * A r j) =
+ 1 + A s j * A r j + A t j * A r j + A s j * A t j := fun j => by
+ obtain hr | hr := hpm r j <;> simp [hr] <;> ring
+ have hdvd : ∀ j, (4 : ℤ) ∣ (1 + A s j * A r j) * (1 + A t j * A r j) := fun j => by
+ obtain hs | hs := hpm s j <;> obtain hr | hr := hpm r j <;>
+ obtain ht | ht := hpm t j <;> simp [hs, hr, ht]
+ have hsum : ∑ j, (1 + A s j * A r j) * (1 + A t j * A r j) = (Fintype.card n : ℤ) := by
+ simp_rw [hexpand]
+ simp [Finset.sum_add_distrib, horth hrs.symm, horth hrt.symm, horth hst]
+ rw [← Int.ofNat_dvd, ← hsum]
+ exact Finset.dvd_sum fun j _ => hdvd j
+
+end Matrix
diff --git a/docs/references.bib b/docs/references.bib
index 96ac2dcb039f3c..064ad6e45eed78 100644
--- a/docs/references.bib
+++ b/docs/references.bib
@@ -1580,6 +1580,17 @@ @Article{ day1972
url = {https://doi.org/10.1016/0022-4049(72)90021-7}
}
+@Book{ deLauneyFlannery2011,
+ author = {de Launey, Warwick and Flannery, Dane L.},
+ title = {Algebraic Design Theory},
+ series = {Mathematical Surveys and Monographs},
+ volume = {175},
+ publisher = {American Mathematical Society},
+ year = {2011},
+ doi = {10.1090/surv/175},
+ url = {https://doi.org/10.1090/surv/175}
+}
+
@InProceedings{ deligne_formulaire,
author = {Deligne, P.},
title = {Courbes elliptiques: formulaire d'apr\`es {J}. {T}ate},
From db127794c79fdeb86f6b0cf6ff2c804026fbaff1 Mon Sep 17 00:00:00 2001
From: teorth <199308+teorth@users.noreply.github.com>
Date: Tue, 16 Jun 2026 18:37:42 +0000
Subject: [PATCH 0078/1300] feat(NumberTheory): costa-pereira inequalities
(#40569)
Co-authored-by: Terence Tao
Co-authored-by: Oliver Nash
---
Mathlib/NumberTheory/Chebyshev.lean | 182 +++++++++++++++++++++++++---
1 file changed, 167 insertions(+), 15 deletions(-)
diff --git a/Mathlib/NumberTheory/Chebyshev.lean b/Mathlib/NumberTheory/Chebyshev.lean
index 7bba721b9fdd3e..85cab1e97e9ab0 100644
--- a/Mathlib/NumberTheory/Chebyshev.lean
+++ b/Mathlib/NumberTheory/Chebyshev.lean
@@ -36,8 +36,12 @@ These give logarithmically weighted sums of primes and prime powers.
- `Chebyshev.theta_eq_log_primorial` shows that `θ x` is the log of the product of primes up to x
- `Chebyshev.theta_le_log4_mul_x` gives Chebyshev's upper bound on `θ`
- `Chebyshev.theta_ge` gives Chebyshev's lower bound on `θ`.
-- `Chebyshev.psi_eq_log_lcmUpto` shows that `ψ n` is the log of the lcm of `{1,...,n}`
-- `Chebyshev.psi_eq_sum_theta` and `Chebyshev.psi_eq_theta_add_sum_theta` relate `psi` to `theta`.
+- `Chebyshev.psi_eq_log_lcmUpto` shows that `ψ n` is the log of the lcm of `{1,...,n}`.
+- `Chebyshev.psi_eq_sum_theta` and `Chebyshev.psi_eq_theta_add_sum_theta` relate `ψ` to `θ`.
+- `Chebyshev.psi_sub_theta_le_mul_sqrt` gives an upper bound on `ψ - θ`.
+- `Chebyshev.psi_sub_theta_le_psi_add_psi_add_psi` and
+ `Chebyshev.psi_sub_theta_ge_psi_add_psi_add_psi` establish the Costa-Pereira inequalities
+ for `ψ - θ`.
- `Chebyshev.psi_le_const_mul_self` gives Chebyshev's upper bound on `ψ`.
- `Chebyshev.psi_ge` gives Chebyshev's lower bound on `ψ`.
- `Chebyshev.primeCounting_eq_theta_div_log_add_integral` relates the prime counting function to `θ`
@@ -113,6 +117,18 @@ theorem psi_eq_zero_of_lt_two {x : ℝ} (hx : x < 2) : ψ x = 0 := by
norm_cast at this
linarith
+@[simp]
+theorem psi_eq_zero_iff {x : ℝ} : ψ x = 0 ↔ x < 2 := by
+ refine ⟨fun h₀ ↦ ?_, psi_eq_zero_of_lt_two⟩
+ by_contra! contra
+ replace contra : 2 ∈ Ioc 0 ⌊x⌋₊ := by rw [mem_Ioc, le_floor_iff (by grind)]; grind
+ have : Λ 2 ≤ ψ x := single_le_sum (fun n _ ↦ vonMangoldt_nonneg (n := n)) contra
+ have := vonMangoldt_pos_iff.mpr prime_two.isPrimePow
+ linarith
+
+theorem psi_eq_zero_of_le_one {x : ℝ} (hx : x ≤ 1) : ψ x = 0 :=
+ psi_eq_zero_of_lt_two (by linarith)
+
@[simp]
theorem psi_zero : ψ 0 = 0 := psi_eq_zero_of_lt_two zero_lt_two
@@ -127,6 +143,19 @@ theorem theta_eq_zero_of_lt_two {x : ℝ} (hx : x < 2) : θ x = 0 := by
norm_cast at ⊢ this
linarith
+@[simp]
+theorem theta_eq_zero_iff {x : ℝ} : θ x = 0 ↔ x < 2 := by
+ refine ⟨fun h₀ ↦ ?_, theta_eq_zero_of_lt_two⟩
+ by_contra! contra
+ replace contra : 2 ∈ Ioc 0 ⌊x⌋₊ := by rw [mem_Ioc, le_floor_iff (by grind)]; grind
+ have h₁ : log (↑(2 : ℕ) : ℝ) ≤ θ x :=
+ single_le_sum (fun p hp ↦ log_nonneg (by aesop)) (by aesop (add simp prime_two))
+ have := Real.log_pos one_lt_two
+ grind
+
+theorem theta_eq_zero_of_le_one {x : ℝ} (hx : x ≤ 1) : θ x = 0 :=
+ theta_eq_zero_of_lt_two (by linarith)
+
@[simp]
theorem theta_zero : θ 0 = 0 := theta_eq_zero_of_lt_two zero_lt_two
@@ -305,10 +334,11 @@ are close.
-/
/-- A sum over prime powers may be written as a double sum over exponents and then primes. -/
-theorem sum_PrimePow_eq_sum_sum {R : Type*} [AddCommMonoid R] (f : ℕ → R) {x : ℝ} (hx : 0 ≤ x) :
+theorem sum_PrimePow_eq_sum_sum' {R : Type*} [AddCommMonoid R] (f : ℕ → R) {x : ℝ} (hx : 0 ≤ x)
+ {N : ℕ} (hN : ⌊log x / log 2⌋₊ ≤ N) :
∑ n ∈ Ioc 0 ⌊x⌋₊ with IsPrimePow n, f n
- = ∑ k ∈ Icc 1 ⌊log x / log 2⌋₊, ∑ p ∈ Ioc 0 ⌊x ^ ((1 : ℝ) / k)⌋₊ with p.Prime, f (p ^ k) := by
- trans ∑ ⟨k, p⟩ ∈ Icc 1 ⌊log x / log 2⌋₊ ×ˢ (Ioc 0 ⌊x⌋₊).filter Nat.Prime
+ = ∑ k ∈ Icc 1 N, ∑ p ∈ Ioc 0 ⌊x ^ ((1 : ℝ) / k)⌋₊ with p.Prime, f (p ^ k) := by
+ trans ∑ ⟨k, p⟩ ∈ Icc 1 N ×ˢ (Ioc 0 ⌊x⌋₊).filter Nat.Prime
with p ≤ ⌊x ^ (k : ℝ)⁻¹⌋₊, f (p ^ k)
· refine (sum_bij (i := fun ⟨k, p⟩ _ ↦ p ^ k) ?_ ?_ ?_ ?_).symm
· simp +contextual [hx, rpow_nonneg, le_floor_iff, ← pos_iff_ne_zero, Prime.isPrimePow,
@@ -321,7 +351,7 @@ theorem sum_PrimePow_eq_sum_sum {R : Type*} [AddCommMonoid R] (f : ℕ → R) {x
mem_product, mem_Icc, succ_le_iff, exists_prop, Prod.exists, exists_and_left, and_imp]
rintro b _ hbx ⟨p, k, hp, hk₀, rfl⟩
rw [cast_pow] at hbx
- refine ⟨k, hk₀, le_floor ?_, p, hp.nat_prime.pos, ?_, hp.nat_prime, ?_, rfl⟩
+ refine ⟨k, hk₀, (le_floor ?_).trans hN, p, hp.nat_prime.pos, ?_, hp.nat_prime, ?_, rfl⟩
· rw [le_div_iff₀ (log_pos (by norm_num)), ← Real.log_pow]
gcongr
apply (LE.le.trans ?_ hbx)
@@ -343,21 +373,35 @@ theorem sum_PrimePow_eq_sum_sum {R : Type*} [AddCommMonoid R] (f : ℕ → R) {x
contrapose! this
apply rpow_lt_one hx this (by bound)
-theorem psi_eq_sum_theta {x : ℝ} (hx : 0 ≤ x) :
- ψ x = ∑ n ∈ Icc 1 ⌊log x / log 2⌋₊, θ (x ^ ((1 : ℝ) / n)) := by
- simp_rw [psi, vonMangoldt_apply, ← sum_filter, sum_PrimePow_eq_sum_sum _ hx]
+theorem sum_PrimePow_eq_sum_sum {R : Type*} [AddCommMonoid R] (f : ℕ → R) {x : ℝ} (hx : 0 ≤ x) :
+ ∑ n ∈ Ioc 0 ⌊x⌋₊ with IsPrimePow n, f n
+ = ∑ k ∈ Icc 1 ⌊log x / log 2⌋₊, ∑ p ∈ Ioc 0 ⌊x ^ ((1 : ℝ) / k)⌋₊ with p.Prime, f (p ^ k) :=
+ sum_PrimePow_eq_sum_sum' f hx (le_refl _)
+
+theorem psi_eq_sum_theta' {x : ℝ} (hx : 0 ≤ x) {N : ℕ} (hN : ⌊log x / log 2⌋₊ ≤ N) :
+ ψ x = ∑ n ∈ Icc 1 N, θ (x ^ ((1 : ℝ) / n)) := by
+ simp_rw [psi, vonMangoldt_apply, ← sum_filter, sum_PrimePow_eq_sum_sum' _ hx hN]
apply sum_congr rfl fun _ hk ↦ sum_congr rfl fun _ _ ↦ ?_
rw [Prime.pow_minFac _ (by linarith [mem_Icc.mp hk])]
simp_all
-theorem psi_eq_theta_add_sum_theta {x : ℝ} (hx : 2 ≤ x) :
- ψ x = θ x + ∑ n ∈ Icc 2 ⌊log x / log 2⌋₊, θ (x ^ ((1 : ℝ) / n)) := by
- rw [psi_eq_sum_theta (by linarith), ← add_sum_Ioc_eq_sum_Icc]
+theorem psi_eq_sum_theta {x : ℝ} (hx : 0 ≤ x) :
+ ψ x = ∑ n ∈ Icc 1 ⌊log x / log 2⌋₊, θ (x ^ ((1 : ℝ) / n)) :=
+ psi_eq_sum_theta' hx (le_refl _)
+
+theorem psi_eq_theta_add_sum_theta' {x : ℝ} (hx : 2 ≤ x) {N : ℕ} (hN : ⌊log x / log 2⌋₊ ≤ N) :
+ ψ x = θ x + ∑ n ∈ Icc 2 N, θ (x ^ ((1 : ℝ) / n)) := by
+ rw [psi_eq_sum_theta' (by linarith) hN, ← add_sum_Ioc_eq_sum_Icc]
· congr
simp
- · rw [le_floor_iff' one_ne_zero, le_div_iff₀ (by positivity), cast_one, one_mul]
+ · apply le_trans _ hN
+ rw [le_floor_iff' one_ne_zero, le_div_iff₀ (by positivity), cast_one, one_mul]
gcongr
+theorem psi_eq_theta_add_sum_theta {x : ℝ} (hx : 2 ≤ x) :
+ ψ x = θ x + ∑ n ∈ Icc 2 ⌊log x / log 2⌋₊, θ (x ^ ((1 : ℝ) / n)) :=
+ psi_eq_theta_add_sum_theta' hx (le_refl _)
+
theorem theta_le_psi (x : ℝ) : θ x ≤ ψ x := by
by_cases! h : x < 2
· rw [theta_eq_zero_of_lt_two h, psi_eq_zero_of_lt_two h]
@@ -365,8 +409,8 @@ theorem theta_le_psi (x : ℝ) : θ x ≤ ψ x := by
simp only [le_add_iff_nonneg_right]
exact sum_nonneg fun _ _ ↦ theta_nonneg _
---Note that a more careful argument could remove the log x in the following with a worse constant.
-/-- `|ψ x - θ x| ≤ c √ x log x` with an explicit constant c. -/
+/-- `|ψ x - θ x| ≤ c √ x log x` with an explicit constant c. To remove the log, see
+`psi_sub_theta_le_mul_sqrt`. -/
theorem abs_psi_sub_theta_le_sqrt_mul_log {x : ℝ} (hx : 1 ≤ x) :
|ψ x - θ x| ≤ 2 * x.sqrt * x.log := by
by_cases! hx : x < 2
@@ -462,6 +506,114 @@ theorem theta_ge' {x : ℝ} (hx : 1 ≤ x) :
grw [psi_ge' (by linarith)]
linarith [psi_sub_theta_le hx]
+section CostaPereira
+
+/-! ## The Costa-Pereira inequalities
+
+The Costa-Pereira inequalities give explicit upper and lower bounds on the difference
+`ψ x - θ x`, namely that they lie between `ψ x^(1/2) + ψ x^(1/3) + ψ x^(1/7)` and
+`ψ x^(1/2) + ψ x^(1/3) + ψ x^(1/5)`. These are useful for applications in explicit
+analytic number theory. -/
+
+variable (x : ℝ) (n : ℕ)
+
+private noncomputable def b := θ (x ^ (n : ℝ)⁻¹)
+
+private noncomputable def c := b x (6 * n - 1) - b x (6 * n) + b x (6 * n + 1)
+
+private theorem b_antitone (hx : 0 ≤ x) : AntitoneOn (b x) (.Ici 1) := by
+ intro n hn m hm hnm; unfold b
+ simp only [Set.mem_Ici] at hn hm
+ rcases le_or_gt x 1 with h | h
+ · repeat rw [theta_eq_zero_of_le_one (rpow_le_one hx h (by positivity))]
+ apply theta_mono (monotone_rpow_of_base_ge_one h.le _)
+ field_simp
+ norm_num [hnm]
+
+private theorem psi_pow_eq_sum_b (hx : 0 ≤ x) : ∃ M, ∀ N ≥ M,
+ ψ (x ^ (n : ℝ)⁻¹) = ∑ k ∈ Icc 1 N, b x (n * k) := by
+ have : 0 ≤ x ^ ((n : ℝ)⁻¹) := by positivity
+ use ⌊log (x ^ (n : ℝ)⁻¹) / log 2⌋₊
+ intro N hN
+ simp_rw [psi_eq_sum_theta' this hN, one_div, b, cast_mul, mul_inv_rev, mul_comm,
+ ← rpow_mul (by positivity)]
+
+private theorem sum_b_eq_b_add_sum_add_sum_add_sum (N : ℕ) :
+ ∑ n ∈ Icc 1 (1 + 6 * N), b x n =
+ b x 1 +
+ ∑ n ∈ Icc 1 (3 * N), b x (2 * n) +
+ ∑ n ∈ Icc 1 (2 * N), b x (3 * n) +
+ ∑ n ∈ Icc 1 N, c x n := by
+ induction N with
+ | zero => simp
+ | succ N ih =>
+ rw [show 1 + 6 * (N + 1) = (1 + 6 * N) + 1 + 1 + 1 + 1 + 1 + 1 by ring,
+ show 3 * (N + 1) = 3 * N + 1 + 1 + 1 by ring,
+ show 2 * (N + 1) = 2 * N + 1 + 1 by ring]
+ simp only [le_add_iff_nonneg_left, _root_.zero_le, sum_Icc_succ_top, ih, c]
+ rw [show 6 * (N + 1) - 1 = 6 * N + 5 by lia]
+ ring_nf
+
+private theorem psi_sub_theta_bounds {x : ℝ} (hx : 0 ≤ x) :
+ ψ x - θ x ≤ ψ (x ^ (2 : ℝ)⁻¹) + ψ (x ^ (3 : ℝ)⁻¹) + ψ (x ^ (5 : ℝ)⁻¹) ∧
+ ψ (x ^ (2 : ℝ)⁻¹) + ψ (x ^ (3 : ℝ)⁻¹) + ψ (x ^ (7 : ℝ)⁻¹) ≤ ψ x - θ x := by
+ obtain ⟨N₁, h1⟩ := psi_pow_eq_sum_b x 1 hx
+ obtain ⟨N₂, h2⟩ := psi_pow_eq_sum_b x 2 hx
+ obtain ⟨N₃, h3⟩ := psi_pow_eq_sum_b x 3 hx
+ obtain ⟨N₅, h5⟩ := psi_pow_eq_sum_b x 5 hx
+ obtain ⟨N₇, h7⟩ := psi_pow_eq_sum_b x 7 hx
+ let N := N₁ + N₂ + N₃ + N₅ + N₇
+ specialize h1 (1 + 6 * N) (by lia)
+ specialize h2 (3 * N) (by lia)
+ specialize h3 (2 * N) (by lia)
+ specialize h5 N (by lia)
+ specialize h7 N (by lia)
+ have : ∑ n ∈ Icc 1 N, c x n ≤ ∑ n ∈ Icc 1 N, b x (5 * n) := by
+ apply sum_le_sum
+ intro n hn
+ unfold c
+ linarith [(b_antitone x hx (by grind) (by grind) (by lia) : b x (6 * n + 1) ≤ b x (6 * n)),
+ (b_antitone x hx (by grind) (by grind) (by lia) : b x (6 * n - 1) ≤ b x (5 * n))]
+ have : ∑ n ∈ Icc 1 N, b x (7 * n) ≤ ∑ n ∈ Icc 1 N, c x n := by
+ apply sum_le_sum; intro n hn; simp only [mem_Icc, c] at hn ⊢
+ linarith [(b_antitone x hx (by grind) (by grind) (by lia) : b x (6 * n) ≤ b x (6 * n - 1)),
+ (b_antitone x hx (by grind) (by grind) (by lia) : b x (7 * n) ≤ b x (6 * n + 1))]
+ have : b x 1 = θ x := by simp [b]
+ simp only [cast_one, one_mul, sum_b_eq_b_add_sum_add_sum_add_sum, inv_one, rpow_one] at h1
+ grind
+
+theorem psi_sub_theta_le_psi_add_psi_add_psi (x : ℝ) :
+ ψ x - θ x ≤ ψ (x ^ (2 : ℝ)⁻¹) + ψ (x ^ (3 : ℝ)⁻¹) + ψ (x ^ (5 : ℝ)⁻¹) := by
+ rcases le_total x 0 with hx | hx
+ · grind [theta_eq_zero_iff, psi_eq_zero_iff, psi_nonneg]
+ · exact (psi_sub_theta_bounds hx).1
+
+theorem psi_sub_theta_ge_psi_add_psi_add_psi {x : ℝ} (hx : 0 ≤ x) :
+ ψ (x ^ (2 : ℝ)⁻¹) + ψ (x ^ (3 : ℝ)⁻¹) + ψ (x ^ (7 : ℝ)⁻¹) ≤ ψ x - θ x :=
+ (psi_sub_theta_bounds hx).2
+
+/-- `ψ x = θ x + O( √x )`. -/
+theorem psi_sub_theta_le_mul_sqrt : ∃ C, ∀ x, ψ x - θ x ≤ C * x.sqrt := by
+ use (log 4 + 4) * 3
+ intro x
+ rcases le_total x 1 with h | h
+ · rw [theta_eq_zero_of_le_one h, psi_eq_zero_of_le_one h, sub_self]; positivity
+ have (n : ℕ) (hn : 2 ≤ n) : ψ (x ^ (1 / (n : ℝ))) ≤ (log 4 + 4) * x.sqrt := by
+ grw [psi_le_const_mul_self (by positivity), sqrt_eq_rpow x]; gcongr; norm_cast
+ linarith [psi_sub_theta_le_psi_add_psi_add_psi x, this 2 (le_refl _), this 3 (by norm_num),
+ this 5 (by norm_num)]
+
+open Asymptotics Filter in
+theorem isBigO_psi_sub_theta_sqrt : IsBigO atTop (ψ - θ) sqrt := by
+ simp_rw [isBigO_iff, Pi.sub_apply, norm_eq_abs, eventually_atTop]
+ obtain ⟨C, hC⟩ := psi_sub_theta_le_mul_sqrt
+ refine ⟨C, 0, fun x _ ↦ ?_⟩
+ have := theta_le_psi x
+ rw [abs_of_nonneg (by positivity), abs_of_nonneg (by positivity)]
+ exact hC x
+
+end CostaPereira
+
section PrimeCounting
/-! ## Relation to prime counting
From 56d32c2bd1a2ad4911817bafc345c72e9159ad8a Mon Sep 17 00:00:00 2001
From: Felix Pernegger <188575194+felixpernegger@users.noreply.github.com>
Date: Tue, 16 Jun 2026 19:20:43 +0000
Subject: [PATCH 0079/1300] chore: golf proofs which use autogenerated lemmas
(#40657)
This removes all exceptions of the linter except in CategoryTheory/Functor/Category.lean (which I do not understand)
Co-authored-by: Batixx
---
.../Calculus/FormalMultilinearSeries.lean | 9 ++-------
Mathlib/Data/List/Sigma.lean | 19 +++++++++----------
2 files changed, 11 insertions(+), 17 deletions(-)
diff --git a/Mathlib/Analysis/Calculus/FormalMultilinearSeries.lean b/Mathlib/Analysis/Calculus/FormalMultilinearSeries.lean
index 487a58b2cdcfc9..c868edf4a29c29 100644
--- a/Mathlib/Analysis/Calculus/FormalMultilinearSeries.lean
+++ b/Mathlib/Analysis/Calculus/FormalMultilinearSeries.lean
@@ -388,17 +388,12 @@ theorem constFormalMultilinearSeries_apply_of_nonzero [NontriviallyNormedField
{n : ℕ} (hn : n ≠ 0) : constFormalMultilinearSeries 𝕜 E c n = 0 :=
Nat.casesOn n (fun hn => (hn rfl).elim) (fun _ _ => rfl) hn
-set_option linter.auxLemma false in
@[simp]
lemma constFormalMultilinearSeries_zero [NontriviallyNormedField 𝕜] [NormedAddCommGroup E]
[NormedAddCommGroup F] [NormedSpace 𝕜 E] [NormedSpace 𝕜 F] :
constFormalMultilinearSeries 𝕜 E (0 : F) = 0 := by
- ext n x
- simp only [FormalMultilinearSeries.zero_apply, ContinuousMultilinearMap.zero_apply,
- constFormalMultilinearSeries]
- induction n
- · simp only [ContinuousMultilinearMap.uncurry0_apply]
- · simp only [constFormalMultilinearSeries.match_1.eq_2, ContinuousMultilinearMap.zero_apply]
+ ext n
+ induction n <;> simp
@[simp]
lemma compContinuousLinearMap_zero [NontriviallyNormedField 𝕜]
diff --git a/Mathlib/Data/List/Sigma.lean b/Mathlib/Data/List/Sigma.lean
index f66a6b2837f6ce..b7dc804c2cc447 100644
--- a/Mathlib/Data/List/Sigma.lean
+++ b/Mathlib/Data/List/Sigma.lean
@@ -547,10 +547,8 @@ theorem kerase_comm (a₁ a₂) (l : List (Sigma β)) :
else by simp [ha₂, mt mem_keys_of_mem_keys_kerase ha₂]
else by simp [ha₁, mt mem_keys_of_mem_keys_kerase ha₁]
-set_option linter.auxLemma false in
theorem sizeOf_kerase [SizeOf (Sigma β)] (x : α)
(xs : List (Sigma β)) : SizeOf.sizeOf (List.kerase x xs) ≤ SizeOf.sizeOf xs := by
- simp only [SizeOf.sizeOf, _sizeOf_1]
induction xs with
| nil => simp
| cons y ys => by_cases x = y.1 <;> simp [*]
@@ -618,7 +616,6 @@ theorem dedupKeys_cons {x : Sigma β} (l : List (Sigma β)) :
dedupKeys (x :: l) = kinsert x.1 x.2 (dedupKeys l) :=
rfl
-
theorem nodupKeys_dedupKeys (l : List (Sigma β)) : NodupKeys (dedupKeys l) := by
dsimp [dedupKeys]
generalize hl : nil = l'
@@ -647,17 +644,19 @@ theorem dlookup_dedupKeys (a : α) (l : List (Sigma β)) : dlookup a (dedupKeys
· rw [dedupKeys_cons, dlookup_kinsert_ne h, l_ih, dlookup_cons_ne]
exact h
-set_option linter.auxLemma false in
+theorem sizeOf_cons_le_sizeOf_cons {α : Type*} [SizeOf α] {l r : List α} (a : α)
+ (h : SizeOf.sizeOf l ≤ SizeOf.sizeOf r) :
+ SizeOf.sizeOf (a :: l) ≤ SizeOf.sizeOf (a :: r) := by
+ rw [cons.sizeOf_spec, cons.sizeOf_spec]
+ exact Nat.add_le_add_iff_left.mpr h
+
theorem sizeOf_dedupKeys [SizeOf (Sigma β)]
(xs : List (Sigma β)) : SizeOf.sizeOf (dedupKeys xs) ≤ SizeOf.sizeOf xs := by
- simp only [SizeOf.sizeOf, _sizeOf_1]
induction xs with
| nil => simp [dedupKeys]
- | cons x xs =>
- simp only [dedupKeys_cons, kinsert_def, Nat.add_le_add_iff_left, Sigma.eta]
- trans
- · apply sizeOf_kerase
- · assumption
+ | cons x xs h =>
+ simp only [dedupKeys_cons, kinsert_def, Sigma.eta]
+ exact sizeOf_cons_le_sizeOf_cons x (le_trans (sizeOf_kerase x.fst xs.dedupKeys) h)
/-! ### `kunion` -/
From da24afce9320416675a1169dd0c73919ceca018e Mon Sep 17 00:00:00 2001
From: Jovan Gerbscheid <56355248+JovanGerb@users.noreply.github.com>
Date: Tue, 16 Jun 2026 21:10:10 +0000
Subject: [PATCH 0080/1300] chore: avoid `meta`-importing math files (#40671)
It should never be neccessary to meta import a maths file, so this PR removes some such imports. In some cases this was caused by a `public meta section` at the top of a tactic file, even though there are some definitions in the file that should not be meta (because they are used as part of the generated proofs).
---
Mathlib/Data/Rat/Floor.lean | 1 -
.../RingTheory/Coalgebra/TensorProduct.lean | 3 +--
Mathlib/Tactic/CancelDenoms/Core.lean | 1 -
.../CategoryTheory/BicategoryCoherence.lean | 11 +++++------
Mathlib/Tactic/CategoryTheory/Coherence.lean | 19 ++++++++-----------
Mathlib/Tactic/CongrExclamation.lean | 2 +-
Mathlib/Tactic/IntervalCases.lean | 1 -
Mathlib/Tactic/ModCases.lean | 11 +++++------
Mathlib/Tactic/NormNum/LegendreSymbol.lean | 7 ++++---
Mathlib/Tactic/Positivity/Basic.lean | 1 -
Mathlib/Tactic/Ring/Basic.lean | 4 +---
Mathlib/Tactic/Ring/Common.lean | 3 ---
Mathlib/Tactic/Sat/FromLRAT.lean | 1 -
Mathlib/Tactic/Simproc/Divisors.lean | 2 +-
Mathlib/Tactic/Simproc/Factors.lean | 1 -
15 files changed, 26 insertions(+), 42 deletions(-)
diff --git a/Mathlib/Data/Rat/Floor.lean b/Mathlib/Data/Rat/Floor.lean
index 8fd070adc7bc46..613f5decc4b6b5 100644
--- a/Mathlib/Data/Rat/Floor.lean
+++ b/Mathlib/Data/Rat/Floor.lean
@@ -9,7 +9,6 @@ public import Mathlib.Algebra.Order.Round
public import Mathlib.Data.Rat.Cast.Order
public import Mathlib.Tactic.FieldSimp
public import Mathlib.Tactic.Ring
-meta import Mathlib.Algebra.Order.Floor.Defs
public meta import Mathlib.Algebra.Order.Round
/-!
diff --git a/Mathlib/RingTheory/Coalgebra/TensorProduct.lean b/Mathlib/RingTheory/Coalgebra/TensorProduct.lean
index 3048b5ec4dd08a..0b22dc3521a3b9 100644
--- a/Mathlib/RingTheory/Coalgebra/TensorProduct.lean
+++ b/Mathlib/RingTheory/Coalgebra/TensorProduct.lean
@@ -8,8 +8,7 @@ module
public import Mathlib.LinearAlgebra.TensorProduct.Tower
public import Mathlib.RingTheory.Coalgebra.Equiv
-meta import Mathlib.RingTheory.Coalgebra.CoassocSimps
-
+import Mathlib.RingTheory.Coalgebra.CoassocSimps
import Mathlib.Algebra.Algebra.Bilinear
/-!
diff --git a/Mathlib/Tactic/CancelDenoms/Core.lean b/Mathlib/Tactic/CancelDenoms/Core.lean
index af4068c577306c..2751270e0cc562 100644
--- a/Mathlib/Tactic/CancelDenoms/Core.lean
+++ b/Mathlib/Tactic/CancelDenoms/Core.lean
@@ -6,7 +6,6 @@ Authors: Robert Y. Lewis
module
public meta import Mathlib.Data.Tree.Basic
-public meta import Mathlib.Logic.Basic
public import Mathlib.Algebra.Field.Basic
public meta import Mathlib.Algebra.Group.Nat.Defs
public import Mathlib.Algebra.Order.Ring.Defs
diff --git a/Mathlib/Tactic/CategoryTheory/BicategoryCoherence.lean b/Mathlib/Tactic/CategoryTheory/BicategoryCoherence.lean
index 3e110d1fdc6191..47d34a4493ae79 100644
--- a/Mathlib/Tactic/CategoryTheory/BicategoryCoherence.lean
+++ b/Mathlib/Tactic/CategoryTheory/BicategoryCoherence.lean
@@ -5,7 +5,6 @@ Authors: Yuma Mizuno
-/
module
-public meta import Mathlib.CategoryTheory.Bicategory.Free
public import Mathlib.CategoryTheory.Bicategory.Free
public import Mathlib.Tactic.CategoryTheory.BicategoricalComp
@@ -22,7 +21,7 @@ tactic is given in `Mathlib/Tactic/CategoryTheory/Coherence.lean` at the same ti
tactic for monoidal categories.
-/
-public meta section
+public section
noncomputable section
@@ -93,11 +92,11 @@ instance liftHom₂WhiskerRight {f g : a ⟶ b} (η : f ⟶ g) [LiftHom f] [Lift
open Lean Elab Tactic Meta
/-- Helper function for throwing exceptions. -/
-def exception {α : Type} (g : MVarId) (msg : MessageData) : MetaM α :=
+meta def exception {α : Type} (g : MVarId) (msg : MessageData) : MetaM α :=
throwTacticEx `bicategorical_coherence g msg
/-- Helper function for throwing exceptions with respect to the main goal. -/
-def exception' (msg : MessageData) : TacticM Unit := do
+meta def exception' (msg : MessageData) : TacticM Unit := do
try
liftMetaTactic (exception (msg := msg))
catch _ =>
@@ -108,13 +107,13 @@ set_option quotPrecheck false in
/-- Auxiliary definition for `bicategorical_coherence`. -/
-- We could construct this expression directly without using `elabTerm`,
-- but it would require preparing many implicit arguments by hand.
-def mkLiftMap₂LiftExpr (e : Expr) : TermElabM Expr := do
+meta def mkLiftMap₂LiftExpr (e : Expr) : TermElabM Expr := do
Term.elabTerm
(← ``((FreeBicategory.lift (Prefunctor.id _)).map₂ (LiftHom₂.lift $(← Term.exprToSyntax e))))
none
/-- Coherence tactic for bicategories. -/
-def bicategoryCoherence (g : MVarId) : TermElabM Unit := g.withContext do
+meta def bicategoryCoherence (g : MVarId) : TermElabM Unit := g.withContext do
withOptions (fun opts => synthInstance.maxSize.set opts
(max 256 (synthInstance.maxSize.get opts))) do
let thms := [``BicategoricalCoherence.iso, ``Iso.trans, ``Iso.symm, ``Iso.refl,
diff --git a/Mathlib/Tactic/CategoryTheory/Coherence.lean b/Mathlib/Tactic/CategoryTheory/Coherence.lean
index 1154d770d6d945..549bbcd6437fb5 100644
--- a/Mathlib/Tactic/CategoryTheory/Coherence.lean
+++ b/Mathlib/Tactic/CategoryTheory/Coherence.lean
@@ -5,10 +5,7 @@ Authors: Kim Morrison, Yuma Mizuno, Oleksandr Manzyuk
-/
module
-public meta import Mathlib.Lean.Meta
public import Mathlib.CategoryTheory.Monoidal.Free.Basic
-public meta import Mathlib.CategoryTheory.Monoidal.Free.Basic
-public import Mathlib.Lean.Meta
public import Mathlib.Tactic.CategoryTheory.BicategoryCoherence
public import Mathlib.Tactic.CategoryTheory.MonoidalComp
@@ -27,7 +24,7 @@ are equal.
-/
-public meta section
+public section
universe v u
@@ -112,11 +109,11 @@ end lifting
open Lean Meta Elab Tactic
/-- Helper function for throwing exceptions. -/
-def exception {α : Type} (g : MVarId) (msg : MessageData) : MetaM α :=
+meta def exception {α : Type} (g : MVarId) (msg : MessageData) : MetaM α :=
throwTacticEx `monoidal_coherence g msg
/-- Helper function for throwing exceptions with respect to the main goal. -/
-def exception' (msg : MessageData) : TacticM Unit := do
+meta def exception' (msg : MessageData) : TacticM Unit := do
try
liftMetaTactic (exception (msg := msg))
catch _ =>
@@ -126,13 +123,13 @@ def exception' (msg : MessageData) : TacticM Unit := do
/-- Auxiliary definition for `monoidal_coherence`. -/
-- We could construct this expression directly without using `elabTerm`,
-- but it would require preparing many implicit arguments by hand.
-def mkProjectMapExpr (e : Expr) : TermElabM Expr := do
+meta def mkProjectMapExpr (e : Expr) : TermElabM Expr := do
Term.elabTerm
(← ``(FreeMonoidalCategory.projectMap _root_.id _ _ (LiftHom.lift $(← Term.exprToSyntax e))))
none
/-- Coherence tactic for monoidal categories. -/
-def monoidalCoherence (g : MVarId) : TermElabM Unit := g.withContext do
+meta def monoidalCoherence (g : MVarId) : TermElabM Unit := g.withContext do
withOptions (fun opts => synthInstance.maxSize.set opts
(max 512 (synthInstance.maxSize.get opts))) do
let thms := [``MonoidalCoherence.iso, ``Iso.trans, ``Iso.symm, ``Iso.refl,
@@ -157,7 +154,7 @@ open Mathlib.Tactic.BicategoryCoherence
/--
If set to `false`, the warning on the use of the deprecated coherence tactic is disabled.
-/
-register_option warn.refl_coherence : Bool := {
+meta register_option warn.refl_coherence : Bool := {
defValue := true
descr := "warn when the deprecated coherence tactic is used"
}
@@ -233,7 +230,7 @@ lemma insert_id_rhs {C : Type*} [Category* C] {X Y : C} (f g : X ⟶ Y) (w : f =
simpa using w
/-- If either the lhs or rhs is not a composition, compose it on the right with an identity. -/
-def insertTrailingIds (g : MVarId) : MetaM MVarId := do
+meta def insertTrailingIds (g : MVarId) : MetaM MVarId := do
let some (_, lhs, rhs) := (← withReducible g.getType').eq? | exception g "Not an equality."
let mut g := g
if !(lhs.isAppOf ``CategoryStruct.comp) then
@@ -248,7 +245,7 @@ def insertTrailingIds (g : MVarId) : MetaM MVarId := do
-- Porting note: this is an ugly port, using too many `evalTactic`s.
-- We can refactor later into either a `macro` (but the flow control is awkward)
-- or a `MetaM` tactic.
-def coherenceLoop (maxSteps := 37) : TacticM Unit :=
+meta def coherenceLoop (maxSteps := 37) : TacticM Unit :=
match maxSteps with
| 0 => exception' "`coherence` tactic reached iteration limit"
| maxSteps' + 1 => do
diff --git a/Mathlib/Tactic/CongrExclamation.lean b/Mathlib/Tactic/CongrExclamation.lean
index 07e5e0acd858cc..dbad9c4977c1c8 100644
--- a/Mathlib/Tactic/CongrExclamation.lean
+++ b/Mathlib/Tactic/CongrExclamation.lean
@@ -10,7 +10,7 @@ public meta import Lean.Elab.Tactic.RCases
public meta import Lean.Meta.Tactic.Assumption
public meta import Lean.Meta.Tactic.Rfl
public meta import Mathlib.Lean.Meta.CongrTheorems
-public meta import Mathlib.Logic.Basic
+public import Mathlib.Logic.Basic
public import Mathlib.Lean.Meta.CongrTheorems
/-!
diff --git a/Mathlib/Tactic/IntervalCases.lean b/Mathlib/Tactic/IntervalCases.lean
index 2ac7324c92f3fa..322522024cf1b9 100644
--- a/Mathlib/Tactic/IntervalCases.lean
+++ b/Mathlib/Tactic/IntervalCases.lean
@@ -5,7 +5,6 @@ Authors: Kim Morrison, Mario Carneiro
-/
module
-public meta import Mathlib.Control.Basic
public import Mathlib.Data.Finset.Attr
public import Mathlib.Tactic.NormNum
diff --git a/Mathlib/Tactic/ModCases.lean b/Mathlib/Tactic/ModCases.lean
index d57edea9e6a4d2..ce38de6ac045da 100644
--- a/Mathlib/Tactic/ModCases.lean
+++ b/Mathlib/Tactic/ModCases.lean
@@ -5,7 +5,6 @@ Authors: Mario Carneiro, Heather Macbeth
-/
module
-public meta import Mathlib.Data.Int.ModEq
public import Mathlib.Data.Int.ModEq
public import Mathlib.Tactic.HaveI
@@ -15,7 +14,7 @@ The `mod_cases` tactic does case disjunction on `e % n`, where `e : ℤ` or `e :
to yield `n` new subgoals corresponding to the possible values of `e` modulo `n`.
-/
-public meta section
+public section
namespace Mathlib.Tactic.ModCases
open Lean Meta Elab Tactic Term Qq
@@ -66,7 +65,7 @@ and the `a ≡ b (mod n) → p` case becomes a subgoal.
Proves an expression of the form `OnModCases n a b p` where `n` and `b` are raw nat literals
and `b ≤ n`. Returns the list of subgoals `?gi : a ≡ i [ZMOD n] → p`.
-/
-partial def proveOnModCases {u : Level} (n : Q(ℕ)) (a : Q(ℤ)) (b : Q(ℕ)) (p : Q(Sort u)) :
+meta partial def proveOnModCases {u : Level} (n : Q(ℕ)) (a : Q(ℤ)) (b : Q(ℕ)) (p : Q(Sort u)) :
MetaM (Q(OnModCases $n $a $b $p) × List MVarId) := do
if n.natLit! ≤ b.natLit! then
haveI' : $b =Q $n := ⟨⟩
@@ -82,7 +81,7 @@ partial def proveOnModCases {u : Level} (n : Q(ℕ)) (a : Q(ℤ)) (b : Q(ℕ)) (
/--
Int case of `mod_cases h : e % n`.
-/
-def modCases (h : TSyntax `Lean.binderIdent) (e : Q(ℤ)) (n : ℕ) : TacticM Unit := do
+meta def modCases (h : TSyntax `Lean.binderIdent) (e : Q(ℤ)) (n : ℕ) : TacticM Unit := do
let ⟨u, p, g⟩ ← inferTypeQ (.mvar (← getMainGoal))
have lit : Q(ℕ) := mkRawNatLit n
have p₁ : Nat.ble 1 $lit =Q true := ⟨⟩
@@ -142,7 +141,7 @@ and the `a ≡ b (mod n) → p` case becomes a subgoal.
Proves an expression of the form `OnModCases n a b p` where `n` and `b` are raw nat literals
and `b ≤ n`. Returns the list of subgoals `?gi : a ≡ i [MOD n] → p`.
-/
-partial def proveOnModCases {u : Level} (n : Q(ℕ)) (a : Q(ℕ)) (b : Q(ℕ)) (p : Q(Sort u)) :
+meta partial def proveOnModCases {u : Level} (n : Q(ℕ)) (a : Q(ℕ)) (b : Q(ℕ)) (p : Q(Sort u)) :
MetaM (Q(OnModCases $n $a $b $p) × List MVarId) := do
if n.natLit! ≤ b.natLit! then
have : $b =Q $n := ⟨⟩
@@ -157,7 +156,7 @@ partial def proveOnModCases {u : Level} (n : Q(ℕ)) (a : Q(ℕ)) (b : Q(ℕ)) (
/--
Nat case of `mod_cases h : e % n`.
-/
-def modCases (h : TSyntax `Lean.binderIdent) (e : Q(ℕ)) (n : ℕ) : TacticM Unit := do
+meta def modCases (h : TSyntax `Lean.binderIdent) (e : Q(ℕ)) (n : ℕ) : TacticM Unit := do
let ⟨u, p, g⟩ ← inferTypeQ (.mvar (← getMainGoal))
have lit : Q(ℕ) := mkRawNatLit n
let p₁ : Q(Nat.ble 1 $lit = true) := (q(Eq.refl true) : Expr)
diff --git a/Mathlib/Tactic/NormNum/LegendreSymbol.lean b/Mathlib/Tactic/NormNum/LegendreSymbol.lean
index 21642667152ab3..25a5be8211c825 100644
--- a/Mathlib/Tactic/NormNum/LegendreSymbol.lean
+++ b/Mathlib/Tactic/NormNum/LegendreSymbol.lean
@@ -5,7 +5,6 @@ Authors: Michael Stoll
-/
module
-public meta import Mathlib.NumberTheory.LegendreSymbol.JacobiSymbol
public import Mathlib.NumberTheory.LegendreSymbol.JacobiSymbol
/-!
@@ -47,7 +46,7 @@ where we encode the residue classes mod 2, mod 4, or mod 8 by using hypotheses l
are the ones occurring in the use of QR above.
-/
-public meta section
+public section
section Lemmas
@@ -193,6 +192,8 @@ end Mathlib.Meta.NormNum
end Lemmas
+meta section
+
section Evaluation
/-!
@@ -208,7 +209,7 @@ namespace Mathlib.Meta.NormNum
open Lean Elab Tactic Qq
-- TODO: redefined here for reduction; should this be special-handled in quote4?
-private meta def mkRawIntLit' (n : ℤ) : Q(ℤ) :=
+private def mkRawIntLit' (n : ℤ) : Q(ℤ) :=
let lit : Q(ℕ) := .lit <| .natVal n.natAbs
if 0 ≤ n then q(.ofNat $lit) else q(.negOfNat $lit)
diff --git a/Mathlib/Tactic/Positivity/Basic.lean b/Mathlib/Tactic/Positivity/Basic.lean
index f9498cc1a2c586..8edffeca629e60 100644
--- a/Mathlib/Tactic/Positivity/Basic.lean
+++ b/Mathlib/Tactic/Positivity/Basic.lean
@@ -11,7 +11,6 @@ public import Mathlib.Data.Nat.Factorial.Basic -- shake: keep (Qq dependency)
public import Mathlib.Data.Int.CharZero -- shake: keep (Qq dependency)
public import Mathlib.Data.PNat.Defs -- shake: keep (Qq dependency)
public import Mathlib.Algebra.Order.Ring.Basic -- shake: keep (Qq dependency)
-public meta import Mathlib.Algebra.Notation.Defs
public import Mathlib.Algebra.Order.Hom.Basic
public import Mathlib.Data.NNRat.Defs
public import Mathlib.Tactic.Positivity.Core
diff --git a/Mathlib/Tactic/Ring/Basic.lean b/Mathlib/Tactic/Ring/Basic.lean
index 2cb699521c625b..6875ffafe604ec 100644
--- a/Mathlib/Tactic/Ring/Basic.lean
+++ b/Mathlib/Tactic/Ring/Basic.lean
@@ -5,10 +5,8 @@ Authors: Mario Carneiro, Aurélien Saue, Anne Baanen
-/
module
-public import Mathlib.Tactic.NormNum.Inv
-public import Mathlib.Tactic.NormNum.Pow
public import Mathlib.Tactic.Ring.Common
-meta import Mathlib.Tactic.Ring.Common
+public meta import Mathlib.Algebra.Order.Ring.Unbundled.Rat -- for the `Ord Rat` instance
/-!
# `ring` tactic
diff --git a/Mathlib/Tactic/Ring/Common.lean b/Mathlib/Tactic/Ring/Common.lean
index b883ee79abcf09..bba23ff5496407 100644
--- a/Mathlib/Tactic/Ring/Common.lean
+++ b/Mathlib/Tactic/Ring/Common.lean
@@ -7,9 +7,6 @@ module
public import Mathlib.Tactic.NormNum.Inv
public import Mathlib.Tactic.NormNum.Pow
-public meta import Mathlib.Tactic.NormNum.Result
-
-meta import Mathlib.Algebra.Order.Ring.Unbundled.Rat
/-!
# `ring`-like tactics
diff --git a/Mathlib/Tactic/Sat/FromLRAT.lean b/Mathlib/Tactic/Sat/FromLRAT.lean
index 3dfdd07a2f8184..919fa47cf47e13 100644
--- a/Mathlib/Tactic/Sat/FromLRAT.lean
+++ b/Mathlib/Tactic/Sat/FromLRAT.lean
@@ -6,7 +6,6 @@ Authors: Mario Carneiro
module
public import Mathlib.Algebra.Group.Nat.Defs
-public meta import Mathlib.Algebra.Notation.Defs
public import Mathlib.Tactic.Push
/-!
diff --git a/Mathlib/Tactic/Simproc/Divisors.lean b/Mathlib/Tactic/Simproc/Divisors.lean
index f534203d18aea2..5259b06d9c77a9 100644
--- a/Mathlib/Tactic/Simproc/Divisors.lean
+++ b/Mathlib/Tactic/Simproc/Divisors.lean
@@ -5,7 +5,7 @@ Authors: Paul Lezeau, Bhavik Mehta
-/
module
-public meta import Mathlib.NumberTheory.Divisors -- TODO: check if `meta` is still needed after https://github.com/leanprover/lean4/pull/13043
+public meta import Mathlib.NumberTheory.Divisors
public meta import Mathlib.Tactic.ToAdditive
public meta import Mathlib.Util.Qq
diff --git a/Mathlib/Tactic/Simproc/Factors.lean b/Mathlib/Tactic/Simproc/Factors.lean
index 600e85a32d9cd8..c0a6e985dacc2c 100644
--- a/Mathlib/Tactic/Simproc/Factors.lean
+++ b/Mathlib/Tactic/Simproc/Factors.lean
@@ -6,7 +6,6 @@ Authors: Mario Carneiro, Eric Wieser
module
import all Mathlib.Tactic.NormNum.Prime -- for accessing `evalMinFac.core`
-public meta import Mathlib.Algebra.BigOperators.Group.List.Defs
public import Mathlib.Data.Nat.Factors
public import Mathlib.Tactic.NormNum.Prime
From 699ca544fb64ebd90bba96837f79f88080fdea55 Mon Sep 17 00:00:00 2001
From: Eric Wieser <425260+eric-wieser@users.noreply.github.com>
Date: Tue, 16 Jun 2026 22:58:40 +0000
Subject: [PATCH 0081/1300] feat: big operator lemmas for congruence quotients
(#40564)
This adds `coe` lemmas for `Con`, `AddCon`, and `RingCon`, for all the big operators for which we already had the relation lemmas.
---
.../GroupTheory/Congruence/BigOperators.lean | 44 +++++++++++-
.../RingTheory/Congruence/BigOperators.lean | 71 +++++++++++++++++++
2 files changed, 112 insertions(+), 3 deletions(-)
diff --git a/Mathlib/GroupTheory/Congruence/BigOperators.lean b/Mathlib/GroupTheory/Congruence/BigOperators.lean
index 994b3f3536c1fa..25e62abbf084bd 100644
--- a/Mathlib/GroupTheory/Congruence/BigOperators.lean
+++ b/Mathlib/GroupTheory/Congruence/BigOperators.lean
@@ -10,9 +10,7 @@ public import Mathlib.Algebra.BigOperators.Group.List.Lemmas
public import Mathlib.Algebra.BigOperators.Group.Finset.Defs
public import Mathlib.Algebra.BigOperators.Finsupp.Basic
public import Mathlib.Data.DFinsupp.BigOperators
-public import Mathlib.GroupTheory.Congruence.Defs
-
-import Mathlib.GroupTheory.Congruence.Basic
+public import Mathlib.GroupTheory.Congruence.Basic
/-!
# Interactions between `∑, ∏` and `(Add)Con`
@@ -35,6 +33,12 @@ protected theorem list_prod {ι M : Type*} [MulOneClass M] (c : Con M) {l : List
rw [List.map_cons, List.map_cons, List.prod_cons, List.prod_cons]
exact c.mul (h _ <| .head _) <| ih fun k hk ↦ h _ (.tail _ hk)
+@[to_additive (attr := simp, norm_cast)]
+protected theorem coe_listProd {ι M : Type*} [MulOneClass M] (c : Con M)
+ (l : List ι) (f : ι → M) :
+ (↑(l.map f).prod : c.Quotient) = (l.map fun i => (f i : c.Quotient)).prod := by
+ induction l with simp [*]
+
/-- Multiplicative congruence relations preserve product indexed by a multiset. -/
@[to_additive /-- Additive congruence relations preserve sum indexed by a multiset. -/]
protected theorem multiset_prod {ι M : Type*} [CommMonoid M] (c : Con M) {s : Multiset ι}
@@ -42,6 +46,12 @@ protected theorem multiset_prod {ι M : Type*} [CommMonoid M] (c : Con M) {s : M
c (s.map f).prod (s.map g).prod := by
rcases s; simpa using c.list_prod h
+@[to_additive (attr := simp, norm_cast)]
+protected theorem coe_multisetProd {ι M : Type*} [CommMonoid M] (c : Con M)
+ (s : Multiset ι) (f : ι → M) :
+ (↑(s.map f).prod : c.Quotient) = (s.map fun i => (f i : c.Quotient)).prod := by
+ simpa using map_multiset_prod c.mk' (s.map f)
+
/-- Multiplicative congruence relations preserve finite product. -/
@[to_additive /-- Additive congruence relations preserve finite sum. -/]
protected theorem finsetProd {ι M : Type*} [CommMonoid M] (c : Con M) (s : Finset ι)
@@ -49,6 +59,12 @@ protected theorem finsetProd {ι M : Type*} [CommMonoid M] (c : Con M) (s : Fins
c (s.prod f) (s.prod g) :=
c.multiset_prod h
+@[to_additive (attr := simp, norm_cast)]
+protected theorem coe_finsetProd {ι M : Type*} [CommMonoid M] (c : Con M) (s : Finset ι)
+ (f : ι → M) :
+ (↑(s.prod f) : c.Quotient) = s.prod fun i => (f i : c.Quotient) :=
+ map_prod c.mk' f s
+
@[to_additive]
protected theorem finsuppProd {ι : Type*} {β : Type*} {M : Type*}
[CommMonoid M] [Zero β]
@@ -63,6 +79,12 @@ protected theorem finsuppProd {ι : Type*} {β : Type*} {M : Type*}
(fun _ _ => Quotient.sound <| H _)
(fun _ _ => Quotient.sound <| hf _) (fun _ _ => Quotient.sound <| hf' _)
+@[to_additive (attr := simp, norm_cast)]
+protected theorem coe_finsuppProd {ι : Type*} {β : Type*} {M : Type*}
+ [CommMonoid M] [Zero β] (c : Con M) (h : ι → β → M) (f : ι →₀ β) :
+ (↑(f.prod h) : c.Quotient) = f.prod fun i b => (h i b : c.Quotient) :=
+ map_finsuppProd c.mk' f h
+
@[to_additive]
protected theorem dfinsuppProd {ι : Type*} {β : ι → Type*} {M : Type*}
[DecidableEq ι] [CommMonoid M] [∀ i, Zero (β i)] [∀ i (y : β i), Decidable (y ≠ 0)]
@@ -77,6 +99,13 @@ protected theorem dfinsuppProd {ι : Type*} {β : ι → Type*} {M : Type*}
(fun _ _ => Quotient.sound <| H _)
(fun _ _ => Quotient.sound <| hf _) (fun _ _ => Quotient.sound <| hf' _)
+@[to_additive (attr := simp, norm_cast)]
+protected theorem coe_dfinsuppProd {ι : Type*} {β : ι → Type*} {M : Type*}
+ [DecidableEq ι] [CommMonoid M] [∀ i, Zero (β i)] [∀ i (y : β i), Decidable (y ≠ 0)]
+ (c : Con M) (h : (i : ι) → β i → M) (f : Π₀ i, β i) :
+ (↑(f.prod h) : c.Quotient) = f.prod fun i b => (h i b : c.Quotient) :=
+ map_dfinsuppProd c.mk' f h
+
protected theorem _root_.AddCon.dfinsuppSumAddHom {ι : Type*} {β : ι → Type*} {M : Type*}
[DecidableEq ι] [AddCommMonoid M] [∀ i, AddCommMonoid (β i)]
(c : AddCon M) (h : (i : ι) → β i →+ M) (h' : (i : ι) → β i →+ M) {f g : Π₀ i, β i}
@@ -87,6 +116,15 @@ protected theorem _root_.AddCon.dfinsuppSumAddHom {ι : Type*} {β : ι → Type
exact c.dfinsuppSum _ _
(bot_le (a := c) <| map_zero <| h ·) (bot_le (a := c) <| map_zero <| h' ·) H
+@[simp, norm_cast]
+protected theorem _root_.AddCon.coe_dfinsuppSumAddHom {ι : Type*} {β : ι → Type*} {M : Type*}
+ [DecidableEq ι] [AddCommMonoid M] [∀ i, AddCommMonoid (β i)]
+ (c : AddCon M) (h : (i : ι) → β i →+ M) (f : Π₀ i, β i) :
+ (↑(f.sumAddHom h) : c.Quotient) = f.sumAddHom fun i => (AddCon.mk' c).comp (h i) := by
+ classical
+ simp_rw [← AddCon.coe_mk', DFinsupp.sumAddHom_apply, map_dfinsuppSum]
+ rfl
+
@[deprecated (since := "2026-04-08")]
protected alias _root_.AddCon.finset_sum := AddCon.finsetSum
diff --git a/Mathlib/RingTheory/Congruence/BigOperators.lean b/Mathlib/RingTheory/Congruence/BigOperators.lean
index d883c7a8dd68a5..9d89edccf68e41 100644
--- a/Mathlib/RingTheory/Congruence/BigOperators.lean
+++ b/Mathlib/RingTheory/Congruence/BigOperators.lean
@@ -11,6 +11,8 @@ public import Mathlib.RingTheory.Congruence.Defs
/-!
# Interactions between `∑, ∏` and `RingCon`
+TODO: some of the typeclass assumptions in this file can be weakened if more instances are added
+for `RingCon.Quotient`.
-/
public section
@@ -23,36 +25,72 @@ protected lemma listProd {ι S : Type*} [Add S] [Monoid S]
t (l.map f).prod (l.map g).prod :=
t.toCon.list_prod h
+@[simp, norm_cast]
+protected lemma coe_listProd {ι S : Type*} [Add S] [Monoid S] (t : RingCon S)
+ (l : List ι) (f : ι → S) :
+ (↑(l.map f).prod : t.Quotient) = (l.map fun i => (f i : t.Quotient)).prod :=
+ t.toCon.coe_listProd l f
+
/-- Congruence relation of a ring preserves finite sum indexed by a list. -/
protected lemma listSum {ι S : Type*} [AddMonoid S] [Mul S]
(t : RingCon S) (l : List ι) {f g : ι → S} (h : ∀ i ∈ l, t (f i) (g i)) :
t (l.map f).sum (l.map g).sum :=
t.toAddCon.list_sum h
+@[simp, norm_cast]
+protected lemma coe_listSum {ι S : Type*} [AddMonoid S] [Mul S] (t : RingCon S)
+ (l : List ι) (f : ι → S) :
+ (↑(l.map f).sum : t.Quotient) = (l.map fun i => (f i : t.Quotient)).sum :=
+ t.toAddCon.coe_listSum l f
+
/-- Congruence relation of a ring preserves finite product indexed by a multiset. -/
protected lemma multisetProd {ι S : Type*} [Add S] [CommMonoid S] (t : RingCon S)
(s : Multiset ι) {f g : ι → S} (h : ∀ i ∈ s, t (f i) (g i)) :
t (s.map f).prod (s.map g).prod :=
t.toCon.multiset_prod h
+@[simp, norm_cast]
+protected lemma coe_multisetProd {ι S : Type*} [Add S] [CommMonoid S] (t : RingCon S)
+ (s : Multiset ι) (f : ι → S) :
+ (↑(s.map f).prod : t.Quotient) = (s.map fun i => (f i : t.Quotient)).prod :=
+ t.toCon.coe_multisetProd s f
+
/-- Congruence relation of a ring preserves finite sum indexed by a multiset. -/
protected lemma multisetSum {ι S : Type*} [AddCommMonoid S] [Mul S] (t : RingCon S)
(s : Multiset ι) {f g : ι → S} (h : ∀ i ∈ s, t (f i) (g i)) :
t (s.map f).sum (s.map g).sum :=
t.toAddCon.multiset_sum h
+@[simp, norm_cast]
+protected lemma coe_multisetSum {ι S : Type*} [AddCommMonoid S] [Mul S] (t : RingCon S)
+ (s : Multiset ι) (f : ι → S) :
+ (↑(s.map f).sum : t.Quotient) = (s.map fun i => (f i : t.Quotient)).sum :=
+ t.toAddCon.coe_multisetSum s f
+
/-- Congruence relation of a ring preserves finite product. -/
protected lemma finsetProd {ι S : Type*} [Add S] [CommMonoid S] (t : RingCon S) (s : Finset ι)
{f g : ι → S} (h : ∀ i ∈ s, t (f i) (g i)) :
t (s.prod f) (s.prod g) :=
t.toCon.finsetProd s h
+@[simp, norm_cast]
+protected lemma coe_finsetProd {ι S : Type*} [Add S] [CommMonoid S] (t : RingCon S) (s : Finset ι)
+ (f : ι → S) :
+ (↑(s.prod f) : t.Quotient) = s.prod fun i => (f i : t.Quotient) :=
+ t.toCon.coe_finsetProd s f
+
/-- Congruence relation of a ring preserves finite sum. -/
protected lemma finsetSum {ι S : Type*} [AddCommMonoid S] [Mul S] (t : RingCon S) (s : Finset ι)
{f g : ι → S} (h : ∀ i ∈ s, t (f i) (g i)) :
t (s.sum f) (s.sum g) :=
t.toAddCon.finsetSum s h
+@[simp, norm_cast]
+protected lemma coe_finsetSum {ι S : Type*} [AddCommMonoid S] [Mul S] (t : RingCon S) (s : Finset ι)
+ (f : ι → S) :
+ (↑(s.sum f) : t.Quotient) = s.sum fun i => (f i : t.Quotient) :=
+ t.toAddCon.coe_finsetSum s f
+
protected lemma finsuppProd {ι : Type*} {β : Type*} {M : Type*}
[Add M] [CommMonoid M] [Zero β]
(c : RingCon M) (h : ι → β → M) (h' : ι → β → M)
@@ -61,6 +99,12 @@ protected lemma finsuppProd {ι : Type*} {β : Type*} {M : Type*}
c (f.prod h) (g.prod h') :=
c.toCon.finsuppProd h h' hf hf' H
+@[simp, norm_cast]
+protected lemma coe_finsuppProd {ι : Type*} {β : Type*} {M : Type*}
+ [Add M] [CommMonoid M] [Zero β] (c : RingCon M) (h : ι → β → M) (f : ι →₀ β) :
+ (↑(f.prod h) : c.Quotient) = f.prod fun i b => (h i b : c.Quotient) :=
+ c.toCon.coe_finsuppProd h f
+
protected lemma finsuppSum {ι : Type*} {β : Type*} {M : Type*}
[AddCommMonoid M] [Mul M] [Zero β]
(c : RingCon M) (h : ι → β → M) (h' : ι → β → M)
@@ -69,6 +113,12 @@ protected lemma finsuppSum {ι : Type*} {β : Type*} {M : Type*}
c (f.sum h) (g.sum h') :=
c.toAddCon.finsuppSum h h' hf hf' H
+@[simp, norm_cast]
+protected lemma coe_finsuppSum {ι : Type*} {β : Type*} {M : Type*}
+ [AddCommMonoid M] [Mul M] [Zero β] (c : RingCon M) (h : ι → β → M) (f : ι →₀ β) :
+ (↑(f.sum h) : c.Quotient) = f.sum fun i b => (h i b : c.Quotient) :=
+ c.toAddCon.coe_finsuppSum h f
+
protected lemma dfinsuppProd {ι : Type*} {β : ι → Type*} {M : Type*}
[DecidableEq ι] [Add M] [CommMonoid M] [∀ i, Zero (β i)] [∀ i (y : β i), Decidable (y ≠ 0)]
(c : RingCon M) (h : (i : ι) → β i → M) (h' : (i : ι) → β i → M)
@@ -77,6 +127,13 @@ protected lemma dfinsuppProd {ι : Type*} {β : ι → Type*} {M : Type*}
c (f.prod h) (g.prod h') :=
c.toCon.dfinsuppProd h h' hf hf' H
+@[simp, norm_cast]
+protected lemma coe_dfinsuppProd {ι : Type*} {β : ι → Type*} {M : Type*}
+ [DecidableEq ι] [Add M] [CommMonoid M] [∀ i, Zero (β i)] [∀ i (y : β i), Decidable (y ≠ 0)]
+ (c : RingCon M) (h : (i : ι) → β i → M) (f : Π₀ i, β i) :
+ (↑(f.prod h) : c.Quotient) = f.prod fun i b => (h i b : c.Quotient) :=
+ c.toCon.coe_dfinsuppProd h f
+
protected lemma dfinsuppSum {ι : Type*} {β : ι → Type*} {M : Type*}
[DecidableEq ι] [AddCommMonoid M] [Mul M] [∀ i, Zero (β i)] [∀ i (y : β i), Decidable (y ≠ 0)]
(c : RingCon M) (h : (i : ι) → β i → M) (h' : (i : ι) → β i → M)
@@ -85,6 +142,13 @@ protected lemma dfinsuppSum {ι : Type*} {β : ι → Type*} {M : Type*}
c (f.sum h) (g.sum h') :=
c.toAddCon.dfinsuppSum h h' hf hf' H
+@[simp, norm_cast]
+protected lemma coe_dfinsuppSum {ι : Type*} {β : ι → Type*} {M : Type*}
+ [DecidableEq ι] [AddCommMonoid M] [Mul M] [∀ i, Zero (β i)] [∀ i (y : β i), Decidable (y ≠ 0)]
+ (c : RingCon M) (h : (i : ι) → β i → M) (f : Π₀ i, β i) :
+ (↑(f.sum h) : c.Quotient) = f.sum fun i b => (h i b : c.Quotient) :=
+ c.toAddCon.coe_dfinsuppSum h f
+
protected lemma dfinsuppSumAddHom {ι : Type*} {β : ι → Type*} {M : Type*}
[DecidableEq ι] [AddCommMonoid M] [Mul M] [∀ i, AddCommMonoid (β i)]
(c : RingCon M) (h : (i : ι) → β i →+ M) (h' : (i : ι) → β i →+ M) {f g : Π₀ i, β i}
@@ -92,4 +156,11 @@ protected lemma dfinsuppSumAddHom {ι : Type*} {β : ι → Type*} {M : Type*}
c (f.sumAddHom h) (g.sumAddHom h') :=
c.toAddCon.dfinsuppSumAddHom h h' H
+@[simp, norm_cast]
+protected lemma coe_dfinsuppSumAddHom {ι : Type*} {β : ι → Type*} {M : Type*}
+ [DecidableEq ι] [AddCommMonoid M] [Mul M] [∀ i, AddCommMonoid (β i)]
+ (c : RingCon M) (h : (i : ι) → β i →+ M) (f : Π₀ i, β i) :
+ (↑(f.sumAddHom h) : c.Quotient) = f.sumAddHom fun i => c.toAddCon.mk'.comp (h i) :=
+ c.toAddCon.coe_dfinsuppSumAddHom h f
+
end RingCon
From b90fc6c341f128f1c27ab34cb06bbeb243cdd1dd Mon Sep 17 00:00:00 2001
From: "Yi.Yuan"
Date: Tue, 16 Jun 2026 23:11:54 +0000
Subject: [PATCH 0082/1300] refactor(Analysis): golf
`Mathlib/Analysis/Complex/Isometry` (#40070)
- simplifies `LinearIsometry.im_apply_eq_im` by deriving real-part equality from the squared distance identity
Extracted from #37968
[](https://gitpod.io/from-referrer/)
---
Mathlib/Analysis/Complex/Isometry.lean | 14 +++-----------
1 file changed, 3 insertions(+), 11 deletions(-)
diff --git a/Mathlib/Analysis/Complex/Isometry.lean b/Mathlib/Analysis/Complex/Isometry.lean
index 9be1f7b76ccabc..8deafc5101dd26 100644
--- a/Mathlib/Analysis/Complex/Isometry.lean
+++ b/Mathlib/Analysis/Complex/Isometry.lean
@@ -101,17 +101,9 @@ theorem LinearIsometry.im_apply_eq_im_or_neg_of_re_apply_eq_re {f : ℂ →ₗ
theorem LinearIsometry.im_apply_eq_im {f : ℂ →ₗᵢ[ℝ] ℂ} (h : f 1 = 1) (z : ℂ) :
z + conj z = f z + conj (f z) := by
- have : ‖f z - 1‖ = ‖z - 1‖ := by rw [← f.norm_map (z - 1), f.map_sub, h]
- apply_fun fun x => x ^ 2 at this
- simp only [← normSq_eq_norm_sq] at this
- rw [← ofReal_inj, ← mul_conj, ← mul_conj] at this
- rw [map_sub, map_sub] at this
- simp only [sub_mul, mul_sub, one_mul] at this
- rw [mul_conj, normSq_eq_norm_sq, LinearIsometry.norm_map] at this
- rw [mul_conj, normSq_eq_norm_sq] at this
- simp only [sub_sub, sub_right_inj, mul_one, ofReal_pow, map_one] at this
- simp only [add_sub, sub_left_inj] at this
- rw [add_comm, ← this, add_comm]
+ have hsq : ‖f z - 1‖ ^ 2 = ‖z - 1‖ ^ 2 := by simpa [h] using f.norm_map (z - 1)
+ simp_rw [← normSq_eq_norm_sq, Complex.normSq_sub] at hsq
+ simpa [normSq_eq_norm_sq, Complex.add_conj, LinearIsometry.norm_map] using hsq.symm
theorem LinearIsometry.re_apply_eq_re {f : ℂ →ₗᵢ[ℝ] ℂ} (h : f 1 = 1) (z : ℂ) : (f z).re = z.re := by
apply LinearIsometry.re_apply_eq_re_of_add_conj_eq
From 939a3b823e06d0fbd1cb47e9cd2618120357093d Mon Sep 17 00:00:00 2001
From: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com>
Date: Tue, 16 Jun 2026 23:11:56 +0000
Subject: [PATCH 0083/1300] feat(Analysis/MeanInequalities): equality condition
for Young's inequality (#40485)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
`a * b = a ^ p / p + b ^ q / q ↔ a ^ p = b ^ q` with `Real`/`NNReal`/`ENNReal` versions that match the existing `≤` theorems.
---
Mathlib/Analysis/MeanInequalities.lean | 38 +++++++++++++++++++++++---
1 file changed, 34 insertions(+), 4 deletions(-)
diff --git a/Mathlib/Analysis/MeanInequalities.lean b/Mathlib/Analysis/MeanInequalities.lean
index c1131e6d0685eb..ac11e444efd9db 100644
--- a/Mathlib/Analysis/MeanInequalities.lean
+++ b/Mathlib/Analysis/MeanInequalities.lean
@@ -499,7 +499,7 @@ namespace Real
/-- **Young's inequality**, a version for nonnegative real numbers. -/
theorem young_inequality_of_nonneg {a b p q : ℝ} (ha : 0 ≤ a) (hb : 0 ≤ b)
(hpq : p.HolderConjugate q) : a * b ≤ a ^ p / p + b ^ q / q := by
- simpa [← rpow_mul, ha, hb, hpq.ne_zero, hpq.symm.ne_zero, _root_.div_eq_inv_mul] using
+ simpa [← rpow_mul, ha, hb, hpq.ne_zero, hpq.symm.ne_zero, div_eq_inv_mul] using
geom_mean_le_arith_mean2_weighted hpq.inv_nonneg hpq.symm.inv_nonneg
(rpow_nonneg ha p) (rpow_nonneg hb q) hpq.inv_add_inv_eq_one
@@ -512,6 +512,13 @@ theorem young_inequality (a b : ℝ) {p q : ℝ} (hpq : p.HolderConjugate q) :
_ ≤ |a| ^ p / p + |b| ^ q / q :=
Real.young_inequality_of_nonneg (abs_nonneg a) (abs_nonneg b) hpq
+/-- **Young's inequality** equality condition for nonnegative real numbers. -/
+theorem young_inequality_eq_iff_of_nonneg {a b p q : ℝ} (ha : 0 ≤ a) (hb : 0 ≤ b)
+ (hpq : p.HolderConjugate q) : a * b = a ^ p / p + b ^ q / q ↔ a ^ p = b ^ q := by
+ simpa [← rpow_mul, ha, hb, hpq.ne_zero, hpq.symm.ne_zero, div_eq_inv_mul] using
+ geom_mean_eq_arith_mean2_weighted_iff_of_nonneg hpq.inv_nonneg hpq.symm.inv_nonneg
+ (rpow_nonneg ha p) (rpow_nonneg hb q) hpq.inv_add_inv_eq_one
+
end Real
namespace NNReal
@@ -524,8 +531,18 @@ theorem young_inequality (a b : ℝ≥0) {p q : ℝ≥0} (hpq : p.HolderConjugat
/-- **Young's inequality**, `ℝ≥0` version with real conjugate exponents. -/
theorem young_inequality_real (a b : ℝ≥0) {p q : ℝ} (hpq : p.HolderConjugate q) :
- a * b ≤ a ^ p / Real.toNNReal p + b ^ q / Real.toNNReal q := by
- simpa [Real.coe_toNNReal, hpq.nonneg, hpq.symm.nonneg] using young_inequality a b hpq.toNNReal
+ a * b ≤ a ^ p / p.toNNReal + b ^ q / q.toNNReal := by
+ simpa [hpq.nonneg, hpq.symm.nonneg] using young_inequality a b hpq.toNNReal
+
+/-- **Young's inequality** equality condition, `ℝ≥0` version. -/
+theorem young_inequality_eq_iff (a b : ℝ≥0) {p q : ℝ≥0} (hpq : p.HolderConjugate q) :
+ a * b = a ^ (p : ℝ) / p + b ^ (q : ℝ) / q ↔ a ^ (p : ℝ) = b ^ (q : ℝ) :=
+ mod_cast Real.young_inequality_eq_iff_of_nonneg a.coe_nonneg b.coe_nonneg hpq.coe
+
+/-- **Young's inequality** equality condition, `ℝ≥0` version with real conjugate exponents. -/
+theorem young_inequality_real_eq_iff (a b : ℝ≥0) {p q : ℝ} (hpq : p.HolderConjugate q) :
+ a * b = a ^ p / p.toNNReal + b ^ q / q.toNNReal ↔ a ^ p = b ^ q := by
+ simpa [hpq.nonneg, hpq.symm.nonneg] using young_inequality_eq_iff a b hpq.toNNReal
end NNReal
@@ -538,13 +555,26 @@ theorem young_inequality (a b : ℝ≥0∞) {p q : ℝ} (hpq : p.HolderConjugate
· refine le_trans le_top (le_of_eq ?_)
repeat rw [div_eq_mul_inv]
rcases h with h | h <;> rw [h] <;> simp [hpq.pos, hpq.symm.pos]
- -- if a ≠ ⊤ and b ≠ ⊤, use the nnreal version: nnreal.young_inequality_real
+ -- if `a ≠ ⊤` and `b ≠ ⊤`, use the `NNReal` version: `NNReal.young_inequality_real`
rw [← coe_toNNReal h.left, ← coe_toNNReal h.right, ← coe_mul, ← coe_rpow_of_nonneg _ hpq.nonneg,
← coe_rpow_of_nonneg _ hpq.symm.nonneg, ENNReal.ofReal, ENNReal.ofReal, ←
@coe_div (Real.toNNReal p) _ (by simp [hpq.pos]), ←
@coe_div (Real.toNNReal q) _ (by simp [hpq.symm.pos]), ← coe_add, coe_le_coe]
exact NNReal.young_inequality_real a.toNNReal b.toNNReal hpq
+/-- **Young's inequality** equality condition, `ℝ≥0∞` version with real conjugate exponents. -/
+theorem young_inequality_eq_iff (a b : ℝ≥0∞) {p q : ℝ} (hpq : p.HolderConjugate q) :
+ a * b = a ^ p / .ofReal p + b ^ q / .ofReal q ↔
+ (a = ⊤ ∧ b ≠ 0) ∨ (a ≠ 0 ∧ b = ⊤) ∨ a ^ p = b ^ q := by
+ by_cases! h0 : a = 0 ∨ b = 0
+ · rcases h0 with rfl | rfl <;> simp [hpq.pos, hpq.symm.pos, eq_comm]
+ by_cases! h : a = ⊤ ∨ b = ⊤
+ · rcases h with rfl | rfl <;> simp [hpq.pos, hpq.symm.pos, h0, div_eq_mul_inv]
+ rw [← coe_toNNReal h.left, ← coe_toNNReal h.right, ← coe_mul, ← coe_rpow_of_nonneg _ hpq.nonneg,
+ ← coe_rpow_of_nonneg _ hpq.symm.nonneg, ← ofNNReal_toNNReal, ← ofNNReal_toNNReal,
+ ← coe_div (by simp [hpq.pos]), ← coe_div (by simp [hpq.symm.pos]), ← coe_add, coe_inj, coe_inj]
+ simp [young_inequality_real_eq_iff a.toNNReal b.toNNReal hpq]
+
end ENNReal
end Young
From 9c1fa80c5dc73a897bdd30caa2ab99c8d3298362 Mon Sep 17 00:00:00 2001
From: Monica Omar <23701951+themathqueen@users.noreply.github.com>
Date: Tue, 16 Jun 2026 23:11:58 +0000
Subject: [PATCH 0084/1300] feat: some API for relating self-adjoint and
skew-adjoints (#40684)
---
Mathlib/Analysis/RCLike/Basic.lean | 15 +++++++
Mathlib/LinearAlgebra/Complex/Module.lean | 51 ++++++++++-------------
2 files changed, 36 insertions(+), 30 deletions(-)
diff --git a/Mathlib/Analysis/RCLike/Basic.lean b/Mathlib/Analysis/RCLike/Basic.lean
index 77997bae54b4e6..1970d7eb45f5ad 100644
--- a/Mathlib/Analysis/RCLike/Basic.lean
+++ b/Mathlib/Analysis/RCLike/Basic.lean
@@ -763,8 +763,23 @@ theorem isCauSeq_norm {f : ℕ → K} (hf : IsCauSeq norm f) : IsCauSeq abs (nor
let ⟨i, hi⟩ := hf ε ε0
⟨i, fun j hj => lt_of_le_of_lt (abs_norm_sub_norm_le _ _) (hi j hj)⟩
+lemma I_mem_skewAdjoint : I ∈ skewAdjoint K := by simp [skewAdjoint.mem_iff]
+
end RCLike
+section
+variable {A : Type*} [AddCommGroup A] [StarAddMonoid A] [Module K A] [StarModule K A] {a : A}
+
+open RCLike
+
+lemma IsSelfAdjoint.I_smul_mem_skewAdjoint (h : IsSelfAdjoint a) :
+ (I : K) • a ∈ skewAdjoint A := h.smul_mem_skewAdjoint I_mem_skewAdjoint
+
+lemma IsSelfAdjoint.I_smul_of_mem_skewAdjoint (h : a ∈ skewAdjoint A) :
+ IsSelfAdjoint ((I : K) • a) := isSelfAdjoint_smul_of_mem_skewAdjoint I_mem_skewAdjoint h
+
+end
+
section Instances
noncomputable instance Real.instRCLike : RCLike ℝ where
diff --git a/Mathlib/LinearAlgebra/Complex/Module.lean b/Mathlib/LinearAlgebra/Complex/Module.lean
index 7db6282b5b709a..6b9d1cdd32c577 100644
--- a/Mathlib/LinearAlgebra/Complex/Module.lean
+++ b/Mathlib/LinearAlgebra/Complex/Module.lean
@@ -362,26 +362,26 @@ section AddCommGroup
variable [AddCommGroup A] [Module ℂ A] [StarAddMonoid A] [StarModule ℂ A]
+lemma Complex.I_mem_skewAdjoint : I ∈ skewAdjoint ℂ := by simp [skewAdjoint.mem_iff]
+
+@[simp] lemma Complex.I_smul_mem_skewAdjoint_iff_isSelfAdjoint {a : A} :
+ I • a ∈ skewAdjoint A ↔ IsSelfAdjoint a := by
+ simp [skewAdjoint.mem_iff, IsSelfAdjoint, smul_right_inj]
+
+@[simp] lemma Complex.isSelfAdjoint_I_smul_iff_mem_skewAdjoint {a : A} :
+ IsSelfAdjoint (I • a) ↔ a ∈ skewAdjoint A := by
+ simp [← I_smul_mem_skewAdjoint_iff_isSelfAdjoint, smul_smul]
+
/-- Create a `selfAdjoint` element from a `skewAdjoint` element by multiplying by the scalar
`-Complex.I`. -/
@[simps]
def skewAdjoint.negISMul : skewAdjoint A →ₗ[ℝ] selfAdjoint A where
- toFun a :=
- ⟨-I • ↑a, by
- simp only [neg_smul, neg_mem_iff, selfAdjoint.mem_iff, star_smul, star_def, conj_I,
- star_val_eq, smul_neg, neg_neg]⟩
- map_add' a b := by
- ext
- simp only [AddSubgroup.coe_add, smul_add, AddMemClass.mk_add_mk]
- map_smul' a b := by
- ext
- simp only [neg_smul, skewAdjoint.val_smul, RingHom.id_apply,
- selfAdjoint.val_smul, smul_neg, neg_inj]
- rw [smul_comm]
+ toFun a := ⟨-I • ↑a, by simp [selfAdjoint.mem_iff]⟩
+ map_add' a b := by simp
+ map_smul' a b := by ext; simp [smul_comm I]
theorem skewAdjoint.I_smul_neg_I (a : skewAdjoint A) : I • (skewAdjoint.negISMul a : A) = a := by
- simp only [smul_smul, skewAdjoint.negISMul_apply_coe, neg_smul, smul_neg, I_mul_I, one_smul,
- neg_neg]
+ simp [smul_smul]
/-- The real part `ℜ a` of an element `a` of a star module over `ℂ`, as a linear map. This is just
`selfAdjointPart ℝ`, but we provide it as a separate definition in order to link it with lemmas
@@ -405,20 +405,15 @@ scoped[ComplexStarModule] notation "ℑ" => imaginaryPart
open ComplexStarModule
theorem realPart_apply_coe (a : A) : (ℜ a : A) = (2 : ℝ)⁻¹ • (a + star a) := by
- unfold realPart
- simp only [selfAdjointPart_apply_coe, invOf_eq_inv]
+ simp [realPart]
theorem imaginaryPart_apply_coe (a : A) : (ℑ a : A) = -I • (2 : ℝ)⁻¹ • (a - star a) := by
- unfold imaginaryPart
- simp only [LinearMap.coe_comp, Function.comp_apply, skewAdjoint.negISMul_apply_coe,
- skewAdjointPart_apply_coe, invOf_eq_inv, neg_smul]
+ simp [imaginaryPart]
/-- The standard decomposition of `ℜ a + Complex.I • ℑ a = a` of an element of a star module over
`ℂ` into a linear combination of self adjoint elements. -/
theorem realPart_add_I_smul_imaginaryPart (a : A) : (ℜ a : A) + I • (ℑ a : A) = a := by
- simpa only [smul_smul, realPart_apply_coe, imaginaryPart_apply_coe, neg_smul, I_mul_I, one_smul,
- neg_sub, add_add_sub_cancel, smul_sub, smul_add, neg_sub_neg, invOf_eq_inv] using
- invOf_two_smul_add_invOf_two_smul ℝ a
+ simp [realPart, imaginaryPart, smul_smul, ← smul_add, inv_smul_eq_iff₀, two_smul]
@[simp]
theorem realPart_I_smul (a : A) : ℜ (I • a) = -ℑ a := by
@@ -460,8 +455,7 @@ lemma realPart_comp_subtype_selfAdjoint :
lemma imaginaryPart_comp_subtype_selfAdjoint :
imaginaryPart.comp (selfAdjoint.submodule ℝ A).subtype = 0 := by
- rw [imaginaryPart, LinearMap.comp_assoc, skewAdjointPart_comp_subtype_selfAdjoint,
- LinearMap.comp_zero]
+ ext; simp [imaginaryPart]
@[simp]
lemma selfAdjoint.realPart_coe {x : selfAdjoint A} : ℜ (x : A) = x :=
@@ -542,12 +536,9 @@ lemma realPart_ofReal (r : ℝ) : (ℜ (r : ℂ) : ℂ) = r := by
lemma imaginaryPart_ofReal (r : ℝ) : ℑ (r : ℂ) = 0 := by
ext1; simp [imaginaryPart_apply_coe, conj_ofReal]
-set_option linter.style.whitespace false in -- manual alignment is not recognised
-lemma Complex.coe_realPart (z : ℂ) : (ℜ z : ℂ) = z.re := calc
- (ℜ z : ℂ) = (↑(ℜ (↑z.re + ↑z.im * I))) := by congrm (ℜ $((re_add_im z).symm))
- _ = z.re := by
- rw [map_add, AddSubmonoid.coe_add, mul_comm, ← smul_eq_mul, realPart_I_smul]
- simp
+lemma Complex.coe_realPart (z : ℂ) : (ℜ z : ℂ) = z.re := by
+ conv_lhs => rw [← re_add_im z]
+ simp [-re_add_im, realPart_I_smul, mul_comm _ I, ← smul_eq_mul]
section NonUnitalNonAssocRing
From 55b749ddf32c6604e7b310d6eb8467691d2d3cf7 Mon Sep 17 00:00:00 2001
From: Monica Omar <23701951+themathqueen@users.noreply.github.com>
Date: Tue, 16 Jun 2026 23:12:00 +0000
Subject: [PATCH 0085/1300] chore(Analysis/Normed/Lp/PiLp): remove some
`backward.defeqAttrib.useBackward` (#40685)
---
Mathlib/Analysis/Normed/Lp/PiLp.lean | 14 ++++----------
1 file changed, 4 insertions(+), 10 deletions(-)
diff --git a/Mathlib/Analysis/Normed/Lp/PiLp.lean b/Mathlib/Analysis/Normed/Lp/PiLp.lean
index 558b4a00548e99..5bc4fbdc2b7368 100644
--- a/Mathlib/Analysis/Normed/Lp/PiLp.lean
+++ b/Mathlib/Analysis/Normed/Lp/PiLp.lean
@@ -947,7 +947,6 @@ variable {ι : Type*} {κ : ι → Type*} (p : ℝ≥0∞) [Fact (1 ≤ p)]
[Fintype ι] [∀ i, Fintype (κ i)]
(α : ∀ i, κ i → Type*) [∀ i k, SeminormedAddCommGroup (α i k)] [∀ i k, Module 𝕜 (α i k)]
-set_option backward.defeqAttrib.useBackward true in
variable (𝕜) in
/-- `LinearEquiv.piCurry` for `PiLp`, as an isometry. -/
def _root_.LinearIsometryEquiv.piLpCurry :
@@ -958,16 +957,11 @@ def _root_.LinearIsometryEquiv.piLpCurry :
≪≫ₗ (LinearEquiv.piCongrRight fun _ => (WithLp.linearEquiv _ _ _).symm)
≪≫ₗ (WithLp.linearEquiv _ _ _).symm
norm_map' := (WithLp.linearEquiv p 𝕜 _).symm.surjective.forall.2 fun x => by
- simp_rw [← coe_nnnorm, NNReal.coe_inj]
- dsimp only [WithLp.linearEquiv_symm_apply]
+ simp_rw [← coe_nnnorm, NNReal.coe_inj, WithLp.linearEquiv_symm_apply]
obtain rfl | hp := eq_or_ne p ⊤
- · simp_rw [← PiLp.nnnorm_ofLp, Pi.nnnorm_def, ← PiLp.nnnorm_ofLp, Pi.nnnorm_def]
- dsimp [Sigma.curry]
- rw [← Finset.univ_sigma_univ, Finset.sup_sigma]
+ · simp [Pi.nnnorm_def, ← Finset.univ_sigma_univ, Finset.sup_sigma, Sigma.curry]
· have : 0 < p.toReal := (toReal_pos_iff_ne_top _).mpr hp
- simp_rw [PiLp.nnnorm_eq_sum hp]
- dsimp [Sigma.curry]
- simp_rw [one_div, NNReal.rpow_inv_rpow this.ne', ← Finset.univ_sigma_univ, Finset.sum_sigma]
+ simp [nnnorm_eq_sum hp, this.ne', ← Finset.univ_sigma_univ, Finset.sum_sigma, Sigma.curry]
@[simp] theorem _root_.LinearIsometryEquiv.piLpCurry_apply
(f : PiLp p (fun i : Sigma κ => α i.1 i.2)) :
@@ -1149,12 +1143,12 @@ lemma coe_continuousLinearEquiv :
lemma coe_symm_continuousLinearEquiv :
⇑(PiLp.continuousLinearEquiv p 𝕜 β).symm = toLp p := rfl
-set_option backward.defeqAttrib.useBackward true in
variable {𝕜} in
/-- The projection on the `i`-th coordinate of `PiLp p β`, as a continuous linear map. -/
@[simps!]
def proj (i : ι) : PiLp p β →L[𝕜] β i where
__ := projₗ p β i
+ cont := (by fun_prop : Continuous fun a : PiLp p β ↦ a.ofLp i)
end
From e4fb9733350d1bb80cbb478b68bc854ac3981f4c Mon Sep 17 00:00:00 2001
From: Vasilii Nesterov <118051017+vasnesterov@users.noreply.github.com>
Date: Tue, 16 Jun 2026 23:34:15 +0000
Subject: [PATCH 0086/1300] feat(Tactic/ComputeAsymptotics/Multiseries): define
`Trimmed` (#40017)
Define `Trimmed` predicate and prove basic lemmas about it.
---
Mathlib.lean | 1 +
Mathlib/Tactic.lean | 1 +
.../ComputeAsymptotics/Multiseries/Defs.lean | 19 ++-
.../Multiseries/Trimming.lean | 140 ++++++++++++++++++
4 files changed, 160 insertions(+), 1 deletion(-)
create mode 100644 Mathlib/Tactic/ComputeAsymptotics/Multiseries/Trimming.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 8a4fbc6a4795c7..c91c39aba616da 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -7200,6 +7200,7 @@ public import Mathlib.Tactic.ComputeAsymptotics.Multiseries.Defs
public import Mathlib.Tactic.ComputeAsymptotics.Multiseries.Majorized
public import Mathlib.Tactic.ComputeAsymptotics.Multiseries.Monomial.Basic
public import Mathlib.Tactic.ComputeAsymptotics.Multiseries.Monomial.Predicates
+public import Mathlib.Tactic.ComputeAsymptotics.Multiseries.Trimming
public import Mathlib.Tactic.ComputeDegree
public import Mathlib.Tactic.CongrExclamation
public import Mathlib.Tactic.CongrM
diff --git a/Mathlib/Tactic.lean b/Mathlib/Tactic.lean
index 38a86214f5cb93..f7fe1f5c1e9e22 100644
--- a/Mathlib/Tactic.lean
+++ b/Mathlib/Tactic.lean
@@ -74,6 +74,7 @@ public import Mathlib.Tactic.ComputeAsymptotics.Multiseries.Defs
public import Mathlib.Tactic.ComputeAsymptotics.Multiseries.Majorized
public import Mathlib.Tactic.ComputeAsymptotics.Multiseries.Monomial.Basic
public import Mathlib.Tactic.ComputeAsymptotics.Multiseries.Monomial.Predicates
+public import Mathlib.Tactic.ComputeAsymptotics.Multiseries.Trimming
public import Mathlib.Tactic.ComputeDegree
public import Mathlib.Tactic.CongrExclamation
public import Mathlib.Tactic.CongrM
diff --git a/Mathlib/Tactic/ComputeAsymptotics/Multiseries/Defs.lean b/Mathlib/Tactic/ComputeAsymptotics/Multiseries/Defs.lean
index 4680297763ea00..2264a94ec0a599 100644
--- a/Mathlib/Tactic/ComputeAsymptotics/Multiseries/Defs.lean
+++ b/Mathlib/Tactic/ComputeAsymptotics/Multiseries/Defs.lean
@@ -42,7 +42,7 @@ in the basis `[b₂, ..., bₙ]` (`basis_tl`).
namespace Tactic.ComputeAsymptotics
-open Filter Stream'
+open Filter Stream' Topology
/-- List of functions used to construct monomials in multiseries. -/
abbrev Basis := List (ℝ → ℝ)
@@ -706,6 +706,23 @@ theorem replaceFun {ms : MultiseriesExpansion (basis_hd :: basis_tl)} {f : ℝ
h_tl, ?_⟩
grw [mk_toFun, h_eq]
+/-- If `f` can be approximated by multiseries with negative leading exponent, then
+it tends to zero. -/
+theorem neg_leadingExp_tendsto_zero {basis_hd : ℝ → ℝ} {basis_tl : Basis}
+ {ms : MultiseriesExpansion (basis_hd :: basis_tl)}
+ (h_neg : ms.leadingExp < 0) (h_approx : ms.Approximates) :
+ Tendsto ms.toFun atTop (𝓝 0) := by
+ cases ms
+ · exact Tendsto.congr' h_approx.elim_nil.symm tendsto_const_nhds
+ · obtain ⟨h_coef, h_maj, h_tl⟩ := h_approx.elim_cons
+ simp only [leadingExp_def, mk_seq, Multiseries.leadingExp_cons, WithBot.coe_lt_zero] at h_neg
+ exact Majorized.tendsto_zero_of_neg h_neg h_maj
+
+theorem nil_tendsto_zero {basis_hd : ℝ → ℝ} {basis_tl : Basis} {f : ℝ → ℝ}
+ (h : MultiseriesExpansion.Approximates (basis := basis_hd :: basis_tl) (mk .nil f)) :
+ Tendsto f atTop (𝓝 0) :=
+ neg_leadingExp_tendsto_zero (by simp) h
+
end Approximates
instance (basis_hd : ℝ → ℝ) (basis_tl : Basis) :
diff --git a/Mathlib/Tactic/ComputeAsymptotics/Multiseries/Trimming.lean b/Mathlib/Tactic/ComputeAsymptotics/Multiseries/Trimming.lean
new file mode 100644
index 00000000000000..15e00292ae25ca
--- /dev/null
+++ b/Mathlib/Tactic/ComputeAsymptotics/Multiseries/Trimming.lean
@@ -0,0 +1,140 @@
+/-
+Copyright (c) 2026 Vasilii Nesterov. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Vasilii Nesterov
+-/
+module
+
+public import Mathlib.Tactic.ComputeAsymptotics.Multiseries.Defs
+
+/-!
+# Trimming of multiseries
+
+A multiseries is *trimmed* when its leading coefficient (the head of its expansion) is itself
+trimmed and non-zero. For a trimmed multiseries, the leading monomial captures the main
+asymptotic behavior of the approximated function.
+
+## Main definitions
+
+* `IsZero`: a multiseries represents the zero function — it is either the real number `0`
+ (for the empty basis) or has an empty underlying sequence (`.nil`).
+* `Trimmed` and `Multiseries.Trimmed`: a multiseries is trimmed in the sense above. The former
+ is defined inductively for `MultiseriesExpansion`, and the latter for `Multiseries` is
+ derived from it.
+
+We also prove structural lemmas relating these predicates to `seq` and to the `cons`/`nil`
+constructors.
+
+-/
+
+@[expose] public section
+
+namespace Tactic.ComputeAsymptotics
+
+namespace MultiseriesExpansion
+
+open Filter Topology Stream'
+
+/-- A multiseries is zero if it is the real constant `0` or has an empty sequence. -/
+inductive IsZero : {basis : Basis} → MultiseriesExpansion basis → Prop
+| const {c : MultiseriesExpansion []} (hc : c.toReal = 0) : IsZero c
+| nil {basis_hd} {basis_tl} (f) : @IsZero (basis_hd :: basis_tl) (mk .nil f)
+
+namespace IsZero
+
+@[simp]
+theorem const_iff {c : MultiseriesExpansion []} : IsZero c ↔ c.toReal = 0 := by
+ constructor <;> grind [IsZero]
+
+@[simp]
+theorem iff_seq_eq_nil {basis_hd basis_tl} {ms : MultiseriesExpansion (basis_hd :: basis_tl)} :
+ IsZero ms ↔ ms.seq = .nil where
+ mp h := by cases h; rw [mk_seq]
+ mpr h := by
+ convert IsZero.nil ms.toFun
+ simp [h]
+
+theorem approximates_zero {basis : Basis} {ms : MultiseriesExpansion basis}
+ (h_zero : IsZero ms) (h_approx : ms.Approximates) :
+ ms.toFun =ᶠ[atTop] 0 := by
+ cases h_zero with
+ | const hc => simp [hc, Pi.zero_def]
+ | nil => simpa using h_approx
+
+theorem not_cons {basis_hd} {basis_tl} {exp : ℝ} {coef : MultiseriesExpansion basis_tl}
+ {tl : Multiseries basis_hd basis_tl} {f : ℝ → ℝ} :
+ ¬ @IsZero (basis_hd :: basis_tl) (mk (.cons exp coef tl) f) := by
+ simp
+
+end IsZero
+
+/-- We call a multiseries `Trimmed` if it is either a constant, `.nil`, or `cons (exp, coef) tl`
+where `coef` is trimmed and is not zero. Intuitively, when a multiseries is trimmed, its leading
+monomial gives the main asymptotic behavior of the approximated function. -/
+inductive Trimmed : {basis : Basis} → MultiseriesExpansion basis → Prop
+| const {c : ℝ} : @Trimmed [] c
+| nil {basis_hd} {basis_tl} {f} : @Trimmed (basis_hd :: basis_tl) (mk .nil f)
+| cons {basis_hd} {basis_tl} {exp : ℝ} {coef : MultiseriesExpansion basis_tl}
+ {tl : Multiseries basis_hd basis_tl} {f : ℝ → ℝ} (h_trimmed : coef.Trimmed)
+ (h_ne_zero : ¬ IsZero coef) :
+ @Trimmed (basis_hd :: basis_tl) (mk (.cons exp coef tl) f)
+
+/-- We call a `Multiseries` `Trimmed` if it is either `.nil` or `cons (exp, coef) tl` where `coef`
+is trimmed and is not zero. -/
+def Multiseries.Trimmed {basis_hd : ℝ → ℝ} {basis_tl : Basis}
+ (ms : Multiseries basis_hd basis_tl) : Prop :=
+ (mk ms 0).Trimmed
+
+theorem trimmed_iff_seq_trimmed {basis_hd : ℝ → ℝ} {basis_tl : Basis}
+ (ms : MultiseriesExpansion (basis_hd :: basis_tl)) :
+ ms.Trimmed ↔ ms.seq.Trimmed where
+ mp h := by
+ cases h <;> constructor <;> grind
+ mpr h := by
+ generalize hs : ms.seq = s at h
+ cases h with
+ | nil =>
+ convert Trimmed.nil (f := ms.toFun)
+ simp [hs]
+ | @cons _ _ exp coef tl _ h_trimmed h_ne_zero =>
+ convert Trimmed.cons h_trimmed h_ne_zero (exp := exp) (tl := tl) (f := ms.toFun)
+ simp only [ms_eq_mk_iff, hs, and_true]
+
+namespace Multiseries.Trimmed
+
+@[simp]
+theorem nil {basis_hd} {basis_tl} :
+ @Multiseries.Trimmed basis_hd basis_tl .nil := by
+ constructor
+
+theorem cons {basis_hd} {basis_tl} {exp : ℝ}
+ {coef : MultiseriesExpansion basis_tl} {tl : Multiseries basis_hd basis_tl}
+ (h_coef : coef.Trimmed) (h_ne_zero : ¬ IsZero coef) :
+ Multiseries.Trimmed (cons exp coef tl) :=
+ MultiseriesExpansion.Trimmed.cons h_coef h_ne_zero
+
+/-- If `cons (exp, coef) tl` is trimmed, then `coef` is trimmed and is not zero. -/
+theorem elim_cons {basis_hd} {basis_tl} {exp : ℝ}
+ {coef : MultiseriesExpansion basis_tl} {tl : Multiseries basis_hd basis_tl}
+ (h : Multiseries.Trimmed (.cons exp coef tl)) :
+ coef.Trimmed ∧ ¬ IsZero coef := by
+ generalize h_ms : Multiseries.cons exp coef tl = ms at h
+ cases h with
+ | nil => simp at h_ms
+ | cons h_trimmed h_ne_zero =>
+ simp at h_ms
+ grind
+
+end Multiseries.Trimmed
+
+/-- If `cons (exp, coef) tl` is trimmed, then `coef` is trimmed and is not zero. -/
+theorem elim_cons {basis_hd} {basis_tl} {exp : ℝ} {coef : MultiseriesExpansion basis_tl}
+ {tl : Multiseries basis_hd basis_tl} {f : ℝ → ℝ}
+ (h : Trimmed (mk (.cons exp coef tl) f)) :
+ coef.Trimmed ∧ ¬ IsZero coef := by
+ simp only [trimmed_iff_seq_trimmed, mk_seq] at h
+ exact h.elim_cons
+
+end MultiseriesExpansion
+
+end Tactic.ComputeAsymptotics
From 727fecc30e2196620e58b7cafed9e154826a76f7 Mon Sep 17 00:00:00 2001
From: Yongle Hu
Date: Tue, 16 Jun 2026 23:48:01 +0000
Subject: [PATCH 0087/1300] doc(RingTheory/DedekindDomain/LinearDisjoint): fix
typos in the module docstring (#40218)
Fix typos in the namespace in the module docstring.
---
Mathlib/RingTheory/DedekindDomain/LinearDisjoint.lean | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Mathlib/RingTheory/DedekindDomain/LinearDisjoint.lean b/Mathlib/RingTheory/DedekindDomain/LinearDisjoint.lean
index d504e598dc6aab..70e4e672eda398 100644
--- a/Mathlib/RingTheory/DedekindDomain/LinearDisjoint.lean
+++ b/Mathlib/RingTheory/DedekindDomain/LinearDisjoint.lean
@@ -18,8 +18,8 @@ and `Frac R` denotes the fraction field of a domain `R`.
## Main results and definitions
-* `FractionalIdeal.differentIdeal_eq_map_differentIdeal`: `𝓓(B/R₁) = 𝓓(R₂/A)`
-* `FractionalIdeal.differentIdeal_eq_differentIdeal_mul_differentIdeal_of_isCoprime`:
+* `IsDedekindDomain.differentIdeal_eq_map_differentIdeal`: `𝓓(B/R₁) = 𝓓(R₂/A)`
+* `IsDedekindDomain.differentIdeal_eq_differentIdeal_mul_differentIdeal_of_isCoprime`:
`𝓓(B/A) = 𝓓(R₁/A) * 𝓓(R₂/A)`.
* `Module.Basis.ofIsCoprimeDifferentIdeal`: Construct a `R₁`-basis of `B` by lifting an
`A`-basis of `R₂`.
From e5591f1062d09a4220a7f6ecc3b1456c29ffb894 Mon Sep 17 00:00:00 2001
From: Felix Pernegger <188575194+felixpernegger@users.noreply.github.com>
Date: Wed, 17 Jun 2026 00:02:22 +0000
Subject: [PATCH 0088/1300] chore(MeasureTheory/Integral/Lebesgue): remove an
`erw` (#40407)
Extracted from #40348
Co-authored-by: Batixx
---
Mathlib/MeasureTheory/Integral/Lebesgue/Countable.lean | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/Mathlib/MeasureTheory/Integral/Lebesgue/Countable.lean b/Mathlib/MeasureTheory/Integral/Lebesgue/Countable.lean
index d0833a711607b1..2d3d72d8434f76 100644
--- a/Mathlib/MeasureTheory/Integral/Lebesgue/Countable.lean
+++ b/Mathlib/MeasureTheory/Integral/Lebesgue/Countable.lean
@@ -238,7 +238,8 @@ theorem exists_pos_lintegral_lt_of_sigmaFinite (μ : Measure α) [SigmaFinite μ
have hN_meas : Measurable N := measurableSet_spanningSetsIndex μ
have hNs : ∀ n, N ⁻¹' {n} = s n := preimage_spanningSetsIndex_singleton μ
refine ⟨δ ∘ N, fun x => δpos _, measurable_from_nat.comp hN_meas, ?_⟩
- erw [lintegral_comp measurable_from_nat.coe_nnreal_ennreal hN_meas]
+ simp_rw [Function.comp_apply, ← Function.comp_apply (f := (fun n ↦ (↑(δ n) : ℝ≥0∞))),
+ lintegral_comp measurable_from_nat.coe_nnreal_ennreal hN_meas]
simpa [N, hNs, lintegral_countable', measurableSet_spanningSetsIndex, mul_comm] using δsum
omit [MeasurableSpace α]
From af5794b60dceddf115f1bc6869cf1669e291d0ec Mon Sep 17 00:00:00 2001
From: Sebastien Gouezel <10818434+sgouezel@users.noreply.github.com>
Date: Wed, 17 Jun 2026 00:02:24 +0000
Subject: [PATCH 0089/1300] chore: use more `inferInstanceAs` to define
instances on type synonyms (#40457)
Otherwise, the instance functions are not declared on the right space.
Co-authored-by: sgouezel
---
Mathlib/Topology/Defs/Basic.lean | 1 +
Mathlib/Topology/EMetricSpace/Weak.lean | 37 +++++++++++--------
.../Topology/VectorBundle/Constructions.lean | 6 ++-
3 files changed, 26 insertions(+), 18 deletions(-)
diff --git a/Mathlib/Topology/Defs/Basic.lean b/Mathlib/Topology/Defs/Basic.lean
index 75f02a3e86df4b..c2976b51382d49 100644
--- a/Mathlib/Topology/Defs/Basic.lean
+++ b/Mathlib/Topology/Defs/Basic.lean
@@ -68,6 +68,7 @@ universe u v
open Set
/-- A topology on `X`. -/
+@[to_dual_dont_translate]
class TopologicalSpace (X : Type u) where
/-- A predicate saying that a set is an open set. Use `IsOpen` in the root namespace instead. -/
protected IsOpen : Set X → Prop
diff --git a/Mathlib/Topology/EMetricSpace/Weak.lean b/Mathlib/Topology/EMetricSpace/Weak.lean
index 8b7d8de1b5a318..add0cc36ac9133 100644
--- a/Mathlib/Topology/EMetricSpace/Weak.lean
+++ b/Mathlib/Topology/EMetricSpace/Weak.lean
@@ -197,22 +197,25 @@ instance [EDist α] : EDist (WithTop α) where
| (x : α), ⊤ => ∞
| (x : α), (y : α) => edist x y
-/-- If `α` has a topology induced by a linear order in is a weak pseudo extended metric space,
-so if `WithTop α` -/
+/-- If `α` has a topology induced by a linear order and is a weak pseudo extended metric space,
+so is `WithTop α` -/
@[to_dual]
instance instWeakPseudoEMetricSpaceWithTop [m : WeakPseudoEMetricSpace α] :
WeakPseudoEMetricSpace (WithTop α) :=
- let : TopologicalSpace (Option α) := TopologicalSpace.instWithTopOfOrderTopology
- Option.WeakPseudoEMetricSpace.OfIsOpenEmbedding (inst := instEDistWithTop) rfl
+ letI : TopologicalSpace (Option α) := TopologicalSpace.instWithTopOfOrderTopology
+ letI : WeakPseudoEMetricSpace (Option α) :=
+ Option.WeakPseudoEMetricSpace.OfIsOpenEmbedding (inst := instEDistWithTop) rfl
WithTop.isOpenEmbedding_some
+ inferInstanceAs <| WeakPseudoEMetricSpace (Option α)
-/-- If `α` has a topology induced by a linear order in is a weak extended metric space,
-so if `WithTop α` -/
+/-- If `α` has a topology induced by a linear order and is a weak extended metric space,
+so is `WithTop α` -/
@[to_dual]
instance instWeakEMetricSpaceWithTop [m : WeakEMetricSpace α] : WeakEMetricSpace (WithTop α) :=
let : TopologicalSpace (Option α) := TopologicalSpace.instWithTopOfOrderTopology
- Option.WeakEMetricSpace.OfIsOpenEmbedding (inst := instEDistWithTop) rfl
- WithTop.isOpenEmbedding_some
+ let : WeakEMetricSpace (Option α) := Option.WeakEMetricSpace.OfIsOpenEmbedding
+ (inst := instEDistWithTop) rfl WithTop.isOpenEmbedding_some
+ inferInstanceAs <| WeakEMetricSpace (Option α)
open scoped OnePoint in
instance [EDist α] : EDist (OnePoint α) where
@@ -227,30 +230,32 @@ extended metric space. -/
instance instWeakPseudoEMetricSpaceOnePoint [m : WeakPseudoEMetricSpace α] :
WeakPseudoEMetricSpace (OnePoint α) :=
let : TopologicalSpace (Option α) := OnePoint.instTopologicalSpace
- Option.WeakPseudoEMetricSpace.OfIsOpenEmbedding (inst := instEDistOnePoint) rfl
- OnePoint.isOpenEmbedding_coe
+ let : WeakPseudoEMetricSpace (Option α) := Option.WeakPseudoEMetricSpace.OfIsOpenEmbedding
+ (inst := instEDistOnePoint) rfl OnePoint.isOpenEmbedding_coe
+ inferInstanceAs <| WeakPseudoEMetricSpace (Option α)
/-- The one point compactification of a weak extended metric space is again a weak extended metric
space. -/
instance instWeakEMetricSpaceOnePoint [m : WeakEMetricSpace α] :
WeakEMetricSpace (OnePoint α) :=
let : TopologicalSpace (Option α) := OnePoint.instTopologicalSpace
- Option.WeakEMetricSpace.OfIsOpenEmbedding (inst := instEDistOnePoint) rfl
- OnePoint.isOpenEmbedding_coe
+ let : WeakEMetricSpace (Option α) := Option.WeakEMetricSpace.OfIsOpenEmbedding
+ (inst := instEDistOnePoint) rfl OnePoint.isOpenEmbedding_coe
+ inferInstanceAs <| WeakEMetricSpace (Option α)
/-- `ℝ≥0∞` is a weak extended metric space with its usual distance function. -/
noncomputable instance instWeakEMetricSpaceENNReal : WeakEMetricSpace ℝ≥0∞ :=
- instWeakEMetricSpaceWithTop
+ inferInstanceAs <| WeakEMetricSpace (WithTop ℝ≥0)
/-- `EReal` is a weak extended metric space with its usual distance function. -/
noncomputable instance instWeakEMetricSpaceEReal : WeakEMetricSpace EReal :=
- instWeakEMetricSpaceWithBot
+ inferInstanceAs <| WeakEMetricSpace (WithBot (WithTop ℝ))
/-- `ℕ∞` is a weak extended metric space with its usual distance function. -/
noncomputable instance instWeakEMetricSpaceENat : WeakEMetricSpace ℕ∞ :=
- instWeakEMetricSpaceWithTop
+ inferInstanceAs <| WeakEMetricSpace (WithTop ℕ)
-theorem ENNReal.edist_eq_top_iff (a b : ℝ≥0∞) : edist a b = ⊤ ↔ a ≠ b ∧ (a = ⊤ ∨ b = ⊤) := by
+theorem ENNReal.edist_eq_top_iff (a b : ℝ≥0∞) : edist a b = ∞ ↔ a ≠ b ∧ (a = ∞ ∨ b = ∞) := by
cases a <;> cases b <;> simp only [ne_eq, not_true_eq_false, or_self, and_true, iff_false,
top_ne_coe, not_false_eq_true, coe_ne_top, or_false, and_self, or_true, and_self, iff_true,
coe_inj, and_false, iff_false]
diff --git a/Mathlib/Topology/VectorBundle/Constructions.lean b/Mathlib/Topology/VectorBundle/Constructions.lean
index ab64b02686f619..1ea7d3026627dc 100644
--- a/Mathlib/Topology/VectorBundle/Constructions.lean
+++ b/Mathlib/Topology/VectorBundle/Constructions.lean
@@ -180,10 +180,12 @@ section
variable (R 𝕜 : Type*) {B : Type*} (F : Type*) (E : B → Type*) {B' : Type*} (f : B' → B)
-instance [i : ∀ x : B, AddCommMonoid (E x)] (x : B') : AddCommMonoid ((f *ᵖ E) x) := i _
+instance [i : ∀ x : B, AddCommMonoid (E x)] (x : B') : AddCommMonoid ((f *ᵖ E) x) :=
+ inferInstanceAs <| AddCommMonoid (E (f x))
instance [Semiring R] [∀ x : B, AddCommMonoid (E x)] [i : ∀ x, Module R (E x)] (x : B') :
- Module R ((f *ᵖ E) x) := i _
+ Module R ((f *ᵖ E) x) :=
+ inferInstanceAs <| Module R (E (f x))
variable {E F} [TopologicalSpace B'] [TopologicalSpace (TotalSpace F E)] [NontriviallyNormedField 𝕜]
[NormedAddCommGroup F] [NormedSpace 𝕜 F] [TopologicalSpace B] [∀ x, AddCommMonoid (E x)]
From 4dbd0e30c53c18da017fbc60ce7609a22fdefb41 Mon Sep 17 00:00:00 2001
From: Moritz Doll <21366319+mcdoll@users.noreply.github.com>
Date: Wed, 17 Jun 2026 00:02:26 +0000
Subject: [PATCH 0090/1300] feat(Analysis): use `IsApply` for `TestFunction`
(#40608)
---
.../Analysis/Distribution/TestFunction.lean | 51 ++++++++++++-------
1 file changed, 32 insertions(+), 19 deletions(-)
diff --git a/Mathlib/Analysis/Distribution/TestFunction.lean b/Mathlib/Analysis/Distribution/TestFunction.lean
index 3134a5962eb12a..8ccaddbd709770 100644
--- a/Mathlib/Analysis/Distribution/TestFunction.lean
+++ b/Mathlib/Analysis/Distribution/TestFunction.lean
@@ -171,55 +171,68 @@ theorem coe_mk {f : E → F} {contDiff : ContDiff ℝ n f} {hasCompactSupport :
section AddCommGroup
-@[simps -fullyApplied]
instance : Zero 𝓓^{n}(Ω, F) where
zero := ⟨0, contDiff_zero_fun, .zero, by simp only [tsupport_zero, empty_subset]⟩
-@[simps -fullyApplied]
+instance : IsZeroApply 𝓓^{n}(Ω, F) E F where
+ zero_apply _ := rfl
+
+@[deprecated (since := "2026-06-15")] alias coe_zero := FunLike.coe_zero
+
instance : Add 𝓓^{n}(Ω, F) where
add f g := ⟨f + g, f.contDiff.add g.contDiff, f.hasCompactSupport.add g.hasCompactSupport,
tsupport_add f g |>.trans <| union_subset f.tsupport_subset g.tsupport_subset⟩
-@[simps -fullyApplied]
+instance : IsAddApply 𝓓^{n}(Ω, F) E F where
+ add_apply _ _ _ := rfl
+
+@[deprecated (since := "2026-06-15")] alias coe_add := FunLike.coe_add
+
instance : Neg 𝓓^{n}(Ω, F) where
neg f := ⟨-f, f.contDiff.neg, f.hasCompactSupport.neg, tsupport_neg f ▸ f.tsupport_subset⟩
-@[simps -fullyApplied]
+instance : IsNegApply 𝓓^{n}(Ω, F) E F where
+ neg_apply _ _ := rfl
+
+@[deprecated (since := "2026-06-15")] alias coe_neg := FunLike.coe_neg
+
instance : Sub 𝓓^{n}(Ω, F) where
sub f g := ⟨f - g, f.contDiff.sub g.contDiff, f.hasCompactSupport.sub g.hasCompactSupport,
tsupport_sub f g |>.trans <| union_subset f.tsupport_subset g.tsupport_subset⟩
-@[simps -fullyApplied]
+instance : IsSubApply 𝓓^{n}(Ω, F) E F where
+ sub_apply _ _ _ := rfl
+
+@[deprecated (since := "2026-06-15")] alias coe_sub := FunLike.coe_sub
+
instance {R} [Semiring R] [Module R F] [SMulCommClass ℝ R F] [ContinuousConstSMul R F] :
SMul R 𝓓^{n}(Ω, F) where
smul c f := ⟨c • f, f.contDiff.const_smul c, f.hasCompactSupport.smul_left,
tsupport_smul_subset_right _ _ |>.trans f.tsupport_subset⟩
-instance : AddCommGroup 𝓓^{n}(Ω, F) := fast_instance%
- DFunLike.coe_injective.addCommGroup _ rfl (fun _ _ ↦ rfl) (fun _ ↦ rfl) (fun _ _ ↦ rfl)
- (fun _ _ ↦ rfl) (fun _ _ ↦ rfl)
+instance {R} [Semiring R] [Module R F] [SMulCommClass ℝ R F] [ContinuousConstSMul R F] :
+ IsSMulApply R 𝓓^{n}(Ω, F) E F where
+ smul_apply _ _ _ := rfl
-variable (Ω F n) in
-/-- Coercion as an additive homomorphism. -/
-@[simps -fullyApplied]
-def coeFnAddMonoidHom : 𝓓^{n}(Ω, F) →+ E → F where
- toFun f := f
- map_zero' := coe_zero
- map_add' _ _ := rfl
+@[deprecated (since := "2026-06-15")] alias coe_smul := FunLike.coe_smul
+
+instance : AddCommGroup 𝓓^{n}(Ω, F) := fast_instance% FunLike.addCommGroup
+
+@[deprecated (since := "2026-06-15")] alias coeFnAddMonoidHom := FunLike.coeAddMonoidHom
+
+@[deprecated (since := "2026-06-15")] alias coeFnAddMonoidHom_apply := FunLike.coeAddMonoidHom_apply
end AddCommGroup
section Module
instance {R} [Semiring R] [Module R F] [SMulCommClass ℝ R F] [ContinuousConstSMul R F] :
- Module R 𝓓^{n}(Ω, F) := fast_instance%
- DFunLike.coe_injective.module R (coeFnAddMonoidHom Ω F n) fun _ _ ↦ rfl
+ Module R 𝓓^{n}(Ω, F) := fast_instance% FunLike.module
instance {R S} [Semiring R] [Semiring S] [Module R F] [Module S F] [SMulCommClass ℝ R F]
[SMulCommClass ℝ S F] [ContinuousConstSMul R F] [ContinuousConstSMul S F] [SMul R S]
[IsScalarTower R S F] :
- IsScalarTower R S 𝓓^{n}(Ω, F) where
- smul_assoc _ _ _ := by ext; simp
+ IsScalarTower R S 𝓓^{n}(Ω, F) := FunLike.isScalarTower
end Module
From c7b36bfb52039c09c9e29ab8612f378ba637537e Mon Sep 17 00:00:00 2001
From: "Yi.Yuan"
Date: Wed, 17 Jun 2026 00:34:42 +0000
Subject: [PATCH 0091/1300] chore(Topology/Instances/Complex): remove an erw
(#40321)
- rewrites the real subfield branch to use `ofRealHom.rangeRestrictField`, allowing the `hr` rewrite to be handled by `rw`
Extracted from #40147
---
Mathlib/Topology/Instances/Complex.lean | 16 +++++-----------
1 file changed, 5 insertions(+), 11 deletions(-)
diff --git a/Mathlib/Topology/Instances/Complex.lean b/Mathlib/Topology/Instances/Complex.lean
index cab97bbcf862d4..f476d7ae6403fc 100644
--- a/Mathlib/Topology/Instances/Complex.lean
+++ b/Mathlib/Topology/Instances/Complex.lean
@@ -69,23 +69,17 @@ theorem Complex.uniformContinuous_ringHom_eq_id_or_conj (K : Subfield ℂ) {ψ :
let j := RingEquiv.subfieldCongr h
-- ψ₁ is the continuous ring hom `ℝ →+* ℂ` constructed from `j : closure (K) ≃+* ℝ`
-- and `extψ : closure (K) →+* ℂ`
- let ψ₁ := RingHom.comp extψ (RingHom.comp j.symm.toRingHom ofRealHom.rangeRestrict)
+ let ψ₁ := RingHom.comp extψ (RingHom.comp j.symm.toRingHom ofRealHom.rangeRestrictField)
-- Porting note: was `by continuity!` and was used inline
have hψ₁ : Continuous ψ₁ := by
simpa only [RingHom.coe_comp] using! hψ.comp ((continuous_algebraMap ℝ ℂ).subtype_mk _)
ext1 x
- rsuffices ⟨r, hr⟩ : ∃ r : ℝ, ofRealHom.rangeRestrict r = j (ι x)
- · have :=
- RingHom.congr_fun (ringHom_eq_ofReal_of_continuous hψ₁) r
- rw [RingHom.comp_apply, RingHom.comp_apply] at this
- -- In `this`, the `DFunLike.coe` thinks it is applying a `(ℝ →+* ↥ofRealHom.fieldRange)`,
- -- while in `hr`, we have a `(ℝ →+* ↥ofRealHom.range)`.
- -- We could add a `@[simp]` lemma fixing this, but it breaks later steps of the proof.
- erw [hr] at this
- rw [RingEquiv.toRingHom_eq_coe] at this
+ rsuffices ⟨r, hr⟩ : ∃ r : ℝ, ofRealHom.rangeRestrictField r = j (ι x)
+ · have := RingHom.congr_fun (ringHom_eq_ofReal_of_continuous hψ₁) r
+ rw [RingHom.comp_apply, RingHom.comp_apply, hr, RingEquiv.toRingHom_eq_coe] at this
convert! this using 1
· exact (IsDenseInducing.extend_eq di hc.continuous _).symm
- · rw [← ofRealHom.coe_rangeRestrict, hr]
+ · rw [← ofRealHom.coe_rangeRestrictField, hr]
rfl
obtain ⟨r, hr⟩ := SetLike.coe_mem (j (ι x))
exact ⟨r, Subtype.ext hr⟩
From 749ea6a384276750699b6b7a8f50727132f4dc4c Mon Sep 17 00:00:00 2001
From: Moritz Doll <21366319+mcdoll@users.noreply.github.com>
Date: Wed, 17 Jun 2026 00:34:45 +0000
Subject: [PATCH 0092/1300] feat(LinearAlgebra): use `Is*Apply` for
`MultilinearMap` (#40466)
---
Mathlib/FieldTheory/Fixed.lean | 2 +-
Mathlib/FieldTheory/JacobsonNoether.lean | 2 +-
.../LinearAlgebra/Alternating/DomCoprod.lean | 12 ++--
Mathlib/LinearAlgebra/Multilinear/Basic.lean | 72 +++++++++----------
.../RootSystem/CartanMatrix.lean | 2 +-
.../LinearAlgebra/TensorPower/Pairing.lean | 9 +--
.../RingTheory/MatrixPolynomialAlgebra.lean | 9 +--
Mathlib/RingTheory/PicardGroup.lean | 2 +-
8 files changed, 53 insertions(+), 57 deletions(-)
diff --git a/Mathlib/FieldTheory/Fixed.lean b/Mathlib/FieldTheory/Fixed.lean
index bf68c06f4bfe70..aac5f0fa40dcf1 100644
--- a/Mathlib/FieldTheory/Fixed.lean
+++ b/Mathlib/FieldTheory/Fixed.lean
@@ -156,7 +156,7 @@ theorem linearIndependent_smul_of_linearIndependent {s : Finset F} :
refine (sum_attach s fun i ↦ (g • l i - l i) • MulAction.toFun G F i).trans ?_
ext g'
conv_lhs =>
- rw [sum_apply]
+ rw [Finset.sum_apply]
congr
· skip
· ext
diff --git a/Mathlib/FieldTheory/JacobsonNoether.lean b/Mathlib/FieldTheory/JacobsonNoether.lean
index 09527bb125b7de..a6f82c4e7180e1 100644
--- a/Mathlib/FieldTheory/JacobsonNoether.lean
+++ b/Mathlib/FieldTheory/JacobsonNoether.lean
@@ -97,7 +97,7 @@ lemma exist_pow_eq_zero_of_le (p : ℕ) [hchar : ExpChar D p]
have inter : (ad k D a)^[p ^ m] = 0 := by
ext x
rw [ad_eq_lmul_left_sub_lmul_right, ← Module.End.pow_apply, Pi.sub_apply,
- sub_pow_expChar_pow_of_commute p m (commute_mulLeft_right a a), sub_apply,
+ sub_pow_expChar_pow_of_commute p m (commute_mulLeft_right a a), LinearMap.sub_apply,
pow_mulLeft, mulLeft_apply, pow_mulRight, mulRight_apply, Pi.zero_apply,
Subring.mem_center_iff.1 hm.2 x]
exact sub_eq_zero_of_eq rfl
diff --git a/Mathlib/LinearAlgebra/Alternating/DomCoprod.lean b/Mathlib/LinearAlgebra/Alternating/DomCoprod.lean
index 5976c2a5dbd070..fb33822f9b1960 100644
--- a/Mathlib/LinearAlgebra/Alternating/DomCoprod.lean
+++ b/Mathlib/LinearAlgebra/Alternating/DomCoprod.lean
@@ -56,7 +56,7 @@ def domCoprod.summand (a : Mᵢ [⋀^ιa]→ₗ[R'] N₁) (b : Mᵢ [⋀^ιb]→
obtain ⟨⟨sl, sr⟩, h⟩ := H
ext v
simp only [MultilinearMap.domDomCongr_apply, MultilinearMap.domCoprod_apply,
- coe_multilinearMap, MultilinearMap.smul_apply]
+ coe_multilinearMap, _root_.smul_apply]
replace h := inv_mul_eq_iff_eq_mul.mp h.symm
have : Equiv.Perm.sign (σ₁ * Perm.sumCongrHom _ _ (sl, sr))
= Equiv.Perm.sign σ₁ * (Equiv.Perm.sign sl * Equiv.Perm.sign sr) := by simp
@@ -83,7 +83,7 @@ theorem domCoprod.summand_add_swap_smul_eq_zero (a : Mᵢ [⋀^ιa]→ₗ[R'] N
domCoprod.summand]
rw [smul_eq_mul, Perm.sign_mul, Perm.sign_swap hij]
simp only [one_mul, neg_mul, Function.comp_apply, Units.neg_smul, Perm.coe_mul,
- MultilinearMap.smul_apply, MultilinearMap.neg_apply, MultilinearMap.domDomCongr_apply,
+ _root_.smul_apply, _root_.neg_apply, MultilinearMap.domDomCongr_apply,
MultilinearMap.domCoprod_apply]
convert! add_neg_cancel (G := N₁ ⊗[R'] N₂) _ using 6 <;>
· ext k
@@ -96,7 +96,7 @@ theorem domCoprod.summand_eq_zero_of_smul_invariant (a : Mᵢ [⋀^ιa]→ₗ[R'
{i j : ιa ⊕ ιb} (hv : v i = v j) (hij : i ≠ j) :
swap i j • σ = σ → domCoprod.summand a b σ v = 0 := by
induction σ using Quotient.inductionOn' with | _ σ
- dsimp only [Quotient.liftOn'_mk'', Quotient.map'_mk'', MultilinearMap.smul_apply,
+ dsimp only [Quotient.liftOn'_mk'', Quotient.map'_mk'', _root_.smul_apply,
MultilinearMap.domDomCongr_apply, MultilinearMap.domCoprod_apply, domCoprod.summand]
intro hσ
obtain ⟨⟨sl, sr⟩, hσ⟩ := QuotientGroup.leftRel_apply.mp (Quotient.exact' hσ)
@@ -139,7 +139,7 @@ def domCoprod (a : Mᵢ [⋀^ιa]→ₗ[R'] N₁) (b : Mᵢ [⋀^ιb]→ₗ[R']
{ ∑ σ : Perm.ModSumCongr ιa ιb, domCoprod.summand a b σ with
toFun := fun v => (⇑(∑ σ : Perm.ModSumCongr ιa ιb, domCoprod.summand a b σ)) v
map_eq_zero_of_eq' := fun v i j hv hij => by
- rw [MultilinearMap.sum_apply]
+ rw [_root_.sum_apply]
exact
Finset.sum_involution (fun σ _ => Equiv.swap i j • σ)
(fun σ _ => domCoprod.summand_add_swap_smul_eq_zero a b σ hv hij)
@@ -163,11 +163,11 @@ def domCoprod' :
fun c m n => ?_ <;>
· ext
simp only [domCoprod_apply, add_apply, smul_apply, ← Finset.sum_add_distrib,
- Finset.smul_sum, MultilinearMap.sum_apply, domCoprod.summand]
+ Finset.smul_sum, _root_.sum_apply, domCoprod.summand]
congr
ext σ
induction σ using Quotient.inductionOn'
- simp only [Quotient.liftOn'_mk'', coe_add, coe_smul, MultilinearMap.smul_apply,
+ simp only [Quotient.liftOn'_mk'', coe_add, coe_smul, _root_.smul_apply,
← MultilinearMap.domCoprod'_apply]
simp only [TensorProduct.add_tmul, ← TensorProduct.smul_tmul', TensorProduct.tmul_add,
TensorProduct.tmul_smul, map_add, map_smul]
diff --git a/Mathlib/LinearAlgebra/Multilinear/Basic.lean b/Mathlib/LinearAlgebra/Multilinear/Basic.lean
index 81ed0952bd1bc5..890dda5223e163 100644
--- a/Mathlib/LinearAlgebra/Multilinear/Basic.lean
+++ b/Mathlib/LinearAlgebra/Multilinear/Basic.lean
@@ -6,9 +6,12 @@ Authors: Sébastien Gouëzel
module
public import Mathlib.Algebra.BigOperators.Group.Finset.Powerset
+public import Mathlib.Algebra.BigOperators.Pi
public import Mathlib.Data.Finset.Sort
public import Mathlib.Data.Fintype.BigOperators
public import Mathlib.Data.Fintype.Powerset
+public import Mathlib.Data.FunLike.Group
+public import Mathlib.Data.FunLike.Module
public import Mathlib.LinearAlgebra.Pi
public import Mathlib.Logic.Equiv.Fintype
public import Mathlib.Tactic.Abel
@@ -183,19 +186,21 @@ instance : Add (MultilinearMap R M₁ M₂) :=
⟨fun x => f x + f' x, fun m i x y => by simp [add_left_comm, add_assoc], fun m i c x => by
simp [smul_add]⟩⟩
-@[simp]
-theorem add_apply (m : ∀ i, M₁ i) : (f + f') m = f m + f' m :=
- rfl
+instance : IsAddApply (MultilinearMap R M₁ M₂) (∀ i, M₁ i) M₂ where
+ add_apply _ _ _ := rfl
+
+@[deprecated (since := "2026-06-10")] protected alias add_apply := add_apply
instance : Zero (MultilinearMap R M₁ M₂) :=
⟨⟨fun _ => 0, fun _ _ _ _ => by simp, fun _ _ c _ => by simp⟩⟩
+instance : IsZeroApply (MultilinearMap R M₁ M₂) (∀ i, M₁ i) M₂ where
+ zero_apply _ := rfl
+
instance : Inhabited (MultilinearMap R M₁ M₂) :=
⟨0⟩
-@[simp]
-theorem zero_apply (m : ∀ i, M₁ i) : (0 : MultilinearMap R M₁ M₂) m = 0 :=
- rfl
+@[deprecated (since := "2026-06-10")] protected alias zero_apply := zero_apply
section SMul
@@ -206,32 +211,28 @@ instance : SMul S (MultilinearMap R M₁ M₂) :=
⟨fun m => c • f m, fun m i x y => by simp [smul_add], fun l i x d => by
simp [← smul_comm x c (_ : M₂)]⟩⟩
-@[simp]
-theorem smul_apply (f : MultilinearMap R M₁ M₂) (c : S) (m : ∀ i, M₁ i) : (c • f) m = c • f m :=
- rfl
+instance : IsSMulApply S (MultilinearMap R M₁ M₂) (∀ i, M₁ i) M₂ where
+ smul_apply _ _ _ := rfl
+
+@[deprecated (since := "2026-06-10")] protected alias smul_apply := smul_apply
-theorem coe_smul (c : S) (f : MultilinearMap R M₁ M₂) : ⇑(c • f) = c • (⇑f) := rfl
+@[deprecated (since := "2026-06-10")] alias coe_smul := FunLike.coe_smul
end SMul
-- The `AddMonoid` instance exists to help speedup unification
-instance : AddMonoid (MultilinearMap R M₁ M₂) := fast_instance%
- coe_injective.addMonoid _ rfl (fun _ _ => rfl) fun _ _ => rfl
+instance : AddMonoid (MultilinearMap R M₁ M₂) := fast_instance% FunLike.addMonoid
instance addCommMonoid : AddCommMonoid (MultilinearMap R M₁ M₂) := fast_instance%
- coe_injective.addCommMonoid _ rfl (fun _ _ => rfl) fun _ _ => rfl
+ FunLike.addCommMonoid
-/-- Coercion of a multilinear map to a function as an additive monoid homomorphism. -/
-@[simps] def coeAddMonoidHom : MultilinearMap R M₁ M₂ →+ (((i : ι) → M₁ i) → M₂) where
- toFun := DFunLike.coe; map_zero' := rfl; map_add' _ _ := rfl
+@[deprecated (since := "2026-06-10")] alias coeAddMonoidHom := FunLike.coeAddMonoidHom
-@[simp]
-theorem coe_sum {α : Type*} (f : α → MultilinearMap R M₁ M₂) (s : Finset α) :
- ⇑(∑ a ∈ s, f a) = ∑ a ∈ s, ⇑(f a) :=
- map_sum coeAddMonoidHom f s
+@[deprecated (since := "2026-06-10")] alias coeAddMonoidHom_apply := FunLike.coeAddMonoidHom_apply
+
+@[deprecated (since := "2026-06-10")] alias coe_sum := FunLike.coe_sum
-theorem sum_apply {α : Type*} (f : α → MultilinearMap R M₁ M₂) (m : ∀ i, M₁ i) {s : Finset α} :
- (∑ a ∈ s, f a) m = ∑ a ∈ s, f a m := by simp
+@[deprecated (since := "2026-06-10")] protected alias sum_apply := _root_.sum_apply
/-- If `f` is a multilinear map, then `f.toLinearMap m i` is the linear map obtained by fixing all
coordinates but `i` equal to those of `m`, and varying the `i`-th coordinate. -/
@@ -885,8 +886,7 @@ variable [Semiring R] [(i : ι) → AddCommMonoid (M₁ i)] [(i : ι) → Module
[AddCommMonoid M₂] [Module R M₂]
instance [Monoid S] [DistribMulAction S M₂] [SMulCommClass R S M₂] :
- DistribMulAction S (MultilinearMap R M₁ M₂) := fast_instance%
- coe_injective.distribMulAction coeAddMonoidHom fun _ _ ↦ rfl
+ DistribMulAction S (MultilinearMap R M₁ M₂) := fast_instance% FunLike.distribMulAction
section Module
@@ -895,10 +895,10 @@ variable [Semiring S] [Module S M₂] [SMulCommClass R S M₂]
/-- The space of multilinear maps over an algebra over `R` is a module over `R`, for the pointwise
addition and scalar multiplication. -/
instance : Module S (MultilinearMap R M₁ M₂) := fast_instance%
- coe_injective.module _ coeAddMonoidHom fun _ _ ↦ rfl
+ FunLike.module
instance [Module.IsTorsionFree S M₂] : Module.IsTorsionFree S (MultilinearMap R M₁ M₂) :=
- coe_injective.moduleIsTorsionFree _ coe_smul
+ coe_injective.moduleIsTorsionFree _ FunLike.coe_smul
variable [AddCommMonoid M₃] [Module S M₃] [Module R M₃] [SMulCommClass R S M₃]
@@ -1273,7 +1273,7 @@ theorem mkPiRing_eq_iff [Fintype ι] {z₁ z₂ : M₂} :
· simp [h]
theorem mkPiRing_zero [Fintype ι] : MultilinearMap.mkPiRing R ι (0 : M₂) = 0 := by
- ext; rw [mkPiRing_apply, smul_zero, MultilinearMap.zero_apply]
+ ext; rw [mkPiRing_apply, smul_zero, zero_apply]
theorem mkPiRing_eq_zero_iff [Fintype ι] (z : M₂) : MultilinearMap.mkPiRing R ι z = 0 ↔ z = 0 := by
rw [← mkPiRing_zero, mkPiRing_eq_iff]
@@ -1288,9 +1288,10 @@ variable [Semiring R] [∀ i, AddCommMonoid (M₁ i)] [AddCommGroup M₂] [∀ i
instance : Neg (MultilinearMap R M₁ M₂) :=
⟨fun f => ⟨fun m => -f m, fun m i x y => by simp [add_comm], fun m i c x => by simp⟩⟩
-@[simp]
-theorem neg_apply (m : ∀ i, M₁ i) : (-f) m = -f m :=
- rfl
+instance : IsNegApply (MultilinearMap R M₁ M₂) (∀ i, M₁ i) M₂ where
+ neg_apply _ _ := rfl
+
+@[deprecated (since := "2026-06-10")] protected alias neg_apply := neg_apply
instance : Sub (MultilinearMap R M₁ M₂) :=
⟨fun f g =>
@@ -1299,13 +1300,12 @@ instance : Sub (MultilinearMap R M₁ M₂) :=
abel,
fun m i c x => by simp only [MultilinearMap.map_update_smul, smul_sub]⟩⟩
-@[simp]
-theorem sub_apply (m : ∀ i, M₁ i) : (f - g) m = f m - g m :=
- rfl
+instance : IsSubApply (MultilinearMap R M₁ M₂) (∀ i, M₁ i) M₂ where
+ sub_apply _ _ _ := rfl
+
+@[deprecated (since := "2026-06-10")] protected alias sub_apply := sub_apply
-instance : AddCommGroup (MultilinearMap R M₁ M₂) := fast_instance%
- coe_injective.addCommGroup _ rfl (fun _ _ => rfl) (fun _ => rfl) (fun _ _ => rfl)
- (fun _ _ => rfl) (fun _ _ => rfl)
+instance : AddCommGroup (MultilinearMap R M₁ M₂) := fast_instance% FunLike.addCommGroup
end RangeAddCommGroup
diff --git a/Mathlib/LinearAlgebra/RootSystem/CartanMatrix.lean b/Mathlib/LinearAlgebra/RootSystem/CartanMatrix.lean
index 8b913425fb324e..f3782786f24566 100644
--- a/Mathlib/LinearAlgebra/RootSystem/CartanMatrix.lean
+++ b/Mathlib/LinearAlgebra/RootSystem/CartanMatrix.lean
@@ -184,7 +184,7 @@ lemma cartanMatrix_mul_diagonal_eq [Fintype ι] [DecidableEq ι] [P.IsRootSystem
(2 : ℤ) • (P.posRootForm ℤ).posForm.toMatrix b.toWeightBasisInt := by
ext i j
apply algebraMap_injective ℤ R
- simp only [mul_diagonal, map_mul, algebraMap_rootFormIn, posRootForm_eq, smul_apply,
+ simp only [mul_diagonal, map_mul, algebraMap_rootFormIn, posRootForm_eq, Matrix.smul_apply,
LinearMap.BilinForm.toMatrix_apply, Int.zsmul_eq_mul]
simpa [← algebraMap_pairingIn P ℤ i j] using
congr_fun₂ (cartanMatrixIn_mul_diagonal_eq ℤ P.toInvariantForm b) i j
diff --git a/Mathlib/LinearAlgebra/TensorPower/Pairing.lean b/Mathlib/LinearAlgebra/TensorPower/Pairing.lean
index 18816895d34cee..891b9cc7a678ba 100644
--- a/Mathlib/LinearAlgebra/TensorPower/Pairing.lean
+++ b/Mathlib/LinearAlgebra/TensorPower/Pairing.lean
@@ -43,15 +43,10 @@ noncomputable def multilinearMapToDual :
(MultilinearMap.compLinearMap (MultilinearMap.mkPiRing R (Fin n) 1) f)
map_update_add' := fun f i φ₁ φ₂ ↦ by
ext v
- dsimp
- simp only [lift.tprod, MultilinearMap.compLinearMap_apply, this,
- LinearMap.add_apply, MultilinearMap.map_update_add]
+ simp [this]
map_update_smul' := fun f i a φ ↦ by
ext v
- dsimp
- simp only [lift.tprod, MultilinearMap.compLinearMap_apply, this,
- LinearMap.smul_apply, MultilinearMap.map_update_smul]
- dsimp }
+ simp [this, Finset.prod_update_of_mem, Semigroup.mul_assoc] }
variable {R M n} in
@[simp]
diff --git a/Mathlib/RingTheory/MatrixPolynomialAlgebra.lean b/Mathlib/RingTheory/MatrixPolynomialAlgebra.lean
index 23c09d35b90c41..49eab5fdaf1b32 100644
--- a/Mathlib/RingTheory/MatrixPolynomialAlgebra.lean
+++ b/Mathlib/RingTheory/MatrixPolynomialAlgebra.lean
@@ -87,7 +87,7 @@ theorem matPolyEquiv_coeff_apply_aux_2 (i j : n) (p : R[X]) (k : ℕ) :
refine Polynomial.induction_on' p ?_ ?_
· intro p q hp hq
ext
- simp [hp, hq, coeff_add, add_apply, single_add]
+ simp [hp, hq, coeff_add, Matrix.add_apply, single_add]
· intro k x
simp only [matPolyEquiv_coeff_apply_aux_1, coeff_monomial]
split_ifs <;>
@@ -118,8 +118,9 @@ theorem matPolyEquiv_symm_apply_coeff (p : (Matrix n n R)[X]) (i j : n) (k : ℕ
theorem matPolyEquiv_smul_one (p : R[X]) :
matPolyEquiv (p • (1 : Matrix n n R[X])) = p.map (algebraMap R (Matrix n n R)) := by
ext m i j
- simp only [matPolyEquiv_coeff_apply, smul_apply, one_apply, smul_eq_mul, mul_ite, mul_one,
- mul_zero, coeff_map, algebraMap_matrix_apply, Algebra.algebraMap_self, RingHom.id_apply]
+ simp only [matPolyEquiv_coeff_apply, Matrix.smul_apply, Matrix.one_apply, smul_eq_mul, mul_ite,
+ mul_one, mul_zero, coeff_map, algebraMap_matrix_apply, Algebra.algebraMap_self,
+ RingHom.id_apply]
split_ifs <;> simp
@[simp]
@@ -153,7 +154,7 @@ theorem support_subset_support_matPolyEquiv (m : Matrix n n R[X]) (i j : n) :
contrapose
simp only [notMem_support_iff]
intro hk
- rw [← matPolyEquiv_coeff_apply, hk, zero_apply]
+ rw [← matPolyEquiv_coeff_apply, hk, Matrix.zero_apply]
theorem eval_det {R : Type*} [CommRing R] (M : Matrix n n R[X]) (r : R) :
Polynomial.eval r M.det = (Polynomial.eval (scalar n r) (matPolyEquiv M)).det := by
diff --git a/Mathlib/RingTheory/PicardGroup.lean b/Mathlib/RingTheory/PicardGroup.lean
index 41ce2bf1b54d93..fa191bb3509d09 100644
--- a/Mathlib/RingTheory/PicardGroup.lean
+++ b/Mathlib/RingTheory/PicardGroup.lean
@@ -648,7 +648,7 @@ private theorem projective_units_and_mul'_comp_lTensor_bijective (I : (Submodule
exact LinearEquiv.ofInjective_symm_apply ..
let g : (S → R) →ₗ[R] I := .lsum _ _ ℕ fun i ↦ .toSpanSingleton _ _ ⟨b i, hT' <| hb i⟩
have hgf : g ∘ₗ f = .id := LinearMap.ext fun x ↦ Subtype.ext <| by
- simp only [g, lsum_apply, comp_apply, sum_apply, toSpanSingleton_apply, proj_apply]
+ simp only [g, lsum_apply, comp_apply, LinearMap.sum_apply, toSpanSingleton_apply, proj_apply]
simp_rw [coe_sum, coe_smul, Algebra.smul_def, hf, mul_assoc, ← Finset.mul_sum,
Algebra.smul_mul_assoc, eq, (Finset.sum_coe_sort ..).trans hr.2, mul_one, id_apply]
set m := mul' R A ∘ₗ I.1.subtype.lTensor A
From 800238935b9c4b495da5771a0dac0db5549b26ab Mon Sep 17 00:00:00 2001
From: Moritz Doll <21366319+mcdoll@users.noreply.github.com>
Date: Wed, 17 Jun 2026 00:34:47 +0000
Subject: [PATCH 0093/1300] feat(Analysis): use `IsApply` for
`ContDiffMapSupportedIn` (#40607)
---
.../Distribution/ContDiffMapSupportedIn.lean | 64 +++++++++++--------
1 file changed, 36 insertions(+), 28 deletions(-)
diff --git a/Mathlib/Analysis/Distribution/ContDiffMapSupportedIn.lean b/Mathlib/Analysis/Distribution/ContDiffMapSupportedIn.lean
index d46a03d077e0d9..d3a90374eea707 100644
--- a/Mathlib/Analysis/Distribution/ContDiffMapSupportedIn.lean
+++ b/Mathlib/Analysis/Distribution/ContDiffMapSupportedIn.lean
@@ -195,63 +195,70 @@ theorem coe_toBoundedContinuousFunction (f : 𝓓^{n}_{K}(E, F)) :
section AddCommGroup
-@[simps -fullyApplied]
instance : Zero 𝓓^{n}_{K}(E, F) where
zero := .mk 0 contDiff_zero_fun fun _ _ ↦ rfl
-@[simps -fullyApplied]
+instance : IsZeroApply 𝓓^{n}_{K}(E, F) E F where
+ zero_apply _ := rfl
+
+@[deprecated (since := "2026-06-15")] alias coe_zero := FunLike.coe_zero
+
instance : Add 𝓓^{n}_{K}(E, F) where
add f g := .mk (f + g) (f.contDiff.add g.contDiff) <| by
rw [← add_zero 0]
exact f.zero_on_compl.comp_left₂ g.zero_on_compl
-@[simps -fullyApplied]
+instance : IsAddApply 𝓓^{n}_{K}(E, F) E F where
+ add_apply _ _ _ := rfl
+
+@[deprecated (since := "2026-06-15")] alias coe_add := FunLike.coe_add
+
instance : Neg 𝓓^{n}_{K}(E, F) where
neg f := .mk (-f) (f.contDiff.neg) <| by
rw [← neg_zero]
exact f.zero_on_compl.comp_left
-@[simps -fullyApplied]
+instance : IsNegApply 𝓓^{n}_{K}(E, F) E F where
+ neg_apply _ _ := rfl
+
+@[deprecated (since := "2026-06-15")] alias coe_neg := FunLike.coe_neg
+
instance instSub : Sub 𝓓^{n}_{K}(E, F) where
sub f g := .mk (f - g) (f.contDiff.sub g.contDiff) <| by
rw [← sub_zero 0]
exact f.zero_on_compl.comp_left₂ g.zero_on_compl
-@[simps -fullyApplied]
+instance : IsSubApply 𝓓^{n}_{K}(E, F) E F where
+ sub_apply _ _ _ := rfl
+
+@[deprecated (since := "2026-06-15")] alias coe_sub := FunLike.coe_sub
+
instance instSMul {R} [Semiring R] [Module R F] [SMulCommClass ℝ R F] [ContinuousConstSMul R F] :
SMul R 𝓓^{n}_{K}(E, F) where
smul c f := .mk (c • (f : E → F)) (f.contDiff.const_smul c) <| by
rw [← smul_zero c]
exact f.zero_on_compl.comp_left
-instance : AddCommGroup 𝓓^{n}_{K}(E, F) := fast_instance%
- DFunLike.coe_injective.addCommGroup _ rfl (fun _ _ ↦ rfl) (fun _ ↦ rfl) (fun _ _ ↦ rfl)
- (fun _ _ ↦ rfl) fun _ _ ↦ rfl
+instance {R} [Semiring R] [Module R F] [SMulCommClass ℝ R F] [ContinuousConstSMul R F] :
+ IsSMulApply R 𝓓^{n}_{K}(E, F) E F where
+ smul_apply _ _ _ := rfl
-variable (E F K n)
+@[deprecated (since := "2026-06-15")] alias coe_smul := FunLike.coe_smul
-/-- Coercion as an additive homomorphism. -/
-def coeHom : 𝓓^{n}_{K}(E, F) →+ E → F where
- toFun f := f
- map_zero' := coe_zero
- map_add' _ _ := rfl
+instance : AddCommGroup 𝓓^{n}_{K}(E, F) := fast_instance% FunLike.addCommGroup
-variable {E F}
+@[deprecated (since := "2026-06-15")] alias coeHom := FunLike.coeAddMonoidHom
-theorem coe_coeHom : (coeHom E F n K : 𝓓^{n}_{K}(E, F) → E → F) = DFunLike.coe :=
- rfl
+@[deprecated (since := "2026-06-15")] alias coe_coeHom := FunLike.coe_coeAddMonoidHom
-theorem coeHom_injective : Function.Injective (coeHom E F n K) := by
- rw [coe_coeHom]
- exact DFunLike.coe_injective
+@[deprecated (since := "2026-06-15")] alias coeHom_injective := FunLike.coeAddMonoidHom_injective
end AddCommGroup
section Module
instance {R} [Semiring R] [Module R F] [SMulCommClass ℝ R F] [ContinuousConstSMul R F] :
- Module R 𝓓^{n}_{K}(E, F) := fast_instance%
- (coeHom_injective n K).module R (coeHom E F n K) fun _ _ ↦ rfl
+ Module R 𝓓^{n}_{K}(E, F) := fast_instance% FunLike.module
end Module
@@ -384,13 +391,14 @@ noncomputable def fderivLM :
· have hk' : 0 < (n : ℕ∞ω) := mod_cast (add_pos_of_right zero_lt_one k).trans_le hk
ext
simp [fderiv_add (f.contDiff.differentiable hk'.ne').differentiableAt
- (g.contDiff.differentiable hk'.ne').differentiableAt]
+ (g.contDiff.differentiable hk'.ne').differentiableAt, FunLike.coe_add]
· simp
map_smul' c f := by
split_ifs with hk
· have hk' : 0 < (n : ℕ∞ω) := mod_cast (add_pos_of_right zero_lt_one k).trans_le hk
ext
- simp [fderiv_const_smul (f.contDiff.differentiable hk'.ne').differentiableAt]
+ simp [fderiv_const_smul (f.contDiff.differentiable hk'.ne').differentiableAt,
+ FunLike.coe_smul]
· simp
@[simp]
@@ -440,13 +448,13 @@ noncomputable def iteratedFDerivLM (i : ℕ) :
split_ifs with hi
· have hi' : (i : ℕ∞ω) ≤ n := mod_cast (le_of_add_le_right hi)
ext
- simp [iteratedFDeriv_add (f.contDiff.of_le hi') (g.contDiff.of_le hi')]
+ simp [iteratedFDeriv_add (f.contDiff.of_le hi') (g.contDiff.of_le hi'), FunLike.coe_add]
· simp
map_smul' c f := by
split_ifs with hi
· have hi' : (i : ℕ∞ω) ≤ n := mod_cast (le_of_add_le_right hi)
ext
- simp [iteratedFDeriv_const_smul_apply (f.contDiff.of_le hi').contDiffAt]
+ simp [iteratedFDeriv_const_smul_apply (f.contDiff.of_le hi').contDiffAt, FunLike.coe_smul]
· simp
@[simp]
@@ -919,12 +927,12 @@ noncomputable def integralAgainstBilinLM (B : F₁ →L[𝕜] F₂ →L[𝕜] F
if IntegrableOn φ K μ then ∫ x, B (f x) (φ x) ∂μ else 0
map_add' f g := by
split_ifs with hφ
- · simp_rw [coe_add, Pi.add_apply, map_add, add_apply,
+ · simp_rw [add_apply, map_add, add_apply,
integral_add (f.integrable_bilin B hφ) (g.integrable_bilin B hφ)]
· simp
map_smul' c f := by
split_ifs with hφ
- · simp_rw [coe_smul, Pi.smul_apply, map_smul, smul_apply, integral_smul c, RingHom.id_apply]
+ · simp_rw [smul_apply, map_smul, smul_apply, integral_smul c, RingHom.id_apply]
· simp
@[simp]
From c5752439da3c6b206cce0b7f8798a02609bc3614 Mon Sep 17 00:00:00 2001
From: Felix Pernegger <188575194+felixpernegger@users.noreply.github.com>
Date: Wed, 17 Jun 2026 02:55:50 +0000
Subject: [PATCH 0094/1300] chore(MeasureTheory/Measure/NullMeasurable): remove
some defEq abuse (#40647)
Co-authored-by: Batixx
---
.../MeasureTheory/Measure/NullMeasurable.lean | 27 ++++++++++++-------
1 file changed, 17 insertions(+), 10 deletions(-)
diff --git a/Mathlib/MeasureTheory/Measure/NullMeasurable.lean b/Mathlib/MeasureTheory/Measure/NullMeasurable.lean
index a837c37899a151..3f645566c47cc3 100644
--- a/Mathlib/MeasureTheory/Measure/NullMeasurable.lean
+++ b/Mathlib/MeasureTheory/Measure/NullMeasurable.lean
@@ -97,6 +97,10 @@ def NullMeasurableSet [MeasurableSpace α] (s : Set α)
theorem _root_.MeasurableSet.nullMeasurableSet (h : MeasurableSet s) : NullMeasurableSet s μ :=
h.eventuallyMeasurableSet
+theorem _root_.MeasureTheory.nullMeasurableSet_iff_eventuallyMeasurableSet (s : Set α) :
+ NullMeasurableSet s μ ↔ EventuallyMeasurableSet m0 (ae μ) s :=
+ Iff.rfl
+
theorem nullMeasurableSet_empty : NullMeasurableSet ∅ μ :=
MeasurableSet.empty
@@ -122,9 +126,9 @@ theorem compl_iff : NullMeasurableSet sᶜ μ ↔ NullMeasurableSet s μ :=
theorem of_subsingleton [Subsingleton α] : NullMeasurableSet s μ :=
Subsingleton.measurableSet
-set_option backward.isDefEq.respectTransparency false in
-protected theorem congr (hs : NullMeasurableSet s μ) (h : s =ᵐ[μ] t) : NullMeasurableSet t μ :=
- EventuallyMeasurableSet.congr hs h.symm
+protected theorem congr (hs : NullMeasurableSet s μ) (h : s =ᵐ[μ] t) : NullMeasurableSet t μ := by
+ rw [nullMeasurableSet_iff_eventuallyMeasurableSet]
+ exact EventuallyMeasurableSet.congr hs h.symm
@[measurability]
protected theorem iUnion {ι : Sort*} [Countable ι] {s : ι → Set α}
@@ -379,7 +383,7 @@ end
section NullMeasurable
-variable [MeasurableSpace α] [MeasurableSpace β] [MeasurableSpace γ] {f : α → β} {μ : Measure α}
+variable [m : MeasurableSpace α] [MeasurableSpace β] [MeasurableSpace γ] {f : α → β} {μ : Measure α}
/-- A function `f : α → β` is null measurable if the preimage of a measurable set is a null
measurable set.
@@ -389,6 +393,9 @@ the σ-algebra on the codomain is countably generated, but stronger in general.
def NullMeasurable (f : α → β) (μ : Measure α := by volume_tac) : Prop :=
∀ ⦃s : Set β⦄, MeasurableSet s → NullMeasurableSet (f ⁻¹' s) μ
+theorem _root_.MeasureTheory.nullMeasurable_iff_eventuallyMeasurable (f : α → β) :
+ NullMeasurable f μ ↔ EventuallyMeasurable m (ae μ) f := by rfl
+
protected theorem _root_.Measurable.nullMeasurable (h : Measurable f) : NullMeasurable f μ :=
h.eventuallyMeasurable
@@ -396,15 +403,15 @@ protected theorem NullMeasurable.measurable' (h : NullMeasurable f μ) :
@Measurable (NullMeasurableSpace α μ) β _ _ f :=
h
-set_option backward.isDefEq.respectTransparency false in
theorem Measurable.comp_nullMeasurable {g : β → γ} (hg : Measurable g) (hf : NullMeasurable f μ) :
- NullMeasurable (g ∘ f) μ :=
- hg.comp_eventuallyMeasurable hf
+ NullMeasurable (g ∘ f) μ := by
+ rw [nullMeasurable_iff_eventuallyMeasurable]
+ exact hg.comp_eventuallyMeasurable hf
-set_option backward.isDefEq.respectTransparency false in
theorem NullMeasurable.congr {g : α → β} (hf : NullMeasurable f μ) (hg : f =ᵐ[μ] g) :
- NullMeasurable g μ :=
- EventuallyMeasurable.congr hf hg.symm
+ NullMeasurable g μ := by
+ rw [nullMeasurable_iff_eventuallyMeasurable]
+ exact EventuallyMeasurable.congr hf hg.symm
end NullMeasurable
From 6a6c957ae0220fbd3e6313d9116a2cbd46bab879 Mon Sep 17 00:00:00 2001
From: Stefan Kebekus <5110976+kebekus@users.noreply.github.com>
Date: Wed, 17 Jun 2026 04:02:27 +0000
Subject: [PATCH 0095/1300] feat: integral presentation of the proximity
function of value distribution theory (#38500)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
If `f : ℂ → ℂ` is meromorphic, establish a presentation of the proximity function `proximity f ⊤` as iterated circle averages. This statement can be used to compare the proximity- and logarithmic counting functions, and is one of the key ingredients in the proof of Cartan's classic formula for the characteristic function.
This is the first section of a multi-part PR, establishing Cartan's formula.
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
---
Mathlib.lean | 1 +
.../Proximity/IntegralPresentation.lean | 201 ++++++++++++++++++
Mathlib/Analysis/Meromorphic/Basic.lean | 2 +-
.../MeasureTheory/Integral/Bochner/Basic.lean | 14 ++
Mathlib/MeasureTheory/Integral/Prod.lean | 9 +
5 files changed, 226 insertions(+), 1 deletion(-)
create mode 100644 Mathlib/Analysis/Complex/ValueDistribution/Proximity/IntegralPresentation.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index c91c39aba616da..5be51fc0034ca1 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -1912,6 +1912,7 @@ public import Mathlib.Analysis.Complex.ValueDistribution.FirstMainTheorem
public import Mathlib.Analysis.Complex.ValueDistribution.LogCounting.Asymptotic
public import Mathlib.Analysis.Complex.ValueDistribution.LogCounting.Basic
public import Mathlib.Analysis.Complex.ValueDistribution.Proximity.Basic
+public import Mathlib.Analysis.Complex.ValueDistribution.Proximity.IntegralPresentation
public import Mathlib.Analysis.ConstantSpeed
public import Mathlib.Analysis.Convex.AmpleSet
public import Mathlib.Analysis.Convex.Approximation
diff --git a/Mathlib/Analysis/Complex/ValueDistribution/Proximity/IntegralPresentation.lean b/Mathlib/Analysis/Complex/ValueDistribution/Proximity/IntegralPresentation.lean
new file mode 100644
index 00000000000000..4652faec23121d
--- /dev/null
+++ b/Mathlib/Analysis/Complex/ValueDistribution/Proximity/IntegralPresentation.lean
@@ -0,0 +1,201 @@
+/-
+Copyright (c) 2026 Stefan Kebekus. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Matteo Cipollina, Stefan Kebekus
+-/
+
+module
+
+public import Mathlib.Analysis.Complex.ValueDistribution.Proximity.Basic
+public import Mathlib.Analysis.SpecialFunctions.Integrals.PosLogEqCircleAverage
+
+/-!
+# Integral Presentation of the Proximity Function
+
+If `f : ℂ → ℂ` is meromorphic, this file establishes a presentation of the proximity function
+`proximity f ⊤` as iterated circle averages. This statement can be used to compare the proximity-
+and logarithmic counting functions, and is one of the key ingredients in the proof of Cartan's
+classic formula for the characteristic function.
+
+See Section VI.2 of [Lang, *Introduction to Complex Hyperbolic Spaces*][MR886677] for a detailed
+discussion.
+-/
+
+public section
+
+open Filter MeasureTheory Real Set
+
+namespace ValueDistribution
+
+variable {f : ℂ → ℂ} {R : ℝ}
+
+namespace Cartan
+
+/-!
+### Integrability of the Cartan Kernel
+
+The proof of the integral presentation of the proximity function relies on an extended computation,
+applying Fubini's theorem to the Cartan kernel of integration. This section defines the kernel and
+establishes its integrability, as a function of two variables.
+-/
+
+/--
+Given `f : ℂ → ℂ` and `R : ℝ`, define the Cartan kernel of integration as the function
+`α β ↦ log ‖f (circleMap 0 R β) - circleMap 0 1 α‖`.
+-/
+noncomputable def cartanKernel (f : ℂ → ℂ) (R : ℝ) (α β : ℝ) : ℝ :=
+ log ‖f (circleMap 0 R β) - circleMap 0 1 α‖
+
+/--
+For every function `f : ℂ → ℂ`, the Cartan kernel of integration `cartanKernel f R α β` is
+integrable as a function in `α`.
+-/
+lemma integrableOn_cartanKernel_left (f : ℂ → ℂ) (R : ℝ) (β : ℝ) :
+ IntegrableOn (cartanKernel f R · β) (Ioc 0 (2 * π)) := by
+ apply (intervalIntegrable_iff_integrableOn_Ioc_of_le two_pi_pos.le).1
+ simpa [cartanKernel, norm_sub_rev, CircleIntegrable] using circleIntegrable_log_norm_sub_const 1
+
+/--
+If `f : ℂ → ℂ` is measurable, then the Cartan kernel of integration is measurable as a function in
+the two variables `α` and `β`.
+-/
+@[fun_prop]
+theorem measurable_cartanKernel (hf : Measurable f) :
+ Measurable (fun p : ℝ × ℝ ↦ cartanKernel f R p.1 p.2) := by
+ unfold cartanKernel; fun_prop
+
+/- Formula for the `L¹` norm of an angular slice of the Cartan kernel. -/
+private lemma integral_norm_cartanKernel_eq (f : ℂ → ℂ) (R β : ℝ) :
+ ∫ α in Ioc 0 (2 * π), ‖cartanKernel f R α β‖ =
+ 2 * (∫ α, (cartanKernel f R α β)⁺ ∂(volume.restrict (Ioc 0 (2 * π)))) -
+ (2 * π) * log⁺ ‖f (circleMap 0 R β)‖ := by
+ let μ : Measure ℝ := volume.restrict (Ioc 0 (2 * π))
+ calc ∫ α, ‖cartanKernel f R α β‖ ∂μ
+ _ = 2 * (∫ α, (cartanKernel f R α β)⁺ ∂μ) - ∫ α, cartanKernel f R α β ∂μ :=
+ integral_abs_eq_two_mul_integral_posPart_sub_integral (integrableOn_cartanKernel_left f R β)
+ _ = 2 * (∫ α, (cartanKernel f R α β)⁺ ∂μ) - 2 * π * log⁺ ‖f (circleMap 0 R β)‖ := by
+ congr
+ set z := f (circleMap 0 R β)
+ suffices h_avg : circleAverage (log ‖z - ·‖) 0 1 = log⁺ ‖z‖ by
+ convert congr(2 * π * $h_avg)
+ simp [circleAverage_def, field, cartanKernel, intervalIntegral.integral_of_le two_pi_pos.le]
+ simp [norm_sub_rev]
+
+/-
+If `f : ℂ → ℂ` is meromorphic,, then the `L¹` norms of the angular slices of the Cartan kernel form
+an integrable family.
+-/
+lemma integrable_integral_norm_cartanKernel (h : Meromorphic f) :
+ Integrable (∫ α, ‖cartanKernel f R α ·‖ ∂(volume.restrict (Ioc 0 (2 * π))))
+ (volume.restrict (Ioc 0 (2 * π))) := by
+ let μ : Measure ℝ := volume.restrict (Ioc 0 (2 * π))
+ have h_meas_K : Measurable (fun a : ℝ × ℝ ↦ (cartanKernel f R a.1 a.2)⁺) := by fun_prop
+ have h_int_posLog : Integrable (fun β ↦ log⁺ ‖f (circleMap 0 R β)‖) μ := by
+ have : CircleIntegrable (log⁺ ‖f ·‖) 0 R := h.meromorphicOn.circleIntegrable_posLog_norm
+ rwa [CircleIntegrable, intervalIntegrable_iff_integrableOn_Ioc_of_le two_pi_pos.le] at this
+ have h_int_Bound : Integrable (fun β ↦ log⁺ ‖f (circleMap 0 R β)‖ + log 2) μ :=
+ h_int_posLog.add (integrable_const _)
+ have h_int_Term1 : Integrable (fun β ↦ ∫ α, (cartanKernel f R α β)⁺ ∂μ) μ := by
+ apply Integrable.mono (h_int_Bound.const_mul (2 * π))
+ h_meas_K.stronglyMeasurable.integral_prod_left'.aestronglyMeasurable
+ filter_upwards with β
+ have h_int_nonneg : 0 ≤ ∫ α, (cartanKernel f R α β)⁺ ∂μ := by positivity
+ have h_bound_nonneg : 0 ≤ (2 * π) * (log⁺ ‖f (circleMap 0 R β)‖ + log 2) := by
+ positivity [posLog_nonneg (x := ‖f (circleMap 0 R β)‖)]
+ rw [norm_of_nonneg h_int_nonneg, norm_of_nonneg h_bound_nonneg]
+ have : ∫ α, (cartanKernel f R α β)⁺ ∂(volume.restrict (Ioc 0 (2 * π))) ≤
+ ∫ _, log⁺ ‖f (circleMap 0 R β)‖ + log 2 ∂(volume.restrict (Ioc 0 (2 * π))) := by
+ refine integral_mono_of_nonneg (.of_forall (by simp [posPart])) (integrable_const _)
+ (.of_forall ?_)
+ intro α
+ calc (cartanKernel f R α β)⁺
+ _ = log⁺ ‖f (circleMap 0 R β) + (-circleMap 0 1 α)‖ := by
+ simp [cartanKernel, posLog_def, posPart, max_comm, sub_eq_add_neg]
+ _ ≤ log⁺ ‖f (circleMap 0 R β)‖ + log⁺ ‖-circleMap 0 1 α‖ + log 2 :=
+ posLog_norm_add_le (f (circleMap 0 R β)) (-circleMap 0 1 α)
+ _ = log⁺ ‖f (circleMap 0 R β)‖ + log 2 := by
+ simp [norm_circleMap_zero, add_comm]
+ rwa [integral_const, smul_eq_mul, mul_comm, measureReal_restrict_apply_univ,
+ mul_comm, volume_real_Ioc_of_le two_pi_pos.le, sub_zero] at this
+ exact Integrable.congr ((h_int_Term1.const_mul 2).sub (h_int_posLog.const_mul (2 * π)))
+ (Eventually.of_forall fun β ↦ (integral_norm_cartanKernel_eq f R β).symm)
+
+/--
+If `f : ℂ → ℂ` is meromorphic, then the Cartan kernel of integration is integrable as a function in
+the two variables `α` and `β`.
+-/
+theorem integrableOn_cartanKernel (h : Meromorphic f) :
+ IntegrableOn (fun p ↦ cartanKernel f R p.1 p.2) (uIoc 0 (2 * π) ×ˢ uIoc 0 (2 * π)) := by
+ rw [IntegrableOn, Measure.volume_eq_prod, ← Measure.prod_restrict]
+ have := h.measurable
+ simpa [uIoc_of_le two_pi_pos.le] using (integrable_prod_iff' (by fun_prop)).2
+ ⟨Eventually.of_forall (integrableOn_cartanKernel_left f R),
+ integrable_integral_norm_cartanKernel h⟩
+
+/--
+Corollary of `integrableOn_cartanKernel`: If `f : ℂ → ℂ` is meromorphic, then the function
+`β ↦ ∫ α in 0..2 * π, Cartan.cartanKernel f R α β` is integrable.
+-/
+lemma integrableOn_intervalIntegral_cartanKernel_left (h : Meromorphic f) :
+ IntegrableOn (∫ α in 0..2 * π, Cartan.cartanKernel f R α ·) (Ioc 0 (2 * π)) := by
+ have h_int := Cartan.integrableOn_cartanKernel (R := R) h
+ rw [uIoc_of_le two_pi_pos.le, IntegrableOn, Measure.volume_eq_prod, ← Measure.prod_restrict]
+ at h_int
+ simpa [IntegrableOn, intervalIntegral.integral_of_le two_pi_pos.le, Cartan.cartanKernel]
+ using h_int.integral_prod_right
+
+/--
+Corollary of `integrableOn_cartanKernel`: If `f : ℂ → ℂ` is meromorphic, then the function
+`α ↦ ∫ β in 0..2 * π, Cartan.cartanKernel f R α β` is integrable.
+-/
+lemma integrableOn_intervalIntegral_cartanKernel_right (h : Meromorphic f) :
+ IntegrableOn (∫ β in 0..2 * π, Cartan.cartanKernel f R · β) (Ioc 0 (2 * π)) := by
+ have h_int := Cartan.integrableOn_cartanKernel (R := R) h
+ rw [uIoc_of_le two_pi_pos.le, IntegrableOn, Measure.volume_eq_prod, ← Measure.prod_restrict]
+ at h_int
+ simpa [IntegrableOn, intervalIntegral.integral_of_le two_pi_pos.le, Cartan.cartanKernel]
+ using h_int.integral_prod_left
+
+end Cartan
+
+/--
+Presentation of the proximity function as iterated circle averages.
+-/
+theorem circleAverage_circleAverage_eq_proximity_top (h : Meromorphic f) :
+ (fun R ↦ circleAverage (fun a ↦ circleAverage (log ‖f · - a‖) 0 R) 0 1) = proximity f ⊤ := by
+ ext R
+ let F : ℝ → ℝ → ℝ := Cartan.cartanKernel f R
+ calc circleAverage (fun a ↦ circleAverage (log ‖f · - a‖) 0 R) 0 1
+ _ = (2 * π)⁻¹ * (2 * π)⁻¹ * ∫ α in 0..2 * π, ∫ β in 0..2 * π, F α β := by
+ simp [circleAverage, F, Cartan.cartanKernel, mul_assoc]
+ _ = (2 * π)⁻¹ * (2 * π)⁻¹ * ∫ β in 0..2 * π, ∫ α in 0..2 * π, F α β := by
+ rw [MeasureTheory.intervalIntegral_intervalIntegral_swap]
+ exact Cartan.integrableOn_cartanKernel h
+ _ = (2 * π)⁻¹ * ∫ β in 0..2 * π, ((2 * π)⁻¹ * ∫ α in 0..2 * π, F α β) := by
+ simp [mul_comm, mul_left_comm, mul_assoc]
+ _ = (2 * π)⁻¹ * ∫ β in 0..2 * π, log⁺ ‖f (circleMap 0 R β)‖ := by
+ congr 1
+ apply intervalIntegral.integral_congr
+ intro β hβ
+ calc (2 * π)⁻¹ * ∫ α in 0..2 * π, F α β
+ _ = circleAverage (log ‖f (circleMap 0 R β) - ·‖) 0 1 := by
+ simp [F, circleAverage, Cartan.cartanKernel]
+ _ = log⁺ ‖f (circleMap 0 R β)‖ := by
+ simp [norm_sub_rev]
+ _ = circleAverage (log⁺ ‖f ·‖) 0 R := by
+ simp [circleAverage, intervalIntegral.integral_of_le two_pi_pos.le]
+
+/--
+Complementary statement to `proximity_top_eq_circleAverage_circleAverage`, providing circle
+integrability of the integrand.
+-/
+theorem circleIntegrable_circleAverage_log_norm_sub (h : Meromorphic f) :
+ CircleIntegrable (fun a ↦ circleAverage (log ‖f · - a‖) 0 R) 0 1 := by
+ by_cases hR : R = 0
+ · simp [hR, circleAverage_zero, norm_sub_rev, circleIntegrable_log_norm_sub_const]
+ rw [CircleIntegrable, intervalIntegrable_iff_integrableOn_Ioc_of_le two_pi_pos.le]
+ apply IntegrableOn.congr_fun
+ ((Cartan.integrableOn_intervalIntegral_cartanKernel_right (R := R) h).const_mul (2 * π)⁻¹)
+ (fun _ _ ↦ by simp [circleAverage, Cartan.cartanKernel]) measurableSet_Ioc
+
+end ValueDistribution
diff --git a/Mathlib/Analysis/Meromorphic/Basic.lean b/Mathlib/Analysis/Meromorphic/Basic.lean
index 3b6f6547e631b9..1d4d2a28f600dd 100644
--- a/Mathlib/Analysis/Meromorphic/Basic.lean
+++ b/Mathlib/Analysis/Meromorphic/Basic.lean
@@ -759,7 +759,7 @@ theorem countable_compl_analyticAt [SecondCountableTopology 𝕜] [CompleteSpace
/--
Meromorphic functions are measurable.
-/
-theorem measurable [MeasurableSpace 𝕜] [SecondCountableTopology 𝕜] [BorelSpace 𝕜]
+@[fun_prop] theorem measurable [MeasurableSpace 𝕜] [SecondCountableTopology 𝕜] [BorelSpace 𝕜]
[MeasurableSpace E] [CompleteSpace E] [BorelSpace E] (h : Meromorphic f) :
Measurable f := by
set s := {z : 𝕜 | AnalyticAt 𝕜 f z}
diff --git a/Mathlib/MeasureTheory/Integral/Bochner/Basic.lean b/Mathlib/MeasureTheory/Integral/Bochner/Basic.lean
index 9a0109eccba0c6..e726c0325904e3 100644
--- a/Mathlib/MeasureTheory/Integral/Bochner/Basic.lean
+++ b/Mathlib/MeasureTheory/Integral/Bochner/Basic.lean
@@ -566,6 +566,20 @@ theorem integral_eq_integral_pos_part_sub_integral_neg_part {f : α → ℝ} (hf
· simp
· exact hf.neg.real_toNNReal
+theorem integral_abs_eq_two_mul_integral_posPart_sub_integral {f : α → ℝ} (hf : Integrable f μ) :
+ ∫ x, |f x| ∂μ = 2 * ∫ x, (f x)⁺ ∂μ - ∫ x, f x ∂μ := by
+ simp only [PosPart.posPart]
+ have h_eq : ∀ x, |f x| = 2 * max (f x) 0 - f x := by grind
+ rw [integral_congr_ae (Eventually.of_forall h_eq), integral_sub (by fun_prop) hf,
+ integral_const_mul]
+
+theorem integral_abs_eq_two_mul_integral_negPart_add_integral {f : α → ℝ} (hf : Integrable f μ) :
+ ∫ x, |f x| ∂μ = 2 * ∫ x, (f x)⁻ ∂μ + ∫ x, f x ∂μ := by
+ simp only [NegPart.negPart]
+ have h_eq : ∀ x, |f x| = 2 * max (-f x) 0 + f x := by grind
+ rw [integral_congr_ae (Eventually.of_forall h_eq), integral_add (by fun_prop) hf,
+ integral_const_mul]
+
end Basic
section Order
diff --git a/Mathlib/MeasureTheory/Integral/Prod.lean b/Mathlib/MeasureTheory/Integral/Prod.lean
index de59485ec6289f..6dd14fda021671 100644
--- a/Mathlib/MeasureTheory/Integral/Prod.lean
+++ b/Mathlib/MeasureTheory/Integral/Prod.lean
@@ -544,6 +544,15 @@ lemma intervalIntegral_integral_swap {a b : ℝ} {f : ℝ → α → E}
simp only [hab, Set.uIoc_of_ge] at h_int
rw [integral_integral_swap h_int, integral_neg]
+/-- Change the order of integration for interval integrals. -/
+lemma intervalIntegral_intervalIntegral_swap {F : ℝ → ℝ → E} {a b c d : ℝ}
+ (h : IntegrableOn F.uncurry (uIoc a b ×ˢ uIoc c d)) :
+ ∫ x in a..b, ∫ y in c..d, F x y = ∫ y in c..d, ∫ x in a..b, F x y := by
+ rw [intervalIntegral.intervalIntegral_eq_integral_uIoc, ← intervalIntegral_integral_swap,
+ ← intervalIntegral.integral_smul]
+ · simp_rw [intervalIntegral.intervalIntegral_eq_integral_uIoc]
+ · rwa [← integrable_swap_iff, Measure.prod_restrict, ← Measure.volume_eq_prod, ← IntegrableOn]
+
/-- **Fubini's Theorem** for set integrals. -/
theorem setIntegral_prod (f : α × β → E) {s : Set α} {t : Set β}
(hf : IntegrableOn f (s ×ˢ t) (μ.prod ν)) :
From 5d1abc4cd8c71e2a463fb58d0e406decab077bdd Mon Sep 17 00:00:00 2001
From: "mathlib-splicebot[bot]"
<261196803+mathlib-splicebot[bot]@users.noreply.github.com>
Date: Wed, 17 Jun 2026 04:19:44 +0000
Subject: [PATCH 0096/1300] chore(LinearAlgebra/Matrix/Hermitian): `star A = A`
from Hermitian `A` shortcut (#40686)
Co-authored-by: ScottWe <4276716+ScottWe@users.noreply.github.com>
Co-authored-by: Monica Omar <23701951+themathqueen@users.noreply.github.com>
---
Mathlib/LinearAlgebra/Matrix/Hermitian.lean | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Mathlib/LinearAlgebra/Matrix/Hermitian.lean b/Mathlib/LinearAlgebra/Matrix/Hermitian.lean
index bfc5286afe2018..f47d0fc51adc7c 100644
--- a/Mathlib/LinearAlgebra/Matrix/Hermitian.lean
+++ b/Mathlib/LinearAlgebra/Matrix/Hermitian.lean
@@ -54,6 +54,8 @@ theorem isHermitian_iff_isSelfAdjoint {A : Matrix n n α} :
protected alias ⟨IsHermitian.isSelfAdjoint, _root_.IsSelfAdjoint.isHermitian⟩ :=
isHermitian_iff_isSelfAdjoint
+theorem IsHermitian.star_eq (hA : A.IsHermitian) : star A = A := hA.isSelfAdjoint.star_eq
+
theorem IsHermitian.ext {A : Matrix n n α} : (∀ i j, star (A j i) = A i j) → A.IsHermitian := by
intro h; ext i j; exact h i j
From 4971f8ebe8c184103f1c267f371377194e460249 Mon Sep 17 00:00:00 2001
From: Adam Kiezun
Date: Wed, 17 Jun 2026 06:03:31 +0000
Subject: [PATCH 0097/1300] feat(NumberTheory): add almost prime numbers
(#39903)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Adds `Nat.IsAlmostPrime k n` for natural numbers with exactly `k` prime factors counted with multiplicity, plus
`Nat.IsAtMostAlmostPrime` and `Nat.IsSemiprime`. The definitions reuse the existing arithmetic function `Ω`, and the initial API proves the basic zero/one cases, prime examples, and closure under multiplication.
---
Mathlib.lean | 1 +
Mathlib/NumberTheory/AlmostPrime.lean | 90 +++++++++++++++++++++++++++
2 files changed, 91 insertions(+)
create mode 100644 Mathlib/NumberTheory/AlmostPrime.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 5be51fc0034ca1..610df212f17b02 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -5650,6 +5650,7 @@ public import Mathlib.ModelTheory.Types
public import Mathlib.ModelTheory.Ultraproducts
public import Mathlib.NumberTheory.ADEInequality
public import Mathlib.NumberTheory.AbelSummation
+public import Mathlib.NumberTheory.AlmostPrime
public import Mathlib.NumberTheory.ArithmeticFunction.Carmichael
public import Mathlib.NumberTheory.ArithmeticFunction.Defs
public import Mathlib.NumberTheory.ArithmeticFunction.LFunction
diff --git a/Mathlib/NumberTheory/AlmostPrime.lean b/Mathlib/NumberTheory/AlmostPrime.lean
new file mode 100644
index 00000000000000..0d6e654eed48fb
--- /dev/null
+++ b/Mathlib/NumberTheory/AlmostPrime.lean
@@ -0,0 +1,90 @@
+/-
+Copyright (c) 2026 Adam Kiezun. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Adam Kiezun
+-/
+module
+
+public import Mathlib.NumberTheory.ArithmeticFunction.Misc
+
+/-!
+# Almost prime numbers
+
+This file defines `Nat.IsAlmostPrime k n`, the predicate that `n` has exactly `k`
+prime factors counted with multiplicity. We also define `Nat.IsAtMostAlmostPrime`,
+the corresponding predicate with at most `k` prime factors, and `Nat.IsSemiprime`,
+the special case of `2`-almost-prime numbers.
+
+Both definitions use the arithmetic function `ArithmeticFunction.cardFactors`, written `Ω`.
+
+The terminology follows the standard definition of an
+[almost prime](https://en.wikipedia.org/wiki/Almost_prime).
+
+## Main statements
+
+* `Nat.IsAlmostPrime.mul`: the product of a `k`-almost-prime number and an
+ `l`-almost-prime number is `(k + l)`-almost-prime.
+* `Nat.IsAtMostAlmostPrime.mul`: the analogous statement for at most `k` prime factors.
+
+-/
+
+@[expose] public section
+
+open scoped ArithmeticFunction.Omega
+
+namespace Nat
+
+/-- `IsAlmostPrime k n` means that `n` is `k`-almost prime: it has exactly `k`
+prime factors, counted with multiplicity. The side condition excludes `0`, so `1` is
+`0`-almost prime. -/
+def IsAlmostPrime (k n : ℕ) : Prop :=
+ n ≠ 0 ∧ Ω n = k
+
+/-- `IsAtMostAlmostPrime k n` means that `n` has at most `k` prime factors,
+counted with multiplicity. -/
+def IsAtMostAlmostPrime (k n : ℕ) : Prop :=
+ n ≠ 0 ∧ Ω n ≤ k
+
+/-- A semiprime is a `2`-almost-prime number. -/
+abbrev IsSemiprime (n : ℕ) : Prop :=
+ IsAlmostPrime 2 n
+
+variable {k l m n p q : ℕ}
+
+@[simp]
+theorem isAlmostPrime_zero_iff : IsAlmostPrime 0 n ↔ n = 1 := by
+ rw [IsAlmostPrime, ArithmeticFunction.cardFactors_eq_zero_iff_eq_zero_or_one]
+ exact ⟨fun h ↦ h.2.resolve_left h.1, fun h ↦ by simp [h]⟩
+
+@[simp]
+theorem isAlmostPrime_one_iff : IsAlmostPrime 1 n ↔ n.Prime := by
+ constructor
+ · exact fun h ↦ ArithmeticFunction.cardFactors_eq_one_iff_prime.mp h.2
+ · exact fun h ↦ ⟨h.ne_zero, ArithmeticFunction.cardFactors_eq_one_iff_prime.mpr h⟩
+
+theorem Prime.isAlmostPrime_one (hp : p.Prime) : IsAlmostPrime 1 p := by
+ simpa using isAlmostPrime_one_iff.mpr hp
+
+theorem IsAlmostPrime.mul (hm : IsAlmostPrime k m) (hn : IsAlmostPrime l n) :
+ IsAlmostPrime (k + l) (m * n) := by
+ refine ⟨mul_ne_zero hm.1 hn.1, ?_⟩
+ rw [ArithmeticFunction.cardFactors_mul hm.1 hn.1, hm.2, hn.2]
+
+theorem IsAtMostAlmostPrime.mul (hm : IsAtMostAlmostPrime k m) (hn : IsAtMostAlmostPrime l n) :
+ IsAtMostAlmostPrime (k + l) (m * n) := by
+ refine ⟨mul_ne_zero hm.1 hn.1, ?_⟩
+ rw [ArithmeticFunction.cardFactors_mul hm.1 hn.1]
+ exact add_le_add hm.2 hn.2
+
+theorem IsAlmostPrime.isAtMost (hn : IsAlmostPrime k n) (hkl : k ≤ l) :
+ IsAtMostAlmostPrime l n :=
+ ⟨hn.1, hn.2 ▸ hkl⟩
+
+theorem Prime.mul_isAlmostPrime_two (hp : p.Prime) (hq : q.Prime) :
+ IsAlmostPrime 2 (p * q) := by
+ simpa using hp.isAlmostPrime_one.mul hq.isAlmostPrime_one
+
+theorem Prime.sq_isAlmostPrime_two (hp : p.Prime) : IsAlmostPrime 2 (p ^ 2) := by
+ simpa [pow_two] using hp.mul_isAlmostPrime_two hp
+
+end Nat
From 8390fffb841318b28e4305ae51a91febbba3f4b5 Mon Sep 17 00:00:00 2001
From: "M. Winter" <112132359+martinwintermath@users.noreply.github.com>
Date: Wed, 17 Jun 2026 06:16:39 +0000
Subject: [PATCH 0098/1300] chore(LinearAlgebra/SesquilinearForm): deprecate
`IsOrtho` and associated lemmas (#37381)
Next steps in cleaning up bilinearity and orthogonality:
- deprecate `IsOrtho` and accompanying trivial lemmas (this also allows to shorten the proofs in `orthogonalBilin`).
- deprecate `ortho_smul_right` and `ortho_smul_left` since now provable from simp.
- remove uses of deprecated definitions.
- replace `IsOrtho` in undergrad.yaml by `iIsOrtho`.
See discussion at [#mathlib4 > Reorganizing bilinearity and orthogonality?](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Reorganizing.20bilinearity.20and.20orthogonality.3F/with/582426197)
Due to too agressive simplification I had to remove simp from
- `QuadraticMap.associated_apply`
- `QuadraticMap.isOrtho_polarBilin`
Co-authored-by: Martin Winter
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
---
Mathlib/Algebra/Lie/InvariantForm.lean | 6 +-
Mathlib/Algebra/Lie/TraceForm.lean | 2 +-
Mathlib/Algebra/Lie/Weights/Killing.lean | 2 +-
.../BilinearForm/Orthogonal.lean | 59 +++++++-----
.../LinearAlgebra/QuadraticForm/Basic.lean | 26 +++---
.../QuadraticForm/IsometryEquiv.lean | 2 +-
.../LinearAlgebra/QuadraticForm/Radical.lean | 2 +-
.../QuadraticForm/TensorProduct.lean | 6 +-
.../TensorProduct/Isometries.lean | 5 +-
.../RootSystem/Finite/Nondegenerate.lean | 4 +-
.../LinearAlgebra/SesquilinearForm/Basic.lean | 90 ++++++++++---------
docs/undergrad.yaml | 2 +-
12 files changed, 112 insertions(+), 94 deletions(-)
diff --git a/Mathlib/Algebra/Lie/InvariantForm.lean b/Mathlib/Algebra/Lie/InvariantForm.lean
index ac4082707e8fba..18f81272293a12 100644
--- a/Mathlib/Algebra/Lie/InvariantForm.lean
+++ b/Mathlib/Algebra/Lie/InvariantForm.lean
@@ -74,7 +74,7 @@ def orthogonal (hΦ_inv : Φ.lieInvariant L) (N : LieSubmodule R L M) : LieSubmo
__ := Φ.orthogonal N
lie_mem {x y} := by
suffices (∀ n ∈ N, Φ n y = 0) → ∀ n ∈ N, Φ n ⁅x, y⁆ = 0 by
- simpa only [LinearMap.BilinForm.isOrtho_def, -- and some default simp lemmas
+ simpa only [
AddSubsemigroup.mem_carrier, AddSubmonoid.mem_toSubsemigroup, Submodule.mem_toAddSubmonoid,
LinearMap.BilinForm.mem_orthogonal_iff, LieSubmodule.mem_toSubmodule]
intro H a ha
@@ -89,7 +89,7 @@ lemma orthogonal_toSubmodule (N : LieSubmodule R L M) :
lemma mem_orthogonal (N : LieSubmodule R L M) (y : M) :
y ∈ orthogonal Φ hΦ_inv N ↔ ∀ x ∈ N, Φ x y = 0 := by
- simp [orthogonal, LinearMap.BilinForm.isOrtho_def, LinearMap.BilinForm.mem_orthogonal_iff]
+ simp [orthogonal, LinearMap.BilinForm.mem_orthogonal_iff]
variable [LieAlgebra R L]
@@ -202,7 +202,7 @@ theorem isSemisimple_of_nondegenerate : IsSemisimple K L := by
intro J hJ hJI
rw [← lie_eq_self_of_isAtom_of_nonabelian J hJ (hL J hJ), lieIdeal_oper_eq_span, lieSpan_le]
rintro _ ⟨x, y, rfl⟩
- simp only [orthogonal_carrier, LinearMap.IsOrtho, Set.mem_setOf_eq]
+ simp only [orthogonal_carrier, Set.mem_setOf_eq]
intro z hz
rw [← neg_eq_zero, ← hΦ_inv]
suffices ⁅(x : L), z⁆ = 0 by simp only [this, map_zero, LinearMap.zero_apply]
diff --git a/Mathlib/Algebra/Lie/TraceForm.lean b/Mathlib/Algebra/Lie/TraceForm.lean
index 1dad3c51e75468..b8117c7d26e145 100644
--- a/Mathlib/Algebra/Lie/TraceForm.lean
+++ b/Mathlib/Algebra/Lie/TraceForm.lean
@@ -404,7 +404,7 @@ noncomputable def killingCompl : LieIdeal R L :=
lemma coe_killingCompl_top :
killingCompl R L ⊤ = LinearMap.ker (killingForm R L) := by
ext x
- simp [LinearMap.ext_iff, LinearMap.BilinForm.IsOrtho, LieModule.traceForm_comm R L L x]
+ simp [LinearMap.ext_iff, LieModule.traceForm_comm R L L x]
lemma restrict_killingForm :
(killingForm R L).restrict I = LieModule.traceForm R I L :=
diff --git a/Mathlib/Algebra/Lie/Weights/Killing.lean b/Mathlib/Algebra/Lie/Weights/Killing.lean
index 6072fbc6715590..0fcc327d084cda 100644
--- a/Mathlib/Algebra/Lie/Weights/Killing.lean
+++ b/Mathlib/Algebra/Lie/Weights/Killing.lean
@@ -524,7 +524,7 @@ lemma traceForm_eq_zero_of_mem_ker_of_mem_span_coroot {α : Weight K H L} {x y :
refine le_antisymm (fun x hx ↦ ?_) (fun x hx y hy ↦ ?_)
· simp only [LinearMap.BilinForm.mem_orthogonal_iff] at hx
specialize hx (coroot α) (Submodule.mem_span_singleton_self _)
- simp only [LinearMap.BilinForm.isOrtho_def, traceForm_coroot, smul_eq_mul, nsmul_eq_mul,
+ simp only [traceForm_coroot, smul_eq_mul, nsmul_eq_mul,
Nat.cast_ofNat, mul_eq_zero, OfNat.ofNat_ne_zero, inv_eq_zero, false_or] at hx
simpa using hx.resolve_left (root_apply_cartanEquivDual_symm_ne_zero hα)
· have := traceForm_eq_zero_of_mem_ker_of_mem_span_coroot hx hy
diff --git a/Mathlib/LinearAlgebra/BilinearForm/Orthogonal.lean b/Mathlib/LinearAlgebra/BilinearForm/Orthogonal.lean
index cf6c510b7622db..6400c41c3c9ff0 100644
--- a/Mathlib/LinearAlgebra/BilinearForm/Orthogonal.lean
+++ b/Mathlib/LinearAlgebra/BilinearForm/Orthogonal.lean
@@ -50,32 +50,47 @@ namespace BilinForm
/-- The proposition that two elements of a bilinear form space are orthogonal. For orthogonality
of an indexed set of elements, use `BilinForm.iIsOrtho`. -/
+@[deprecated "Use `B x y = 0`." (since := "2026-03-30")]
def IsOrtho (B : BilinForm R M) (x y : M) : Prop :=
B x y = 0
+set_option linter.deprecated false in
+@[deprecated "`BilinMap.IsOrtho` has been deprecated" (since := "2026-03-30")]
theorem isOrtho_def {B : BilinForm R M} {x y : M} : B.IsOrtho x y ↔ B x y = 0 :=
Iff.rfl
+set_option linter.deprecated false in
+@[deprecated "`BilinMap.IsOrtho` has been deprecated" (since := "2026-03-30")]
theorem isOrtho_zero_left (x : M) : IsOrtho B (0 : M) x := LinearMap.isOrtho_zero_left B x
+set_option linter.deprecated false in
+@[deprecated "`BilinMap.IsOrtho` has been deprecated" (since := "2026-03-30")]
theorem isOrtho_zero_right (x : M) : IsOrtho B x (0 : M) :=
zero_right x
-theorem ne_zero_of_not_isOrtho_self {B : BilinForm K V} (x : V) (hx₁ : ¬B.IsOrtho x x) : x ≠ 0 :=
- fun hx₂ => hx₁ (hx₂.symm ▸ isOrtho_zero_left _)
+theorem ne_zero_of_not_isOrtho_self {B : BilinForm K V} (x : V) (hx₁ : B x x ≠ 0) : x ≠ 0 := by
+ by_contra; simp [this] at hx₁
-theorem IsRefl.ortho_comm (H : B.IsRefl) {x y : M} : IsOrtho B x y ↔ IsOrtho B y x :=
+theorem IsRefl.eq_iff (H : B.IsRefl) {x y : M} : B x y = 0 ↔ B y x = 0 :=
⟨eq_zero H, eq_zero H⟩
-theorem IsAlt.ortho_comm (H : B₁.IsAlt) {x y : M₁} : IsOrtho B₁ x y ↔ IsOrtho B₁ y x :=
- LinearMap.IsAlt.ortho_comm H
+@[deprecated (since := "2026-03-31")]
+alias IsRefl.ortho_comm := IsRefl.eq_iff
-theorem IsSymm.ortho_comm (H : B.IsSymm) {x y : M} : IsOrtho B x y ↔ IsOrtho B y x :=
- LinearMap.IsSymm.ortho_comm (isSymm_iff.1 H)
+theorem IsAlt.eq_iff (H : B₁.IsAlt) {x y : M₁} : B₁ x y = 0 ↔ B₁ y x = 0 :=
+ LinearMap.IsAlt.eq_iff H
+
+@[deprecated (since := "2026-03-31")]
+alias IsAlt.ortho_comm := IsAlt.eq_iff
+
+theorem IsSymm.eq_iff (H : B.IsSymm) {x y : M} : B x y = 0 ↔ B y x = 0 :=
+ LinearMap.IsSymm.eq_iff (isSymm_iff.1 H)
+
+@[deprecated (since := "2026-03-31")]
+alias IsSymm.ortho_comm := IsSymm.eq_iff
/-- A set of vectors `v` is orthogonal with respect to some bilinear form `B` if and only
-if for all `i ≠ j`, `B (v i) (v j) = 0`. For orthogonality between two elements, use
-`BilinForm.IsOrtho` -/
+if for all `i ≠ j`, `B (v i) (v j) = 0`. -/
def iIsOrtho {n : Type w} (B : BilinForm R M) (v : n → M) : Prop :=
B.IsOrthoᵢ v
@@ -88,7 +103,8 @@ section
variable {R₄ M₄ : Type*} [CommRing R₄] [IsDomain R₄]
variable [AddCommGroup M₄] [Module R₄ M₄] {G : BilinForm R₄ M₄}
-@[simp]
+set_option linter.deprecated false in
+@[deprecated "`BilinMap.IsOrtho` has been deprecated" (since := "2026-03-30")]
theorem isOrtho_smul_left {x y : M₄} {a : R₄} (ha : a ≠ 0) :
IsOrtho G (a • x) y ↔ IsOrtho G x y := by
dsimp only [IsOrtho]
@@ -96,7 +112,8 @@ theorem isOrtho_smul_left {x y : M₄} {a : R₄} (ha : a ≠ 0) :
simp only [LinearMap.smul_apply, smul_eq_mul, mul_eq_zero, or_iff_right_iff_imp]
exact fun a ↦ (ha a).elim
-@[simp]
+set_option linter.deprecated false in
+@[deprecated "`BilinMap.IsOrtho` has been deprecated" (since := "2026-03-30")]
theorem isOrtho_smul_right {x y : M₄} {a : R₄} (ha : a ≠ 0) :
IsOrtho G x (a • y) ↔ IsOrtho G x y := by
dsimp only [IsOrtho]
@@ -107,7 +124,7 @@ theorem isOrtho_smul_right {x y : M₄} {a : R₄} (ha : a ≠ 0) :
/-- A set of orthogonal vectors `v` with respect to some bilinear form `B` is linearly independent
if for all `i`, `B (v i) (v i) ≠ 0`. -/
theorem linearIndependent_of_iIsOrtho {n : Type w} {B : BilinForm K V} {v : n → V}
- (hv₁ : B.iIsOrtho v) (hv₂ : ∀ i, ¬B.IsOrtho (v i) (v i)) : LinearIndependent K v := by
+ (hv₁ : B.iIsOrtho v) (hv₂ : ∀ i, B (v i) (v i) ≠ 0) : LinearIndependent K v := by
classical
rw [linearIndependent_iff']
intro s w hs i hi
@@ -136,10 +153,10 @@ variable {N L : Submodule R M}
@[simp]
theorem mem_orthogonal_iff {N : Submodule R M} {m : M} :
- m ∈ B.orthogonal N ↔ ∀ n ∈ N, IsOrtho B n m :=
+ m ∈ B.orthogonal N ↔ ∀ n ∈ N, B n m = 0 :=
Iff.rfl
-@[simp] lemma orthogonal_bot : B.orthogonal ⊥ = ⊤ := by ext; simp [IsOrtho]
+@[simp] lemma orthogonal_bot : B.orthogonal ⊥ = ⊤ := by ext; simp
theorem orthogonal_le (h : N ≤ L) : B.orthogonal L ≤ B.orthogonal N := fun _ hn l hl => hn l (h hl)
@@ -148,14 +165,14 @@ theorem le_orthogonal_orthogonal (b : B.IsRefl) : N ≤ B.orthogonal (B.orthogon
lemma orthogonal_top_eq_ker (hB : B.IsRefl) :
B.orthogonal ⊤ = LinearMap.ker B := by
- ext; simp [LinearMap.BilinForm.IsOrtho, LinearMap.ext_iff, hB.eq_iff]
+ ext; simp [LinearMap.ext_iff, hB.eq_iff]
lemma orthogonal_top_eq_bot (hB : B.Nondegenerate) :
B.orthogonal ⊤ = ⊥ :=
(Submodule.eq_bot_iff _).mpr fun x hx ↦ hB.2 x (by simpa using! hx)
-- ↓ This lemma only applies in fields as we require `a * b = 0 → a = 0 ∨ b = 0`
-theorem span_singleton_inf_orthogonal_eq_bot {B : BilinForm K V} {x : V} (hx : ¬B.IsOrtho x x) :
+theorem span_singleton_inf_orthogonal_eq_bot {B : BilinForm K V} {x : V} (hx : B x x ≠ 0) :
K ∙ x ⊓ B.orthogonal (K ∙ x) = ⊥ :=
LinearMap.span_singleton_inf_orthogonal_eq_bot B _ hx
@@ -164,13 +181,13 @@ theorem orthogonal_span_singleton_eq_toLin_ker {B : BilinForm K V} (x : V) :
B.orthogonal (K ∙ x) = LinearMap.ker (LinearMap.BilinForm.toLinHomAux₁ B x) :=
LinearMap.orthogonal_span_singleton_eq_to_lin_ker ..
-theorem span_singleton_sup_orthogonal_eq_top {B : BilinForm K V} {x : V} (hx : ¬B.IsOrtho x x) :
+theorem span_singleton_sup_orthogonal_eq_top {B : BilinForm K V} {x : V} (hx : B x x ≠ 0) :
K ∙ x ⊔ B.orthogonal (K ∙ x) = ⊤ :=
LinearMap.span_singleton_sup_orthogonal_eq_top hx
/-- Given a bilinear form `B` and some `x` such that `B x x ≠ 0`, the span of the singleton of `x`
is complement to its orthogonal complement. -/
-theorem isCompl_span_singleton_orthogonal {B : BilinForm K V} {x : V} (hx : ¬B.IsOrtho x x) :
+theorem isCompl_span_singleton_orthogonal {B : BilinForm K V} {x : V} (hx : B x x ≠ 0) :
IsCompl (K ∙ x) (B.orthogonal <| K ∙ x) :=
LinearMap.isCompl_span_singleton_orthogonal hx
@@ -189,14 +206,14 @@ theorem nondegenerate_restrict_of_disjoint_orthogonal (B : BilinForm R₁ M₁)
elements. -/
theorem iIsOrtho.not_isOrtho_basis_self_of_nondegenerate {n : Type w} [Nontrivial R]
{B : BilinForm R M} {v : Basis n R M} (h : B.iIsOrtho v) (hB : B.Nondegenerate) (i : n) :
- ¬B.IsOrtho (v i) (v i) :=
+ B (v i) (v i) ≠ 0 :=
h.not_isOrtho_basis_self_of_separatingLeft hB.1 i
/-- Given an orthogonal basis with respect to a bilinear form, the bilinear form is nondegenerate
iff the basis has no elements which are self-orthogonal. -/
theorem iIsOrtho.nondegenerate_iff_not_isOrtho_basis_self {n : Type w} [IsDomain R]
(B : BilinForm R M) (v : Basis n R M) (hO : B.iIsOrtho v) :
- B.Nondegenerate ↔ ∀ i, ¬B.IsOrtho (v i) (v i) :=
+ B.Nondegenerate ↔ ∀ i, B (v i) (v i) ≠ 0 :=
⟨hO.not_isOrtho_basis_self_of_nondegenerate, hO.nondegenerate_of_not_isOrtho_basis_self _⟩
section
@@ -351,7 +368,7 @@ on the whole space. -/
/-- The restriction of a reflexive, non-degenerate bilinear form on the orthogonal complement of
the span of a singleton is also non-degenerate. -/
theorem restrict_nondegenerate_orthogonal_spanSingleton (B : BilinForm K V) (b₁ : B.Nondegenerate)
- (b₂ : B.IsRefl) {x : V} (hx : ¬B.IsOrtho x x) :
+ (b₂ : B.IsRefl) {x : V} (hx : B x x ≠ 0) :
Nondegenerate <| B.restrict <| B.orthogonal (K ∙ x) := by
have (n : V) : n ∈ K ∙ x ⊔ B.orthogonal (K ∙ x) :=
(span_singleton_sup_orthogonal_eq_top hx).symm ▸ Submodule.mem_top
diff --git a/Mathlib/LinearAlgebra/QuadraticForm/Basic.lean b/Mathlib/LinearAlgebra/QuadraticForm/Basic.lean
index ecb33bc815b2ae..7a28de8535feb4 100644
--- a/Mathlib/LinearAlgebra/QuadraticForm/Basic.lean
+++ b/Mathlib/LinearAlgebra/QuadraticForm/Basic.lean
@@ -901,16 +901,14 @@ def associatedHom : QuadraticMap R M N →ₗ[S] (BilinMap R M N) where
variable (Q : QuadraticMap R M N)
-@[simp]
theorem associated_apply (x y : M) :
- associatedHom S Q x y = ⅟(2 : Module.End R N) • (Q (x + y) - Q x - Q y) :=
- rfl
+ associatedHom S Q x y = ⅟(2 : Module.End R N) • (Q (x + y) - Q x - Q y) := rfl
set_option backward.defeqAttrib.useBackward true in
/-- Twice the associated bilinear map of `Q` is the same as the polar of `Q`. -/
@[simp] theorem two_nsmul_associated : 2 • associatedHom S Q = Q.polarBilin := by
ext
- dsimp
+ dsimp [associated_apply]
rw [← LinearMap.smul_apply, nsmul_eq_mul, Nat.cast_ofNat, mul_invOf_self', Module.End.one_apply,
polar]
@@ -1065,9 +1063,9 @@ alias ⟨IsOrtho.symm, _⟩ := isOrtho_comm
theorem _root_.LinearMap.BilinForm.toQuadraticMap_isOrtho [IsCancelAdd R]
[NoZeroDivisors R] [CharZero R] {B : BilinMap R M R} {x y : M} (h : B.IsSymm) :
- B.toQuadraticMap.IsOrtho x y ↔ B.IsOrtho x y := by
+ B.toQuadraticMap.IsOrtho x y ↔ B x y = 0 := by
letI : AddCancelMonoid R := { ‹IsCancelAdd R›, (inferInstance : AddCommMonoid R) with }
- simp_rw [isOrtho_def, LinearMap.isOrtho_def, B.toQuadraticMap_apply, map_add,
+ simp_rw [isOrtho_def, B.toQuadraticMap_apply, map_add,
LinearMap.add_apply, add_comm _ (B y y), add_add_add_comm _ _ (B y y), add_comm (B y y)]
rw [add_eq_left (a := B x x + B y y), ← h.eq, RingHom.id_apply, add_self_eq_zero]
@@ -1077,18 +1075,16 @@ section CommRing
variable [CommRing R] [AddCommGroup M] [Module R M] [AddCommGroup N] [Module R N]
{Q : QuadraticMap R M N}
-@[simp]
-theorem isOrtho_polarBilin {x y : M} : Q.polarBilin.IsOrtho x y ↔ IsOrtho Q x y := by
- simp_rw [isOrtho_def, LinearMap.isOrtho_def, polarBilin_apply_apply, polar, sub_sub, sub_eq_zero]
+theorem isOrtho_polarBilin {x y : M} : Q.polarBilin x y = 0 ↔ IsOrtho Q x y := by
+ simp_rw [isOrtho_def, polarBilin_apply_apply, polar, sub_sub, sub_eq_zero]
theorem IsOrtho.polar_eq_zero {x y : M} (h : IsOrtho Q x y) : polar Q x y = 0 :=
isOrtho_polarBilin.mpr h
@[simp]
theorem associated_isOrtho [Invertible (2 : R)] {x y : M} :
- Q.associated.IsOrtho x y ↔ Q.IsOrtho x y := by
- simp_rw [isOrtho_def, LinearMap.isOrtho_def, associated_apply, invOf_smul_eq_iff,
- smul_zero, sub_sub, sub_eq_zero]
+ Q.associated x y = 0 ↔ Q.IsOrtho x y := by
+ simp_rw [isOrtho_def, associated_apply, invOf_smul_eq_iff, smul_zero, sub_sub, sub_eq_zero]
end CommRing
@@ -1366,7 +1362,7 @@ variable [CommRing R] [AddCommGroup M] [Module R M]
on a module `M` over a ring `R` with invertible `2`, i.e. there exists some
`x : M` such that `B x x ≠ 0`. -/
theorem exists_bilinForm_self_ne_zero [htwo : Invertible (2 : R)] {B : BilinForm R M}
- (hB₁ : B ≠ 0) (hB₂ : B.IsSymm) : ∃ x, ¬B.IsOrtho x x := by
+ (hB₁ : B ≠ 0) (hB₂ : B.IsSymm) : ∃ x, B x x ≠ 0 := by
lift B to QuadraticForm R M using hB₂ with Q
obtain ⟨x, hx⟩ := QuadraticMap.exists_quadraticMap_ne_zero hB₁
exact ⟨x, fun h => hx (Q.associated_eq_self_apply ℕ x ▸ h)⟩
@@ -1410,7 +1406,7 @@ theorem exists_orthogonal_basis [hK : Invertible (2 : K)] {B : LinearMap.BilinFo
intro y
refine ⟨-B x y / B x x, fun z hz => ?_⟩
obtain ⟨c, rfl⟩ := Submodule.mem_span_singleton.1 hz
- rw [IsOrtho, map_smul, smul_apply, map_add, map_smul, smul_eq_mul, smul_eq_mul,
+ rw [map_smul, smul_apply, map_add, map_smul, smul_eq_mul, smul_eq_mul,
div_mul_cancel₀ _ hx, add_neg_cancel, mul_zero])
refine ⟨b, ?_⟩
rw [Basis.coe_mkFinCons]
@@ -1418,7 +1414,7 @@ theorem exists_orthogonal_basis [hK : Invertible (2 : K)] {B : LinearMap.BilinFo
refine Fin.cases ?_ (fun i => ?_) i <;> refine Fin.cases ?_ (fun j => ?_) j <;> intro hij <;>
simp only [Function.onFun, Fin.cons_zero, Fin.cons_succ, Function.comp_apply]
· exact (hij rfl).elim
- · rw [IsOrtho, ← hB₂.eq]
+ · rw [← hB₂.eq]
exact (v' j).prop _ (Submodule.mem_span_singleton_self x)
· exact (v' i).prop _ (Submodule.mem_span_singleton_self x)
· exact hv₁ (ne_of_apply_ne _ hij)
diff --git a/Mathlib/LinearAlgebra/QuadraticForm/IsometryEquiv.lean b/Mathlib/LinearAlgebra/QuadraticForm/IsometryEquiv.lean
index 34157f8ca08d6e..bda0fcc2cee93b 100644
--- a/Mathlib/LinearAlgebra/QuadraticForm/IsometryEquiv.lean
+++ b/Mathlib/LinearAlgebra/QuadraticForm/IsometryEquiv.lean
@@ -171,7 +171,7 @@ theorem equivalent_weightedSumSquares_units_of_nondegenerate' (Q : QuadraticForm
∃ w : Fin (Module.finrank K V) → Kˣ, Equivalent Q (weightedSumSquares K w) := by
obtain ⟨v, hv₁⟩ := exists_orthogonal_basis (associated_isSymm K Q)
have hv₂ := hv₁.not_isOrtho_basis_self_of_separatingLeft hQ
- simp_rw [LinearMap.IsOrtho, associated_eq_self_apply] at hv₂
+ simp_rw [associated_eq_self_apply] at hv₂
exact ⟨fun i => Units.mk0 _ (hv₂ i), ⟨Q.isometryEquivWeightedSumSquares v hv₁⟩⟩
variable {ι S R : Type*}
diff --git a/Mathlib/LinearAlgebra/QuadraticForm/Radical.lean b/Mathlib/LinearAlgebra/QuadraticForm/Radical.lean
index df01c340bda52d..62ebdc80a84f20 100644
--- a/Mathlib/LinearAlgebra/QuadraticForm/Radical.lean
+++ b/Mathlib/LinearAlgebra/QuadraticForm/Radical.lean
@@ -138,7 +138,7 @@ the radical of a quadratic map is the kernel of its associated bilinear map. -/
lemma radical_eq_ker_associated : Q.radical = (QuadraticMap.associated Q).ker := by
rw [radical_eq_ker_polarBilin]
ext m
- simp [LinearMap.ext_iff, QuadraticMap.polar, -smul_eq_mul, invOf_smul_eq_iff]
+ simp [associated_apply, LinearMap.ext_iff, QuadraticMap.polar, invOf_smul_eq_iff]
/--
If `2` is invertible in the coefficient ring,
diff --git a/Mathlib/LinearAlgebra/QuadraticForm/TensorProduct.lean b/Mathlib/LinearAlgebra/QuadraticForm/TensorProduct.lean
index c562808d6c6af6..f2e5f4e238dccb 100644
--- a/Mathlib/LinearAlgebra/QuadraticForm/TensorProduct.lean
+++ b/Mathlib/LinearAlgebra/QuadraticForm/TensorProduct.lean
@@ -110,8 +110,10 @@ protected abbrev tmul (Q₁ : QuadraticForm A M₁) (Q₂ : QuadraticForm R M₂
theorem associated_tmul [Invertible (2 : A)] (Q₁ : QuadraticForm A M₁) (Q₂ : QuadraticForm R M₂) :
(Q₁.tmul Q₂).associated = BilinForm.tmul Q₁.associated Q₂.associated := by
rw [BilinForm.tmul, BilinForm.tensorDistrib, LinearMap.comp_apply, ← BilinMap.tmul,
- ← QuadraticMap.associated_tmul Q₁ Q₂]
- aesop
+ ← QuadraticMap.associated_tmul Q₁ Q₂, LinearEquiv.coe_coe, LinearEquiv.congrRight₂_apply]
+ ext : 6
+ simp [associated_apply]
+ rfl
set_option backward.isDefEq.respectTransparency false in
theorem polarBilin_tmul [Invertible (2 : A)] (Q₁ : QuadraticForm A M₁) (Q₂ : QuadraticForm R M₂) :
diff --git a/Mathlib/LinearAlgebra/QuadraticForm/TensorProduct/Isometries.lean b/Mathlib/LinearAlgebra/QuadraticForm/TensorProduct/Isometries.lean
index 7f057783b837c2..1bd58fe2a8f641 100644
--- a/Mathlib/LinearAlgebra/QuadraticForm/TensorProduct/Isometries.lean
+++ b/Mathlib/LinearAlgebra/QuadraticForm/TensorProduct/Isometries.lean
@@ -47,7 +47,7 @@ theorem tmul_comp_tensorMap
have h₃ : Q₃ = Q₄.comp g.toLinearMap := QuadraticMap.ext fun x => (g.map_app x).symm
refine (QuadraticMap.associated_rightInverse R).injective ?_
ext m₁ m₃ m₁' m₃'
- simp [-associated_apply, h₁, h₃, associated_tmul]
+ simp [h₁, h₃, associated_tmul]
@[simp]
theorem tmul_tensorMap_apply
@@ -156,8 +156,7 @@ theorem comp_tensorRId_eq (Q₁ : QuadraticForm R M₁) :
Q₁.comp (TensorProduct.rid R M₁) = Q₁.tmul (sq (R := R)) := by
refine (QuadraticMap.associated_rightInverse R).injective ?_
ext m₁ m₁'
- simp only [associated_tmul, QuadraticMap.associated_comp]
- simp [-associated_apply, one_mul]
+ simp [associated_tmul, QuadraticMap.associated_comp, one_mul]
@[simp]
theorem tmul_tensorRId_apply
diff --git a/Mathlib/LinearAlgebra/RootSystem/Finite/Nondegenerate.lean b/Mathlib/LinearAlgebra/RootSystem/Finite/Nondegenerate.lean
index 72d1c45be90d40..20eda0b8dfc45e 100644
--- a/Mathlib/LinearAlgebra/RootSystem/Finite/Nondegenerate.lean
+++ b/Mathlib/LinearAlgebra/RootSystem/Finite/Nondegenerate.lean
@@ -328,11 +328,11 @@ lemma orthogonal_rootSpan_eq :
rw [← LinearMap.BilinForm.orthogonal_top_eq_ker P.rootForm_symmetric.isRefl]
refine le_antisymm ?_ (by intro; simp_all)
rintro x hx y -
- simp only [LinearMap.BilinForm.mem_orthogonal_iff, LinearMap.BilinForm.IsOrtho] at hx ⊢
+ simp only [LinearMap.BilinForm.mem_orthogonal_iff] at hx ⊢
obtain ⟨u, hu, v, hv, rfl⟩ : ∃ᵉ (u ∈ P.rootSpan R) (v ∈ LinearMap.ker P.RootForm), u + v = y := by
rw [← Submodule.mem_sup, P.isCompl_rootSpan_ker_rootForm.sup_eq_top]; exact Submodule.mem_top
simp only [LinearMap.mem_ker] at hv
- simp [LinearMap.IsOrtho, hx _ hu, hv]
+ simp [hx _ hu, hv]
@[simp]
lemma orthogonal_corootSpan_eq :
diff --git a/Mathlib/LinearAlgebra/SesquilinearForm/Basic.lean b/Mathlib/LinearAlgebra/SesquilinearForm/Basic.lean
index b46d360514ce54..bd8ccfce7388c1 100644
--- a/Mathlib/LinearAlgebra/SesquilinearForm/Basic.lean
+++ b/Mathlib/LinearAlgebra/SesquilinearForm/Basic.lean
@@ -25,9 +25,8 @@ basic lemmas about construction and elementary calculations are found there.
## Main declarations
-* `IsOrtho`: states that two vectors are orthogonal with respect to a sesquilinear map
* `IsSymm`, `IsAlt`: states that a sesquilinear form is symmetric and alternating, respectively
-* `orthogonalBilin` provides the orthogonal complement with respect to a sesquilinear form
+* `orthogonalBilin` provides the orthogonal complement with respect to a sesquilinear map
## References
@@ -57,28 +56,36 @@ variable [CommSemiring R] [CommSemiring R₁] [AddCommMonoid M₁] [Module R₁
{I₁ : R₁ →+* R} {I₂ : R₂ →+* R} {I₁' : R₁ →+* R}
/-- The proposition that two elements of a sesquilinear map space are orthogonal -/
+@[deprecated "Use `B x y = 0`" (since := "2026-03-30")]
def IsOrtho (B : M₁ →ₛₗ[I₁] M₂ →ₛₗ[I₂] M) (x : M₁) (y : M₂) : Prop :=
B x y = 0
+set_option linter.deprecated false in
+@[deprecated "`LinearMap.IsOrtho` has been deprecated" (since := "2026-03-30")]
theorem isOrtho_def {B : M₁ →ₛₗ[I₁] M₂ →ₛₗ[I₂] M} {x y} : B.IsOrtho x y ↔ B x y = 0 :=
Iff.rfl
+set_option linter.deprecated false in
+@[deprecated "`LinearMap.IsOrtho` has been deprecated" (since := "2026-03-30")]
theorem isOrtho_zero_left (B : M₁ →ₛₗ[I₁] M₂ →ₛₗ[I₂] M) (x) : IsOrtho B (0 : M₁) x := by
dsimp only [IsOrtho]
rw [map_zero B, zero_apply]
+set_option linter.deprecated false in
+@[deprecated "`LinearMap.IsOrtho` has been deprecated" (since := "2026-03-30")]
theorem isOrtho_zero_right (B : M₁ →ₛₗ[I₁] M₂ →ₛₗ[I₂] M) (x) : IsOrtho B x (0 : M₂) :=
map_zero (B x)
+set_option linter.deprecated false in
+@[deprecated "`LinearMap.IsOrtho` has been deprecated" (since := "2026-03-30")]
theorem isOrtho_flip {B : M₁ →ₛₗ[I₁] M₁ →ₛₗ[I₁'] M} {x y} : B.IsOrtho x y ↔ B.flip.IsOrtho y x := by
simp_rw [isOrtho_def, flip_apply]
open scoped Function in -- required for scoped `on` notation
/-- A set of vectors `v` is orthogonal with respect to some bilinear map `B` if and only
-if for all `i ≠ j`, `B (v i) (v j) = 0`. For orthogonality between two elements, use
-`BilinForm.isOrtho` -/
+if for all `i ≠ j`, `B (v i) (v j) = 0`. -/
def IsOrthoᵢ (B : M₁ →ₛₗ[I₁] M₁ →ₛₗ[I₁'] M) (v : n → M₁) : Prop :=
- Pairwise (B.IsOrtho on v)
+ Pairwise ((fun n m => B n m = 0) on v)
theorem isOrthoᵢ_def {B : M₁ →ₛₗ[I₁] M₁ →ₛₗ[I₁'] M} {v : n → M₁} :
B.IsOrthoᵢ v ↔ ∀ i j : n, i ≠ j → B (v i) (v j) = 0 :=
@@ -97,7 +104,8 @@ variable [Field K] [AddCommGroup V] [Module K V] [Field K₁] [AddCommGroup V₁
[Field K₂] [AddCommGroup V₂] [Module K₂ V₂]
{I₁ : K₁ →+* K} {I₂ : K₂ →+* K} {I₁' : K₁ →+* K} {J₁ : K →+* K} {J₂ : K →+* K}
--- todo: this also holds for [CommRing R] [IsDomain R] when J₁ is invertible
+set_option linter.deprecated false in
+@[deprecated "`LinearMap.IsOrtho` has been deprecated" (since := "2026-03-30")]
theorem ortho_smul_left {B : V₁ →ₛₗ[I₁] V₂ →ₛₗ[I₂] V} {x y} {a : K₁} (ha : a ≠ 0) :
IsOrtho B x y ↔ IsOrtho B (a • x) y := by
dsimp only [IsOrtho]
@@ -109,7 +117,8 @@ theorem ortho_smul_left {B : V₁ →ₛₗ[I₁] V₂ →ₛₗ[I₂] V} {x y}
trivial
· exact H
--- todo: this also holds for [CommRing R] [IsDomain R] when J₂ is invertible
+set_option linter.deprecated false in
+@[deprecated "`LinearMap.IsOrtho` has been deprecated" (since := "2026-03-30")]
theorem ortho_smul_right {B : V₁ →ₛₗ[I₁] V₂ →ₛₗ[I₂] V} {x y} {a : K₂} {ha : a ≠ 0} :
IsOrtho B x y ↔ IsOrtho B x (a • y) := by
simp_all [IsOrtho]
@@ -117,7 +126,7 @@ theorem ortho_smul_right {B : V₁ →ₛₗ[I₁] V₂ →ₛₗ[I₂] V} {x y}
/-- A set of orthogonal vectors `v` with respect to some sesquilinear map `B` is linearly
independent if for all `i`, `B (v i) (v i) ≠ 0`. -/
theorem linearIndependent_of_isOrthoᵢ {B : V₁ →ₛₗ[I₁] V₁ →ₛₗ[I₁'] V} {v : n → V₁}
- (hv₁ : B.IsOrthoᵢ v) (hv₂ : ∀ i, ¬B.IsOrtho (v i) (v i)) : LinearIndependent K₁ v := by
+ (hv₁ : B.IsOrthoᵢ v) (hv₂ : ∀ i, B (v i) (v i) ≠ 0) : LinearIndependent K₁ v := by
classical
rw [linearIndependent_iff']
intro s w hs i hi
@@ -134,7 +143,6 @@ end Field
/-! ### Reflexive bilinear maps -/
-
section Reflexive
variable [CommSemiring R] [AddCommMonoid M] [Module R M] [CommSemiring R₁] [AddCommMonoid M₁]
@@ -154,8 +162,8 @@ theorem eq_zero : ∀ {x y}, B x y = 0 → B y x = 0 := fun {x y} ↦ H x y
theorem eq_iff {x y} : B x y = 0 ↔ B y x = 0 := ⟨H x y, H y x⟩
-theorem ortho_comm {x y} : IsOrtho B x y ↔ IsOrtho B y x :=
- ⟨eq_zero H, eq_zero H⟩
+@[deprecated (since := "2026-03-30")]
+alias ortho_comm := eq_iff
theorem domRestrict (p : Submodule R₁ M₁) : (B.domRestrict₁₂ p p).IsRefl :=
fun _ _ ↦ by
@@ -184,7 +192,6 @@ end Reflexive
/-! ### Symmetric bilinear forms -/
-
section Symmetric
variable [CommSemiring R] [AddCommMonoid M] [Module R M] {I : R →+* R} {B : M →ₛₗ[I] M →ₗ[R] R}
@@ -202,8 +209,10 @@ theorem isRefl (H : B.IsSymm) : B.IsRefl := fun x y H1 ↦ by
rw [← H.eq]
simp [H1]
-theorem ortho_comm (H : B.IsSymm) {x y} : IsOrtho B x y ↔ IsOrtho B y x :=
- H.isRefl.ortho_comm
+theorem eq_iff (H : B.IsSymm) {x y} : B x y = 0 ↔ B y x = 0 := H.isRefl.eq_iff
+
+@[deprecated (since := "2026-03-30")]
+alias ortho_comm := eq_iff
theorem domRestrict (H : B.IsSymm) (p : Submodule R M) : (B.domRestrict₁₂ p p).IsSymm where
eq _ _ := by
@@ -273,7 +282,6 @@ end PositiveSemidefinite
/-! ### Alternating bilinear maps -/
-
section Alternating
section CommSemiring
@@ -317,8 +325,10 @@ theorem isRefl (H : B.IsAlt) : B.IsRefl := by
intro x y h
rw [← neg H, h, neg_zero]
-theorem ortho_comm (H : B.IsAlt) {x y} : IsOrtho B x y ↔ IsOrtho B y x :=
- H.isRefl.ortho_comm
+theorem eq_iff (H : B.IsAlt) {x y} : B x y = 0 ↔ B y x = 0 := H.isRefl.eq_iff
+
+@[deprecated (since := "2026-03-30")]
+alias ortho_comm := eq_iff
end IsAlt
@@ -371,16 +381,13 @@ chirality; in addition to this "left" orthogonal complement one could define a "
complement for which, for all `y` in `N`, `B y x = 0`. This variant definition is not currently
provided in mathlib. -/
def orthogonalBilin (N : Submodule R₁ M₁) : Submodule R₂ M₂ where
- carrier := { m | ∀ n ∈ N, B.IsOrtho n m }
- zero_mem' x _ := B.isOrtho_zero_right x
- add_mem' hx hy n hn := by
- rw [LinearMap.IsOrtho, map_add, show B n _ = 0 from hx n hn, show B n _ = 0 from hy n hn,
- zero_add]
- smul_mem' c x hx n hn := by
- rw [LinearMap.IsOrtho, map_smulₛₗ, show B n x = 0 from hx n hn, smul_zero]
+ carrier := { m | ∀ n ∈ N, B n m = 0 }
+ zero_mem' x _ := map_zero _
+ add_mem' {u v} hu hv x hx := by simp [hu _ hx, hv _ hx]
+ smul_mem' c y hy x hx := by simp [hy _ hx]
@[simp]
-theorem mem_orthogonalBilin_iff {m : M₂} : m ∈ N.orthogonalBilin B ↔ ∀ n ∈ N, B.IsOrtho n m :=
+theorem mem_orthogonalBilin_iff {m : M₂} : m ∈ N.orthogonalBilin B ↔ ∀ n ∈ N, B n m = 0:=
Iff.rfl
theorem orthogonalBilin_le (h : N ≤ L) : L.orthogonalBilin B ≤ N.orthogonalBilin B :=
@@ -408,14 +415,14 @@ variable [Field K] [AddCommGroup V] [Module K V] [Field K₁] [AddCommGroup V₁
-- ↓ This lemma only applies in fields as we require `a * b = 0 → a = 0 ∨ b = 0`
theorem span_singleton_inf_orthogonal_eq_bot (B : V₁ →ₛₗ[J₁] V₁ →ₛₗ[J₁'] V₂) (x : V₁)
- (hx : ¬B.IsOrtho x x) : (K₁ ∙ x) ⊓ (K₁ ∙ x).orthogonalBilin B = ⊥ := by
+ (hx : B x x ≠ 0) : (K₁ ∙ x) ⊓ (K₁ ∙ x).orthogonalBilin B = ⊥ := by
rw [← Finset.coe_singleton]
refine eq_bot_iff.2 fun y h ↦ ?_
obtain ⟨μ, -, rfl⟩ := Submodule.mem_span_finset.1 h.1
replace h := h.2 x (by simp [Submodule.mem_span] : x ∈ Submodule.span K₁ ({x} : Finset V₁))
rw [Finset.sum_singleton] at h ⊢
suffices hμzero : μ x = 0 by rw [hμzero, zero_smul, Submodule.mem_bot]
- rw [isOrtho_def, map_smulₛₗ] at h
+ rw [map_smulₛₗ] at h
exact Or.elim (smul_eq_zero.mp h)
(fun y ↦ by simpa using y)
(fun hfalse ↦ False.elim <| hx hfalse)
@@ -428,11 +435,11 @@ theorem orthogonal_span_singleton_eq_to_lin_ker {B : V →ₗ[K] V →ₛₗ[J]
constructor
· exact fun h ↦ h x ⟨1, one_smul _ _⟩
· rintro h _ ⟨z, rfl⟩
- rw [isOrtho_def, map_smulₛₗ₂, smul_eq_zero]
+ rw [map_smulₛₗ₂, smul_eq_zero]
exact Or.intro_right _ h
-- todo: Generalize this to sesquilinear maps
-theorem span_singleton_sup_orthogonal_eq_top {B : V →ₗ[K] V →ₗ[K] K} {x : V} (hx : ¬B.IsOrtho x x) :
+theorem span_singleton_sup_orthogonal_eq_top {B : V →ₗ[K] V →ₗ[K] K} {x : V} (hx : B x x ≠ 0) :
(K ∙ x) ⊔ (K ∙ x).orthogonalBilin B = ⊤ := by
rw [orthogonal_span_singleton_eq_to_lin_ker]
exact (B x).span_singleton_sup_ker_eq_top hx
@@ -440,7 +447,7 @@ theorem span_singleton_sup_orthogonal_eq_top {B : V →ₗ[K] V →ₗ[K] K} {x
-- todo: Generalize this to sesquilinear maps
/-- Given a bilinear form `B` and some `x` such that `B x x ≠ 0`, the span of the singleton of `x`
is complement to its orthogonal complement. -/
-theorem isCompl_span_singleton_orthogonal {B : V →ₗ[K] V →ₗ[K] K} {x : V} (hx : ¬B.IsOrtho x x) :
+theorem isCompl_span_singleton_orthogonal {B : V →ₗ[K] V →ₗ[K] K} {x : V} (hx : B x x ≠ 0) :
IsCompl (K ∙ x) ((K ∙ x).orthogonalBilin B) :=
{ disjoint := disjoint_iff.2 <| span_singleton_inf_orthogonal_eq_bot B x hx
codisjoint := codisjoint_iff.2 <| span_singleton_sup_orthogonal_eq_top hx }
@@ -449,7 +456,6 @@ end Orthogonal
/-! ### Adjoint pairs -/
-
section AdjointPair
section AddCommMonoid
@@ -558,7 +564,6 @@ end AdjointPair
/-! ### Self-adjoint pairs -/
-
section SelfadjointPair
section AddCommMonoid
@@ -657,7 +662,6 @@ end SelfadjointPair
/-! ### Nondegenerate bilinear maps -/
-
section Nondegenerate
section CommSemiring
@@ -837,8 +841,7 @@ theorem nondegenerate_restrict_of_disjoint_orthogonal {B : M →ₗ[R] M →ₗ[
refine hW.le_bot ⟨hx, fun y hy ↦ ?_⟩
specialize b₁ ⟨y, hy⟩
simp_rw [domRestrict₁₂_apply] at b₁
- rw [hB.ortho_comm]
- exact b₁
+ exact hB.eq_zero b₁
end CommRing
@@ -851,7 +854,7 @@ variable {R M M₁ : Type*} [CommSemiring R] [AddCommMonoid M] [AddCommMonoid M
elements. -/
theorem IsOrthoᵢ.not_isOrtho_basis_self_of_separatingLeft [Nontrivial R]
{v : Basis n R M} (h : B.IsOrthoᵢ v) (hB : B.SeparatingLeft)
- (i : n) : ¬B.IsOrtho (v i) (v i) := by
+ (i : n) : B (v i) (v i) ≠ 0 := by
intro ho
refine v.ne_zero i (hB (v i) fun m ↦ ?_)
obtain ⟨vi, rfl⟩ := v.repr.symm.surjective m
@@ -868,9 +871,8 @@ theorem IsOrthoᵢ.not_isOrtho_basis_self_of_separatingLeft [Nontrivial R]
elements. -/
theorem IsOrthoᵢ.not_isOrtho_basis_self_of_separatingRight [Nontrivial R]
{v : Basis n R M} (h : B.IsOrthoᵢ v) (hB : B.SeparatingRight)
- (i : n) : ¬B.IsOrtho (v i) (v i) := by
+ (i : n) : B (v i) (v i) ≠ 0 := by
rw [isOrthoᵢ_flip] at h
- rw [isOrtho_flip]
exact h.not_isOrtho_basis_self_of_separatingLeft (flip_separatingLeft.mpr hB) i
variable [IsDomain R] [IsTorsionFree R M₁]
@@ -878,7 +880,7 @@ variable [IsDomain R] [IsTorsionFree R M₁]
/-- Given an orthogonal basis with respect to a bilinear map, the bilinear map is left-separating if
the basis has no elements which are self-orthogonal. -/
theorem IsOrthoᵢ.separatingLeft_of_not_isOrtho_basis_self {B : M →ₗ[R] M →ₗ[R] M₁} (v : Basis n R M)
- (hO : B.IsOrthoᵢ v) (h : ∀ i, ¬B.IsOrtho (v i) (v i)) : B.SeparatingLeft := by
+ (hO : B.IsOrthoᵢ v) (h : ∀ i, B (v i) (v i) ≠ 0) : B.SeparatingLeft := by
intro m hB
obtain ⟨vi, rfl⟩ := v.repr.symm.surjective m
rw [LinearEquiv.map_eq_zero_iff]
@@ -888,7 +890,10 @@ theorem IsOrthoᵢ.separatingLeft_of_not_isOrtho_basis_self {B : M →ₗ[R] M
simp_rw [Basis.repr_symm_apply, Finsupp.linearCombination_apply, Finsupp.sum, map_sum₂,
map_smulₛₗ₂] at hB
rw [Finset.sum_eq_single i] at hB
- · exact (smul_eq_zero.mp hB).elim _root_.id (h i).elim
+ · cases smul_eq_zero.mp hB
+ · assumption
+ · specialize h i
+ contradiction
· intro j _hj hij
replace hij : B (v j) (v i) = 0 := hO hij
rw [hij, RingHom.id_apply, smul_zero]
@@ -899,17 +904,16 @@ theorem IsOrthoᵢ.separatingLeft_of_not_isOrtho_basis_self {B : M →ₗ[R] M
/-- Given an orthogonal basis with respect to a bilinear map, the bilinear map is right-separating
if the basis has no elements which are self-orthogonal. -/
lemma IsOrthoᵢ.separatingRight_iff_not_isOrtho_basis_self {B : M →ₗ[R] M →ₗ[R] M₁} (v : Basis n R M)
- (hO : B.IsOrthoᵢ v) (h : ∀ i, ¬B.IsOrtho (v i) (v i)) : B.SeparatingRight := by
+ (hO : B.IsOrthoᵢ v) (h : ∀ i, B (v i) (v i) ≠ 0) : B.SeparatingRight := by
rw [isOrthoᵢ_flip] at hO
rw [← flip_separatingLeft]
refine IsOrthoᵢ.separatingLeft_of_not_isOrtho_basis_self v hO fun i ↦ ?_
- rw [isOrtho_flip]
exact h i
/-- Given an orthogonal basis with respect to a bilinear map, the bilinear map is nondegenerate
if the basis has no elements which are self-orthogonal. -/
theorem IsOrthoᵢ.nondegenerate_of_not_isOrtho_basis_self {B : M →ₗ[R] M →ₗ[R] M₁} (v : Basis n R M)
- (hO : B.IsOrthoᵢ v) (h : ∀ i, ¬B.IsOrtho (v i) (v i)) : B.Nondegenerate :=
+ (hO : B.IsOrthoᵢ v) (h : ∀ i, B (v i) (v i) ≠ 0) : B.Nondegenerate :=
⟨IsOrthoᵢ.separatingLeft_of_not_isOrtho_basis_self v hO h,
IsOrthoᵢ.separatingRight_iff_not_isOrtho_basis_self v hO h⟩
diff --git a/docs/undergrad.yaml b/docs/undergrad.yaml
index 5daf196a4b20b8..710b7695d179c4 100644
--- a/docs/undergrad.yaml
+++ b/docs/undergrad.yaml
@@ -204,7 +204,7 @@ Bilinear and Quadratic Forms Over a Vector Space:
quadratic form: 'QuadraticForm'
polar form of a quadratic: 'QuadraticMap.polar'
Orthogonality:
- orthogonal elements: 'LinearMap.BilinForm.IsOrtho'
+ orthogonal elements: 'LinearMap.BilinForm.iIsOrtho'
adjoint endomorphism: 'LinearMap.BilinForm.leftAdjointOfNondegenerate'
Sylvester's law of inertia (existence): 'QuadraticForm.equivalent_one_zero_neg_one_weighted_sum_squared'
Sylvester's law of inertia (uniqueness): 'QuadraticForm.sigPos_of_equiv_weightedSumSquares'
From 691477963dcf9f47ef6dda6419e864200b5f3ffd Mon Sep 17 00:00:00 2001
From: Christian Merten <136261474+chrisflav@users.noreply.github.com>
Date: Wed, 17 Jun 2026 06:16:41 +0000
Subject: [PATCH 0099/1300] chore(AlgebraicGeometry/AffineTransitionLimit):
deduce that `Hom(-, X)` preserves certain cofiltered limits (#40546)
We deduce this from the unbundled statement. Usually the unbundled formulation is more useful, but sometimes we need the categorical spelling to apply general API. We also add some API for descending a finite affine open cover.
From Proetale.
---
.../AffineTransitionLimit.lean | 108 ++++++++++++++++--
1 file changed, 96 insertions(+), 12 deletions(-)
diff --git a/Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean b/Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean
index 4f7291ff066e06..c12b8899b8151a 100644
--- a/Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean
+++ b/Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean
@@ -11,6 +11,7 @@ public import Mathlib.AlgebraicGeometry.Morphisms.Separated
public import Mathlib.AlgebraicGeometry.Morphisms.FinitePresentation
public import Mathlib.AlgebraicGeometry.QuasiAffine
public import Mathlib.CategoryTheory.Limits.Shapes.Pullback.Connected
+public import Mathlib.CategoryTheory.Limits.Types.ColimitTypeFiltered
public import Mathlib.CategoryTheory.Monad.Limits
/-!
@@ -24,7 +25,7 @@ following EGA IV 8 and https://stacks.math.columbia.edu/tag/01YT.
@[expose] public section
-universe uI u
+universe w uI u
open CategoryTheory Limits
@@ -1070,6 +1071,30 @@ lemma exists_isAffineOpen_preimage_eq
obtain ⟨j, hj⟩ := Scheme.exists_isAffine_of_isLimit _ _ (isLimitOpensCone D c hc i U)
exact ⟨_, _, hj, by simp [← Scheme.Hom.comp_preimage]⟩
+set_option backward.isDefEq.respectTransparency false in
+open TopologicalSpace in
+include hc in
+lemma Scheme.exists_isOpenCover_and_isAffine_of_finite [IsCofiltered I]
+ [∀ {i j} (f : i ⟶ j), IsAffineHom (D.map f)] [∀ (i : I), CompactSpace (D.obj i)]
+ [∀ (i : I), QuasiSeparatedSpace (D.obj i)]
+ {J : Type*} [Finite J] (U : J → c.pt.Opens) (hU : IsOpenCover U)
+ (hU' : ∀ i, IsAffineOpen (U i)) :
+ ∃ (i : I) (V : J → (D.obj i).Opens),
+ IsOpenCover V ∧ ∀ j, IsAffineOpen (V j) ∧ U j = c.π.app i ⁻¹ᵁ (V j) := by
+ classical
+ choose j V hV hVU using fun k ↦ exists_isAffineOpen_preimage_eq D c hc (U k) (hU' k)
+ cases nonempty_fintype J
+ obtain ⟨i, fi⟩ := IsCofiltered.inf_objs_exists (Finset.univ.image j)
+ replace fi : ∀ k, i ⟶ j k := fun k ↦ (fi (by simp)).some
+ obtain ⟨k, fkj, e⟩ := exists_map_eq_top D c hc (⨆ (k), D.map (fi k) ⁻¹ᵁ V k) (by
+ simp_rw [Hom.preimage_iSup, ← Hom.comp_preimage, c.w, hVU]
+ exact hU)
+ refine ⟨k, fun x ↦ D.map (fkj ≫ fi x) ⁻¹ᵁ V _, ?_, fun k ↦ ⟨(hV k).preimage _, ?_⟩⟩
+ · refine top_le_iff.mp (e.symm.trans_le ?_)
+ simp_rw [Hom.preimage_iSup, ← Hom.comp_preimage, ← D.map_comp]
+ simp
+ · rw [← hVU, ← Hom.comp_preimage, c.w]
+
set_option backward.isDefEq.respectTransparency false in
open TopologicalSpace in
include hc in
@@ -1084,19 +1109,48 @@ lemma Scheme.exists_isOpenCover_and_isAffine [IsCofiltered I]
IsOpenCover V ∧ ∀ j, IsAffineOpen (V j) ∧ U j = c.π.app i ⁻¹ᵁ (V j) := by
classical
have := compactSpace_of_isLimit D c hc
- choose j V hV hVU using fun k ↦ exists_isAffineOpen_preimage_eq D c hc (U k) (hU' k)
obtain ⟨s, hs⟩ := isCompact_univ.elim_finite_subcover _
(fun i ↦ (U i).isOpen) hU.iSup_set_eq_univ.ge
- obtain ⟨i, fi⟩ := IsCofiltered.inf_objs_exists (s.image j)
- replace fi : ∀ k ∈ s, i ⟶ j k := fun k hk ↦ (fi (Finset.mem_image_of_mem _ hk)).some
- obtain ⟨k, fkj, e⟩ := exists_map_eq_top D c hc (⨆ (k) (hk : k ∈ s), D.map (fi k hk) ⁻¹ᵁ V k) (by
- simp_rw [Hom.preimage_iSup, ← Hom.comp_preimage, c.w, hVU]
- exact top_le_iff.mp fun x _ ↦ by simpa using hs (Set.mem_univ x))
- refine ⟨k, s, fun x ↦ D.map (fkj ≫ fi x.1 x.2) ⁻¹ᵁ V _, ?_, fun k ↦ ⟨(hV k).preimage _, ?_⟩⟩
- · refine top_le_iff.mp (e.symm.trans_le ?_)
- simp_rw [Hom.preimage_iSup, ← Hom.comp_preimage, iSup_subtype, ← D.map_comp]
- simp
- · rw [← hVU, ← Hom.comp_preimage, c.w]
+ have hU : IsOpenCover fun j : s ↦ U ↑j := by
+ simpa only [IsOpenCover, eq_top_iff, ← SetLike.coe_subset_coe, Opens.coe_top, Opens.iSup_mk,
+ Opens.carrier_eq_coe, Opens.coe_mk, Set.iUnion_subtype]
+ obtain ⟨i, V, hV, heq⟩ := Scheme.exists_isOpenCover_and_isAffine_of_finite _ _ hc _ hU (hU' ·)
+ use i, s, V, hV
+
+set_option backward.defeqAttrib.useBackward true in
+set_option backward.isDefEq.respectTransparency false in
+include hc in
+/-- Variant of `Scheme.exists_isOpenCover_and_isAffine_of_finite` in terms of `Scheme.OpenCover`. -/
+lemma Scheme.OpenCover.exists_of_isCofiltered_of_finite [IsCofiltered I]
+ [∀ {i j} (f : i ⟶ j), IsAffineHom (D.map f)] [∀ (i : I), CompactSpace (D.obj i)]
+ [∀ (i : I), QuasiSeparatedSpace (D.obj i)]
+ (𝒰 : OpenCover.{w} c.pt) [∀ i, IsAffine (𝒰.X i)] [Finite 𝒰.I₀] :
+ ∃ (i : I) (R : 𝒰.I₀ → CommRingCat.{u}) (f : ∀ (a : 𝒰.I₀), Spec (R a) ⟶ (D.obj i))
+ (_ : Presieve.ofArrows _ f ∈ zariskiPrecoverage _) (g : ∀ (j : 𝒰.I₀), 𝒰.X j ⟶ Spec (R j)),
+ ∀ (j : 𝒰.I₀), IsPullback (g j) (𝒰.f j) (f j) (c.π.app i) := by
+ obtain ⟨i, V, hV, hV'⟩ := Scheme.exists_isOpenCover_and_isAffine_of_finite _ _ hc _
+ 𝒰.isOpenCover_opensRange fun k ↦ isAffineOpen_opensRange (𝒰.f k)
+ have hV'' (k) := dsimp% congr($((hV' k).right).carrier)
+ refine ⟨i, fun k ↦ Γ(_, V k), fun k ↦ (hV' k).left.isoSpec.inv ≫ (V k).ι, ?_, ?_, ?_⟩
+ · simp only [IsAffineOpen.isoSpec_inv_ι, ofArrows_mem_precoverage_iff,
+ IsAffineOpen.range_fromSpec, SetLike.mem_coe]
+ exact ⟨fun x ↦ hV.exists_mem x, inferInstance⟩
+ · intro k
+ exact IsOpenImmersion.lift (V k).ι (𝒰.f _ ≫ c.π.app i) (by simp [hV'', Set.range_comp]) ≫
+ (hV' k).left.isoSpec.hom
+ · intro k
+ dsimp
+ refine ⟨⟨?_⟩, ⟨PullbackCone.IsLimit.mk _ ?_ ?_ ?_ ?_⟩⟩
+ · simp [← IsAffineOpen.isoSpec_inv_ι]
+ · intro s
+ refine IsOpenImmersion.lift (𝒰.f k) s.snd ?_
+ simp only [hV'', Set.range_subset_iff, Set.mem_preimage, SetLike.mem_coe]
+ intro y
+ rw [← Scheme.Hom.comp_apply, ← s.condition]
+ simp [← IsAffineOpen.isoSpec_inv_ι]
+ · simp [← cancel_mono (hV' _).left.isoSpec.inv, ← cancel_mono (V k).ι, PullbackCone.condition]
+ · simp
+ · simp [← cancel_mono (𝒰.f k)]
end IsAffine
@@ -1283,6 +1337,36 @@ lemma Scheme.exists_π_app_comp_eq_of_locallyOfFinitePresentation
· refine 𝒲.hom_ext _ _ fun j ↦ ?_
simp [F, Cover.ι_glueMorphisms_assoc, hak]; rfl
+set_option backward.defeqAttrib.useBackward true in
+set_option backward.isDefEq.respectTransparency false in
+/-- `Hom_S(-, X)` sends a cofiltered limit of qcqs `S`-schemes with affine transition maps
+to a filtered colimit if `X` is locally of finite presentation over `X`. -/
+instance Scheme.preservesColimit_yoneda (D : I ⥤ Over S) [IsCofiltered I]
+ [∀ {i j} (f : i ⟶ j), IsAffineHom (D.map f).left]
+ [∀ (i : I), CompactSpace (D.obj i).left] [∀ (i : I), QuasiSeparatedSpace (D.obj i).left]
+ (X : Over S) [LocallyOfFinitePresentation X.hom] :
+ PreservesColimit D.op (yoneda.obj X) where
+ preserves {c hc} := by
+ rw [Limits.Types.isColimit_iff_coconeTypesIsColimit]
+ have (i : I) : CompactSpace ((D ⋙ Over.forget S).obj i) := by dsimp; infer_instance
+ have (i : I) : QuasiSeparatedSpace ((D ⋙ Over.forget S).obj i) := by dsimp; infer_instance
+ have {i j : I} (f : i ⟶ j) : IsAffineHom ((D ⋙ Over.forget S).map f) := by
+ dsimp; infer_instance
+ refine ⟨⟨?_, ?_⟩⟩
+ · rw [Functor.CoconeTypes.descColimitType_injective_iff_of_isFiltered']
+ intro k g₁ g₂ hg
+ obtain ⟨k, hik, heq⟩ := Scheme.exists_hom_comp_eq_comp_of_locallyOfFiniteType
+ (D ⋙ Over.forget _) (.mk (fun _ ↦ (D.obj _).hom)) X.hom _ (isLimitOfPreserves _ hc.unop)
+ g₁.left g₂.left (Over.w g₁).symm (Over.w g₂).symm congr($(hg).left)
+ use .op k, hik.op
+ cat_disch
+ · intro g
+ obtain ⟨k, u, h, h'⟩ := Scheme.exists_π_app_comp_eq_of_locallyOfFinitePresentation
+ (D ⋙ Over.forget _) (.mk (fun _ ↦ (D.obj _).hom)) X.hom _ (isLimitOfPreserves _ hc.unop)
+ g.left (by ext; simp)
+ use Functor.ιColimitType _ (.op k) (Over.homMk u)
+ cat_disch
+
end LocallyOfFinitePresentation
end AlgebraicGeometry
From 598d514da0055c5d4d63a6d268dbdb61574f42ee Mon Sep 17 00:00:00 2001
From: Sebastien Gouezel <10818434+sgouezel@users.noreply.github.com>
Date: Wed, 17 Jun 2026 07:42:24 +0000
Subject: [PATCH 0100/1300] chore: refactor the vector measure integral by
changing the reference measure (#40603)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Given a bilinear form `B` on `E x F` to `G`, a function `f` with values in `E` and a vector measure `v` with values in `F`, the integral wrt the vector measure is defined when the function is integrable wrt the measure `(v.transpose B).variation`, which is the minimal condition for the integral to make sense.
I have played a lot recently with integrals for vector measures, and I have realized that assuming this minimal condition creates a lot of complications, for essentially no gain. In this PR, I require the stronger condition that the function is integrable wrt `v.variation`. So, the integrability condition does not depend on `B` any more. This makes for smoother statements and smoother proofs (especially in the forthcoming Fubini theorem). In all standard applications, `B` is an isometry, so `(v.transpose B).variation = v.variation`, and the theory is unchanged.
Note that the new approach does not lose any generality: in the unlikely event one wants to integrate a function which is only integrable wrt `(v.transpose B).variation` for some exotic `B`, then one can integrate with the vector measure `v.transpose B` (which takes values in `E -> G`) and the bilinear form which is the function application, i.e., `E -> (E -> G) -> G`.
Zulip discussion at [#mathlib4 > Refactoring vector measure integral @ 💬](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Refactoring.20vector.20measure.20integral/near/603283077). Everyone seems to agree the refactor is a good idea.
Co-authored-by: sgouezel
---
Mathlib/Data/NNReal/Defs.lean | 2 +
.../MeasureTheory/VectorMeasure/Integral.lean | 393 ++++++++++--------
.../VectorMeasure/Variation/Basic.lean | 3 +-
3 files changed, 216 insertions(+), 182 deletions(-)
diff --git a/Mathlib/Data/NNReal/Defs.lean b/Mathlib/Data/NNReal/Defs.lean
index 612791983d60ef..41e15f10ecf1e6 100644
--- a/Mathlib/Data/NNReal/Defs.lean
+++ b/Mathlib/Data/NNReal/Defs.lean
@@ -127,6 +127,8 @@ noncomputable instance : LinearOrderedCommGroupWithZero ℝ≥0 where
example {p q : ℝ≥0} (h1p : 0 < p) (h2p : p ≤ q) : q⁻¹ ≤ p⁻¹ := by
with_reducible_and_instances exact inv_anti₀ h1p h2p
+@[simp] lemma mk_coe (a : ℝ≥0) (ha : 0 ≤ (a : ℝ)) : NNReal.mk (a : ℝ) ha = a := rfl
+
-- Simp lemma to put back `n.val` into the normal form given by the coercion.
@[simp]
theorem val_eq_coe (n : ℝ≥0) : n.val = n :=
diff --git a/Mathlib/MeasureTheory/VectorMeasure/Integral.lean b/Mathlib/MeasureTheory/VectorMeasure/Integral.lean
index 9c96314769d4c9..bc0a720f0b4a69 100644
--- a/Mathlib/MeasureTheory/VectorMeasure/Integral.lean
+++ b/Mathlib/MeasureTheory/VectorMeasure/Integral.lean
@@ -58,7 +58,7 @@ We often consider integrable functions with respect to the total variation of
`μ.transpose B` = `μ.mapRange B.flip.toAddMonoidHom B.flip.continuous`, which is the reference
measure for the pairing integral.
-When `f` is not integrable with respect to `(μ.transpose B).variation`, the value of
+When `f` is not integrable with respect to `μ.variation`, the value of
`μ.integral B f` is set to `0`. This is an analogous convention to the Bochner integral. However,
there are cases where a natural definition of the integral as an unconditional sum exists, but `f`
is not integrable in this sense: Let `μ` be the `L∞(ℕ)`-valued measure on `ℕ` defined by extending
@@ -73,10 +73,11 @@ public section
open Set MeasureTheory VectorMeasure ContinuousLinearMap Filter Topology
open scoped ENNReal NNReal
-variable {ι X Y E F G : Type*} {mX : MeasurableSpace X} [MeasurableSpace Y]
+variable {ι X Y E F G H : Type*} {mX : MeasurableSpace X} [MeasurableSpace Y]
[NormedAddCommGroup E] [NormedSpace ℝ E]
[NormedAddCommGroup F] [NormedSpace ℝ F]
[NormedAddCommGroup G] [NormedSpace ℝ G]
+ [NormedAddCommGroup H] [NormedSpace ℝ H]
namespace MeasureTheory
@@ -112,17 +113,25 @@ theorem cbmApplyMeasure_union (μ : VectorMeasure X F) (B : E →L[ℝ] F →L[
ext x
simp [of_union hdisj hs ht]
-theorem dominatedFinMeasAdditive_cbmApplyMeasure (μ : VectorMeasure X F) (B : E →L[ℝ] F →L[ℝ] G) :
- DominatedFinMeasAdditive (μ.transpose B).variation (μ.transpose B) 1 := by
- refine ⟨fun s t hs ht _ _ hdisj ↦ cbmApplyMeasure_union μ B hs ht hdisj, fun s hs hsf ↦ ?_⟩
- simpa using! norm_measure_le_variation hsf.ne
-
theorem norm_cbmApplyMeasure_le (μ : VectorMeasure X F) (B : E →L[ℝ] F →L[ℝ] G) (s : Set X) :
‖cbmApplyMeasure μ B s‖ ≤ ‖B‖ * ‖μ s‖ := by
rw [opNorm_le_iff (by positivity)]
intro x
grw [cbmApplyMeasure_apply, le_opNorm₂, mul_right_comm]
+theorem dominatedFinMeasAdditive_cbmApplyMeasure (μ : VectorMeasure X F) (B : E →L[ℝ] F →L[ℝ] G) :
+ DominatedFinMeasAdditive μ.variation (μ.transpose B) ‖B‖ := by
+ refine ⟨fun s t hs ht _ _ hdisj ↦ cbmApplyMeasure_union μ B hs ht hdisj, fun s hs hsf ↦ ?_⟩
+ apply (norm_cbmApplyMeasure_le _ _ _).trans
+ gcongr
+ exact norm_measure_le_variation hsf.ne
+
+theorem dominatedFinMeasAdditive_transpose_cbmApplyMeasure
+ (μ : VectorMeasure X F) (B : E →L[ℝ] F →L[ℝ] G) :
+ DominatedFinMeasAdditive (μ.transpose B).variation (μ.transpose B) 1 := by
+ refine ⟨fun s t hs ht _ _ hdisj ↦ cbmApplyMeasure_union μ B hs ht hdisj, fun s hs hsf ↦ ?_⟩
+ simpa using! norm_measure_le_variation hsf.ne
+
end cbmApplyMeasure
namespace VectorMeasure
@@ -221,15 +230,15 @@ function with respect to a signed measure. -/
/-- `f : X → E` is said to be integrable with respect to `μ` and `B` if it is integrable with
respect to `(μ.transpose B).variation`. -/
-protected abbrev Integrable (μ : VectorMeasure X F) (f : X → E) (B : E →L[ℝ] F →L[ℝ] G) : Prop :=
- MeasureTheory.Integrable f (μ.transpose B).variation
+protected abbrev Integrable (μ : VectorMeasure X F) (f : X → E) : Prop :=
+ MeasureTheory.Integrable f μ.variation
/-- `f : X → E` is said to be integrable with respect to `μ` and `B` on `s` if it is integrable with
respect to the vector measure `μ.restrict s`. When `s` is measurable, this is equivalent to
integrability with respect to `(μ.transpose B).variation.restrict s`. -/
protected abbrev IntegrableOn
- (μ : VectorMeasure X F) (f : X → E) (B : E →L[ℝ] F →L[ℝ] G) (s : Set X) : Prop :=
- (μ.restrict s).Integrable f B
+ (μ : VectorMeasure X F) (f : X → E) (s : Set X) : Prop :=
+ (μ.restrict s).Integrable f
open Classical in
/-- The `G`-valued integral of `E`-valued function and the `F`-valued vector measure `μ` with linear
@@ -242,7 +251,7 @@ When `μ` is a signed measure, to get the integral in `G` of a `G`-valued functi
`B = (ContinousLinearMap.lsmul ℝ ℝ).flip`. Notation `∫ᵛ x, f x ∂<•μ`.
-/
noncomputable def integral (μ : VectorMeasure X F) (f : X → E) (B : E →L[ℝ] F →L[ℝ] G) : G :=
- setToFun (μ.transpose B).variation (μ.transpose B)
+ setToFun μ.variation (μ.transpose B)
(dominatedFinMeasAdditive_cbmApplyMeasure μ B) f
@[inherit_doc integral]
@@ -274,9 +283,14 @@ notation3 "∫ᵛ "(...)" in "s", "r:60:(scoped f => f)" ∂<•"μ:70 =>
variable {μ ν B}
-lemma integral_eq_setToFun : ∫ᵛ x, f x ∂[B; μ] = setToFun (μ.transpose B).variation (μ.transpose B)
+lemma integral_eq_setToFun : ∫ᵛ x, f x ∂[B; μ] = setToFun μ.variation (μ.transpose B)
(dominatedFinMeasAdditive_cbmApplyMeasure μ B) f := by rfl
+lemma integral_eq_setToFun_transpose (hf : μ.Integrable f) :
+ ∫ᵛ x, f x ∂[B; μ] = setToFun (μ.transpose B).variation (μ.transpose B)
+ (dominatedFinMeasAdditive_transpose_cbmApplyMeasure μ B) f :=
+ setToFun_congr_measure_of_integrable _ (by simp) (variation_transpose_le _ _) _ _ _ hf
+
theorem integral_of_not_completeSpace (hG : ¬CompleteSpace G) :
∫ᵛ x, f x ∂[B; μ] = 0 := by
simp [integral, setToFun, hG]
@@ -343,7 +357,7 @@ theorem transpose_sub_cbm (μ : VectorMeasure X F) (B C : E →L[ℝ] F →L[ℝ
section Function
-theorem integral_undef (h : ¬ μ.Integrable f B) :
+theorem integral_undef (h : ¬ μ.Integrable f) :
∫ᵛ x, f x ∂[B; μ] = 0 := by
simp [integral, setToFun_undef _ h]
@@ -351,48 +365,54 @@ theorem integral_undef (h : ¬ μ.Integrable f B) :
theorem integral_zero : ∫ᵛ _, 0 ∂[B; μ] = 0 :=
setToFun_zero _
-theorem integral_congr_ae (h : f =ᵐ[(μ.transpose B).variation] g) :
+theorem integral_congr_ae (h : f =ᵐ[μ.variation] g) :
∫ᵛ x, f x ∂[B; μ] = ∫ᵛ x, g x ∂[B; μ] :=
setToFun_congr_ae _ h
-theorem integral_eq_zero_of_ae (hf : f =ᵐ[(μ.transpose B).variation] 0) :
+theorem integral_eq_zero_of_ae (hf : f =ᵐ[μ.variation] 0) :
∫ᵛ x, f x ∂[B; μ] = 0 := by
simp [integral_congr_ae hf]
-@[to_fun] lemma Integrable.add (hf : μ.Integrable f B) (hg : μ.Integrable g B) :
- μ.Integrable (f + g) B :=
+omit [NormedSpace ℝ E] [NormedSpace ℝ F] in
+@[to_fun] lemma Integrable.add (hf : μ.Integrable f) (hg : μ.Integrable g) :
+ μ.Integrable (f + g) :=
MeasureTheory.Integrable.add hf hg
-@[to_fun] lemma Integrable.neg (hf : μ.Integrable f B) :
- μ.Integrable (-f) B :=
+omit [NormedSpace ℝ E] [NormedSpace ℝ F] in
+@[to_fun] lemma Integrable.neg (hf : μ.Integrable f) :
+ μ.Integrable (-f) :=
MeasureTheory.Integrable.neg hf
-@[to_fun] lemma Integrable.sub (hf : μ.Integrable f B) (hg : μ.Integrable g B) :
- μ.Integrable (f - g) B :=
+omit [NormedSpace ℝ E] [NormedSpace ℝ F] in
+@[to_fun] lemma Integrable.sub (hf : μ.Integrable f) (hg : μ.Integrable g) :
+ μ.Integrable (f - g) :=
MeasureTheory.Integrable.sub hf hg
+omit [NormedSpace ℝ E] [NormedSpace ℝ F] in
@[to_fun] lemma Integrable.smul {𝕜 : Type*} [NormedAddCommGroup 𝕜] [SMulZeroClass 𝕜 E]
- [IsBoundedSMul 𝕜 E] (c : 𝕜) (hf : μ.Integrable f B) :
- μ.Integrable (c • f) B :=
+ [IsBoundedSMul 𝕜 E] (c : 𝕜) (hf : μ.Integrable f) :
+ μ.Integrable (c • f) :=
MeasureTheory.Integrable.smul c hf
+omit [NormedSpace ℝ E] [NormedSpace ℝ F] in
theorem Integrable.finsetSum {ι : Type*} (s : Finset ι) {f : ι → X → E}
- (hf : ∀ i ∈ s, μ.Integrable (f i) B) : μ.Integrable (∑ i ∈ s, f i) B :=
+ (hf : ∀ i ∈ s, μ.Integrable (f i)) : μ.Integrable (∑ i ∈ s, f i) :=
integrable_finsetSum' s hf
+omit [NormedSpace ℝ E] [NormedSpace ℝ F] in
theorem Integrable.fun_finsetSum {ι : Type*} (s : Finset ι) {f : ι → X → E}
- (hf : ∀ i ∈ s, μ.Integrable (f i) B) : μ.Integrable (fun x ↦ ∑ i ∈ s, f i x) B :=
+ (hf : ∀ i ∈ s, μ.Integrable (f i)) : μ.Integrable (fun x ↦ ∑ i ∈ s, f i x) :=
integrable_finsetSum s hf
-theorem integral_fun_add (hf : μ.Integrable f B) (hg : μ.Integrable g B) :
+theorem integral_fun_add (hf : μ.Integrable f) (hg : μ.Integrable g) :
∫ᵛ x, f x + g x ∂[B; μ] = ∫ᵛ x, f x ∂[B; μ] + ∫ᵛ x, g x ∂[B; μ] :=
setToFun_add _ hf hg
-theorem integral_add (hf : μ.Integrable f B) (hg : μ.Integrable g B) :
+theorem integral_add (hf : μ.Integrable f) (hg : μ.Integrable g) :
∫ᵛ x, (f + g) x ∂[B; μ] = ∫ᵛ x, f x ∂[B; μ] + ∫ᵛ x, g x ∂[B; μ] := integral_fun_add hf hg
theorem integral_finsetSum (s : Finset ι) {f : ι → X → E}
- (hf : ∀ i ∈ s, μ.Integrable (f i) B) :
+ (hf : ∀ i ∈ s, μ.Integrable (f i)) :
∫ᵛ x, ∑ i ∈ s, f i x ∂[B; μ] = ∑ i ∈ s, ∫ᵛ x, f i x ∂[B; μ] :=
setToFun_finsetSum _ s hf
@@ -407,11 +427,11 @@ variable (f μ B) in
theorem integral_neg :
∫ᵛ x, (-f) x ∂[B; μ] = -∫ᵛ x, f x ∂[B; μ] := integral_fun_neg μ B f
-theorem integral_fun_sub (hf : μ.Integrable f B) (hg : μ.Integrable g B) :
+theorem integral_fun_sub (hf : μ.Integrable f) (hg : μ.Integrable g) :
∫ᵛ x, f x - g x ∂[B; μ] = ∫ᵛ x, f x ∂[B; μ] - ∫ᵛ x, g x ∂[B; μ] :=
setToFun_sub _ hf hg
-theorem integral_sub (hf : μ.Integrable f B) (hg : μ.Integrable g B) :
+theorem integral_sub (hf : μ.Integrable f) (hg : μ.Integrable g) :
∫ᵛ x, (f - g) x ∂[B; μ] = ∫ᵛ x, f x ∂[B; μ] - ∫ᵛ x, g x ∂[B; μ] := integral_fun_sub hf hg
variable (f μ B) in
@@ -426,7 +446,7 @@ theorem integral_smul (c : ℝ) :
∫ᵛ x, (c • f) x ∂[B; μ] = c • ∫ᵛ x, f x ∂[B; μ] := integral_fun_smul μ B c f
@[simp]
-theorem integral_const [CompleteSpace G] [IsFiniteMeasure (μ.transpose B).variation] (c : E) :
+theorem integral_const [CompleteSpace G] [IsFiniteMeasure μ.variation] (c : E) :
∫ᵛ _ : X, c ∂[B; μ] = B c (μ univ) :=
setToFun_const _ _
@@ -434,34 +454,40 @@ end Function
section VectorMeasure
+omit [NormedSpace ℝ E] [NormedSpace ℝ F] in
/- `simpNF` complains that this lemma can be proved by `simp`, because the `simp`-generated lemma
unfolds the abbrev `VectorMeasure.Integrable`. TODO: fix `simp`. See lean4#13958. -/
@[nolint simpNF, simp]
-lemma Integrable.zero_vectorMeasure : (0 : VectorMeasure X F).Integrable f B := by
+lemma Integrable.zero_vectorMeasure : (0 : VectorMeasure X F).Integrable f := by
simp [VectorMeasure.Integrable]
-lemma Integrable.add_vectorMeasure (hμ : μ.Integrable f B) (hν : ν.Integrable f B) :
- (μ + ν).Integrable f B := by
+omit [NormedSpace ℝ E] [NormedSpace ℝ F] in
+lemma Integrable.add_vectorMeasure (hμ : μ.Integrable f) (hν : ν.Integrable f) :
+ (μ + ν).Integrable f := by
apply Integrable.mono_measure (integrable_add_measure.2 ⟨hμ, hν⟩)
- grw [transpose_add, variation_add_le]
+ grw [variation_add_le]
-lemma Integrable.neg_vectorMeasure (hμ : μ.Integrable f B) :
- (-μ).Integrable f B :=
+omit [NormedSpace ℝ E] [NormedSpace ℝ F] in
+lemma Integrable.neg_vectorMeasure (hμ : μ.Integrable f) :
+ (-μ).Integrable f :=
Integrable.mono_measure hμ (by simp)
-lemma Integrable.sub_vectorMeasure (hμ : μ.Integrable f B) (hν : ν.Integrable f B) :
- (μ - ν).Integrable f B := by
+omit [NormedSpace ℝ E] [NormedSpace ℝ F] in
+lemma Integrable.sub_vectorMeasure (hμ : μ.Integrable f) (hν : ν.Integrable f) :
+ (μ - ν).Integrable f := by
convert hμ.add_vectorMeasure hν.neg_vectorMeasure using 1
exact sub_eq_add_neg μ ν
-lemma Integrable.smul_vectorMeasure (hμ : μ.Integrable f B) (c : ℝ) :
- (c • μ).Integrable f B := by
+omit [NormedSpace ℝ E] in
+lemma Integrable.smul_vectorMeasure (hμ : μ.Integrable f) (c : ℝ) :
+ (c • μ).Integrable f := by
apply Integrable.mono_measure (Integrable.smul_measure_nnreal hμ (c := ‖c‖₊))
- simp [transpose_smul, variation_smul]
+ simp [variation_smul]
+omit [NormedSpace ℝ E] [NormedSpace ℝ F] in
lemma Integrable.finsetSum_vectorMeasure {ι : Type*} {μ : ι → VectorMeasure X F} {s : Finset ι}
- (h : ∀ i ∈ s, (μ i).Integrable f B) :
- (∑ i ∈ s, μ i).Integrable f B := by
+ (h : ∀ i ∈ s, (μ i).Integrable f) :
+ (∑ i ∈ s, μ i).Integrable f := by
classical
induction s using Finset.induction_on with
| empty => simp
@@ -470,8 +496,9 @@ lemma Integrable.finsetSum_vectorMeasure {ι : Type*} {μ : ι → VectorMeasure
Finset.sum_insert] at h ⊢
exact h.1.add_vectorMeasure (ih h.2)
-lemma Integrable.restrict (hf : μ.Integrable f B) {s : Set X} :
- (μ.restrict s).Integrable f B := by
+omit [NormedSpace ℝ E] [NormedSpace ℝ F] in
+lemma Integrable.restrict (hf : μ.Integrable f) {s : Set X} :
+ (μ.restrict s).Integrable f := by
by_cases hs : MeasurableSet s
· simpa [VectorMeasure.Integrable, transpose_restrict, variation_restrict hs] using
MeasureTheory.Integrable.restrict hf
@@ -489,12 +516,12 @@ theorem integral_smul_vectorMeasure (f : X → E) (c : ℝ) :
by_cases hG : CompleteSpace G; swap
· simp [integral, setToFun, hG]
simp_rw [integral, ← setToFun_smul_left]
- have : ((c • μ).transpose B).variation = ‖c‖₊ • (μ.transpose B).variation := by
- simp [transpose, mapRange_smul, variation_smul]
- simp only [this, mul_one]
- have : DominatedFinMeasAdditive (μ.transpose B).variation ((c • μ).transpose B) ‖c‖ := by
- simp only [transpose_smul, FunLike.coe_smul, Real.norm_eq_abs]
- simpa using! (dominatedFinMeasAdditive_cbmApplyMeasure μ B).smul c
+ have : (c • μ).variation = ‖c‖₊ • μ.variation := by
+ simp [variation_smul]
+ simp only [this]
+ have : DominatedFinMeasAdditive μ.variation ((c • μ).transpose B) (‖c‖ * ‖B‖) := by
+ simp only [transpose_smul, FunLike.coe_smul]
+ exact (dominatedFinMeasAdditive_cbmApplyMeasure μ B).smul c
rw! [← setToFun_congr_smul_measure' _ this, transpose_smul]
rfl
@@ -503,13 +530,13 @@ theorem integral_smul_nnreal_vectorMeasure (f : X → E) (c : ℝ≥0) :
∫ᵛ x, f x ∂[B; c • μ] = c • ∫ᵛ x, f x ∂[B; μ] :=
integral_smul_vectorMeasure f (c : ℝ)
-theorem integral_add_vectorMeasure (hμ : μ.Integrable f B) (hν : ν.Integrable f B) :
+theorem integral_add_vectorMeasure (hμ : μ.Integrable f) (hν : ν.Integrable f) :
∫ᵛ x, f x ∂[B; μ + ν] = ∫ᵛ x, f x ∂[B; μ] + ∫ᵛ x, f x ∂[B; ν] :=
- setToFun_add_left'' (by simp [transpose]) hμ hν (by grw [transpose_add, variation_add_le])
- zero_le_one zero_le_one zero_le_one
+ setToFun_add_left'' (by simp [transpose]) hμ hν (by grw [variation_add_le])
+ (norm_nonneg _) (norm_nonneg _) (norm_nonneg _)
theorem integral_finsetSum_vectorMeasure {μ : ι → VectorMeasure X F}
- {s : Finset ι} (hf : ∀ i ∈ s, (μ i).Integrable f B) :
+ {s : Finset ι} (hf : ∀ i ∈ s, (μ i).Integrable f) :
∫ᵛ x, f x ∂[B; ∑ i ∈ s, μ i] = ∑ i ∈ s, ∫ᵛ x, f x ∂[B; μ i] := by
classical
induction s using Finset.induction_on with
@@ -524,7 +551,7 @@ theorem integral_neg_vectorMeasure :
∫ᵛ x, f x ∂[B; -μ] = -∫ᵛ x, f x ∂[B; μ] := by
simp [integral, ← setToFun_neg', FunLike.coe_neg]
-theorem integral_sub_vectorMeasure (hμ : μ.Integrable f B) (hν : ν.Integrable f B) :
+theorem integral_sub_vectorMeasure (hμ : μ.Integrable f) (hν : ν.Integrable f) :
∫ᵛ x, f x ∂[B; μ - ν] = ∫ᵛ x, f x ∂[B; μ] - ∫ᵛ x, f x ∂[B; ν] := by
rw [sub_eq_add_neg, integral_add_vectorMeasure hμ hν.neg_vectorMeasure,
integral_neg_vectorMeasure, ← sub_eq_add_neg]
@@ -533,76 +560,46 @@ end VectorMeasure
section cbm
-/- `simpNF` complains that this lemma can be proved by `simp`, because the `simp`-generated lemma
-unfolds the abbrev `VectorMeasure.Integrable`. TODO: fix `simp`. See lean4#13958. -/
-@[nolint simpNF, simp]
-lemma Integrable.zero_cbm : μ.Integrable f (0 : E →L[ℝ] F →L[ℝ] G) := by
- simp [VectorMeasure.Integrable]
-
-lemma Integrable.add_cbm (hB : μ.Integrable f B) (hC : μ.Integrable f C) :
- μ.Integrable f (B + C) := by
- apply Integrable.mono_measure (integrable_add_measure.2 ⟨hB, hC⟩)
- grw [transpose_add_cbm, variation_add_le]
-
-lemma Integrable.neg_cbm (hB : μ.Integrable f B) :
- μ.Integrable f (-B) := by
- apply Integrable.mono_measure hB
- simp
-
-lemma Integrable.sub_cbm (hB : μ.Integrable f B) (hC : μ.Integrable f C) :
- μ.Integrable f (B - C) := by
- convert hB.add_cbm hC.neg_cbm using 1
- exact sub_eq_add_neg B C
-
-lemma Integrable.finsetSum_cbm {ι : Type*} {B : ι → E →L[ℝ] F →L[ℝ] G} {s : Finset ι}
- (h : ∀ i ∈ s, μ.Integrable f (B i)) : μ.Integrable f (∑ i ∈ s, B i) := by
- classical
- induction s using Finset.induction_on with
- | empty => simp
- | insert a s ha ih =>
- simp only [Finset.mem_insert, forall_eq_or_imp, ha, not_false_eq_true,
- Finset.sum_insert] at h ⊢
- exact h.1.add_cbm (ih h.2)
-
variable (f μ) in
@[simp]
theorem integral_zero_cbm :
∫ᵛ x, f x ∂[(0 : E →L[ℝ] F →L[ℝ] G); μ] = 0 := by
simp [integral, FunLike.coe_zero]
-theorem integral_add_cbm (hB : μ.Integrable f B) (hC : μ.Integrable f C) :
- ∫ᵛ x, f x ∂[B + C; μ] = ∫ᵛ x, f x ∂[B; μ] + ∫ᵛ x, f x ∂[C; μ] :=
- setToFun_add_left'' (by simp [transpose]) hB hC (by simp [variation_add_le])
- zero_le_one zero_le_one zero_le_one
+theorem integral_add_cbm (hB : μ.Integrable f) :
+ ∫ᵛ x, f x ∂[B + C; μ] = ∫ᵛ x, f x ∂[B; μ] + ∫ᵛ x, f x ∂[C; μ] := by
+ refine setToFun_add_left'' (by simp [transpose]) hB hB ?_
+ (norm_nonneg _) (norm_nonneg _) (norm_nonneg _)
+ nth_rw 1 [← add_zero μ.variation]
+ gcongr
+ exact Measure.zero_le μ.variation
theorem integral_finsetSum_cbm {B : ι → E →L[ℝ] F →L[ℝ] G}
- {s : Finset ι} (hf : ∀ i ∈ s, μ.Integrable f (B i)) :
+ {s : Finset ι} (hf : μ.Integrable f) :
∫ᵛ x, f x ∂[∑ i ∈ s, B i; μ] = ∑ i ∈ s, ∫ᵛ x, f x ∂[B i; μ] := by
classical
induction s using Finset.induction_on with
| empty => simp
| insert a s ha ih =>
- simp only [Finset.mem_insert, forall_eq_or_imp, ha, not_false_eq_true,
- Finset.sum_insert] at hf ⊢
- rw [integral_add_cbm hf.1 (Integrable.finsetSum_cbm hf.2), ih hf.2]
+ simp only [ha, not_false_eq_true, Finset.sum_insert]
+ rw [integral_add_cbm hf, ih]
@[integral_simps]
theorem integral_neg_cbm :
∫ᵛ x, f x ∂[-B; μ] = -∫ᵛ x, f x ∂[B; μ] := by
simp [integral, ← setToFun_neg', FunLike.coe_neg]
-theorem integral_sub_cbm (hB : μ.Integrable f B) (hC : μ.Integrable f C) :
+theorem integral_sub_cbm (hB : μ.Integrable f) :
∫ᵛ x, f x ∂[B - C; μ] = ∫ᵛ x, f x ∂[B; μ] - ∫ᵛ x, f x ∂[C; μ] := by
rw [sub_eq_add_neg, integral_add_cbm hB, integral_neg_cbm, ← sub_eq_add_neg]
- simpa [VectorMeasure.Integrable] using hC
end cbm
-theorem Integrable.of_integral_ne_zero (h : ∫ᵛ a, f a ∂[B; μ] ≠ 0) : μ.Integrable f B :=
+theorem Integrable.of_integral_ne_zero (h : ∫ᵛ a, f a ∂[B; μ] ≠ 0) : μ.Integrable f :=
Not.imp_symm integral_undef h
theorem integral_non_aestronglyMeasurable {f : X → E}
- (h : ¬AEStronglyMeasurable f (μ.transpose B).variation) :
+ (h : ¬AEStronglyMeasurable f μ.variation) :
∫ᵛ a, f a ∂[B; μ] = 0 :=
integral_undef <| not_and_of_not_left _ h
@@ -610,28 +607,56 @@ lemma integral_indicator₂ {β : Type*} (f : β → X → E) (s : Set β) (b :
∫ᵛ y, s.indicator (f · y) b ∂[B; μ] = s.indicator (fun x ↦ ∫ᵛ y, f x y ∂[B; μ]) b := by
by_cases hb : b ∈ s <;> simp [hb]
+@[fun_prop]
+theorem continuous_integral : Continuous fun f : X →₁[μ.variation] E => ∫ᵛ a, f a ∂[B; μ] := by
+ simp only [integral_eq_setToFun]
+ exact continuous_setToFun _
+
theorem norm_integral_le_lintegral_norm :
- ‖∫ᵛ a, f a ∂[B; μ]‖ ≤ ENNReal.toReal (∫⁻ a, ENNReal.ofReal ‖f a‖ ∂(μ.transpose B).variation) :=
+ ‖∫ᵛ a, f a ∂[B; μ]‖ ≤ ‖B‖ * ENNReal.toReal (∫⁻ a, ENNReal.ofReal ‖f a‖ ∂μ.variation) :=
(norm_setToFun_le_toReal _ (by simp)).trans (by simp)
-theorem enorm_integral_le_lintegral_enorm :
- ‖∫ᵛ a, f a ∂[B; μ]‖ₑ ≤ ∫⁻ a, ‖f a‖ₑ ∂(μ.transpose B).variation :=
- (enorm_setToFun_le _ (by simp)).trans (by simp)
+theorem norm_integral_le_integral_norm :
+ ‖∫ᵛ a, f a ∂[B; μ]‖ ≤ ‖B‖ * ∫ a, ‖f a‖ ∂μ.variation := by
+ have le_ae : ∀ᵐ a ∂μ.variation, 0 ≤ ‖f a‖ :=
+ Eventually.of_forall fun a => norm_nonneg _
+ by_cases h : AEStronglyMeasurable f μ.variation
+ · calc ‖∫ᵛ a, f a ∂[B; μ]‖
+ _ ≤ ‖B‖ * ENNReal.toReal (∫⁻ a, ENNReal.ofReal ‖f a‖ ∂μ.variation) :=
+ norm_integral_le_lintegral_norm
+ _ = ‖B‖ * ∫ a, ‖f a‖ ∂μ.variation := by
+ rw [integral_eq_lintegral_of_nonneg_ae le_ae <| h.norm]
+ · rw [integral_non_aestronglyMeasurable h, norm_zero]
+ positivity
-theorem dist_integral_le_lintegral_edist (hf : μ.Integrable f B) (hg : μ.Integrable g B) :
+theorem enorm_integral_le_lintegral_enorm :
+ ‖∫ᵛ a, f a ∂[B; μ]‖ₑ ≤ ‖B‖ₑ * ∫⁻ a, ‖f a‖ₑ ∂μ.variation := by
+ apply (enorm_setToFun_le _ (by simp)).trans
+ gcongr
+ simp [← coe_nnnorm]
+
+theorem enorm_integral_le_lintegral_enorm_transpose :
+ ‖∫ᵛ a, f a ∂[B; μ]‖ₑ ≤ ∫⁻ a, ‖f a‖ₑ ∂(μ.transpose B).variation := by
+ by_cases hf : μ.Integrable f
+ · rw [integral_eq_setToFun_transpose hf]
+ apply (enorm_setToFun_le _ (by simp)).trans (by simp)
+ · simp [integral_undef hf]
+
+theorem dist_integral_le_lintegral_edist (hf : μ.Integrable f) (hg : μ.Integrable g) :
dist (∫ᵛ a, f a ∂[B; μ]) (∫ᵛ a, g a ∂[B; μ]) ≤
- (∫⁻ a, edist (f a) (g a) ∂(μ.transpose B).variation).toReal := by
+ ‖B‖ * (∫⁻ a, edist (f a) (g a) ∂μ.variation).toReal := by
grw [dist_eq_norm, ← integral_sub hf hg, norm_integral_le_lintegral_norm]
simp [edist_eq_enorm_sub]
-theorem edist_integral_le_lintegral_edist (hf : μ.Integrable f B) (hg : μ.Integrable g B) :
+theorem edist_integral_le_lintegral_edist (hf : μ.Integrable f) (hg : μ.Integrable g) :
edist (∫ᵛ a, f a ∂[B; μ]) (∫ᵛ a, g a ∂[B; μ]) ≤
- ∫⁻ a, edist (f a) (g a) ∂(μ.transpose B).variation := by
+ ‖B‖ₑ * ∫⁻ a, edist (f a) (g a) ∂μ.variation := by
rw [edist_dist]
- exact ENNReal.ofReal_le_of_le_toReal (dist_integral_le_lintegral_edist hf hg)
+ apply ENNReal.ofReal_le_of_le_toReal
+ grw [dist_integral_le_lintegral_edist hf hg, ENNReal.toReal_mul, toReal_enorm]
theorem frequently_ae_ne_zero_of_integral_ne_zero
- (h : ∫ᵛ a, f a ∂[B; μ] ≠ 0) : ∃ᶠ a in ae (μ.transpose B).variation, f a ≠ 0 :=
+ (h : ∫ᵛ a, f a ∂[B; μ] ≠ 0) : ∃ᶠ a in ae μ.variation, f a ≠ 0 :=
fun h' ↦ h (integral_eq_zero_of_ae (h'.mono fun _ ↦ not_not.mp))
theorem exists_ne_zero_of_integral_ne_zero
@@ -643,7 +668,7 @@ theorem exists_ne_zero_of_integral_ne_zero
rcases subsingleton_or_nontrivial G with h'G | h'G
· apply Subsingleton.elim
rw [integral_eq_setToFun, MeasureTheory.integral_eq_setToFun]
- simp only [variation_transpose_lsmul_flip, variation_toSignedMeasure]
+ simp only [Measure.variation_toSignedMeasure]
apply setToFun_congr_left' _ _ (fun s hs h's ↦ ?_)
simp only [transpose, ContinuousLinearMap.flip_flip, mapRange_apply,
Measure.toSignedMeasure_apply_measurable hs, LinearMap.toAddMonoidHom_coe,
@@ -662,7 +687,7 @@ theorem integral_dirac' [MeasurableSpace X] [CompleteSpace G] {a : X} {v : F}
calc
∫ᵛ x, f x ∂[B; VectorMeasure.dirac a v] = ∫ᵛ _, f a ∂[B; VectorMeasure.dirac a v] := by
apply integral_congr_ae
- simp only [transpose_dirac, variation_dirac]
+ simp only [variation_dirac]
exact Measure.ae_smul_measure (ae_eq_dirac' hfm.measurable) _
_ = B (f a) v := by simp
@@ -677,7 +702,7 @@ theorem integral_dirac [MeasurableSpace X] [MeasurableSingletonClass X] [Complet
calc
∫ᵛ x, f x ∂[B; VectorMeasure.dirac a v] = ∫ᵛ _, f a ∂[B; VectorMeasure.dirac a v] := by
apply integral_congr_ae
- simp only [transpose_dirac, variation_dirac]
+ simp only [variation_dirac]
exact Measure.ae_smul_measure (ae_eq_dirac f) _
_ = B (f a) v := by simp
@@ -689,17 +714,17 @@ theorem integral_unique [Unique X] [CompleteSpace G] :
/-- If `F i → f` in `L1`, then `∫ᵛ x, F i x ∂[B; μ] → ∫ᵛ x, f x ∂[B; μ]`. -/
theorem tendsto_integral_of_L1 {ι} (f : X → E)
- (hfi : AEStronglyMeasurable f (μ.transpose B).variation) {F : ι → X → E}
- {l : Filter ι} (hFi : ∀ᶠ i in l, μ.Integrable (F i) B)
- (hF : Tendsto (fun i ↦ ∫⁻ x, ‖F i x - f x‖ₑ ∂(μ.transpose B).variation) l (𝓝 0)) :
+ (hfi : AEStronglyMeasurable f μ.variation) {F : ι → X → E}
+ {l : Filter ι} (hFi : ∀ᶠ i in l, μ.Integrable (F i))
+ (hF : Tendsto (fun i ↦ ∫⁻ x, ‖F i x - f x‖ₑ ∂μ.variation) l (𝓝 0)) :
Tendsto (fun i ↦ ∫ᵛ x, F i x ∂[B; μ]) l (𝓝 <| ∫ᵛ x, f x ∂[B; μ]) :=
tendsto_setToFun_of_L1 _ f hfi hFi hF
/-- If `F i → f` in `L1`, then `∫ᵛ x, F i x ∂[B; μ] → ∫ᵛ x, f x ∂[B; μ]`. -/
lemma tendsto_integral_of_L1' {ι} (f : X → E)
- (hfi : AEStronglyMeasurable f (μ.transpose B).variation) {F : ι → X → E}
- {l : Filter ι} (hFi : ∀ᶠ i in l, μ.Integrable (F i) B)
- (hF : Tendsto (fun i ↦ eLpNorm (F i - f) 1 (μ.transpose B).variation) l (𝓝 0)) :
+ (hfi : AEStronglyMeasurable f μ.variation) {F : ι → X → E}
+ {l : Filter ι} (hFi : ∀ᶠ i in l, μ.Integrable (F i))
+ (hF : Tendsto (fun i ↦ eLpNorm (F i - f) 1 μ.variation) l (𝓝 0)) :
Tendsto (fun i ↦ ∫ᵛ x, F i x ∂[B; μ]) l (𝓝 (∫ᵛ x, f x ∂[B; μ])) := by
refine tendsto_integral_of_L1 f hfi hFi ?_
simp_rw [eLpNorm_one_eq_lintegral_enorm, Pi.sub_apply] at hF
@@ -708,67 +733,72 @@ lemma tendsto_integral_of_L1' {ι} (f : X → E)
variable {Y : Type*} [TopologicalSpace Y] [FirstCountableTopology Y]
theorem continuousWithinAt_of_dominated {F : Y → X → E} {x₀ : Y} {bound : X → ℝ} {s : Set Y}
- (hF_meas : ∀ᶠ x in 𝓝[s] x₀, AEStronglyMeasurable (F x) (μ.transpose B).variation)
- (h_bound : ∀ᶠ x in 𝓝[s] x₀, ∀ᵐ a ∂(μ.transpose B).variation, ‖F x a‖ ≤ bound a)
- (bound_integrable : Integrable bound (μ.transpose B).variation)
- (h_cont : ∀ᵐ a ∂(μ.transpose B).variation, ContinuousWithinAt (fun x ↦ F x a) s x₀) :
+ (hF_meas : ∀ᶠ x in 𝓝[s] x₀, AEStronglyMeasurable (F x) μ.variation)
+ (h_bound : ∀ᶠ x in 𝓝[s] x₀, ∀ᵐ a ∂μ.variation, ‖F x a‖ ≤ bound a)
+ (bound_integrable : Integrable bound μ.variation)
+ (h_cont : ∀ᵐ a ∂μ.variation, ContinuousWithinAt (fun x ↦ F x a) s x₀) :
ContinuousWithinAt (fun x ↦ ∫ᵛ a, F x a ∂[B; μ]) s x₀ :=
continuousWithinAt_setToFun_of_dominated _ hF_meas h_bound bound_integrable h_cont
theorem continuousAt_of_dominated {F : Y → X → E} {x₀ : Y} {bound : X → ℝ}
- (hF_meas : ∀ᶠ x in 𝓝 x₀, AEStronglyMeasurable (F x) (μ.transpose B).variation)
- (h_bound : ∀ᶠ x in 𝓝 x₀, ∀ᵐ a ∂(μ.transpose B).variation, ‖F x a‖ ≤ bound a)
- (bound_integrable : Integrable bound (μ.transpose B).variation)
- (h_cont : ∀ᵐ a ∂(μ.transpose B).variation, ContinuousAt (fun x ↦ F x a) x₀) :
+ (hF_meas : ∀ᶠ x in 𝓝 x₀, AEStronglyMeasurable (F x) μ.variation)
+ (h_bound : ∀ᶠ x in 𝓝 x₀, ∀ᵐ a ∂μ.variation, ‖F x a‖ ≤ bound a)
+ (bound_integrable : Integrable bound μ.variation)
+ (h_cont : ∀ᵐ a ∂μ.variation, ContinuousAt (fun x ↦ F x a) x₀) :
ContinuousAt (fun x ↦ ∫ᵛ a, F x a ∂[B; μ]) x₀ :=
continuousAt_setToFun_of_dominated _ hF_meas h_bound bound_integrable h_cont
theorem continuousOn_of_dominated {F : Y → X → E} {bound : X → ℝ} {s : Set Y}
- (hF_meas : ∀ x ∈ s, AEStronglyMeasurable (F x) (μ.transpose B).variation)
- (h_bound : ∀ x ∈ s, ∀ᵐ a ∂(μ.transpose B).variation, ‖F x a‖ ≤ bound a)
- (bound_integrable : Integrable bound (μ.transpose B).variation)
- (h_cont : ∀ᵐ a ∂(μ.transpose B).variation, ContinuousOn (fun x ↦ F x a) s) :
+ (hF_meas : ∀ x ∈ s, AEStronglyMeasurable (F x) μ.variation)
+ (h_bound : ∀ x ∈ s, ∀ᵐ a ∂μ.variation, ‖F x a‖ ≤ bound a)
+ (bound_integrable : Integrable bound μ.variation)
+ (h_cont : ∀ᵐ a ∂μ.variation, ContinuousOn (fun x ↦ F x a) s) :
ContinuousOn (fun x ↦ ∫ᵛ a, F x a ∂[B; μ]) s :=
continuousOn_setToFun_of_dominated _ hF_meas h_bound bound_integrable h_cont
theorem continuous_of_dominated {F : Y → X → E} {bound : X → ℝ}
- (hF_meas : ∀ x, AEStronglyMeasurable (F x) (μ.transpose B).variation)
- (h_bound : ∀ x, ∀ᵐ a ∂(μ.transpose B).variation, ‖F x a‖ ≤ bound a)
- (bound_integrable : Integrable bound (μ.transpose B).variation)
- (h_cont : ∀ᵐ a ∂(μ.transpose B).variation, Continuous fun x ↦ F x a) :
+ (hF_meas : ∀ x, AEStronglyMeasurable (F x) μ.variation)
+ (h_bound : ∀ x, ∀ᵐ a ∂μ.variation, ‖F x a‖ ≤ bound a)
+ (bound_integrable : Integrable bound μ.variation)
+ (h_cont : ∀ᵐ a ∂μ.variation, Continuous fun x ↦ F x a) :
Continuous fun x ↦ ∫ᵛ a, F x a ∂[B; μ] :=
continuous_setToFun_of_dominated _ hF_meas h_bound bound_integrable h_cont
-theorem norm_integral_le_of_norm_le_const [IsFiniteMeasure (μ.transpose B).variation]
- {C : ℝ} (h : ∀ᵐ x ∂(μ.transpose B).variation, ‖f x‖ ≤ C) :
- ‖∫ᵛ x, f x ∂[B; μ]‖ ≤ C * (μ.transpose B).variation.real univ := calc
+theorem norm_integral_le_of_norm_le_const [IsFiniteMeasure μ.variation]
+ {C : ℝ} (h : ∀ᵐ x ∂μ.variation, ‖f x‖ ≤ C) :
+ ‖∫ᵛ x, f x ∂[B; μ]‖ ≤ C * ‖B‖ * μ.variation.real univ := calc
‖∫ᵛ x, f x ∂[B; μ]‖
- _ ≤ (∫⁻ a, ENNReal.ofReal ‖f a‖ ∂(μ.transpose B).variation).toReal :=
+ _ ≤ ‖B‖ * (∫⁻ a, ENNReal.ofReal ‖f a‖ ∂μ.variation).toReal :=
norm_integral_le_lintegral_norm
- _ ≤ (∫⁻ a, ENNReal.ofReal C ∂(μ.transpose B).variation).toReal := by
+ _ ≤ ‖B‖ * (∫⁻ a, ENNReal.ofReal C ∂μ.variation).toReal := by
+ gcongr 1
apply ENNReal.toReal_mono
· simp only [lintegral_const, ne_eq]
finiteness
· apply lintegral_mono_ae
filter_upwards [h] with x hx using ENNReal.ofReal_mono hx
- _ = C * (μ.transpose B).variation.real univ := by
- by_cases hμ : (μ.transpose B).variation = 0
+ _ = ‖B‖ * (C * μ.variation.real univ) := by
+ by_cases hμ : μ.variation = 0
· simp [hμ]
- have : (ae (μ.transpose B).variation).NeBot := ae_neBot.mpr hμ
+ have : (ae μ.variation).NeBot := ae_neBot.mpr hμ
have hC : 0 ≤ C := by
obtain ⟨x, hx⟩ := h.exists
exact (norm_nonneg _).trans hx
simp [ENNReal.toReal_ofReal hC, Measure.real]
+ _ = C * ‖B‖ * μ.variation.real univ := by ring
theorem enorm_integral_le_of_enorm_le_const
- {C : ℝ≥0∞} (h : ∀ᵐ x ∂(μ.transpose B).variation, ‖f x‖ₑ ≤ C) :
- ‖∫ᵛ x, f x ∂[B; μ]‖ₑ ≤ C * (μ.transpose B).variation univ :=
- enorm_integral_le_lintegral_enorm.trans ((lintegral_mono_ae h).trans (by simp))
+ {C : ℝ≥0∞} (h : ∀ᵐ x ∂μ.variation, ‖f x‖ₑ ≤ C) :
+ ‖∫ᵛ x, f x ∂[B; μ]‖ₑ ≤ C * ‖B‖ₑ * μ.variation univ := by
+ apply enorm_integral_le_lintegral_enorm.trans
+ rw [mul_comm C, mul_assoc]
+ gcongr
+ exact (lintegral_mono_ae h).trans (by simp)
theorem nndist_integral_add_vectorMeasure_le_lintegral
- (h₁ : μ.Integrable f B) (h₂ : ν.Integrable f B) :
+ (h₁ : μ.Integrable f) (h₂ : ν.Integrable f) :
(nndist (∫ᵛ x, f x ∂[B; μ]) (∫ᵛ x, f x ∂[B; (μ + ν)]) : ℝ≥0∞) ≤
- ∫⁻ x, ‖f x‖ₑ ∂(ν.transpose B).variation := by
+ ‖B‖ₑ * ∫⁻ x, ‖f x‖ₑ ∂ν.variation := by
rw [integral_add_vectorMeasure h₁ h₂, nndist_comm, nndist_eq_nnnorm, add_sub_cancel_left]
exact enorm_integral_le_lintegral_enorm
@@ -778,21 +808,22 @@ lemma variation_transpose_map_le :
((μ.map φ).transpose B).variation ≤ Measure.map φ (μ.transpose B).variation := by
grw [transpose_map, variation_map_le]
+omit [NormedSpace ℝ E] [NormedSpace ℝ F] in
theorem Integrable.map {β : Type*} [MeasurableSpace β] {φ : X → β}
- {f : β → E} (hfm : AEStronglyMeasurable f ((μ.transpose B).variation.map φ))
- (h : μ.Integrable (f ∘ φ) B) : (μ.map φ).Integrable f B := by
+ {f : β → E} (hfm : AEStronglyMeasurable f (μ.variation.map φ))
+ (h : μ.Integrable (f ∘ φ)) : (μ.map φ).Integrable f := by
by_cases hφ : Measurable φ; swap
· simp [VectorMeasure.map, hφ]
simp_rw [VectorMeasure.Integrable] at h ⊢
apply ((integrable_map_measure hfm hφ.aemeasurable).2 h).mono_measure
- apply variation_transpose_map_le
+ apply variation_map_le
theorem integral_map {β : Type*} [MeasurableSpace β]
{φ : X → β} (hφ : Measurable φ) {f : β → E}
- (hfm : AEStronglyMeasurable f ((μ.transpose B).variation.map φ))
- (hfi' : μ.Integrable (f ∘ φ) B) :
+ (hfm : AEStronglyMeasurable f (μ.variation.map φ))
+ (hfi' : μ.Integrable (f ∘ φ)) :
∫ᵛ y, f y ∂[B; μ.map φ] = ∫ᵛ x, f (φ x) ∂[B; μ] := by
- apply setToFun_of_le_map _ _ hfi' hfm hφ variation_transpose_map_le
+ apply setToFun_of_le_map _ _ hfi' hfm hφ variation_map_le
intro s x hs
simp [hs, VectorMeasure.map, transpose, hφ]
@@ -800,24 +831,24 @@ theorem _root_.MeasurableEmbedding.variation_transpose_map (hφ : MeasurableEmbe
((μ.map φ).transpose B).variation = (μ.transpose B).variation.map φ := by
rw [transpose_map, hφ.variation_map]
+omit [NormedSpace ℝ E] [NormedSpace ℝ F] in
theorem _root_.MeasurableEmbedding.integrable_map_vectorMeasure
(hφ : MeasurableEmbedding φ) {f : β → E} :
- (μ.map φ).Integrable f B ↔ μ.Integrable (f ∘ φ) B := by
- simp_rw [VectorMeasure.Integrable,
- ← hφ.integrable_map_iff (g := f) (μ := (μ.transpose B).variation), hφ.variation_transpose_map]
+ (μ.map φ).Integrable f ↔ μ.Integrable (f ∘ φ) := by
+ simp_rw [VectorMeasure.Integrable, ← hφ.integrable_map_iff, hφ.variation_map]
theorem _root_.MeasurableEmbedding.integral_map_vectorMeasure
(hφ : MeasurableEmbedding φ) {f : β → E} :
∫ᵛ y, f y ∂[B; μ.map φ] = ∫ᵛ x, f (φ x) ∂[B; μ] := by
- by_cases hfm : AEStronglyMeasurable f ((μ.transpose B).variation.map φ)
- · by_cases h'fm : μ.Integrable (f ∘ φ) B
+ by_cases hfm : AEStronglyMeasurable f (μ.variation.map φ)
+ · by_cases h'fm : μ.Integrable (f ∘ φ)
· apply integral_map hφ.measurable hfm h'fm
· rw [integral_undef, integral_undef]
· exact h'fm
· rwa [hφ.integrable_map_vectorMeasure]
· rw [integral_non_aestronglyMeasurable, integral_non_aestronglyMeasurable]
· rwa [hφ.aestronglyMeasurable_map_iff] at hfm
- · rwa [hφ.variation_transpose_map]
+ · rwa [hφ.variation_map]
theorem _root_.Topology.IsClosedEmbedding.integral_map_vectorMeasure
[TopologicalSpace X] [BorelSpace X]
@@ -832,40 +863,40 @@ theorem integral_map_equiv {β} [MeasurableSpace β] (e : X ≃ᵐ β) (f : β
/-- **Lebesgue dominated convergence theorem** provides sufficient conditions under which almost
everywhere convergence of a sequence of functions implies the convergence of their integrals.
We could weaken the condition `bound_integrable` to require
- `HasFiniteIntegral bound (μ.transpose B).variation` instead (i.e. not requiring that `bound` is
+ `HasFiniteIntegral bound μ.variation` instead (i.e. not requiring that `bound` is
measurable), but in all applications proving integrability is easier. -/
theorem tendsto_integral_of_dominated_convergence {F : ℕ → X → E} {f : X → E} (bound : X → ℝ)
- (F_measurable : ∀ n, AEStronglyMeasurable (F n) (μ.transpose B).variation)
- (bound_integrable : Integrable bound (μ.transpose B).variation)
- (h_bound : ∀ n, ∀ᵐ a ∂(μ.transpose B).variation, ‖F n a‖ ≤ bound a)
- (h_lim : ∀ᵐ a ∂(μ.transpose B).variation, Tendsto (fun n ↦ F n a) atTop (𝓝 (f a))) :
+ (F_measurable : ∀ n, AEStronglyMeasurable (F n) μ.variation)
+ (bound_integrable : Integrable bound μ.variation)
+ (h_bound : ∀ n, ∀ᵐ a ∂μ.variation, ‖F n a‖ ≤ bound a)
+ (h_lim : ∀ᵐ a ∂μ.variation, Tendsto (fun n ↦ F n a) atTop (𝓝 (f a))) :
Tendsto (fun n ↦ ∫ᵛ a, F n a ∂[B; μ]) atTop (𝓝 <| ∫ᵛ a, f a ∂[B; μ]) :=
tendsto_setToFun_of_dominated_convergence _ bound F_measurable bound_integrable h_bound h_lim
/-- Lebesgue dominated convergence theorem for filters with a countable basis -/
theorem tendsto_integral_filter_of_dominated_convergence {l : Filter ι} [l.IsCountablyGenerated]
{F : ι → X → E} {f : X → E} (bound : X → ℝ)
- (hF_meas : ∀ᶠ n in l, AEStronglyMeasurable (F n) (μ.transpose B).variation)
- (h_bound : ∀ᶠ n in l, ∀ᵐ a ∂(μ.transpose B).variation, ‖F n a‖ ≤ bound a)
- (bound_integrable : Integrable bound (μ.transpose B).variation)
- (h_lim : ∀ᵐ a ∂(μ.transpose B).variation, Tendsto (fun n ↦ F n a) l (𝓝 (f a))) :
+ (hF_meas : ∀ᶠ n in l, AEStronglyMeasurable (F n) μ.variation)
+ (h_bound : ∀ᶠ n in l, ∀ᵐ a ∂μ.variation, ‖F n a‖ ≤ bound a)
+ (bound_integrable : Integrable bound μ.variation)
+ (h_lim : ∀ᵐ a ∂μ.variation, Tendsto (fun n ↦ F n a) l (𝓝 (f a))) :
Tendsto (fun n ↦ ∫ᵛ a, F n a ∂[B; μ]) l (𝓝 <| ∫ᵛ a, f a ∂[B; μ]) :=
tendsto_setToFun_filter_of_dominated_convergence _ bound hF_meas h_bound bound_integrable h_lim
/-- Lebesgue dominated convergence theorem for series. -/
theorem hasSum_integral_of_dominated_convergence [Countable ι] {F : ι → X → E} {f : X → E}
- (bound : ι → X → ℝ) (hF_meas : ∀ n, AEStronglyMeasurable (F n) (μ.transpose B).variation)
- (h_bound : ∀ n, ∀ᵐ a ∂(μ.transpose B).variation, ‖F n a‖ ≤ bound n a)
- (bound_summable : ∀ᵐ a ∂(μ.transpose B).variation, Summable fun n ↦ bound n a)
- (bound_integrable : Integrable (fun a ↦ ∑' n, bound n a) (μ.transpose B).variation)
- (h_lim : ∀ᵐ a ∂(μ.transpose B).variation, HasSum (fun n ↦ F n a) (f a)) :
+ (bound : ι → X → ℝ) (hF_meas : ∀ n, AEStronglyMeasurable (F n) μ.variation)
+ (h_bound : ∀ n, ∀ᵐ a ∂μ.variation, ‖F n a‖ ≤ bound n a)
+ (bound_summable : ∀ᵐ a ∂μ.variation, Summable fun n ↦ bound n a)
+ (bound_integrable : Integrable (fun a ↦ ∑' n, bound n a) μ.variation)
+ (h_lim : ∀ᵐ a ∂μ.variation, HasSum (fun n ↦ F n a) (f a)) :
HasSum (fun n ↦ ∫ᵛ a, F n a ∂[B; μ]) (∫ᵛ a, f a ∂[B; μ]) :=
hasSum_setToFun_of_dominated_convergence _ bound hF_meas h_bound bound_summable bound_integrable
h_lim
theorem integral_tsum [CompleteSpace E] [Countable ι]
- {f : ι → X → E} (hf : ∀ i, AEStronglyMeasurable (f i) (μ.transpose B).variation)
- (hf' : ∑' i, ∫⁻ a : X, ‖f i a‖ₑ ∂(μ.transpose B).variation ≠ ∞) :
+ {f : ι → X → E} (hf : ∀ i, AEStronglyMeasurable (f i) μ.variation)
+ (hf' : ∑' i, ∫⁻ a : X, ‖f i a‖ₑ ∂μ.variation ≠ ∞) :
∫ᵛ a, ∑' i, f i a ∂[B; μ] = ∑' i, ∫ᵛ a, f i a ∂[B; μ] :=
setToFun_tsum _ hf hf'
@@ -874,10 +905,10 @@ theorem integral_tsum [CompleteSpace E] [Countable ι]
function `f`, then the integrals of `F n` with respect to a vector measure `μ` with finite
variation converge to the integral of `f`. -/
theorem tendsto_integral_filter_of_norm_le_const {l : Filter ι} [l.IsCountablyGenerated]
- {F : ι → X → E} [IsFiniteMeasure (μ.transpose B).variation] {f : X → E}
- (h_meas : ∀ᶠ n in l, AEStronglyMeasurable (F n) (μ.transpose B).variation)
- (h_bound : ∃ C, ∀ᶠ n in l, ∀ᵐ a ∂(μ.transpose B).variation, ‖F n a‖ ≤ C)
- (h_lim : ∀ᵐ a ∂(μ.transpose B).variation, Tendsto (fun n ↦ F n a) l (𝓝 (f a))) :
+ {F : ι → X → E} [IsFiniteMeasure μ.variation] {f : X → E}
+ (h_meas : ∀ᶠ n in l, AEStronglyMeasurable (F n) μ.variation)
+ (h_bound : ∃ C, ∀ᶠ n in l, ∀ᵐ a ∂μ.variation, ‖F n a‖ ≤ C)
+ (h_lim : ∀ᵐ a ∂μ.variation, Tendsto (fun n ↦ F n a) l (𝓝 (f a))) :
Tendsto (fun n ↦ ∫ᵛ a, F n a ∂[B; μ]) l (𝓝 (∫ᵛ a, f a ∂[B; μ])) :=
tendsto_setToFun_filter_of_norm_le_const _ h_meas h_bound h_lim
diff --git a/Mathlib/MeasureTheory/VectorMeasure/Variation/Basic.lean b/Mathlib/MeasureTheory/VectorMeasure/Variation/Basic.lean
index 2c0ba7a93d9b1e..de8e791a846bee 100644
--- a/Mathlib/MeasureTheory/VectorMeasure/Variation/Basic.lean
+++ b/Mathlib/MeasureTheory/VectorMeasure/Variation/Basic.lean
@@ -306,7 +306,8 @@ instance {x : X} {v : V} : IsFiniteMeasure (VectorMeasure.dirac x v).variation :
simp only [variation_dirac, enorm_eq_nnnorm, Measure.coe_nnreal_smul]
infer_instance
-@[simp] lemma variation_toSignedMeasure {μ : Measure X} [IsFiniteMeasure μ] :
+@[simp] lemma _root_.MeasureTheory.Measure.variation_toSignedMeasure
+ {μ : Measure X} [IsFiniteMeasure μ] :
μ.toSignedMeasure.variation = μ := by
apply le_antisymm
· apply variation_le_of_forall_enorm_le (fun s hs ↦ ?_)
From 7da7c276aa3f0d0dbcea1e2fb9adcff0dba08651 Mon Sep 17 00:00:00 2001
From: Ben Eltschig <43812953+peabrainiac@users.noreply.github.com>
Date: Wed, 17 Jun 2026 08:21:18 +0000
Subject: [PATCH 0101/1300] chore(Algebra): module deprecations for moved
`AddTorsor` files (#39134)
---
Mathlib.lean | 4 ++++
Mathlib/Algebra/AddTorsor/Basic.lean | 5 +++++
Mathlib/Algebra/AddTorsor/Defs.lean | 5 +++++
Mathlib/Topology/Algebra/Group/AddTorsor.lean | 5 +++++
Mathlib/Topology/Algebra/ProperAction/AddTorsor.lean | 5 +++++
5 files changed, 24 insertions(+)
create mode 100644 Mathlib/Algebra/AddTorsor/Basic.lean
create mode 100644 Mathlib/Algebra/AddTorsor/Defs.lean
create mode 100644 Mathlib/Topology/Algebra/Group/AddTorsor.lean
create mode 100644 Mathlib/Topology/Algebra/ProperAction/AddTorsor.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 610df212f17b02..8be7995c7dde01 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -4,6 +4,8 @@ public import Std
public import Batteries
public import Mathlib.Algebra.AddConstMap.Basic
public import Mathlib.Algebra.AddConstMap.Equiv
+public import Mathlib.Algebra.AddTorsor.Basic
+public import Mathlib.Algebra.AddTorsor.Defs
public import Mathlib.Algebra.AffineMonoid.Basic
public import Mathlib.Algebra.AffineMonoid.Embedding
public import Mathlib.Algebra.AffineMonoid.Irreducible
@@ -7499,6 +7501,7 @@ public import Mathlib.Topology.Algebra.ContinuousMonoidHom
public import Mathlib.Topology.Algebra.Equicontinuity
public import Mathlib.Topology.Algebra.Field
public import Mathlib.Topology.Algebra.FilterBasis
+public import Mathlib.Topology.Algebra.Group.AddTorsor
public import Mathlib.Topology.Algebra.Group.Basic
public import Mathlib.Topology.Algebra.Group.ClosedSubgroup
public import Mathlib.Topology.Algebra.Group.Compact
@@ -7613,6 +7616,7 @@ public import Mathlib.Topology.Algebra.Order.Support
public import Mathlib.Topology.Algebra.Order.UpperLower
public import Mathlib.Topology.Algebra.Polynomial
public import Mathlib.Topology.Algebra.PontryaginDual
+public import Mathlib.Topology.Algebra.ProperAction.AddTorsor
public import Mathlib.Topology.Algebra.ProperAction.Basic
public import Mathlib.Topology.Algebra.ProperAction.CompactlyGenerated
public import Mathlib.Topology.Algebra.ProperAction.ProperlyDiscontinuous
diff --git a/Mathlib/Algebra/AddTorsor/Basic.lean b/Mathlib/Algebra/AddTorsor/Basic.lean
new file mode 100644
index 00000000000000..b1f613e225f0ca
--- /dev/null
+++ b/Mathlib/Algebra/AddTorsor/Basic.lean
@@ -0,0 +1,5 @@
+module -- shake: keep-all
+
+public import Mathlib.Algebra.Torsor.Basic
+
+deprecated_module (since := "2026-06-12")
diff --git a/Mathlib/Algebra/AddTorsor/Defs.lean b/Mathlib/Algebra/AddTorsor/Defs.lean
new file mode 100644
index 00000000000000..52df5cca935bbe
--- /dev/null
+++ b/Mathlib/Algebra/AddTorsor/Defs.lean
@@ -0,0 +1,5 @@
+module -- shake: keep-all
+
+public import Mathlib.Algebra.Torsor.Defs
+
+deprecated_module (since := "2026-06-12")
diff --git a/Mathlib/Topology/Algebra/Group/AddTorsor.lean b/Mathlib/Topology/Algebra/Group/AddTorsor.lean
new file mode 100644
index 00000000000000..6b42000ee362d3
--- /dev/null
+++ b/Mathlib/Topology/Algebra/Group/AddTorsor.lean
@@ -0,0 +1,5 @@
+module -- shake: keep-all
+
+public import Mathlib.Topology.Algebra.Group.Torsor
+
+deprecated_module (since := "2026-06-12")
diff --git a/Mathlib/Topology/Algebra/ProperAction/AddTorsor.lean b/Mathlib/Topology/Algebra/ProperAction/AddTorsor.lean
new file mode 100644
index 00000000000000..2ce65c6dba91ef
--- /dev/null
+++ b/Mathlib/Topology/Algebra/ProperAction/AddTorsor.lean
@@ -0,0 +1,5 @@
+module -- shake: keep-all
+
+public import Mathlib.Topology.Algebra.ProperAction.Torsor
+
+deprecated_module (since := "2026-06-12")
From 2fb84f40125bfc66936eff5dea70c236315f17b2 Mon Sep 17 00:00:00 2001
From: ooovi <79147175+ooovi@users.noreply.github.com>
Date: Wed, 17 Jun 2026 08:53:23 +0000
Subject: [PATCH 0102/1300] feat(Geometry/Convex/Cone/Pointed): faces of
pointed cones (#39185)
- Define PointedCone.IsFaceOf, for a pointed cone being a face of another pointed cone.
- Prove some basic properties, that faces are extreme sets of their cone, and how they behave under intersection, map and product operations.
Co-authored-by: Martin Winter
Co-authored-by: ovi
---
Mathlib.lean | 1 +
Mathlib/Geometry/Convex/Cone/Face/Basic.lean | 287 +++++++++++++++++++
2 files changed, 288 insertions(+)
create mode 100644 Mathlib/Geometry/Convex/Cone/Face/Basic.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 8be7995c7dde01..3231bbedb8ef7a 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -4549,6 +4549,7 @@ public import Mathlib.FieldTheory.Tower
public import Mathlib.Geometry.Convex.Cone.Basic
public import Mathlib.Geometry.Convex.Cone.Dual
public import Mathlib.Geometry.Convex.Cone.DualFinite
+public import Mathlib.Geometry.Convex.Cone.Face.Basic
public import Mathlib.Geometry.Convex.Cone.Pointed
public import Mathlib.Geometry.Convex.Cone.Simplicial
public import Mathlib.Geometry.Convex.Cone.TensorProduct
diff --git a/Mathlib/Geometry/Convex/Cone/Face/Basic.lean b/Mathlib/Geometry/Convex/Cone/Face/Basic.lean
new file mode 100644
index 00000000000000..777c71f0d0b5a7
--- /dev/null
+++ b/Mathlib/Geometry/Convex/Cone/Face/Basic.lean
@@ -0,0 +1,287 @@
+/-
+Copyright (c) 2025 Olivia Röhrig. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Olivia Röhrig
+-/
+module
+
+public import Mathlib.Analysis.Convex.Extreme
+public import Mathlib.Geometry.Convex.Cone.Pointed
+
+/-!
+# Faces of pointed cones
+
+This file defines what it means for a pointed cone to be a face of another pointed cone and
+establishes basic properties of this relation.
+A subcone `F` of a cone `C` is a face if any two points in `C` that have a positive combination
+in `F` are also in `F`.
+
+## Main declarations
+
+* `IsFaceOf F C`: States that the pointed cone `F` is a face of the pointed cone `C`.
+
+## Implementation notes
+
+* We do not use `IsExtreme` as a definition because this is an affine notion and does not allow the
+ flexibility necessary to deal wth cones over general rings. E.g. the cone of positive integers has
+ no proper subset that are extreme. We prove that every face is an extreme set of its cone.
+* Most results proven over a division ring hold more generally over an Archimedean ring. In
+ particular, `iff_mem_of_add_mem_left` holds whenever for every `x ∈ R` there is a `y ∈ R` with
+ `1 ≤ x * y`.
+
+-/
+
+open Submodule
+
+public section
+
+namespace PointedCone
+
+variable {R M N : Type*}
+
+section Semiring
+
+variable [Semiring R] [PartialOrder R] [IsOrderedRing R]
+variable [AddCommGroup M] [Module R M]
+
+/-- A sub-cone `F` of a pointed cone `C` is a face of `C` if any two points of `C` with a strictly
+positive combination in `F` are also in `F`. -/
+@[mk_iff]
+structure IsFaceOf (F C : PointedCone R M) : Prop where
+ le : F ≤ C
+ mem_of_smul_add_mem {x y : M} {a : R} :
+ x ∈ C → y ∈ C → 0 < a → a • x + y ∈ F → x ∈ F
+
+variable {C C₁ C₂ F F₁ F₂ : PointedCone R M}
+
+namespace IsFaceOf
+
+theorem mem_of_smul_add_smul_mem_left {x y : M} {a b : R} (hF : F.IsFaceOf C) (hx : x ∈ C)
+ (hy : y ∈ C) (ha : 0 < a) (hb : 0 < b) (h : a • x + b • y ∈ F) : x ∈ F :=
+ hF.2 hx (smul_mem _ hb.le hy) ha h
+
+theorem mem_of_smul_add_smul_mem_right {x y : M} {a b : R} (hF : F.IsFaceOf C) (hx : x ∈ C)
+ (hy : y ∈ C) (ha : 0 < a) (hb : 0 < b) (h : a • x + b • y ∈ F) : y ∈ F :=
+ by apply hF.2 hy (smul_mem _ ha.le hx) hb; rwa [add_comm]
+
+/-- A pointed cone `C` is a face of itself. -/
+@[refl, simp]
+protected theorem refl (C : PointedCone R M) : C.IsFaceOf C := ⟨fun _ a ↦ a, fun hx _ _ _ ↦ hx⟩
+
+protected theorem rfl {C : PointedCone R M} : C.IsFaceOf C := .refl _
+
+/-- A face of a cone is a face of another if and only if they are contained in each other. -/
+theorem isFaceOf_iff_le (h₁ : F₁.IsFaceOf C) (h₂ : F₂.IsFaceOf C) :
+ F₁.IsFaceOf F₂ ↔ F₁ ≤ F₂ :=
+ ⟨IsFaceOf.le, fun h ↦ ⟨h, fun hx hy ha hxy ↦ h₁.2 (h₂.le hx) (h₂.le hy) ha hxy⟩⟩
+
+/-- A face of a cone is an extreme subset of the cone. -/
+theorem isExtreme (h : F.IsFaceOf C) : IsExtreme R (C : Set M) F := by
+ refine ⟨h.1, ?_⟩
+ rintro _ xc _ yc _ zf ⟨_, _, a0, b0, -, rfl⟩
+ exact h.mem_of_smul_add_smul_mem_left xc yc a0 b0 zf
+
+/-- The intersection of two faces of two cones is a face of the intersection of the cones. -/
+protected theorem inf (h₁ : F₁.IsFaceOf C₁) (h₂ : F₂.IsFaceOf C₂) :
+ (F₁ ⊓ F₂).IsFaceOf (C₁ ⊓ C₂) := by
+ use le_inf_iff.mpr ⟨Set.inter_subset_left.trans h₁.le, Set.inter_subset_right.trans h₂.le⟩
+ simp only [mem_inf, and_imp]
+ refine fun xc₁ xc₂ yc₁ yc₂ a0 hz₁ hz₂ ↦ ⟨?_, ?_⟩
+ · exact h₁.mem_of_smul_add_mem xc₁ yc₁ a0 hz₁
+ · exact h₂.mem_of_smul_add_mem xc₂ yc₂ a0 hz₂
+
+/-- The intersection of two faces of a cone is a face of the cone. -/
+theorem inf_left (h₁ : F₁.IsFaceOf C) (h₂ : F₂.IsFaceOf C) : (F₁ ⊓ F₂).IsFaceOf C :=
+ inf_idem C ▸ IsFaceOf.inf h₁ h₂
+
+/-- If a cone is a face of two cones simultaneously, then it's also a face of their intersection. -/
+theorem inf_right (h₁ : F.IsFaceOf C₁) (h₂ : F.IsFaceOf C₂) : F.IsFaceOf (C₁ ⊓ C₂) :=
+ inf_idem F ▸ IsFaceOf.inf h₁ h₂
+
+protected theorem sInf (F : Set (PointedCone R M)) (h : ∀ f ∈ F, f.IsFaceOf C) :
+ (C ⊓ sInf F).IsFaceOf C where
+ le _ sm := sm.1
+ mem_of_smul_add_mem := by
+ simp only [mem_inf, mem_sInf, and_imp]
+ intro _ _ a xc yc a0 _ h'
+ simpa [xc] using fun F Fs ↦ (h F Fs).mem_of_smul_add_mem xc yc a0 (h' F Fs)
+
+theorem mem_of_add_mem_left (hF : F.IsFaceOf C) {x y : M}
+ (hx : x ∈ C) (hy : y ∈ C) (hxy : x + y ∈ F) : x ∈ F := by
+ nontriviality R using Module.subsingleton R M
+ simpa [hxy] using hF.mem_of_smul_add_mem hx hy zero_lt_one
+
+theorem mem_of_add_mem_right (hF : F.IsFaceOf C) {x y : M}
+ (hx : x ∈ C) (hy : y ∈ C) (hxy : x + y ∈ F) : y ∈ F := by
+ rw [add_comm x y] at hxy; exact mem_of_add_mem_left hF hy hx hxy
+
+theorem add_mem_iff_mem (hF : F.IsFaceOf C) {x y : M} (hx : x ∈ C) (hy : y ∈ C) :
+ x + y ∈ F ↔ x ∈ F ∧ y ∈ F := by
+ refine ⟨?_, fun ⟨hx, hy⟩ ↦ F.add_mem hx hy⟩
+ exact fun h ↦ ⟨mem_of_add_mem_left hF hx hy h, mem_of_add_mem_right hF hx hy h⟩
+
+/-- If the sum of points of a cone is in a face, then all the points are in the face. -/
+theorem mem_of_sum_mem {ι : Type*} [Fintype ι] {f : ι → M} (hF : F.IsFaceOf C)
+ (hsC : ∀ i : ι, f i ∈ C) (hs : ∑ i : ι, f i ∈ F) (i : ι) : f i ∈ F := by classical
+ apply hF.mem_of_add_mem_left (hsC i) (sum_mem (fun j (_ : j ∈ Finset.univ.erase i) ↦ hsC j))
+ simp [hs]
+
+theorem sum_mem_iff_mem {ι : Type*} [Fintype ι] {f : ι → M} (hF : F.IsFaceOf C)
+ (hsC : ∀ i, f i ∈ C) : ∑ i, f i ∈ F ↔ ∀ i, f i ∈ F :=
+ ⟨mem_of_sum_mem hF hsC, fun a ↦ Submodule.sum_mem F fun c _ ↦ a c⟩
+
+/-- If the positive combination of points of a cone is in a face, then all the points are
+in the face. -/
+theorem mem_of_sum_smul_mem {ι : Type*} [Fintype ι] {f : ι → M} {c : ι → R}
+ (hF : F.IsFaceOf C) (hsC : ∀ i : ι, f i ∈ C) (hc : ∀ i, 0 ≤ c i) (hs : ∑ i : ι, c i • f i ∈ F)
+ (i : ι) (hci : 0 < c i) : f i ∈ F := by classical
+ rw [Finset.sum_eq_add_sum_sdiff_singleton i] at hs
+ · refine hF.mem_of_smul_add_mem (hsC i) ?_ hci hs
+ exact C.sum_mem fun i _ ↦ C.smul_mem (hc i) (hsC i)
+ · simp
+
+/-- The face of a face of a cone is also a face of the cone. -/
+@[trans]
+protected theorem trans (h₁ : F₂.IsFaceOf F₁) (h₂ : F₁.IsFaceOf C) : F₂.IsFaceOf C := by
+ refine ⟨h₁.1.trans h₂.1, fun hx hy ha hxy ↦ h₁.2 (h₂.2 hx hy ha (h₁.le hxy)) ?_ ha hxy⟩
+ exact h₂.mem_of_add_mem_right (smul_mem _ ha.le hx) hy (h₁.le hxy)
+
+section Map
+
+variable [AddCommGroup N] [Module R N]
+
+/-- The image of a face of a cone under an injective linear map is a face of the
+image of the cone. -/
+protected theorem map (f : M →ₗ[R] N) (hf : Function.Injective f) (hF : F.IsFaceOf C) :
+ (F.map f).IsFaceOf (C.map f) where
+ le := map_mono hF.le
+ mem_of_smul_add_mem := by
+ rintro _ _ a ⟨x, hx, rfl⟩ ⟨y, hy, rfl⟩ ha ⟨z, hz₁, hz₂⟩
+ dsimp at hz₂
+ rw [← map_smul, ← map_add] at hz₂
+ exact ⟨x, hF.mem_of_smul_add_mem hx hy ha (hf hz₂ ▸ hz₁), rfl⟩
+
+/-- The image of a face of a cone under an equivalence is a face of the image of the cone. -/
+theorem map_equiv (e : M ≃ₗ[R] N) (hF : F.IsFaceOf C) :
+ (F.map (e : M →ₗ[R] N)).IsFaceOf (C.map e) := hF.map _ e.injective
+
+theorem of_map_injective {f : M →ₗ[R] N} (hf : Function.Injective f)
+ (hc : (map f F).IsFaceOf (map f C)) : F.IsFaceOf C := by
+ obtain ⟨sub, hF⟩ := hc
+ refine ⟨fun x xf ↦ ?_, fun hx hy ha h ↦ ?_⟩
+ · obtain ⟨y, yC, hy⟩ := mem_map.mp <| sub (mem_map_of_mem xf)
+ rwa [hf hy] at yC
+ · simp only [mem_map, forall_exists_index, and_imp] at hF
+ obtain ⟨_, ⟨hx', hhx'⟩⟩ := hF _ hx rfl _ hy rfl ha _ h (by simp)
+ convert hx'
+ exact hf hhx'.symm
+
+/-- The comap of a face of a cone under a linear map is a face of the comap of the cone. -/
+protected theorem comap (f : N →ₗ[R] M) (hF : F.IsFaceOf C) : (F.comap f).IsFaceOf (C.comap f) := by
+ refine ⟨comap_mono hF.le, ?_⟩
+ simp only [mem_comap, map_add, map_smul]
+ exact hF.mem_of_smul_add_mem
+
+theorem of_comap_surjective {f : N →ₗ[R] M} (hf : Function.Surjective f)
+ (hc : (F.comap f).IsFaceOf (C.comap f)) : F.IsFaceOf C := by
+ refine ⟨fun x xF ↦ ?_, fun {x y _} xC yC a0 h ↦ ?_⟩
+ · rw [← (hf x).choose_spec] at xF ⊢
+ exact mem_comap.mp (hc.1 xF)
+ · rw [← (hf x).choose_spec] at h ⊢ xC
+ rw [← (hf y).choose_spec] at h yC
+ exact hc.2 xC yC a0 (by simpa)
+
+end Map
+
+end IsFaceOf
+
+/-- The image of a cone `F` under an injective linear map is a face of the
+image of another cone `C` if and only if `F` is a face of `C`. -/
+theorem isFaceOf_map_iff [AddCommGroup N] [Module R N] {f : M →ₗ[R] N} (hf : Function.Injective f) :
+ (F.map f).IsFaceOf (C.map f) ↔ F.IsFaceOf C :=
+ ⟨IsFaceOf.of_map_injective hf, IsFaceOf.map _ hf⟩
+
+/-- The comap of a cone `F` under a surjective linear map is a face of the
+comap of another cone `F` if and only if `F` is a face of `C`. -/
+theorem isFaceOf_comap_iff [AddCommGroup N] [Module R N] {f : N →ₗ[R] M}
+ (hf : Function.Surjective f) : (F.comap f).IsFaceOf (C.comap f) ↔ F.IsFaceOf C :=
+ ⟨IsFaceOf.of_comap_surjective hf, IsFaceOf.comap _⟩
+
+end Semiring
+
+section DivisionRing
+
+variable [DivisionRing R] [LinearOrder R] [IsOrderedRing R]
+variable [AddCommGroup M] [Module R M]
+variable {C F F₁ F₂ : PointedCone R M}
+
+namespace IsFaceOf
+
+theorem of_mem_of_add_mem_left (h₁ : F ≤ C) (h₂ : ∀ {x y : M}, x ∈ C → y ∈ C → x + y ∈ F → x ∈ F) :
+ F.IsFaceOf C := by
+ refine ⟨h₁, fun hx hy ha haxy ↦ ?_⟩
+ simpa [← smul_assoc, inv_mul_cancel₀ (ne_of_gt ha)] using smul_mem _
+ (inv_nonneg.mpr (le_of_lt ha)) <| h₂ (smul_mem _ (le_of_lt ha) hx) hy haxy
+
+/-- The lineality space of a cone is a face. -/
+lemma lineal (C : PointedCone R M) : IsFaceOf C.lineal C := by
+ apply of_mem_of_add_mem_left (lineal_le C)
+ intro _ _ xc yc xyf
+ simp [neg_add_rev, xc, true_and] at xyf ⊢
+ simpa [neg_add_cancel_comm] using add_mem xyf.2 yc
+
+/-- The lineality space of a cone lies in every face. -/
+lemma lineal_le (hF : F.IsFaceOf C) : C.lineal ≤ F :=
+ fun _ hx ↦ hF.mem_of_add_mem_left hx.1 hx.2 (by simp)
+
+/-- The lineality space of a face of a cone agrees with the lineality space of the cone. -/
+lemma lineal_congr (hF : F.IsFaceOf C) : F.lineal = C.lineal := by
+ ext
+ refine ⟨fun ⟨hx, hx'⟩ ↦ ⟨hF.le hx, hF.le hx'⟩, fun ⟨hx, hx'⟩ ↦ ⟨?_, ?_⟩⟩
+ · exact hF.mem_of_add_mem_left hx hx' (by simp)
+ · exact hF.mem_of_add_mem_left hx' hx (by simp)
+
+section Prod
+
+variable [AddCommGroup N] [Module R N]
+
+/-- The product of two faces of two cones is a face of the product of the cones. -/
+protected theorem prod {C₁ F₁ : PointedCone R M} {C₂ F₂ : PointedCone R N}
+ (hF₁ : F₁.IsFaceOf C₁) (hF₂ : F₂.IsFaceOf C₂) : IsFaceOf (F₁.prod F₂) (C₁.prod C₂) := by
+ refine ⟨fun x hx ↦ by simpa [mem_prod] using ⟨hF₁.le hx.1, hF₂.le hx.2⟩, ?_⟩
+ simp only [mem_prod, Prod.fst_add, Prod.smul_fst, Prod.snd_add,
+ Prod.smul_snd, and_imp, Prod.forall]
+ intro _ _ _ _ _ xc₁ xc₂ yc₁ yc₂ a0 hab₁ hab₂
+ exact ⟨hF₁.mem_of_smul_add_mem xc₁ yc₁ a0 hab₁, hF₂.mem_of_smul_add_mem xc₂ yc₂ a0 hab₂⟩
+
+/-- The projection of a face of a product cone onto the first component is a face of the
+projection of the product cone onto the first component. -/
+protected theorem fst {C₁ : PointedCone R M} {C₂ : PointedCone R N}
+ {F : PointedCone R (M × N)}
+ (hF : F.IsFaceOf (C₁.prod C₂)) : (F.map (.fst R M N)).IsFaceOf C₁ := by
+ constructor
+ · intro x hx
+ simp only [mem_map, LinearMap.fst_apply, Prod.exists, exists_and_right, exists_eq_right] at hx
+ exact (Set.mem_prod.mp <| hF.le hx.choose_spec).1
+ · simp only [mem_map, LinearMap.fst_apply, Prod.exists, exists_and_right, exists_eq_right,
+ forall_exists_index]
+ intro x y a hx hy ha z h
+ refine ⟨0, hF.mem_of_smul_add_mem (x := (x, 0)) (y := (y, z)) ?_ ?_ ha (by simpa)⟩
+ · exact mem_prod.mp ⟨hx, zero_mem C₂⟩
+ · exact mem_prod.mp ⟨hy, (hF.le h).2⟩
+
+/-- The projection of a face of a product cone onto the second component is a face of the
+projection of the product cone onto the second component. -/
+protected theorem snd {C₁ : PointedCone R M} {C₂ : PointedCone R N} {F : PointedCone R (M × N)}
+ (hF : F.IsFaceOf (C₁.prod C₂)) : (F.map (.snd R M N)).IsFaceOf C₂ := by
+ have := hF.map _ (LinearEquiv.prodComm R M N).injective
+ convert IsFaceOf.fst (by simpa [PointedCone.map, Submodule.map])
+ ext; simp
+
+end Prod
+
+end IsFaceOf
+
+end DivisionRing
+
+end PointedCone
From 69892ec2946cc9efacff8bff6dc3700fec6a527a Mon Sep 17 00:00:00 2001
From: William Coram
Date: Wed, 17 Jun 2026 10:37:35 +0000
Subject: [PATCH 0103/1300] feat: lemmas towards showing gaussNorm on
MvPowerSeries is an absolute value (#38049)
We prove lemmas: ``gaussNorm_mul_le`` and ``gaussNorm_le_mul`` which will allow us to show it is an absolute value on Mv restricted power series.
Co-authored-by: WilliamCoram
---
.../Algebra/Order/Ring/IsNonarchimedean.lean | 103 +++++++++-------
.../Normed/Unbundled/FiniteExtension.lean | 4 +-
.../Normed/Unbundled/SpectralNorm.lean | 3 +-
.../RingTheory/MvPowerSeries/GaussNorm.lean | 112 +++++++++++++++++-
Mathlib/RingTheory/Polynomial/GaussNorm.lean | 2 +-
5 files changed, 173 insertions(+), 51 deletions(-)
diff --git a/Mathlib/Algebra/Order/Ring/IsNonarchimedean.lean b/Mathlib/Algebra/Order/Ring/IsNonarchimedean.lean
index 7cc52beee21e32..92d41588b31803 100644
--- a/Mathlib/Algebra/Order/Ring/IsNonarchimedean.lean
+++ b/Mathlib/Algebra/Order/Ring/IsNonarchimedean.lean
@@ -19,6 +19,9 @@ nonarchimedean functions.
public section
+/- TODO: Remove the Funlike hypothesis on these statements and turn them all into the form
+ {f : α → R} + properties on f. -/
+
namespace IsNonarchimedean
variable {R : Type*} [Semiring R] [LinearOrder R] {a b : R} {m n : ℕ}
@@ -112,6 +115,37 @@ theorem add_eq_max_of_ne {F α : Type*} [AddGroup α] [FunLike F α R]
· rw [add_eq_left_of_lt hna h_lt]
exact (max_eq_left_of_lt h_lt).symm
+/- TODO: Remove the funlike conditions on the lemmas required for add_max_of_ne, this will allow us
+ to remove the CommGroup part in the below which is unnecessary. -/
+
+lemma add_eq_max_of_ne' {α S : Type*} [Semiring S] [LinearOrder S] [AddCommGroup α]
+ (f : α → S) (fna : IsNonarchimedean f) (Neg : ∀ a, f a = f (-a)) {a b : α}
+ (hne : f a ≠ f b) : f (a + b) = max (f a) (f b) := by
+ wlog hab : f a > f b generalizing a b with H
+ · simpa [add_comm, max_comm] using (H hne.symm ((not_lt.mp hab).lt_of_ne hne))
+ apply le_antisymm (fna a b)
+ rcases le_max_iff.mp (fna (a + b) (-b)) with h | h
+ · simpa [max_eq_left (le_of_lt hab)] using h
+ · exact absurd h (not_le.mpr (by simpa [Neg b] using hab))
+
+omit [Semiring R] in
+open Finset in
+/-- Ultrametric inequality with `Finset.sum`. -/
+lemma apply_sum_le_sup {α β : Type*} [AddCommMonoid α] {f : α → R}
+ (nonarch : IsNonarchimedean f) {s : Finset β} (hnonempty : s.Nonempty) {l : β → α} :
+ f (∑ i ∈ s, l i) ≤ s.sup' hnonempty fun i => f (l i) := by
+ induction hnonempty using Nonempty.cons_induction with
+ | singleton i => simp
+ | cons i s _ hs hind =>
+ simp only [sum_cons, le_sup'_iff, mem_cons, exists_eq_or_imp]
+ rw [← le_sup'_iff hs]
+ rcases le_max_iff.mp <| nonarch (l i) (∑ i ∈ s, l i) with h₁ | h₂
+ · exact .inl h₁
+ · exact .inr <| le_trans h₂ hind
+
+@[deprecated (since := "2026-04-27")]
+alias apply_sum_le_sup_of_isNonarchimedean := apply_sum_le_sup
+
omit [Semiring R] in
/-- Given a nonarchimedean function `α → R`, a function `g : β → α` and a nonempty multiset
`s : Multiset β`, we can always find `b : β` belonging to `s` such that
@@ -133,11 +167,10 @@ theorem multiset_image_add_of_nonempty {α β : Type*} [AddCommMonoid α] [Nonem
omit [Semiring R] in
/-- Given a nonarchimedean function `α → R`, a function `g : β → α` and a nonempty finset
`t : Finset β`, we can always find `b : β` belonging to `t` such that `f (t.sum g) ≤ f (g b)` . -/
-theorem finset_image_add_of_nonempty {α β : Type*} [AddCommMonoid α] [Nonempty β] {f : α → R}
+theorem finset_image_add_of_nonempty {α β : Type*} [AddCommMonoid α] {f : α → R}
(hna : IsNonarchimedean f) (g : β → α) {t : Finset β} (ht : t.Nonempty) :
∃ b ∈ t, f (t.sum g) ≤ f (g b) := by
- apply multiset_image_add_of_nonempty hna
- simp_all [Finset.nonempty_iff_ne_empty]
+ simpa [Finset.le_sup'_iff] using IsNonarchimedean.apply_sum_le_sup hna ht
/-- Given a nonnegative nonarchimedean function `α → R` such that `f 0 = 0`, a function `g : β → α`
and a multiset `s : Multiset β`, we can always find `b : β`, belonging to `s` if `s` is nonempty,
@@ -155,13 +188,13 @@ theorem multiset_image_add {F α β : Type*} [AddCommMonoid α] [FunLike F α R]
/-- Given a nonnegative nonarchimedean function `α → R` such that `f 0 = 0`, a function `g : β → α`
and a finset `t : Finset β`, we can always find `b : β`, belonging to `t` if `t` is nonempty,
such that `f (t.sum g) ≤ f (g b)` . -/
-theorem finset_image_add {F α β : Type*} [AddCommMonoid α] [FunLike F α R]
- [ZeroHomClass F α R] [NonnegHomClass F α R] [Nonempty β] {f : F} (hna : IsNonarchimedean f)
- (g : β → α) (t : Finset β) :
- ∃ b : β, (t.Nonempty → b ∈ t) ∧ f (t.sum g) ≤ f (g b) := by
- have h1 : t.Nonempty ↔ t.val ≠ 0 := by simp [Finset.nonempty_iff_ne_empty]
- rw [h1]
- exact multiset_image_add hna g t.val
+lemma finset_image_add {α β : Type*} [AddCommMonoid α] [Nonempty β] {f : α → R} (f_zero : f 0 = 0)
+ (f_nonneg : ∀ x, 0 ≤ f x) (hna : IsNonarchimedean f) (g : β → α) (t : Finset β) :
+ ∃ i, (t.Nonempty → i ∈ t) ∧ f (t.sum g) ≤ f (g i) := by
+ rcases t.eq_empty_or_nonempty with rfl | ht
+ · simp [f_zero, f_nonneg]
+ · exact (fun ⟨i, h, h'⟩ => ⟨i, fun _ ↦ h, h'⟩) <|
+ IsNonarchimedean.finset_image_add_of_nonempty hna g ht
open Multiset in
theorem multiset_powerset_image_add [IsStrictOrderedRing R]
@@ -186,42 +219,22 @@ theorem finset_powerset_image_add [IsStrictOrderedRing R]
f ((powersetCard (s.card - m) s).sum fun t : Finset β ↦
t.prod fun i : β ↦ -b i) ≤ f (u.val.prod fun i : β ↦ -b i) := by
set g := fun t : Finset β ↦ t.prod fun i : β ↦ - b i
- obtain ⟨b, hb_in, hb⟩ := hf_na.finset_image_add g (powersetCard (s.card - m) s)
+ obtain ⟨b, hb_in, hb⟩ := hf_na.finset_image_add (by grind) (apply_nonneg f)
+ g (powersetCard (s.card - m) s)
exact ⟨⟨b, hb_in (powersetCard_nonempty.mpr (Nat.sub_le s.card m))⟩, hb⟩
-omit [Semiring R] in
-open Finset in
-/-- Ultrametric inequality with `Finset.sum`. -/
-lemma apply_sum_le_sup {α β : Type*} [AddCommMonoid α] {f : α → R}
- (nonarch : IsNonarchimedean f) {s : Finset β} (hnonempty : s.Nonempty) {l : β → α} :
- f (∑ i ∈ s, l i) ≤ s.sup' hnonempty fun i => f (l i) := by
- induction hnonempty using Nonempty.cons_induction with
- | singleton i => simp
- | cons i s _ hs hind =>
- simp only [sum_cons, le_sup'_iff, mem_cons, exists_eq_or_imp]
- rw [← le_sup'_iff hs]
- rcases le_max_iff.mp <| nonarch (l i) (∑ i ∈ s, l i) with h₁ | h₂
- · exact .inl h₁
- · exact .inr <| le_trans h₂ hind
-
-@[deprecated (since := "2026-04-27")]
-alias apply_sum_le_sup_of_isNonarchimedean := apply_sum_le_sup
-
-open Finset in
-lemma apply_sum_eq_of_lt {α β F : Type*} [AddCommGroup α] [FunLike F α R]
- [AddGroupSeminormClass F α R] {f : F} (nonarch : IsNonarchimedean f) {s : Finset β} {l : β → α}
- {k : β} (hk : k ∈ s) (hmax : ∀ j ∈ s, j ≠ k → f (l j) < f (l k)) :
- f (∑ i ∈ s, l i) = f (l k) := by
- have : s.Nonempty := by use k
- induction this using Nonempty.cons_induction generalizing k with
- | singleton a => simp_all
- | cons a s _ hs _ =>
- by_cases ha : k = a
- · rw [sum_cons, ha]
- apply add_eq_left_of_lt nonarch
- grw [apply_sum_le_sup nonarch hs]
- grind [sup'_lt_iff]
- · grind [add_eq_right_of_lt nonarch]
+lemma apply_sum_eq_of_lt {α β : Type*} [AddCommGroup α] {f : α → R} (fna : IsNonarchimedean f)
+ (f_neg : ∀ a, f a = f (-a)) {s : Finset β} {l : β → α} {k : β} (hk : k ∈ s)
+ (hmax : ∀ j ∈ s, j ≠ k → f (l j) < f (l k)) : f (∑ i ∈ s, l i) = f (l k) := by
+ by_cases hcard : s.card = 1
+ · grind [Finset.card_eq_one.mp hcard]
+ · classical
+ rw [← Finset.add_sum_erase _ _ hk]
+ have hNonempty : (s.erase k).Nonempty :=
+ Finset.Nontrivial.erase_nonempty (Finset.one_lt_card_iff_nontrivial.mp (by grind))
+ have hrest_le := IsNonarchimedean.apply_sum_le_sup fna hNonempty (l := l)
+ simp only [Finset.le_sup'_iff, Finset.mem_erase, ne_eq] at hrest_le
+ rw [add_eq_max_of_ne' f fna f_neg (by grind), max_eq_left (le_of_lt (by grind))]
/-- If `f` is a nonarchimedean additive group seminorm on a commutative ring `α`, `n : ℕ`, and
`a b : α`, then we can find `m : ℕ` such that `m ≤ n` and
@@ -229,7 +242,7 @@ lemma apply_sum_eq_of_lt {α β F : Type*} [AddCommGroup α] [FunLike F α R]
theorem add_pow_le {F α : Type*} [CommRing α] [FunLike F α R] [ZeroHomClass F α R]
[NonnegHomClass F α R] [SubmultiplicativeHomClass F α R] {f : F} (hna : IsNonarchimedean f)
(n : ℕ) (a b : α) : ∃ m < n + 1, f ((a + b) ^ n) ≤ f (a ^ m) * f (b ^ (n - m)) := by
- obtain ⟨m, hm_lt, hM⟩ := finset_image_add hna
+ obtain ⟨m, hm_lt, hM⟩ := finset_image_add (by aesop) (by aesop) hna
(fun m => a ^ m * b ^ (n - m) * ↑(n.choose m)) (Finset.range (n + 1))
simp only [Finset.nonempty_range_iff, ne_eq, Nat.succ_ne_zero, not_false_iff, Finset.mem_range,
forall_true_left] at hm_lt
diff --git a/Mathlib/Analysis/Normed/Unbundled/FiniteExtension.lean b/Mathlib/Analysis/Normed/Unbundled/FiniteExtension.lean
index 028dffb4ec2f00..f887dd057b06cd 100644
--- a/Mathlib/Analysis/Normed/Unbundled/FiniteExtension.lean
+++ b/Mathlib/Analysis/Normed/Unbundled/FiniteExtension.lean
@@ -120,7 +120,7 @@ theorem norm_mul_le_const_mul_norm {i : ι} (hBi : B i = (1 : L))
obtain ⟨k, -, (hk : ‖∑ i : ι, (B.repr x i • ∑ i_1 : ι,
B.repr y i_1 • B.repr (B i * B i_1)) ixy‖ ≤
‖(B.repr x k • ∑ j : ι, B.repr y j • B.repr (B k * B j)) ixy‖)⟩ :=
- IsNonarchimedean.finset_image_add hna'
+ IsNonarchimedean.finset_image_add (map_zero _) (apply_nonneg _) hna'
(fun i ↦ (B.repr x i • ∑ i_1 : ι, B.repr y i_1 • B.repr (B i * B i_1)) ixy)
(univ : Finset ι)
simp only [Finsupp.coe_smul, Finsupp.coe_finsetSum, Pi.smul_apply, Finset.sum_apply,
@@ -130,7 +130,7 @@ theorem norm_mul_le_const_mul_norm {i : ι} (hBi : B i = (1 : L))
obtain ⟨k', hk'⟩ : ∃ (k' : ι),
‖∑ j : ι, B.repr y j • B.repr (B k * B j) ixy‖ ≤
‖B.repr y k' • B.repr (B k * B k') ixy‖ := by
- obtain ⟨k, hk0, hk⟩ := IsNonarchimedean.finset_image_add hna'
+ obtain ⟨k, hk0, hk⟩ := IsNonarchimedean.finset_image_add (map_zero _) (apply_nonneg _) hna'
(fun i ↦ B.repr y i • B.repr (B k * B i) ixy) (univ : Finset ι)
exact ⟨k, hk⟩
apply le_trans (mul_le_mul_of_nonneg_left hk' (norm_nonneg _))
diff --git a/Mathlib/Analysis/Normed/Unbundled/SpectralNorm.lean b/Mathlib/Analysis/Normed/Unbundled/SpectralNorm.lean
index ac24e149280c5f..e8e8c4ef5c0ea9 100644
--- a/Mathlib/Analysis/Normed/Unbundled/SpectralNorm.lean
+++ b/Mathlib/Analysis/Normed/Unbundled/SpectralNorm.lean
@@ -271,7 +271,8 @@ theorem norm_root_le_spectralValue {f : AlgebraNorm K L} (hf_pm : IsPowMul f)
set g := fun i : ℕ ↦ p.coeff i • x ^ i
obtain ⟨m, hm_in, hm⟩ : ∃ (m : ℕ) (_ : 0 < p.natDegree → m < p.natDegree),
f ((Finset.range p.natDegree).sum g) ≤ f (g m) := by
- obtain ⟨m, hm, h⟩ := IsNonarchimedean.finset_image_add hf_na g (Finset.range p.natDegree)
+ obtain ⟨m, hm, h⟩ := IsNonarchimedean.finset_image_add (map_zero _) (apply_nonneg _) hf_na g
+ (Finset.range p.natDegree)
rw [Finset.nonempty_range_iff, ← zero_lt_iff, Finset.mem_range] at hm
exact ⟨m, hm, h⟩
exact lt_of_le_of_lt hm (hn' m (hm_in h_deg))
diff --git a/Mathlib/RingTheory/MvPowerSeries/GaussNorm.lean b/Mathlib/RingTheory/MvPowerSeries/GaussNorm.lean
index 8dcad1cf5683a4..db2ea3a8a78c2e 100644
--- a/Mathlib/RingTheory/MvPowerSeries/GaussNorm.lean
+++ b/Mathlib/RingTheory/MvPowerSeries/GaussNorm.lean
@@ -8,6 +8,8 @@ module
public import Mathlib.Analysis.Normed.Ring.Basic
public import Mathlib.RingTheory.MvPowerSeries.Basic
+public import Mathlib.Algebra.Order.Ring.IsNonarchimedean
+
/-!
# Gauss norm for multivariate power series
@@ -37,7 +39,11 @@ the set of all values of `v (coeff t f) * ∏ i : t.support, c i` for all `t :
namespace MvPowerSeries
-variable {R σ : Type*} [Semiring R] (v : R → ℝ) (c : σ → ℝ) (f : MvPowerSeries σ R)
+variable {R σ : Type*} (v : R → ℝ) (c : σ → ℝ) (f : MvPowerSeries σ R)
+
+section Semiring
+
+variable [Semiring R]
/-- Given a multivariate power series `f` in, a function `v : R → ℝ` and a tuple `c` of real
numbers, the Gauss norm is defined as the supremum of the set of all values of
@@ -81,7 +87,7 @@ lemma gaussNorm_eq_zero_iff (vZero : v 0 = 0) (vNonneg : ∀ a, v a ≥ 0)
_ ≤ _ := le_gaussNorm v c f hbd n
lemma gaussNorm_add_le_max (f g : MvPowerSeries σ R) (hc : 0 ≤ c)
- (vNonneg : ∀ a, v a ≥ 0) (hv : ∀ x y, v (x + y) ≤ max (v x) (v y))
+ (vNonneg : ∀ a, v a ≥ 0) (hv : IsNonarchimedean v)
(hbfd : HasGaussNorm v c f) (hbgd : HasGaussNorm v c g) :
gaussNorm v c (f + g) ≤ max (gaussNorm v c f) (gaussNorm v c g) := by
have H (t : σ →₀ ℕ) : 0 ≤ ∏ i ∈ t.support, c i ^ t i :=
@@ -120,4 +126,106 @@ lemma gaussNorm_add_le_max (f g : MvPowerSeries σ R) (hc : 0 ≤ c)
left
exact gaussNorm_nonneg v c f vNonneg
+private lemma c_prod_nonneg (hc : 0 ≤ c) (t : σ →₀ ℕ) : 0 ≤ t.prod (c · ^ ·) :=
+ Finset.prod_nonneg (fun i _ ↦ pow_nonneg (hc i) (t i))
+
+lemma gaussNorm_mul_le (f g : MvPowerSeries σ R) (hc : 0 ≤ c) (vNonneg : ∀ a, v a ≥ 0)
+ (vMul : ∀ a b, v (a * b) ≤ v a * v b) (vna : IsNonarchimedean v)
+ (vZero : v 0 = 0) (hbfd : HasGaussNorm v c f) (hbgd : HasGaussNorm v c g) :
+ gaussNorm v c (f * g) ≤ gaussNorm v c f * gaussNorm v c g := by
+ classical
+ refine Real.iSup_le ?_ ?_
+ · intro t
+ obtain ⟨k, hk, hsum⟩ := IsNonarchimedean.finset_image_add vZero vNonneg vna
+ (fun a ↦ coeff a.1 f * coeff a.2 g) (Finset.antidiagonal t)
+ have hk' : k.1 + k.2 = t := by
+ simpa [Finset.mem_antidiagonal] using hk (Finset.nonempty_def.mpr ⟨(t, 0), by simp⟩)
+ have hprod : t.prod (c · ^ ·) = k.1.prod (c · ^ ·) * k.2.prod (c · ^ ·) := by
+ simp [← hk', Finsupp.prod_add_index' (h := (c · ^ ·)) (by grind) (by grind)]
+ rw [hprod]
+ refine (mul_le_mul hsum (by rfl) (mul_nonneg (c_prod_nonneg c hc k.1) (c_prod_nonneg c hc k.2))
+ (vNonneg _)).trans ?_
+ have : v ((coeff k.1) f * (coeff k.2) g) * (k.1.prod (c · ^ ·) * k.2.prod (c · ^ ·)) ≤
+ (v (coeff k.1 f) * k.1.prod (c · ^ ·)) * (v (coeff k.2 g) * k.2.prod (c · ^ ·)) := by
+ calc
+ _ ≤ v (coeff k.1 f) * v (coeff k.2 g) * (k.1.prod (c · ^ ·) * k.2.prod (c · ^ ·)) :=
+ mul_le_mul (vMul _ _) (by rfl) (mul_nonneg (c_prod_nonneg c hc k.1)
+ (c_prod_nonneg c hc k.2)) (mul_nonneg (vNonneg _) (vNonneg _))
+ _ = _ := by ring
+ exact this.trans (mul_le_mul (le_gaussNorm v c f hbfd k.1) (le_gaussNorm v c g hbgd k.2)
+ (mul_nonneg (vNonneg _) (c_prod_nonneg c hc k.2)) (gaussNorm_nonneg v c f vNonneg))
+ · exact mul_nonneg (gaussNorm_nonneg v c f vNonneg) (gaussNorm_nonneg v c g vNonneg)
+
+end Semiring
+
+variable [Ring R]
+
+/-- Predicate for when the gaussNorm is achieved by an index. -/
+abbrev AchievesGaussNorm (i : σ →₀ ℕ) : Prop :=
+ v (coeff i f) * i.prod (c · ^ ·) = gaussNorm v c f
+
+section absoluteValue
+
+variable {α S : Type*} [LinearOrder S] [AddCommGroup α] (f : α → S)
+
+lemma ultrametric_strict (na : IsNonarchimedean f)
+ (Neg : ∀ a, f a = f (-a)) {a b : α} (hne : f a ≠ f b) : f (a + b) = max (f a) (f b) := by
+ wlog hab : f a > f b generalizing a b with H
+ · simpa [add_comm, max_comm] using (H hne.symm ((not_lt.mp hab).lt_of_ne hne))
+ apply le_antisymm (na a b)
+ rcases le_max_iff.mp (na (a + b) (-b)) with h | h
+ · simpa [max_eq_left (le_of_lt hab)] using h
+ · exact absurd h (not_le.mpr (by simpa [Neg b] using hab))
+
+variable [Semiring S]
+
+lemma Finset.Nonempty.map_sum_le_sup'_map
+ {α S : Type*} [Semiring S] [LinearOrder S] [AddCommMonoid α] (g : α → S)
+ {ι : Type*} {s : Finset ι} (hs : s.Nonempty) (f : ι → α)
+ (na : ∀ a b, g (a + b) ≤ max (g a) (g b)) :
+ g (∑ i ∈ s, f i) ≤ s.sup' hs fun x ↦ g (f x) := by
+ simp only [Finset.le_sup'_iff]
+ induction hs using Finset.Nonempty.cons_induction with
+ | singleton j => simp only [Finset.mem_singleton, Finset.sum_singleton, exists_eq_left, le_refl]
+ | cons j s hj _ IH =>
+ simp only [Finset.sum_cons, Finset.mem_cons, exists_eq_or_imp]
+ refine (le_total (g (∑ i ∈ s, f i)) (g (f j))).imp ?_ ?_ <;> intro h
+ · exact (na _ _).trans (max_eq_left h).le
+ · exact ⟨_, IH.choose_spec.left, (na _ _).trans <|
+ ((max_eq_right h).le.trans IH.choose_spec.right)⟩
+
+variable [DecidableEq σ] (f g : MvPowerSeries σ R)
+
+lemma antidiagonal_dominant (i j : σ →₀ ℕ) (vna : IsNonarchimedean v)
+ (vMulEq : ∀ a b, v (a * b) = v a * v b) (vNeg : ∀ a, v a = v (-a))
+ (hdom : ∀ p ∈ Finset.antidiagonal (i + j), p ≠ (i, j) →
+ v (coeff p.1 f * coeff p.2 g) < v (coeff i f) * v (coeff j g)) :
+ v (coeff (i + j) (f * g)) = v (coeff i f * coeff j g) := by
+ rw [← vMulEq] at hdom
+ rw [coeff_mul, IsNonarchimedean.apply_sum_eq_of_lt vna (by grind) (k := (i, j))
+ (s := Finset.antidiagonal (i + j)) (Finset.mem_antidiagonal.mpr rfl) hdom]
+
+lemma gaussNorm_le_mul (vMulEq : ∀ a b, v (a * b) = v a * v b)
+ (vna : IsNonarchimedean v) (vNeg : ∀ a, v a = v (-a))
+ (hbfg : HasGaussNorm v c (f * g))
+ (hdom : ∃ i j, AchievesGaussNorm v c f i ∧ AchievesGaussNorm v c g j ∧
+ ∀ p ∈ Finset.antidiagonal (i + j), p ≠ (i, j) →
+ v (coeff p.1 f * coeff p.2 g) < v (coeff i f) * v (coeff j g)) :
+ gaussNorm v c f * gaussNorm v c g ≤ gaussNorm v c (f * g) := by
+ obtain ⟨i₀, j₀, hi₀, hj₀, hdom'⟩ := hdom
+ unfold AchievesGaussNorm at hi₀ hj₀
+ calc
+ _ = (v (coeff i₀ f) * i₀.prod (c · ^ ·)) * (v (coeff j₀ g) * j₀.prod (c · ^ ·)) := by
+ rw [← hi₀, ← hj₀]
+ _ = v (coeff i₀ f) * v (coeff j₀ g) * ((i₀ + j₀).prod (c · ^ ·)) := by
+ have hprod : (i₀ + j₀).prod (c · ^ ·) = i₀.prod (c · ^ ·) * j₀.prod (c · ^ ·) := by
+ simp [Finsupp.prod_add_index', pow_add]
+ rw [hprod]; ring
+ _ = v (coeff i₀ f * coeff j₀ g) * (i₀ + j₀).prod (c · ^ ·) := by rw [vMulEq]
+ _ = v (coeff (i₀ + j₀) (f * g)) * (i₀ + j₀).prod (c · ^ ·) := by
+ rw [antidiagonal_dominant v f g i₀ j₀ vna vMulEq vNeg hdom']
+ _ ≤ gaussNorm v c (f * g) := le_gaussNorm v c (f * g) hbfg (i₀ + j₀)
+
+end absoluteValue
+
end MvPowerSeries
diff --git a/Mathlib/RingTheory/Polynomial/GaussNorm.lean b/Mathlib/RingTheory/Polynomial/GaussNorm.lean
index 858acd5a73cb03..04250ee856e304 100644
--- a/Mathlib/RingTheory/Polynomial/GaussNorm.lean
+++ b/Mathlib/RingTheory/Polynomial/GaussNorm.lean
@@ -235,7 +235,7 @@ private theorem mul_gaussNorm_le_gaussNorm_mul (p q : R[X]) :
apply le_of_eq_of_le _ <| (p * q).le_gaussNorm v hc0 (i + j)
-- gaussNorm v c p * gaussNorm v c q is actually equal to v ((p * q).coeff (i + j)) * c ^ (i + j)
rw [hi_p, hj_q, coeff_mul, Nat.sum_antidiagonal_eq_sum_range_succ_mk,
- IsNonarchimedean.apply_sum_eq_of_lt hna (k := i) (by simp)]
+ IsNonarchimedean.apply_sum_eq_of_lt hna (k := i) (by simp) (by simp)]
/- IsNonarchimedean.apply_sum_eq_of_lt makes the goal almost trivial so we are left to prove
the hmax hypothesis -/
· grind
From ce224279646af25dc80c231f5257bcfd6e70af45 Mon Sep 17 00:00:00 2001
From: Riccardo Brasca
Date: Wed, 17 Jun 2026 11:02:51 +0000
Subject: [PATCH 0104/1300] feat: add `Algebra.norm_zpow` (#40238)
We generalize `Algebra.norm_inv` from fields to division rings and we add `Algebra.norm_zpow`.
From flt-regular.
---
Mathlib/RingTheory/Norm/Basic.lean | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/Mathlib/RingTheory/Norm/Basic.lean b/Mathlib/RingTheory/Norm/Basic.lean
index 6859260ed69c9f..57096d1816ef5f 100644
--- a/Mathlib/RingTheory/Norm/Basic.lean
+++ b/Mathlib/RingTheory/Norm/Basic.lean
@@ -127,12 +127,21 @@ theorem norm_ne_zero_iff_of_basis [IsDomain R] [IsDomain S] (b : Basis ι R S) {
Algebra.norm R x ≠ 0 ↔ x ≠ 0 :=
not_iff_not.mpr (norm_eq_zero_iff_of_basis b)
-theorem norm_inv [Module.Finite K L] (x : L) : Algebra.norm K x⁻¹ = (Algebra.norm K x)⁻¹ := by
+end EqZeroIff
+
+section DivisionRing
+
+variable {L : Type*} [DivisionRing L] [Algebra K L] [Module.Finite K L]
+
+theorem norm_inv (x : L) : Algebra.norm K x⁻¹ = (Algebra.norm K x)⁻¹ := by
by_cases hx : x = 0
· simp [hx]
exact mul_left_injective₀ (norm_ne_zero_iff.mpr hx) (by simp [hx, ← map_mul])
-end EqZeroIff
+theorem norm_zpow (x : L) (n : ℤ) : Algebra.norm K (x ^ n) = Algebra.norm K x ^ n :=
+ map_zpow' _ norm_inv _ _
+
+end DivisionRing
open IntermediateField
From 2d792c9bea7859d32dd125ee02ad72fa54a7a237 Mon Sep 17 00:00:00 2001
From: Kamille Bidan <25210160030@m.fudan.edu.cn>
Date: Wed, 17 Jun 2026 11:18:00 +0000
Subject: [PATCH 0105/1300] feat(ModelTheory): add `exClosure` definition for
first-order formulas (#36667)
Prepare for moving realizations between elementarily equivalent structures.
Co-authored-by: NoneMore
---
Mathlib/ModelTheory/Semantics.lean | 39 ++++++++++++++++++++++++++++++
Mathlib/ModelTheory/Syntax.lean | 6 +++++
2 files changed, 45 insertions(+)
diff --git a/Mathlib/ModelTheory/Semantics.lean b/Mathlib/ModelTheory/Semantics.lean
index 7fe1a9e0d5939a..dfcdfdc43b2eed 100644
--- a/Mathlib/ModelTheory/Semantics.lean
+++ b/Mathlib/ModelTheory/Semantics.lean
@@ -933,6 +933,45 @@ theorem realize_iExsUnique [Finite γ] {φ : L.Formula (α ⊕ γ)} {v : α →
end BoundedFormula
+namespace Formula
+
+@[simp]
+theorem realize_exClosure [DecidableEq α] (φ : L.Formula α) :
+ φ.exClosure.Realize M ↔
+ ∃ v : φ.freeVarFinset → M, Formula.Realize (φ.restrictFreeVar id) v := by
+ simp [Sentence.Realize, Formula.exClosure, Formula.realize_iExs]
+
+theorem realize_exClosure_of_realize_equivSentence [DecidableEq α] [L[[α]].Structure M]
+ [(L.lhomWithConstants α).IsExpansionOn M] {φ : L.Formula α}
+ (h : (Formula.equivSentence φ).Realize M) : φ.exClosure.Realize M := by
+ rw [Formula.realize_exClosure]
+ exists fun a => (L.con (a : α) : M)
+ simpa [Formula.Realize, BoundedFormula.realize_restrictFreeVar] using h
+
+theorem exists_realize_equivSentence_iff_realize_exClosure
+ [DecidableEq α] [Nonempty M] {φ : L.Formula α} :
+ (∃ v : α → M,
+ letI := (constantsOn.structure v);
+ (Formula.equivSentence φ).Realize M) ↔ (φ.exClosure.Realize M) := by
+ constructor
+ · rintro ⟨v, hv⟩
+ exact (Formula.realize_exClosure φ).mpr ⟨fun a => v a,
+ (BoundedFormula.realize_restrictFreeVar (φ := φ) (f := id) (v := fun a => v a) (v' := v)
+ (fun _ => rfl)).2
+ (by simpa [Formula.Realize]
+ using (realize_equivSentence_symm M (Formula.equivSentence φ) v).2 hv)⟩
+ · intro h
+ classical
+ obtain ⟨v, hv⟩ := (Formula.realize_exClosure φ).1 h
+ let v' := fun a => if hmem : a ∈ φ.freeVarFinset
+ then v ⟨a, hmem⟩ else Classical.choice inferInstance
+ exists v'
+ refine (Formula.realize_equivSentence_symm M (Formula.equivSentence φ) v').mp ?_
+ simpa [Equiv.symm_apply_apply, Formula.Realize] using
+ (BoundedFormula.realize_restrictFreeVar v' (by grind)).1 hv
+
+end Formula
+
namespace StrongHomClass
variable {F : Type*} [EquivLike F M N] [StrongHomClass L F M N] (g : F)
diff --git a/Mathlib/ModelTheory/Syntax.lean b/Mathlib/ModelTheory/Syntax.lean
index 1b7dfa56d6698b..3a24b6664bcf24 100644
--- a/Mathlib/ModelTheory/Syntax.lean
+++ b/Mathlib/ModelTheory/Syntax.lean
@@ -784,6 +784,12 @@ noncomputable def iExsUnique [Finite β] (φ : L.Formula (α ⊕ β)) : L.Formul
((φ.relabel (fun a => Sum.elim (.inl ∘ .inl) .inr a)).imp <|
.iInf fun g => Term.equal (var (.inr g)) (var (.inl (.inr g))))
+variable [DecidableEq α] in
+/-- `exClosure φ` is the sentence asserting that there exist values for all free variables of `φ`
+such that `φ` holds. -/
+noncomputable def exClosure (φ : L.Formula α) : L.Sentence :=
+ iExs φ.freeVarFinset (Formula.relabel Sum.inr (φ.restrictFreeVar id))
+
/-- The biimplication between formulas, as a formula. -/
protected nonrec abbrev iff (φ ψ : L.Formula α) : L.Formula α :=
φ.iff ψ
From 31beafde0b4e8eab3184a0e18e4da4a8ca06eda0 Mon Sep 17 00:00:00 2001
From: Sebastien Gouezel <10818434+sgouezel@users.noreply.github.com>
Date: Wed, 17 Jun 2026 11:33:09 +0000
Subject: [PATCH 0106/1300] chore: fix non-reducible diamonds around scalar
multiplication in `RingCon` (#40704)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The following fails before the PR, succeeds after it.
```
example {R : Type*} [Ring R] (c : RingCon R) :
(RingCon.hasZSMul c : SMul ℤ c.Quotient) = RingCon.instSMulQuotient c := by
with_reducible_and_instances rfl -- fails
```
Co-authored-by: sgouezel
---
Mathlib/RingTheory/Congruence/Basic.lean | 2 +-
Mathlib/RingTheory/Congruence/Defs.lean | 21 +++++++++++++++------
2 files changed, 16 insertions(+), 7 deletions(-)
diff --git a/Mathlib/RingTheory/Congruence/Basic.lean b/Mathlib/RingTheory/Congruence/Basic.lean
index ed6041e7d612d6..40ad52ee203bd3 100644
--- a/Mathlib/RingTheory/Congruence/Basic.lean
+++ b/Mathlib/RingTheory/Congruence/Basic.lean
@@ -52,7 +52,7 @@ variable [SMul α R] [IsScalarTower α R R]
variable [SMul β R] [IsScalarTower β R R]
variable (c : RingCon R)
-instance : SMul α c.Quotient := inferInstanceAs (SMul α c.toCon.Quotient)
+instance : SMul α c.Quotient := ⟨c.smulAux (Con.smul c.toCon)⟩
@[simp, norm_cast]
theorem coe_smul (a : α) (x : R) : (↑(a • x) : c.Quotient) = a • (x : c.Quotient) :=
diff --git a/Mathlib/RingTheory/Congruence/Defs.lean b/Mathlib/RingTheory/Congruence/Defs.lean
index c4521330d750d4..16f78f06af0245 100644
--- a/Mathlib/RingTheory/Congruence/Defs.lean
+++ b/Mathlib/RingTheory/Congruence/Defs.lean
@@ -252,6 +252,13 @@ theorem coe_one : (↑(1 : R) : c.Quotient) = 1 :=
end One
+/-- A function used to define scalar actions on `RingCon.Quotient`. To make sure such actions coming
+from different sources are reducibly defeq, they should all go through this function. -/
+def smulAux [Add R] [Mul R] {α : Type*} [SMul α R]
+ (c : RingCon R) (h : ∀ (a : α) (x y : R), c x y → c (a • x) (a • y))
+ (a : α) (x : c.Quotient) : c.Quotient :=
+ Quotient.map' (a • ·) (h a) x
+
section NegSubZSMul
variable [AddGroup R] [Mul R] (c : RingCon R)
@@ -268,7 +275,7 @@ instance : Sub c.Quotient := inferInstanceAs (Sub c.toAddCon.Quotient)
theorem coe_sub (x y : R) : (↑(x - y) : c.Quotient) = x - y :=
rfl
-instance hasZSMul : SMul ℤ c.Quotient := inferInstanceAs (SMul ℤ c.toAddCon.Quotient)
+instance hasZSMul : SMul ℤ c.Quotient := ⟨c.smulAux (RingCon.zsmul c)⟩
@[simp, norm_cast]
theorem coe_zsmul (z : ℤ) (x : R) : (↑(z • x) : c.Quotient) = z • (x : c.Quotient) :=
@@ -280,7 +287,7 @@ section NSMul
variable [AddMonoid R] [Mul R] (c : RingCon R)
-instance hasNSMul : SMul ℕ c.Quotient := inferInstanceAs (SMul ℕ c.toAddCon.Quotient)
+instance hasNSMul : SMul ℕ c.Quotient := ⟨c.smulAux (RingCon.nsmul c)⟩
@[simp, norm_cast]
theorem coe_nsmul (n : ℕ) (x : R) : (↑(n • x) : c.Quotient) = n • (x : c.Quotient) :=
@@ -353,14 +360,16 @@ instance [AddCommMagma R] [Mul R] (c : RingCon R) : AddCommMagma c.Quotient :=
instance [AddCommSemigroup R] [Mul R] (c : RingCon R) : AddCommSemigroup c.Quotient :=
inferInstanceAs <| AddCommSemigroup c.toAddCon.Quotient
-instance [AddMonoid R] [Mul R] (c : RingCon R) : AddMonoid c.Quotient :=
- inferInstanceAs <| AddMonoid c.toAddCon.Quotient
+instance [AddMonoid R] [Mul R] (c : RingCon R) : AddMonoid c.Quotient where
+ nsmul n x := n • x
+ __ : AddMonoid c.Quotient := inferInstanceAs <| AddMonoid c.toAddCon.Quotient
instance [AddCommMonoid R] [Mul R] (c : RingCon R) : AddCommMonoid c.Quotient :=
inferInstanceAs <| AddCommMonoid c.toAddCon.Quotient
-instance [AddGroup R] [Mul R] (c : RingCon R) : AddGroup c.Quotient :=
- inferInstanceAs <| AddGroup c.toAddCon.Quotient
+instance [AddGroup R] [Mul R] (c : RingCon R) : AddGroup c.Quotient where
+ zsmul n x := n • x
+ __ : AddGroup c.Quotient := inferInstanceAs <| AddGroup c.toAddCon.Quotient
instance [AddCommGroup R] [Mul R] (c : RingCon R) : AddCommGroup c.Quotient :=
inferInstanceAs <| AddCommGroup c.toAddCon.Quotient
From 3203991ba3604bf3ac6d980d140afcbdcfbba8a9 Mon Sep 17 00:00:00 2001
From: Sebastien Gouezel <10818434+sgouezel@users.noreply.github.com>
Date: Wed, 17 Jun 2026 11:33:22 +0000
Subject: [PATCH 0107/1300] chore: move generic SMul definition before
AddMonoid or AddGroup instances to make sure they can use it (#40706)
Co-authored-by: sgouezel
---
Mathlib/Algebra/Quaternion.lean | 9 ++++++---
Mathlib/Algebra/TrivSqZeroExt/Basic.lean | 16 +++++++++-------
.../Analysis/CStarAlgebra/CStarMatrix.lean | 16 +++++++++-------
.../IsAlgClosed/AlgebraicClosure.lean | 7 ++++++-
Mathlib/RingTheory/AdjoinRoot.lean | 19 ++++++++++++-------
5 files changed, 42 insertions(+), 25 deletions(-)
diff --git a/Mathlib/Algebra/Quaternion.lean b/Mathlib/Algebra/Quaternion.lean
index b76bf93cde6f72..4c57a1cd268505 100644
--- a/Mathlib/Algebra/Quaternion.lean
+++ b/Mathlib/Algebra/Quaternion.lean
@@ -723,11 +723,14 @@ variable {S T R : Type*} [CommRing R] (r x y : R) (a b : ℍ[R])
instance : CoeTC R ℍ[R] := ⟨coe⟩
-instance instRing : Ring ℍ[R] := inferInstanceAs <| Ring ℍ[R,-1,0,-1]
+instance [SMul S R] : SMul S ℍ[R] := inferInstanceAs <| SMul S ℍ[R,-1,0,-1]
-instance : Inhabited ℍ[R] := inferInstanceAs <| Inhabited ℍ[R,-1,0,-1]
+instance instRing : Ring ℍ[R] where
+ nsmul := letI := Quaternion.instSMul (S := ℕ) (R := R); (· • ·)
+ zsmul := letI := Quaternion.instSMul (S := ℤ) (R := R); (· • ·)
+ __ : Ring ℍ[R] := inferInstanceAs <| Ring ℍ[R,-1,0,-1]
-instance [SMul S R] : SMul S ℍ[R] := inferInstanceAs <| SMul S ℍ[R,-1,0,-1]
+instance : Inhabited ℍ[R] := inferInstanceAs <| Inhabited ℍ[R,-1,0,-1]
instance [SMul S T] [SMul S R] [SMul T R] [IsScalarTower S T R] : IsScalarTower S T ℍ[R] :=
inferInstanceAs <| IsScalarTower S T ℍ[R,-1,0,-1]
diff --git a/Mathlib/Algebra/TrivSqZeroExt/Basic.lean b/Mathlib/Algebra/TrivSqZeroExt/Basic.lean
index 7133a5c4559176..d9a220b6c71c85 100644
--- a/Mathlib/Algebra/TrivSqZeroExt/Basic.lean
+++ b/Mathlib/Algebra/TrivSqZeroExt/Basic.lean
@@ -197,11 +197,16 @@ instance addSemigroup [AddSemigroup R] [AddSemigroup M] : AddSemigroup (tsze R M
instance addZeroClass [AddZeroClass R] [AddZeroClass M] : AddZeroClass (tsze R M) :=
inferInstanceAs <| AddZeroClass (R × M)
-instance addMonoid [AddMonoid R] [AddMonoid M] : AddMonoid (tsze R M) :=
- inferInstanceAs <| AddMonoid (R × M)
+instance smul [SMul S R] [SMul S M] : SMul S (tsze R M) :=
+ inferInstanceAs <| SMul S (R × M)
+
+instance addMonoid [AddMonoid R] [AddMonoid M] : AddMonoid (tsze R M) where
+ nsmul := letI := smul (S := ℕ) (R := R) (M := M); (· • ·)
+ __ : AddMonoid (tsze R M) := inferInstanceAs <| AddMonoid (R × M)
-instance addGroup [AddGroup R] [AddGroup M] : AddGroup (tsze R M) :=
- inferInstanceAs <| AddGroup (R × M)
+instance addGroup [AddGroup R] [AddGroup M] : AddGroup (tsze R M) where
+ zsmul := letI := smul (S := ℤ) (R := R) (M := M); (· • ·)
+ __ : AddGroup (tsze R M) := inferInstanceAs <| AddGroup (R × M)
instance addCommSemigroup [AddCommSemigroup R] [AddCommSemigroup M] : AddCommSemigroup (tsze R M) :=
inferInstanceAs <| AddCommSemigroup (R × M)
@@ -212,9 +217,6 @@ instance addCommMonoid [AddCommMonoid R] [AddCommMonoid M] : AddCommMonoid (tsze
instance addCommGroup [AddCommGroup R] [AddCommGroup M] : AddCommGroup (tsze R M) :=
inferInstanceAs <| AddCommGroup (R × M)
-instance smul [SMul S R] [SMul S M] : SMul S (tsze R M) :=
- inferInstanceAs <| SMul S (R × M)
-
instance isScalarTower [SMul T R] [SMul T M] [SMul S R] [SMul S M] [SMul T S]
[IsScalarTower T S R] [IsScalarTower T S M] : IsScalarTower T S (tsze R M) :=
inferInstanceAs <| IsScalarTower T S (R × M)
diff --git a/Mathlib/Analysis/CStarAlgebra/CStarMatrix.lean b/Mathlib/Analysis/CStarAlgebra/CStarMatrix.lean
index 7ed03d6b564b4e..3efc1706d31ec5 100644
--- a/Mathlib/Analysis/CStarAlgebra/CStarMatrix.lean
+++ b/Mathlib/Analysis/CStarAlgebra/CStarMatrix.lean
@@ -141,8 +141,12 @@ instance instZero [Zero A] : Zero (CStarMatrix m n A) :=
instance instAddZeroClass [AddZeroClass A] : AddZeroClass (CStarMatrix m n A) :=
inferInstanceAs <| AddZeroClass (Matrix m n A)
-instance instAddMonoid [AddMonoid A] : AddMonoid (CStarMatrix m n A) :=
- inferInstanceAs <| AddMonoid (Matrix m n A)
+instance instSMul [SMul R A] : SMul R (CStarMatrix m n A) :=
+ inferInstanceAs <| SMul R (Matrix m n A)
+
+instance instAddMonoid [AddMonoid A] : AddMonoid (CStarMatrix m n A) where
+ nsmul := letI := instSMul (R := ℕ) (A := A) (m := m) (n := n); (· • · )
+ __ : AddMonoid (CStarMatrix m n A) := inferInstanceAs <| AddMonoid (Matrix m n A)
instance instAddCommMonoid [AddCommMonoid A] : AddCommMonoid (CStarMatrix m n A) :=
inferInstanceAs <| AddCommMonoid (Matrix m n A)
@@ -153,8 +157,9 @@ instance instNeg [Neg A] : Neg (CStarMatrix m n A) :=
instance instSub [Sub A] : Sub (CStarMatrix m n A) :=
inferInstanceAs <| Sub (Matrix m n A)
-instance instAddGroup [AddGroup A] : AddGroup (CStarMatrix m n A) :=
- inferInstanceAs <| AddGroup (Matrix m n A)
+instance instAddGroup [AddGroup A] : AddGroup (CStarMatrix m n A) where
+ zsmul := letI := instSMul (R := ℤ) (A := A) (m := m) (n := n); (· • · )
+ __ : AddGroup (CStarMatrix m n A) := inferInstanceAs <| AddGroup (Matrix m n A)
instance instAddCommGroup [AddCommGroup A] : AddCommGroup (CStarMatrix m n A) :=
inferInstanceAs <| AddCommGroup (Matrix m n A)
@@ -168,9 +173,6 @@ instance instSubsingleton [Subsingleton A] : Subsingleton (CStarMatrix m n A) :=
instance instNontrivial [Nonempty m] [Nonempty n] [Nontrivial A] : Nontrivial (CStarMatrix m n A) :=
inferInstanceAs <| Nontrivial (Matrix m n A)
-instance instSMul [SMul R A] : SMul R (CStarMatrix m n A) :=
- inferInstanceAs <| SMul R (Matrix m n A)
-
instance instSMulCommClass [SMul R A] [SMul S A] [SMulCommClass R S A] :
SMulCommClass R S (CStarMatrix m n A) :=
inferInstanceAs <| SMulCommClass R S (Matrix m n A)
diff --git a/Mathlib/FieldTheory/IsAlgClosed/AlgebraicClosure.lean b/Mathlib/FieldTheory/IsAlgClosed/AlgebraicClosure.lean
index addd091a272214..aa9c04459b4788 100644
--- a/Mathlib/FieldTheory/IsAlgClosed/AlgebraicClosure.lean
+++ b/Mathlib/FieldTheory/IsAlgClosed/AlgebraicClosure.lean
@@ -129,11 +129,16 @@ def AlgebraicClosure : Type u :=
namespace AlgebraicClosure
-deriving instance CommRing, Inhabited for AlgebraicClosure
+deriving instance Inhabited for AlgebraicClosure
instance {S : Type*} [DistribSMul S k] [IsScalarTower S k k] : SMul S (AlgebraicClosure k) :=
inferInstanceAs <| SMul S (_ ⧸ _)
+instance : CommRing (AlgebraicClosure k) where
+ nsmul := letI := AlgebraicClosure.instSMulOfIsScalarTower k (S := ℕ); (· • · )
+ zsmul := letI := AlgebraicClosure.instSMulOfIsScalarTower k (S := ℤ); (· • · )
+ __ : CommRing (AlgebraicClosure k) := inferInstanceAs <| CommRing (_ ⧸ _)
+
instance instAlgebra {R : Type*} [CommSemiring R] [Algebra R k] : Algebra R (AlgebraicClosure k) :=
inferInstanceAs <| Algebra R (_ ⧸ _)
diff --git a/Mathlib/RingTheory/AdjoinRoot.lean b/Mathlib/RingTheory/AdjoinRoot.lean
index 829bc3c3d9bc5b..46e60c23c827e6 100644
--- a/Mathlib/RingTheory/AdjoinRoot.lean
+++ b/Mathlib/RingTheory/AdjoinRoot.lean
@@ -68,7 +68,18 @@ section CommRing
variable [CommRing R] (f g : R[X])
-deriving instance CommRing, Inhabited for AdjoinRoot
+deriving instance Inhabited for AdjoinRoot
+
+instance instSMulAdjoinRoot [DistribSMul S R] [IsScalarTower S R R] : SMul S (AdjoinRoot f) :=
+ inferInstanceAs <| SMul S (_ ⧸ _)
+
+instance : CommRing (AdjoinRoot f) where
+ nsmul := letI := instSMulAdjoinRoot (S := ℕ) (R := R); (· • ·)
+ zsmul := letI := instSMulAdjoinRoot (S := ℤ) (R := R); (· • ·)
+ __ : CommRing (AdjoinRoot f) := inferInstanceAs <| CommRing (_ ⧸ _)
+
+instance [DistribSMul S R] [IsScalarTower S R R] : DistribSMul S (AdjoinRoot f) :=
+ inferInstanceAs <| DistribSMul S (_ ⧸ _)
instance : DecidableEq (AdjoinRoot f) :=
Classical.decEq _
@@ -92,12 +103,6 @@ theorem induction_on {C : AdjoinRoot f → Prop} (x : AdjoinRoot f) (ih : ∀ p
def of : R →+* AdjoinRoot f :=
(mk f).comp C
-instance instSMulAdjoinRoot [DistribSMul S R] [IsScalarTower S R R] : SMul S (AdjoinRoot f) :=
- inferInstanceAs <| SMul S (_ ⧸ _)
-
-instance [DistribSMul S R] [IsScalarTower S R R] : DistribSMul S (AdjoinRoot f) :=
- inferInstanceAs <| DistribSMul S (_ ⧸ _)
-
@[simp]
theorem smul_mk [DistribSMul S R] [IsScalarTower S R R] (a : S) (x : R[X]) :
a • mk f x = mk f (a • x) :=
From 34c002db86b155aa62dd7da60462b70412fc46f9 Mon Sep 17 00:00:00 2001
From: "mathlib-splicebot[bot]"
<261196803+mathlib-splicebot[bot]@users.noreply.github.com>
Date: Wed, 17 Jun 2026 13:07:13 +0000
Subject: [PATCH 0108/1300] feat: `isClosed_eqLocus` (#40712)
This PR was automatically created from PR #39100 by @ADedecker via a [review comment](https://github.com/leanprover-community/mathlib4/pull/39100#discussion_r3428059834) by @ADedecker.
Co-authored-by: ADedecker <48656793+ADedecker@users.noreply.github.com>
---
.../Algebra/Module/ContinuousLinearMap/Basic.lean | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/Mathlib/Topology/Algebra/Module/ContinuousLinearMap/Basic.lean b/Mathlib/Topology/Algebra/Module/ContinuousLinearMap/Basic.lean
index bbe7c8ee90e94e..f0f7d0b1425042 100644
--- a/Mathlib/Topology/Algebra/Module/ContinuousLinearMap/Basic.lean
+++ b/Mathlib/Topology/Algebra/Module/ContinuousLinearMap/Basic.lean
@@ -686,13 +686,22 @@ end ApplyAction
theorem isClosed_ker [T1Space M₂] (f : M₁ →SL[σ₁₂] M₂) :
IsClosed (f.ker : Set M₁) :=
- continuous_iff_isClosed.1 (map_continuous f) _ isClosed_singleton
+ isClosed_singleton.preimage f.continuous
+
+theorem isClosed_eqLocus [T2Space M₂] (f g : M₁ →SL[σ₁₂] M₂) :
+ IsClosed (f.eqLocus g : Set M₁) :=
+ isClosed_eq f.continuous g.continuous
theorem isComplete_ker {M' : Type*} [UniformSpace M'] [CompleteSpace M'] [AddCommMonoid M']
[Module R₁ M'] [T1Space M₂] (f : M' →SL[σ₁₂] M₂) :
IsComplete (f.ker : Set M') :=
(isClosed_ker f).isComplete
+theorem isComplete_eqLocus {M' : Type*} [UniformSpace M'] [CompleteSpace M'] [AddCommMonoid M']
+ [Module R₁ M'] [T2Space M₂] (f g : M' →SL[σ₁₂] M₂) :
+ IsComplete (f.eqLocus g : Set M') :=
+ (isClosed_eqLocus f g).isComplete
+
instance completeSpace_ker {M' : Type*} [UniformSpace M'] [CompleteSpace M']
[AddCommMonoid M'] [Module R₁ M'] [T1Space M₂]
(f : M' →SL[σ₁₂] M₂) : CompleteSpace f.ker :=
@@ -701,7 +710,7 @@ instance completeSpace_ker {M' : Type*} [UniformSpace M'] [CompleteSpace M']
instance completeSpace_eqLocus {M' : Type*} [UniformSpace M'] [CompleteSpace M']
[AddCommMonoid M'] [Module R₁ M'] [T2Space M₂]
(f g : M' →SL[σ₁₂] M₂) : CompleteSpace (f.toLinearMap.eqLocus g.toLinearMap) :=
- IsClosed.completeSpace_coe (hs := isClosed_eq (map_continuous f) (map_continuous g))
+ (isComplete_eqLocus f g).completeSpace_coe
section
From c00d191d1f49ea170b3a61b8b0b298c2d7c1eea1 Mon Sep 17 00:00:00 2001
From: Eric Wieser <425260+eric-wieser@users.noreply.github.com>
Date: Wed, 17 Jun 2026 13:37:13 +0000
Subject: [PATCH 0109/1300] fix: add missing coercion lemma (#40698)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Instead of having the obvious lemma `⇑f.toNonUnitalRingHom = f`, we had some weirder lemmas to try and simplify larger terms containing `⇑f.toNonUnitalRingHom`.
The unprimed name is already taken by `NonUnitalRingHomClass.toNonUnitalRingHom`; I believe the plan is to eliminate that in future, but that's out of scope for this PR.
---
Mathlib/Algebra/Ring/Equiv.lean | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/Mathlib/Algebra/Ring/Equiv.lean b/Mathlib/Algebra/Ring/Equiv.lean
index 88edfb047f529d..9297647839f674 100644
--- a/Mathlib/Algebra/Ring/Equiv.lean
+++ b/Mathlib/Algebra/Ring/Equiv.lean
@@ -701,6 +701,10 @@ theorem toNonUnitalRingHom_eq_coe (f : R ≃+* S) : f.toNonUnitalRingHom = ↑f
theorem coe_toNonUnitalRingHom (f : R ≃+* S) : ⇑(f : R →ₙ+* S) = f :=
rfl
+@[simp]
+theorem coe_toNonUnitalRingHom' (f : R ≃+* S) : ⇑f.toNonUnitalRingHom = f :=
+ rfl
+
theorem coe_nonUnitalRingHom_inj_iff {R S : Type*} [NonUnitalNonAssocSemiring R]
[NonUnitalNonAssocSemiring S] (f g : R ≃+* S) : f = g ↔ (f : R →ₙ+* S) = g :=
⟨fun h => by rw [h], fun h => ext <| NonUnitalRingHom.ext_iff.mp h⟩
@@ -710,12 +714,12 @@ theorem toNonUnitalRingHom_refl :
(RingEquiv.refl R).toNonUnitalRingHom = NonUnitalRingHom.id R :=
rfl
-@[simp]
+@[deprecated apply_symm_apply (since := "2026-06-16")]
theorem toNonUnitalRingHom_apply_symm_toNonUnitalRingHom_apply (e : R ≃+* S) :
∀ y : S, e.toNonUnitalRingHom (e.symm.toNonUnitalRingHom y) = y :=
e.toEquiv.apply_symm_apply
-@[simp]
+@[deprecated symm_apply_apply (since := "2026-06-16")]
theorem symm_toNonUnitalRingHom_apply_toNonUnitalRingHom_apply (e : R ≃+* S) :
∀ x : R, e.symm.toNonUnitalRingHom (e.toNonUnitalRingHom x) = x :=
Equiv.symm_apply_apply e.toEquiv
From ddd4c00ead8d964427c49289ee4be172a08ec4f5 Mon Sep 17 00:00:00 2001
From: Vasilii Nesterov <118051017+vasnesterov@users.noreply.github.com>
Date: Wed, 17 Jun 2026 14:20:38 +0000
Subject: [PATCH 0110/1300] feat(Tactic/ComputeAsymptotics/Multiseries): basic
constructions (#40014)
Introduce basic constructions for multiseries: `const`, `monomial`, `monomialRpow`.
---
Mathlib.lean | 1 +
Mathlib/Tactic.lean | 1 +
.../ComputeAsymptotics/Multiseries/Basic.lean | 287 ++++++++++++++++++
.../ComputeAsymptotics/Multiseries/Basis.lean | 2 +-
4 files changed, 290 insertions(+), 1 deletion(-)
create mode 100644 Mathlib/Tactic/ComputeAsymptotics/Multiseries/Basic.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 3231bbedb8ef7a..57d9493e5cebd8 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -7199,6 +7199,7 @@ public import Mathlib.Tactic.ClickSuggestions.Util
public import Mathlib.Tactic.Coe
public import Mathlib.Tactic.Common
public import Mathlib.Tactic.ComputeAsymptotics.Lemmas
+public import Mathlib.Tactic.ComputeAsymptotics.Multiseries.Basic
public import Mathlib.Tactic.ComputeAsymptotics.Multiseries.Basis
public import Mathlib.Tactic.ComputeAsymptotics.Multiseries.Corecursion
public import Mathlib.Tactic.ComputeAsymptotics.Multiseries.Defs
diff --git a/Mathlib/Tactic.lean b/Mathlib/Tactic.lean
index f7fe1f5c1e9e22..50686dca468d2e 100644
--- a/Mathlib/Tactic.lean
+++ b/Mathlib/Tactic.lean
@@ -68,6 +68,7 @@ public import Mathlib.Tactic.ClickSuggestions.Util
public import Mathlib.Tactic.Coe
public import Mathlib.Tactic.Common
public import Mathlib.Tactic.ComputeAsymptotics.Lemmas
+public import Mathlib.Tactic.ComputeAsymptotics.Multiseries.Basic
public import Mathlib.Tactic.ComputeAsymptotics.Multiseries.Basis
public import Mathlib.Tactic.ComputeAsymptotics.Multiseries.Corecursion
public import Mathlib.Tactic.ComputeAsymptotics.Multiseries.Defs
diff --git a/Mathlib/Tactic/ComputeAsymptotics/Multiseries/Basic.lean b/Mathlib/Tactic/ComputeAsymptotics/Multiseries/Basic.lean
new file mode 100644
index 00000000000000..7ebab0b38796a1
--- /dev/null
+++ b/Mathlib/Tactic/ComputeAsymptotics/Multiseries/Basic.lean
@@ -0,0 +1,287 @@
+/-
+Copyright (c) 2026 Vasilii Nesterov. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Vasilii Nesterov
+-/
+module
+
+public import Mathlib.Tactic.ComputeAsymptotics.Multiseries.Defs
+public import Mathlib.Tactic.ComputeAsymptotics.Multiseries.Basis
+
+/-!
+# Basic constructions for multiseries
+
+## Main definitions
+
+Let `[b₁, ..., bₙ]` be our basis.
+
+* `const c` represents a constant multiseries `c • b₁⁰ ... bₙ⁰`.
+ Then we define `zero` and `one` in terms of it.
+* `monomial k` represents a monomial `bₖ`.
+* `monomialRpow k r` represents a monomial `bₖʳ`.
+
+For each construction, we provide two definitions: one for `Multiseries` and one for
+`MultiseriesExpansion`. We then prove structural `simp`-lemmas describing their relationships with
+`MultiseriesExpansion.seq` and `MultiseriesExpansion.toFun`. Finally, we prove that all
+constructions are `Sorted` and `Approximates` their attached functions.
+
+-/
+
+@[expose] public section
+
+namespace Tactic.ComputeAsymptotics
+
+namespace MultiseriesExpansion
+
+open Filter Stream' Topology
+
+mutual
+
+/-- `Multiseries`-part of `MultiseriesExpansion.const`. -/
+def Multiseries.const (basis_hd : ℝ → ℝ) (basis_tl : Basis) (c : ℝ) :
+ Multiseries basis_hd basis_tl :=
+ .cons 0 (const basis_tl c) .nil
+
+/-- Multiseries representing a constant. -/
+def const (basis : Basis) (c : ℝ) : MultiseriesExpansion basis :=
+ match basis with
+ | [] => ofReal c
+ | List.cons basis_hd basis_tl => mk (Multiseries.const basis_hd basis_tl c) (fun _ ↦ c)
+
+end
+
+/-- Neutral element for addition. It is `0 : ℝ` for the empty basis and `[]` otherwise. -/
+def zero {basis : Basis} : MultiseriesExpansion basis :=
+ match basis with
+ | [] => ofReal 0
+ | List.cons _ _ => mk .nil (fun _ ↦ 0)
+
+/-- This instance is needed to create an instance for `AddCommMonoid (MultiseriesExpansion basis)`,
+which is necessary for using the `abel` tactic in our proofs. -/
+instance {basis : Basis} : Zero (MultiseriesExpansion basis) where
+ zero := zero
+
+/-- This instance is needed to create an instance for `AddCommMonoid (MultiseriesExpansion basis)`,
+which is necessary for using the `abel` tactic in our proofs. -/
+instance {basis_hd : ℝ → ℝ} {basis_tl : Basis} : Zero (Multiseries basis_hd basis_tl) where
+ zero := .nil
+
+/-- `Multiseries`-part of `MultiseriesExpansion.one`. -/
+def Multiseries.one {basis_hd : ℝ → ℝ} {basis_tl : Basis} : Multiseries basis_hd basis_tl :=
+ Multiseries.const _ _ 1
+
+/-- Neutral element for multiplication. -/
+def one {basis : Basis} : MultiseriesExpansion basis :=
+ const basis 1
+
+mutual
+
+/-- `Multiseries`-part of `MultiseriesExpansion.monomialRpow`. -/
+noncomputable def Multiseries.monomialRpow (basis_hd : ℝ → ℝ) (basis_tl : Basis) (n : ℕ) (r : ℝ) :
+ Multiseries basis_hd basis_tl :=
+ match n with
+ | 0 => .cons r one .nil
+ | m + 1 => .cons 0 (monomialRpow _ m r) .nil
+
+/-- Multiseries representing `basis[n] ^ r`. -/
+noncomputable def monomialRpow (basis : Basis) (n : ℕ) (r : ℝ) : MultiseriesExpansion basis :=
+ match basis with
+ | [] => default
+ | List.cons basis_hd basis_tl =>
+ mk (Multiseries.monomialRpow _ _ n r) ((basis_hd :: basis_tl)[n]! ^ r)
+
+end
+
+/-- `Multiseries`-part of `MultiseriesExpansion.monomial`. -/
+noncomputable def Multiseries.monomial (basis_hd : ℝ → ℝ) (basis_tl : Basis) (n : ℕ) :
+ Multiseries basis_hd basis_tl :=
+ Multiseries.monomialRpow _ _ n 1
+
+/-- Multiseries representing `basis[n]`. -/
+noncomputable def monomial (basis : Basis) (n : ℕ) : MultiseriesExpansion basis :=
+ monomialRpow _ n 1
+
+theorem zero_def {basis_hd basis_tl} :
+ (0 : MultiseriesExpansion (basis_hd :: basis_tl)) = mk .nil (fun _ ↦ 0) :=
+ rfl
+
+@[simp]
+theorem Multiseries.zero_def {basis_hd : ℝ → ℝ} {basis_tl : Basis} :
+ (0 : Multiseries basis_hd basis_tl) = .nil := rfl
+
+theorem Multiseries.const_def {basis_hd basis_tl} (c : ℝ) :
+ Multiseries.const basis_hd basis_tl c =
+ Multiseries.cons 0 (MultiseriesExpansion.const basis_tl c) .nil := by
+ simp [Multiseries.const]
+
+@[simp]
+theorem const_toFun' {basis : Basis} {c : ℝ} : (const basis c).toFun = fun _ ↦ c := by
+ match basis with
+ | [] => simp [const, ofReal, toReal]
+ | List.cons _ _ => simp [const]
+
+@[simp]
+theorem const_seq {basis_hd basis_tl} {c : ℝ} :
+ (const (basis_hd :: basis_tl) c).seq = Multiseries.const basis_hd basis_tl c := by
+ simp [const, Multiseries.const]
+
+@[simp]
+theorem zero_toFun {basis : Basis} : (@zero basis).toFun = 0 := by
+ match basis with
+ | [] => rfl
+ | List.cons _ _ => rfl
+
+theorem Multiseries.one_def {basis_hd basis_tl} :
+ @Multiseries.one basis_hd basis_tl = Multiseries.cons 0 MultiseriesExpansion.one .nil := by
+ simp [Multiseries.one, Multiseries.const_def, MultiseriesExpansion.one]
+
+@[simp]
+theorem one_toFun {basis : Basis} : (@one basis).toFun = 1 := by
+ simp [one]
+ rfl
+
+@[simp]
+theorem one_seq {basis_hd : ℝ → ℝ} {basis_tl : Basis} :
+ (@one (basis_hd :: basis_tl)).seq = Multiseries.one := by
+ simp [one, Multiseries.one, const]
+
+mutual
+
+theorem Multiseries.const_sorted {basis_hd : ℝ → ℝ} {basis_tl : Basis} {c : ℝ} :
+ (Multiseries.const basis_hd basis_tl c).Sorted := by
+ simp only [Multiseries.const]
+ exact const_sorted.cons_nil
+
+/-- Constants are well-ordered. -/
+theorem const_sorted {basis : Basis} {c : ℝ} :
+ (const basis c).Sorted := by
+ cases basis with
+ | nil => constructor
+ | cons basis_hd basis_tl =>
+ simpa only [const, sorted_iff_seq_sorted, mk_seq] using Multiseries.const_sorted
+
+end
+
+/-- Zero is well-ordered. -/
+theorem zero_sorted {basis : Basis} : (0 : MultiseriesExpansion basis).Sorted := by
+ cases basis with
+ | nil => constructor
+ | cons => apply Sorted.nil
+
+theorem Multiseries.one_sorted {basis_hd : ℝ → ℝ} {basis_tl : Basis} :
+ (Multiseries.one : Multiseries basis_hd basis_tl).Sorted :=
+ Multiseries.const_sorted
+
+/-- `one` is Sorted. -/
+theorem one_sorted {basis : Basis} : one.Sorted (basis := basis) :=
+ const_sorted
+
+/-- The constant multiseries approximates the constant function. -/
+theorem const_approximates {c : ℝ} {basis : Basis} (h_basis : WellFormedBasis basis) :
+ (const basis c).Approximates := by
+ cases basis with
+ | nil => simp
+ | cons basis_hd basis_tl =>
+ simp only [const, Multiseries.const]
+ apply (const_approximates h_basis.tail).cons _ (by simp)
+ exact Majorized.const <| h_basis.tendsto_atTop (by simp)
+
+/-- `zero` approximates the zero function. -/
+theorem zero_approximates {basis : Basis} :
+ (@zero basis).Approximates := by
+ cases basis with
+ | nil => simp [zero]
+ | cons => exact Approximates.nil (by rfl)
+
+/-- `one` approximates the unit function. -/
+theorem one_approximates {basis : Basis} (h_basis : WellFormedBasis basis) :
+ (@one basis).Approximates :=
+ const_approximates h_basis
+
+@[simp]
+theorem monomialRpow_toFun {basis : Basis} {n : Fin (List.length basis)} {r : ℝ} :
+ (monomialRpow basis n r).toFun = basis[n] ^ r := by
+ cases basis with
+ | nil => grind
+ | cons basis_hd basis_tl => cases n using Fin.cases <;> simp [monomialRpow]
+
+@[simp]
+theorem monomialRpow_seq {basis_hd : ℝ → ℝ} {basis_tl : Basis} {n : ℕ} {r : ℝ} :
+ (monomialRpow (basis_hd :: basis_tl) n r).seq = Multiseries.monomialRpow _ _ n r := by
+ simp [monomialRpow]
+
+mutual
+
+theorem Multiseries.monomialRpow_sorted {basis_hd : ℝ → ℝ} {basis_tl : Basis} {n : ℕ} {r : ℝ} :
+ (@Multiseries.monomialRpow basis_hd basis_tl n r).Sorted := by
+ cases n with
+ | zero =>
+ simp only [Multiseries.monomialRpow]
+ exact Sorted.cons_nil const_sorted
+ | succ m =>
+ simp only [Multiseries.monomialRpow]
+ exact Sorted.cons_nil monomialRpow_sorted
+
+/-- `monomial` is well-ordered. -/
+theorem monomialRpow_sorted {basis : Basis} {n : ℕ} {r : ℝ} :
+ (monomialRpow basis n r).Sorted := by
+ cases basis with
+ | nil => constructor
+ | cons basis_hd basis_tl =>
+ simpa only [sorted_iff_seq_sorted, monomialRpow_seq] using Multiseries.monomialRpow_sorted
+
+end
+
+/-- `monomialRpow` approximates the monomial function. -/
+theorem monomialRpow_approximates {basis : Basis} {n : Fin (List.length basis)} {r : ℝ}
+ (h_basis : WellFormedBasis basis) :
+ (monomialRpow basis n r).Approximates := by
+ cases basis with
+ | nil => simp
+ | cons basis_hd basis_tl =>
+ simp only [List.length_cons, monomialRpow, Fin.is_lt, getElem!_pos]
+ cases n using Fin.cases with
+ | zero =>
+ simp only [Fin.coe_ofNat_eq_mod, Nat.zero_mod, Multiseries.monomialRpow,
+ List.getElem_cons_zero]
+ apply (one_approximates h_basis.tail).cons _ (by simp)
+ exact Majorized.self <| h_basis.tendsto_atTop (by simp)
+ | succ m =>
+ simp only [Fin.val_succ, Multiseries.monomialRpow, List.getElem_cons_succ]
+ apply (monomialRpow_approximates h_basis.tail).cons _ (by simp)
+ apply h_basis.tail_pow_majorized_head (by simp)
+
+@[simp]
+theorem monomial_toFun {basis : Basis} {n : ℕ} (h : n < basis.length) :
+ (monomial basis n).toFun = basis[n] := by
+ let n' : Fin basis.length := ⟨n, h⟩
+ conv_lhs => rw [show n = n'.val by simp [n']]
+ convert! monomialRpow_toFun
+ simp
+ grind
+
+theorem monomial_toFun' {basis : Basis} {n : Fin basis.length} :
+ (monomial basis n).toFun = basis[n] := by
+ simp
+
+@[simp]
+theorem monomial_seq {basis_hd : ℝ → ℝ} {basis_tl : Basis} {n : ℕ} :
+ (monomial (basis_hd :: basis_tl) n).seq = Multiseries.monomial _ _ n :=
+ monomialRpow_seq
+
+theorem Multiseries.monomial_sorted {basis_hd : ℝ → ℝ} {basis_tl : Basis} {n : ℕ} :
+ (@Multiseries.monomial basis_hd basis_tl n).Sorted :=
+ Multiseries.monomialRpow_sorted
+
+/-- `monomial` is well-ordered. -/
+theorem monomial_sorted {basis : Basis} {n : ℕ} : (monomial basis n).Sorted :=
+ monomialRpow_sorted
+
+/-- `monomial` approximates the monomial function. -/
+theorem monomial_approximates {basis : Basis} {n : Fin (List.length basis)}
+ (h_basis : WellFormedBasis basis) : (monomial basis n).Approximates :=
+ monomialRpow_approximates h_basis
+
+end MultiseriesExpansion
+
+end Tactic.ComputeAsymptotics
diff --git a/Mathlib/Tactic/ComputeAsymptotics/Multiseries/Basis.lean b/Mathlib/Tactic/ComputeAsymptotics/Multiseries/Basis.lean
index 771abcedd265ef..81f1baed725e6f 100644
--- a/Mathlib/Tactic/ComputeAsymptotics/Multiseries/Basis.lean
+++ b/Mathlib/Tactic/ComputeAsymptotics/Multiseries/Basis.lean
@@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Vasilii Nesterov
-/
module
-public import Mathlib.Analysis.Complex.Exponential
+
public import Mathlib.Analysis.Asymptotics.AsymptoticEquivalent
public import Mathlib.Analysis.SpecialFunctions.Pow.NNReal
public import Mathlib.Tactic.ComputeAsymptotics.Multiseries.Defs
From 14f97d34085433072577aa03ce025ce8867016f4 Mon Sep 17 00:00:00 2001
From: Whysoserioushah <109107491+Whysoserioushah@users.noreply.github.com>
Date: Wed, 17 Jun 2026 14:20:48 +0000
Subject: [PATCH 0111/1300] chore(Matrix/GeneralLinearGroup/Projective): sort
out namespace and golf the proof (#40666)
---
Mathlib/GroupTheory/QuotientGroup/Defs.lean | 6 ++
.../Matrix/GeneralLinearGroup/Basic.lean | 25 ++++-
.../Matrix/GeneralLinearGroup/Defs.lean | 17 +++-
.../Matrix/GeneralLinearGroup/Projective.lean | 97 +++++++++++++------
.../Projectivization/Action.lean | 13 ++-
5 files changed, 121 insertions(+), 37 deletions(-)
diff --git a/Mathlib/GroupTheory/QuotientGroup/Defs.lean b/Mathlib/GroupTheory/QuotientGroup/Defs.lean
index 4d6ae589c68783..dcec9bc41d43c6 100644
--- a/Mathlib/GroupTheory/QuotientGroup/Defs.lean
+++ b/Mathlib/GroupTheory/QuotientGroup/Defs.lean
@@ -278,6 +278,12 @@ theorem ker_lift (φ : G →* M) (HN : N ≤ φ.ker) :
rw [← congrArg MonoidHom.ker (lift_comp_mk' N φ HN), ← MonoidHom.comap_ker,
Subgroup.map_comap_eq_self_of_surjective (mk'_surjective N)]
+@[to_additive]
+lemma injective_lift_iff (φ : G →* M) (HN : N ≤ φ.ker) :
+ Function.Injective (QuotientGroup.lift N φ HN) ↔ N = φ.ker := by
+ rw [← MonoidHom.ker_eq_bot_iff, QuotientGroup.ker_lift, Subgroup.map_eq_bot_iff]
+ grind [QuotientGroup.ker_mk']
+
/-- A surjective group homomorphism `φ : G →* H` with `N = ker(φ)` descends (i.e. `lift`s) to a
group isomorphism `G/N ≃* H`. -/
@[to_additive /-- A surjective `AddGroup` homomorphism `φ : G →+ H` with `N = ker(φ)` descends
diff --git a/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/Basic.lean b/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/Basic.lean
index c8afa36c790e37..33cf339f419e47 100644
--- a/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/Basic.lean
+++ b/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/Basic.lean
@@ -46,7 +46,7 @@ lemma mem_center_iff_val_mem_range_scalar {g : GL n R} :
refine Matrix.mem_range_scalar_of_commute_transvectionStruct fun t ↦ ?_
simpa [Units.ext_iff] using! Subgroup.mem_center_iff.mp hg (.mk _ _ t.mul_inv t.inv_mul)
· refine fun ⟨a, ha⟩ ↦ Subgroup.mem_center_iff.mpr fun h ↦ ?_
- simpa [Units.ext_iff, ← ha] using! (scalar_commute a (mul_comm a ·) h.val).symm
+ simp [-scalar_apply, Units.ext_iff, ← ha, Matrix.scalar_comm a (Commute.all _)]
@[deprecated (since := "2026-02-08")]
alias mem_center_iff_val_eq_scalar := mem_center_iff_val_mem_range_scalar
@@ -74,8 +74,31 @@ lemma center_eq_range_scalar :
@[deprecated (since := "2026-02-08")]
alias center_eq_range_units := center_eq_range_scalar
+lemma map_center_le {S : Type*} [CommRing S] (f : R →+* S) :
+ Subgroup.center (GL n R) ≤ (Subgroup.center (GL n S)).comap (map f) := fun u hu ↦ by
+ simp only [GeneralLinearGroup.center_eq_range_scalar, MonoidHom.mem_range,
+ Subgroup.mem_comap] at hu ⊢
+ obtain ⟨r, rfl⟩ := hu
+ exact ⟨(Units.map f) r, GeneralLinearGroup.map_scalar _ _ |>.symm⟩
+
end Center
end GeneralLinearGroup
+lemma SpecialLinearGroup.toGL_mem_center_iff {n R : Type*} [Fintype n] [DecidableEq n] [CommRing R]
+ (g : SpecialLinearGroup n R) :
+ toGL g ∈ Subgroup.center (GL n R) ↔ g ∈ Subgroup.center (SpecialLinearGroup n R) := by
+ if hn : IsEmpty n then simp [Subgroup.center_eq_top] else
+ replace hn : Nonempty n := by simpa using hn
+ obtain ⟨i⟩ := hn
+ simp only [GeneralLinearGroup.center_eq_range_scalar, MonoidHom.mem_range,
+ mem_center_iff, scalar_apply]
+ refine ⟨fun ⟨r, hr⟩ ↦ ⟨r, by simpa [Units.ext_iff] using congr(GeneralLinearGroup.det $hr),
+ by simpa [Units.ext_iff] using hr⟩, fun ⟨r, hr1, hr⟩ ↦ ⟨⟨r, g⁻¹.1 i i, ?_, ?_⟩,
+ by simp [Units.ext_iff, hr]⟩⟩
+ · simpa [-mul_inv_cancel, ← hr, ← pow_succ',
+ Nat.sub_one_add_one Fintype.card_pos.ne.symm] using
+ Matrix.ext_iff.2 (Subtype.ext_iff.1 (mul_inv_cancel g)) i i
+ · simpa [-inv_mul_cancel, ← hr] using Matrix.ext_iff.2 (Subtype.ext_iff.1 (inv_mul_cancel g)) i i
+
end Matrix
diff --git a/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/Defs.lean b/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/Defs.lean
index 5572c3144c2f05..966882903956a8 100644
--- a/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/Defs.lean
+++ b/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/Defs.lean
@@ -52,23 +52,25 @@ variable {n : Type u} [DecidableEq n] [Fintype n] {R : Type v}
variable (n) in
/-- Scalar matrix as an element of `GL n R`. -/
-@[simps!]
def scalar [Semiring R] : Rˣ →* GL n R :=
Units.map (Matrix.scalar n).toMonoidHom
-#adaptation_note /-- As of nightly-2026-04-29, the simpNF linter is failing here.
-Assistance investigating this would be appreciated. -/
-attribute [nolint simpNF] _root_.Matrix.GeneralLinearGroup.val_inv_scalar_apply
-
section CoeFnInstance
instance instCoeFun [Semiring R] : CoeFun (GL n R) fun _ => n → n → R where
coe A := (A : Matrix n n R)
+@[simp]
+lemma coe_scalar [Semiring R] (u : Rˣ) : ↑(scalar n u) = Matrix.scalar n u.1 := rfl
+
end CoeFnInstance
variable [CommRing R]
+lemma scalar_commute (u : Rˣ) (A : GL n R) : scalar n u * A = A * scalar n u := by
+ ext : 1
+ rw [Units.val_mul, Units.val_mul, coe_scalar, Matrix.scalar_comm _ (Commute.all _)]
+
/-- The determinant of a unit matrix is itself a unit. -/
@[simps]
def det : GL n R →* Rˣ where
@@ -220,6 +222,11 @@ lemma coe_map_inv_mul_map (g : GL n R) : g.val⁻¹.map f * g.val.map f = 1 := b
rw [← Matrix.map_mul]
simp only [isUnits_det_units, nonsing_inv_mul, map_zero, map_one, Matrix.map_one]
+lemma map_scalar (u : Rˣ) : map f (scalar n u) = scalar n (Units.map f u) := by
+ ext
+ simp [Matrix.diagonal_apply]
+ split <;> simp
+
section kronecker
variable {R m : Type*} [CommSemiring R] [Fintype m] [DecidableEq m]
diff --git a/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/Projective.lean b/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/Projective.lean
index e71b01a2ac3414..d7cc42e2829ffb 100644
--- a/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/Projective.lean
+++ b/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/Projective.lean
@@ -1,7 +1,7 @@
/-
Copyright (c) 2026 Yury G. Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
-Authors: Yury G. Kudryashov
+Authors: Yury G. Kudryashov, Edison Xie
-/
module
@@ -17,11 +17,22 @@ In this file we define `Matrix.ProjGenLinGroup n R` as the quotient of `GL n R`
We introduce notation `PGL(n, R)` for this group,
which works if `n` is either a finite type or a natural number.
If `n` is a number, then `PGL(n, R)` is interpreted as `PGL(Fin n, R)`.
+
+## Main definitions
+
+* `Matrix.SpecialLinearGroup.toPGL` is the natural map from `SL(n, R)` to `PGL(n, R)`.
+
+* `Matrix.ProjectiveSpecialLinearGroup.toPGL` is the natural
+ inclusion from `PSL(n, R)` to `PGL(n, R)`.
+
+* `Matrix.ProjectiveSpecialLinearGroup.isoPSLOfAlgClosed` is an isomorphism between
+ `PGL(n, F)` and `PSL(n, F)` in the case of an algebraically closed field.
+
-/
open scoped MatrixGroups
-public section
+@[expose] public section
namespace Matrix
@@ -61,6 +72,9 @@ theorem ker_mk : mk.ker = Subgroup.center (GL n R) := QuotientGroup.ker_mk' _
theorem mk_eq_one {g : GL n R} : mk g = 1 ↔ g ∈ Subgroup.center (GL n R) := by
rw [← MonoidHom.mem_ker, ker_mk]
+@[simp]
+lemma mk_one : mk (1 : GL n R) = 1 := rfl
+
@[simp]
theorem mk_scalar (u : Rˣ) : mk (.scalar n u) = 1 := by
rw [← MonoidHom.mem_ker, ker_mk, GeneralLinearGroup.center_eq_range_scalar]
@@ -71,41 +85,43 @@ theorem induction_on {motive : PGL(n, R) → Prop} (g : PGL(n, R))
(mk : ∀ g : GL n R, motive (ProjGenLinGroup.mk g)) : motive g :=
Quotient.inductionOn g mk
+end ProjGenLinGroup
+
section isoPSL
+variable {n R : Type*} [Fintype n] [DecidableEq n] [CommRing R]
+
+open Matrix.ProjGenLinGroup
+
+namespace SpecialLinearGroup
+
+/-- The natural map from `SL(n, R)` to `PGL(n, R)` by composing the maps from `SL` to `GL` and the
+ quotient map from `GL` to `PGL`. -/
+abbrev toPGL : SpecialLinearGroup n R →* PGL(n, R) := mk.comp toGL
+
+lemma toPGL_ker : toPGL.ker = Subgroup.center (SpecialLinearGroup n R) := by
+ ext; simp [toGL_mem_center_iff]
+
+end SpecialLinearGroup
+
+namespace ProjectiveSpecialLinearGroup
+
open Matrix.SpecialLinearGroup
/-- The natural inclusion map from `PSL(n, R)` to `PGL(n, R)` induced by the inclusion
map from `SL(n, R)` to `GL(n, R)`. -/
-@[expose]
-def _root_.Matrix.ProjectiveSpecialLinearGroup.toPGL :
- ProjectiveSpecialLinearGroup n R →* PGL(n, R) :=
- QuotientGroup.lift _ (mk.comp toGL) fun x hx ↦ by
- simp only [mem_center_iff, scalar_apply, MonoidHom.mem_ker, MonoidHom.coe_comp,
- Function.comp_apply, mk_eq_one, GeneralLinearGroup.mem_center_iff_val_mem_range_scalar,
- coe_GL_coe_matrix, Set.mem_range] at hx ⊢
- exact ⟨hx.choose, hx.choose_spec.2⟩
+def toPGL : ProjectiveSpecialLinearGroup n R →* PGL(n, R) :=
+ QuotientGroup.lift _ SpecialLinearGroup.toPGL <| le_of_eq toPGL_ker.symm
@[simp]
-lemma _root_.Matrix.ProjectiveSpecialLinearGroup.toPGL_mk (g : SpecialLinearGroup n R) :
+lemma toPGL_mk (g : SpecialLinearGroup n R) :
ProjectiveSpecialLinearGroup.toPGL g = mk (toGL g) := rfl
-lemma _root_.Matrix.ProjectiveSpecialLinearGroup.toPGL_injective :
- Function.Injective (ProjectiveSpecialLinearGroup.toPGL (n := n) (R := R)) := fun x y h ↦ by
- induction x using QuotientGroup.induction_on with | H x =>
- induction y using QuotientGroup.induction_on with | H y =>
- simp only [ProjectiveSpecialLinearGroup.toPGL_mk, mk_eq_mk_iff] at h
- rw [← QuotientGroup.mk'_apply, ← QuotientGroup.mk'_apply]
- simp only [QuotientGroup.mk'_eq_mk', mem_center_iff]
- obtain ⟨u, hu'⟩ := h
- have hu : u.1 ^ Fintype.card n = 1 := by
- simpa [Units.ext_iff] using congr(Matrix.GeneralLinearGroup.det $hu')
- set z : SpecialLinearGroup n R := ⟨scalar n u.1, by simpa using hu⟩ with hz_eq
- have hz : (GeneralLinearGroup.scalar n) u = toGL z := by ext; simp [hz_eq]
- refine ⟨z, ⟨u.1, hu, by simp [hz_eq]⟩, ?_⟩
- rwa [hz, ← map_mul, toGL_inj] at hu'
-
-lemma _root_.Matrix.ProjectiveSpecialLinearGroup.toPGL_surj_of_roots
+lemma toPGL_injective :
+ Function.Injective (ProjectiveSpecialLinearGroup.toPGL (n := n) (R := R)) :=
+ QuotientGroup.injective_lift_iff _ _ _ |>.2 toPGL_ker.symm
+
+lemma toPGL_surj_of_roots
(hR : ∀ r : Rˣ, ∃ k : Rˣ, k ^ Fintype.card n = r) :
Function.Surjective (ProjectiveSpecialLinearGroup.toPGL (n := n) (R := R)) := fun g ↦ by
induction g using Matrix.ProjGenLinGroup.induction_on with | mk g =>
@@ -117,10 +133,10 @@ lemma _root_.Matrix.ProjectiveSpecialLinearGroup.toPGL_surj_of_roots
use QuotientGroup.mk ⟨r.1 • g.1, hr⟩
simp only [ProjectiveSpecialLinearGroup.toPGL_mk, mk_eq_mk_iff]
refine ⟨r⁻¹, Units.ext ?_⟩
- simp only [Units.val_mul, coe_GL_coe_matrix,GeneralLinearGroup.val_scalar_apply]
+ simp only [Units.val_mul, coe_GL_coe_matrix, GeneralLinearGroup.coe_scalar]
simp [← Matrix.mul_smul, ← Matrix.diagonal_smul, Pi.smul_def, smul_eq_mul]
-lemma _root_.Matrix.ProjectiveSpecialLinearGroup.toPGL_surj_iff [Nonempty n] :
+lemma toPGL_surj_iff [Nonempty n] :
Function.Surjective (ProjectiveSpecialLinearGroup.toPGL (n := n) (R := R)) ↔
∀ r : Rˣ, ∃ k : Rˣ, k ^ Fintype.card n = r := by
refine ⟨fun h r ↦ ?_, ProjectiveSpecialLinearGroup.toPGL_surj_of_roots⟩
@@ -156,9 +172,13 @@ noncomputable def isoPSLOfAlgClosed {F : Type*} [Field F] [IsAlgClosed F] :
MulEquiv.symm (MulEquiv.ofBijective Matrix.ProjectiveSpecialLinearGroup.toPGL
⟨Matrix.ProjectiveSpecialLinearGroup.toPGL_injective, Function.surjective_to_subsingleton _⟩)
+end ProjectiveSpecialLinearGroup
+
end isoPSL
-variable {M : Type*} [Monoid M]
+namespace ProjGenLinGroup
+
+variable {n R : Type*} [Fintype n] [DecidableEq n] [CommRing R] {M : Type*} [Monoid M]
/-- Lift a monoid homomorphism `f : GL n R →* M` that vanishes on all scalar matrices
to a homomorphism from `PGL(n, R)`. -/
@@ -191,6 +211,23 @@ theorem mk_smul {α : Type*} [MulAction (GL n R) α] (h) (g : GL n R) (a : α) :
mk g • a = g • a := by
rfl
+/-- The monoid hom between `PGL(n, R)` and `PGL(n, S)` induced by a
+ ring homomorphism `f : R →+* S`. -/
+def map {S : Type*} [CommRing S] (f : R →+* S) : PGL(n, R) →* PGL(n, S) :=
+ QuotientGroup.map _ _ (GeneralLinearGroup.map (n := n) f) <| GeneralLinearGroup.map_center_le f
+
+@[simp]
+lemma map_id : map (RingHom.id R) = MonoidHom.id (PGL(n, R)) := QuotientGroup.map_id _
+
+@[simp]
+lemma map_mk {S : Type*} [CommRing S] (f : R →+* S) (g : GL n R) :
+ map f (mk g) = mk (GeneralLinearGroup.map f g) := rfl
+
+lemma map_comp {S T : Type*} [CommRing S] [CommRing T] (f : R →+* S) (g : S →+* T) :
+ map (n := n) (g.comp f) = (map g).comp (map f) := by
+ ext g
+ induction g using Matrix.ProjGenLinGroup.induction_on with | mk g => simp
+
variable [Fact (Even (Fintype.card n))] [LinearOrder R] [IsStrictOrderedRing R]
/-- In case of an even dimension, the sign of the determinant of `g : PGL(n, R)` is well-defined. -/
diff --git a/Mathlib/LinearAlgebra/Projectivization/Action.lean b/Mathlib/LinearAlgebra/Projectivization/Action.lean
index 1cbfb0e7a71d0f..7ff05dfca850b4 100644
--- a/Mathlib/LinearAlgebra/Projectivization/Action.lean
+++ b/Mathlib/LinearAlgebra/Projectivization/Action.lean
@@ -11,7 +11,7 @@ public import Mathlib.LinearAlgebra.Projectivization.Basic
public import Mathlib.LinearAlgebra.SpecialLinearGroup
public import Mathlib.LinearAlgebra.Transvection.Basic
public import Mathlib.LinearAlgebra.Matrix.IsDiag
-public import Mathlib.LinearAlgebra.Matrix.ProjectiveSpecialLinearGroup
+public import Mathlib.LinearAlgebra.Matrix.GeneralLinearGroup.Projective
public import Mathlib.LinearAlgebra.Center
/-!
@@ -240,6 +240,17 @@ instance : IsPreprimitive (Matrix.ProjectiveSpecialLinearGroup ι K) (ℙ K (ι
{toFun := id, map_smul' := by intros; simp; rfl} (prePrimitive_SL (ι := ι) (K := K))
Function.surjective_id
+open MatrixGroups Matrix.ProjGenLinGroup
+
+instance : MulAction PGL(ι, K) (ℙ K (ι → K)) :=
+ mulActionOfGL fun u ↦ ind fun v hv ↦ by
+ simp only [smul_mk, mk_eq_mk_iff]
+ exact ⟨u, by simp [Units.smul_def]⟩
+
+@[simp]
+lemma PGL.mk_smul_mk (g : GL ι K) {v : ι → K} (hv : v ≠ 0) :
+ (.mk g : PGL(ι, K)) • mk K v hv = mk K (g • v) (smul_ne_zero_iff_ne g|>.2 hv) := rfl
+
end Field
end Projectivization
From 6ac35b674ab1680c90d29efb843a5ed238ca258f Mon Sep 17 00:00:00 2001
From: Felix Pernegger <188575194+felixpernegger@users.noreply.github.com>
Date: Wed, 17 Jun 2026 14:20:58 +0000
Subject: [PATCH 0112/1300] chore: remove redudant `nolint simpNF` (#40680)
Remove the attribute whenever its redundant.
Some of this comes from #23201, not sure about why others work.
Co-authored-by: Batixx
---
Mathlib/Algebra/Group/Subgroup/Map.lean | 6 +-----
Mathlib/Algebra/Group/Submonoid/Operations.lean | 6 +-----
Mathlib/CategoryTheory/Localization/Construction.lean | 8 ++------
Mathlib/CategoryTheory/PathCategory/Basic.lean | 4 +---
Mathlib/CategoryTheory/WithTerminal/Basic.lean | 4 +---
Mathlib/Data/Multiset/MapFold.lean | 6 +-----
Mathlib/Data/Option/Basic.lean | 6 +-----
Mathlib/FieldTheory/IntermediateField/Adjoin/Basic.lean | 4 ++--
Mathlib/LinearAlgebra/CliffordAlgebra/Contraction.lean | 4 +---
Mathlib/ModelTheory/Order.lean | 2 +-
Mathlib/Order/Filter/Map.lean | 6 +-----
Mathlib/RingTheory/Extension/Generators.lean | 8 ++------
Mathlib/Topology/MetricSpace/Snowflaking.lean | 2 +-
13 files changed, 16 insertions(+), 50 deletions(-)
diff --git a/Mathlib/Algebra/Group/Subgroup/Map.lean b/Mathlib/Algebra/Group/Subgroup/Map.lean
index d05286dc6eca13..5946958e6bda6c 100644
--- a/Mathlib/Algebra/Group/Subgroup/Map.lean
+++ b/Mathlib/Algebra/Group/Subgroup/Map.lean
@@ -158,11 +158,7 @@ theorem mem_map_equiv {f : G ≃* N} {K : Subgroup G} {x : N} :
x ∈ K.map f.toMonoidHom ↔ f.symm x ∈ K :=
Set.mem_image_equiv
--- The simpNF linter says that the LHS can be simplified via `Subgroup.mem_map`.
--- However this is a higher priority lemma.
--- It seems the side condition `hf` is not applied by `simpNF`.
--- https://github.com/leanprover/std4/issues/207
-@[to_additive (attr := simp 1100, nolint simpNF)]
+@[to_additive (attr := simp 1100)]
theorem mem_map_iff_mem {f : G →* N} (hf : Function.Injective f) {K : Subgroup G} {x : G} :
f x ∈ K.map f ↔ x ∈ K :=
hf.mem_set_image
diff --git a/Mathlib/Algebra/Group/Submonoid/Operations.lean b/Mathlib/Algebra/Group/Submonoid/Operations.lean
index 621a9b3897d077..82fdff1c1df514 100644
--- a/Mathlib/Algebra/Group/Submonoid/Operations.lean
+++ b/Mathlib/Algebra/Group/Submonoid/Operations.lean
@@ -235,11 +235,7 @@ theorem apply_coe_mem_map (f : F) (S : Submonoid M) (x : S) : f x ∈ S.map f :=
theorem map_map (g : N →* P) (f : M →* N) : (S.map f).map g = S.map (g.comp f) :=
SetLike.coe_injective <| image_image _ _ _
--- The simpNF linter says that the LHS can be simplified via `Submonoid.mem_map`.
--- However this is a higher priority lemma.
--- It seems the side condition `hf` is not applied by `simpNF`.
--- https://github.com/leanprover/std4/issues/207
-@[to_additive (attr := simp 1100, nolint simpNF)]
+@[to_additive (attr := simp 1100)]
theorem mem_map_iff_mem {f : F} (hf : Function.Injective f) {S : Submonoid M} {x : M} :
f x ∈ S.map f ↔ x ∈ S :=
hf.mem_set_image
diff --git a/Mathlib/CategoryTheory/Localization/Construction.lean b/Mathlib/CategoryTheory/Localization/Construction.lean
index 15b685a2cd1179..25bf8f4eb82a31 100644
--- a/Mathlib/CategoryTheory/Localization/Construction.lean
+++ b/Mathlib/CategoryTheory/Localization/Construction.lean
@@ -70,20 +70,16 @@ the category `C` -/
def ιPaths (X : C) : Paths (LocQuiver W) :=
⟨X⟩
-#adaptation_note /-- As of nightly-2026-04-29, the simpNF linter is failing here.
-Assistance investigating this would be appreciated. -/
/-- The morphism in the path category associated to a morphism in the original category. -/
-@[simp, nolint simpNF]
+@[simp]
def ψ₁ {X Y : C} (f : X ⟶ Y) : ιPaths W X ⟶ ιPaths W Y := (Paths.of _).map (Sum.inl f)
#adaptation_note /-- As of nightly-2026-04-29, the simpNF linter is failing here.
Assistance investigating this would be appreciated. -/
attribute [nolint simpNF] ψ₁.eq_1
-#adaptation_note /-- As of nightly-2026-04-29, the simpNF linter is failing here.
-Assistance investigating this would be appreciated. -/
/-- The morphism in the path category corresponding to a formal inverse. -/
-@[simp, nolint simpNF]
+@[simp]
def ψ₂ {X Y : C} (w : X ⟶ Y) (hw : W w) : ιPaths W Y ⟶ ιPaths W X :=
(Paths.of _).map (Sum.inr ⟨w, hw⟩)
diff --git a/Mathlib/CategoryTheory/PathCategory/Basic.lean b/Mathlib/CategoryTheory/PathCategory/Basic.lean
index 7e012cc4e81fa5..ad3b72ce3b1c92 100644
--- a/Mathlib/CategoryTheory/PathCategory/Basic.lean
+++ b/Mathlib/CategoryTheory/PathCategory/Basic.lean
@@ -247,11 +247,9 @@ def pathComposition : Paths C ⥤ C where
-- the `HomRel` for the kernel of any functor.
-- Indeed, this should be part of an equivalence between congruence relations on a category `C`
-- and full, essentially surjective functors out of `C`.
-#adaptation_note /-- As of nightly-2026-04-29, the simpNF linter is failing here.
-Assistance investigating this would be appreciated. -/
/-- The canonical relation on the path category of a category:
two paths are related if they compose to the same morphism. -/
-@[simp, nolint simpNF]
+@[simp]
def pathsHomRel : HomRel (Paths C) := fun _ _ p q =>
(pathComposition C).map p = (pathComposition C).map q
diff --git a/Mathlib/CategoryTheory/WithTerminal/Basic.lean b/Mathlib/CategoryTheory/WithTerminal/Basic.lean
index dc00c176e29eb2..f53ba944c15c1d 100644
--- a/Mathlib/CategoryTheory/WithTerminal/Basic.lean
+++ b/Mathlib/CategoryTheory/WithTerminal/Basic.lean
@@ -83,10 +83,8 @@ def id : ∀ X : WithTerminal C, Hom X X
| of _ => 𝟙 _
| star => PUnit.unit
-#adaptation_note /-- As of nightly-2026-04-29, the simpNF linter is failing here.
-Assistance investigating this would be appreciated. -/
/-- Composition of morphisms for `WithTerminal C`. -/
-@[simp, nolint simpNF]
+@[simp]
def comp : ∀ {X Y Z : WithTerminal C}, Hom X Y → Hom Y Z → Hom X Z
| of _X, of _Y, of _Z => fun f g => f ≫ g
| of _X, _, star => fun _f _g => PUnit.unit
diff --git a/Mathlib/Data/Multiset/MapFold.lean b/Mathlib/Data/Multiset/MapFold.lean
index 78ddf10a56632d..232137c36eb954 100644
--- a/Mathlib/Data/Multiset/MapFold.lean
+++ b/Mathlib/Data/Multiset/MapFold.lean
@@ -142,11 +142,7 @@ theorem map_eq_cons [DecidableEq α] (f : α → β) (s : Multiset α) (t : Mult
refine ⟨a, mem_cons_self _ _, rfl, ?_⟩
rw [Multiset.erase_cons_head, h]
--- The simpNF linter says that the LHS can be simplified via `Multiset.mem_map`.
--- However this is a higher priority lemma.
--- It seems the side condition `H` is not applied by `simpNF`.
--- https://github.com/leanprover/std4/issues/207
-@[simp 1100, nolint simpNF]
+@[simp 1100]
theorem mem_map_of_injective {f : α → β} (H : Function.Injective f) {a : α} {s : Multiset α} :
f a ∈ map f s ↔ a ∈ s :=
Quot.inductionOn s fun _l => List.mem_map_of_injective H
diff --git a/Mathlib/Data/Option/Basic.lean b/Mathlib/Data/Option/Basic.lean
index 79684a9bb2ad13..e66b8f9b8b3ef2 100644
--- a/Mathlib/Data/Option/Basic.lean
+++ b/Mathlib/Data/Option/Basic.lean
@@ -47,11 +47,7 @@ theorem coe_def : (fun a ↦ ↑a : α → Option α) = some :=
theorem mem_map {f : α → β} {y : β} {o : Option α} : y ∈ o.map f ↔ ∃ x ∈ o, f x = y := by simp
--- The simpNF linter says that the LHS can be simplified via `Option.mem_def`.
--- However this is a higher priority lemma.
--- It seems the side condition `H` is not applied by `simpNF`.
--- https://github.com/leanprover/std4/issues/207
-@[simp 1100, nolint simpNF]
+@[simp 1100]
theorem mem_map_of_injective {f : α → β} (H : Function.Injective f) {a : α} {o : Option α} :
f a ∈ o.map f ↔ a ∈ o := by
aesop
diff --git a/Mathlib/FieldTheory/IntermediateField/Adjoin/Basic.lean b/Mathlib/FieldTheory/IntermediateField/Adjoin/Basic.lean
index 3a118004d1cb36..a1f6d27897f034 100644
--- a/Mathlib/FieldTheory/IntermediateField/Adjoin/Basic.lean
+++ b/Mathlib/FieldTheory/IntermediateField/Adjoin/Basic.lean
@@ -287,14 +287,14 @@ protected theorem finrank_bot : finrank F (⊥ : IntermediateField F E) = 1 := b
@[simp] theorem rank_bot' : Module.rank (⊥ : IntermediateField F E) E = Module.rank F E := by
rw [← rank_mul_rank F (⊥ : IntermediateField F E) E, IntermediateField.rank_bot, one_mul]
-@[simp, nolint simpNF] -- `simpNF` hits a (deterministic) timeout at `typeclass`
+@[simp]
theorem finrank_bot' : finrank (⊥ : IntermediateField F E) E = finrank F E :=
congr(Cardinal.toNat $(rank_bot'))
@[simp] protected theorem rank_top : Module.rank (⊤ : IntermediateField F E) E = 1 :=
Subalgebra.bot_eq_top_iff_rank_eq_one.mp <| top_le_iff.mp fun x _ ↦ ⟨⟨x, trivial⟩, rfl⟩
-@[simp, nolint simpNF] -- `simpNF` hits a (deterministic) timeout at `typeclass`
+@[simp]
protected theorem finrank_top : finrank (⊤ : IntermediateField F E) E = 1 :=
rank_eq_one_iff_finrank_eq_one.mp IntermediateField.rank_top
diff --git a/Mathlib/LinearAlgebra/CliffordAlgebra/Contraction.lean b/Mathlib/LinearAlgebra/CliffordAlgebra/Contraction.lean
index 9d2e68a1004432..75e22d61ca3c93 100644
--- a/Mathlib/LinearAlgebra/CliffordAlgebra/Contraction.lean
+++ b/Mathlib/LinearAlgebra/CliffordAlgebra/Contraction.lean
@@ -346,13 +346,11 @@ theorem changeFormEquiv_symm :
variable (Q)
-#adaptation_note /-- As of nightly-2026-04-29, the simpNF linter is failing here.
-Assistance investigating this would be appreciated. -/
/-- The module isomorphism to the exterior algebra.
Note that this holds more generally when `Q` is divisible by two, rather than only when `1` is
divisible by two; but that would be more awkward to use. -/
-@[simp, nolint simpNF]
+@[simp]
def equivExterior [Invertible (2 : R)] : CliffordAlgebra Q ≃ₗ[R] ExteriorAlgebra R M :=
changeFormEquiv changeForm.associated_neg_proof
diff --git a/Mathlib/ModelTheory/Order.lean b/Mathlib/ModelTheory/Order.lean
index 0b85297e2ca919..fc01bc4c2f53c7 100644
--- a/Mathlib/ModelTheory/Order.lean
+++ b/Mathlib/ModelTheory/Order.lean
@@ -135,7 +135,7 @@ language. -/
@[simps] def orderLHom : Language.order →ᴸ L where
onRelation | _, .le => leSymb
-@[simp, nolint simpNF]
+@[simp]
theorem orderLHom_leSymb :
(orderLHom L).onRelation leSymb = (leSymb : L.Relations 2) :=
rfl
diff --git a/Mathlib/Order/Filter/Map.lean b/Mathlib/Order/Filter/Map.lean
index 671beda2c6109e..bd63d6f641c8eb 100644
--- a/Mathlib/Order/Filter/Map.lean
+++ b/Mathlib/Order/Filter/Map.lean
@@ -60,11 +60,7 @@ theorem mem_map' : t ∈ map m f ↔ { x | m x ∈ t } ∈ f :=
theorem image_mem_map (hs : s ∈ f) : m '' s ∈ map m f :=
f.sets_of_superset hs <| subset_preimage_image m s
--- The simpNF linter says that the LHS can be simplified via `Filter.mem_map`.
--- However this is a higher priority lemma.
--- It seems the side condition `hf` is not applied by `simpNF`.
--- https://github.com/leanprover/std4/issues/207
-@[simp 1100, nolint simpNF]
+@[simp 1100]
theorem image_mem_map_iff (hf : Injective m) : m '' s ∈ map m f ↔ s ∈ f :=
⟨fun h => by rwa [← preimage_image_eq s hf], image_mem_map⟩
diff --git a/Mathlib/RingTheory/Extension/Generators.lean b/Mathlib/RingTheory/Extension/Generators.lean
index 1e3ede9f668cb7..8f0c380fec7e72 100644
--- a/Mathlib/RingTheory/Extension/Generators.lean
+++ b/Mathlib/RingTheory/Extension/Generators.lean
@@ -639,18 +639,14 @@ lemma ker_naive {σ : Type*} {I : Ideal (MvPolynomial σ R)}
set_option backward.defeqAttrib.useBackward true in
set_option backward.isDefEq.respectTransparency false in
--- FIXME: `simpNF` times out synthesizing
--- `FaithfulSMul (Algebra.Generators.ofAlgHom f h).toExtension.Ring S`.
-@[simp, nolint simpNF]
+@[simp]
lemma ker_ofAlgHom {I : Type*} (f : MvPolynomial I R →ₐ[R] S) (h : Function.Surjective ⇑f) :
(ofAlgHom f h).ker = RingHom.ker f.toRingHom := by
change RingHom.ker _ = _
congr
exact MvPolynomial.ringHom_ext (by simp) (by simp [ofAlgHom])
--- FIXME: `simpNF` times out synthesizing
--- `FaithfulSMul (P.ofAlgEquiv e).toExtension.Ring T`.
-@[simp, nolint simpNF]
+@[simp]
lemma ker_ofAlgEquiv (P : Generators R S ι) {T : Type*} [CommRing T] [Algebra R T] (e : S ≃ₐ[R] T) :
(P.ofAlgEquiv e).ker = P.ker := by
rw [ker_eq_ker_aeval_val, ofAlgEquiv_val, Function.comp_def, ← AlgHom.coe_coe,
diff --git a/Mathlib/Topology/MetricSpace/Snowflaking.lean b/Mathlib/Topology/MetricSpace/Snowflaking.lean
index 11b2fb82b74c91..365ed2843654fb 100644
--- a/Mathlib/Topology/MetricSpace/Snowflaking.lean
+++ b/Mathlib/Topology/MetricSpace/Snowflaking.lean
@@ -84,7 +84,7 @@ theorem toSnowflaking.sizeOf_spec [SizeOf X] (x : X) :
sizeOf (toSnowflaking x : Snowflaking X α hα₀ hα₁) = 1 + sizeOf x :=
rfl
-attribute [nolint simpNF] mk.injEq mk.sizeOf_spec
+attribute [nolint simpNF] mk.injEq
/-- This definition makes `cases x` and `induction x` use `toSnowflaking` instead of `mk`. -/
@[elab_as_elim, cases_eliminator, induction_eliminator]
From dcb1501721aa814cf2eae1e761398b076627b7b2 Mon Sep 17 00:00:00 2001
From: Anatole Dedecker
Date: Wed, 17 Jun 2026 14:21:01 +0000
Subject: [PATCH 0113/1300] chore: move `IsOpenQuotientMap.baireSpace` to
`Baire.Lemmas` (#40713)
This makes the import tree a bit cleaner, so that importing `OpenQuotient` is cheaper.
---
Mathlib/Topology/Baire/Lemmas.lean | 12 +++++++++++-
Mathlib/Topology/Homeomorph/Lemmas.lean | 1 +
Mathlib/Topology/Maps/OpenQuotient.lean | 10 ----------
Mathlib/Topology/Order/IsLUB.lean | 1 +
4 files changed, 13 insertions(+), 11 deletions(-)
diff --git a/Mathlib/Topology/Baire/Lemmas.lean b/Mathlib/Topology/Baire/Lemmas.lean
index 0f15e154154f65..7d9c592d303eeb 100644
--- a/Mathlib/Topology/Baire/Lemmas.lean
+++ b/Mathlib/Topology/Baire/Lemmas.lean
@@ -6,8 +6,9 @@ Authors: Sébastien Gouëzel
module
public import Mathlib.Data.Fintype.Powerset
-public import Mathlib.Topology.GDelta.Basic
public import Mathlib.Topology.Constructions
+public import Mathlib.Topology.GDelta.Basic
+public import Mathlib.Topology.Maps.OpenQuotient
public import Mathlib.Tactic.CrossRefAttribute
/-!
@@ -111,6 +112,15 @@ theorem Topology.IsOpenEmbedding.baireSpace {Y : Type*} [TopologicalSpace Y] {p
theorem IsOpen.baireSpace {s : Set X} (hO : IsOpen s) : BaireSpace s :=
hO.isOpenEmbedding_subtypeVal.baireSpace
+/-- If `f` is an open quotient map and `X` is Baire, then `Y` is Baire. -/
+theorem IsOpenQuotientMap.baireSpace {Y : Type*} [TopologicalSpace Y] {f : X → Y}
+ (hf : IsOpenQuotientMap f) : BaireSpace Y := by
+ constructor
+ intro u hou hdu
+ have := dense_iInter_of_isOpen_nat (fun n => hf.continuous.isOpen_preimage (u n) (hou n))
+ (fun n => (IsOpenQuotientMap.dense_preimage_iff hf).mpr (hdu n))
+ simp_all [← preimage_iInter, IsOpenQuotientMap.dense_preimage_iff]
+
/-- Baire theorem: a countable intersection of dense open sets is dense. Formulated here with ⋂₀. -/
theorem dense_sInter_of_isOpen {S : Set (Set X)} (ho : ∀ s ∈ S, IsOpen s) (hS : S.Countable)
(hd : ∀ s ∈ S, Dense s) : Dense (⋂₀ S) := by
diff --git a/Mathlib/Topology/Homeomorph/Lemmas.lean b/Mathlib/Topology/Homeomorph/Lemmas.lean
index 9fc328a54765e0..1dbcf2a6d5059b 100644
--- a/Mathlib/Topology/Homeomorph/Lemmas.lean
+++ b/Mathlib/Topology/Homeomorph/Lemmas.lean
@@ -9,6 +9,7 @@ public import Mathlib.Logic.Equiv.Fin.Basic
public import Mathlib.Topology.Connected.LocallyConnected
public import Mathlib.Topology.DenseEmbedding
public import Mathlib.Topology.Connected.TotallyDisconnected
+public import Mathlib.Topology.Baire.Lemmas
/-!
# Further properties of homeomorphisms
diff --git a/Mathlib/Topology/Maps/OpenQuotient.lean b/Mathlib/Topology/Maps/OpenQuotient.lean
index ccbd3c06193cac..be2ce3cb93e7f3 100644
--- a/Mathlib/Topology/Maps/OpenQuotient.lean
+++ b/Mathlib/Topology/Maps/OpenQuotient.lean
@@ -6,7 +6,6 @@ Authors: Yury Kudryashov
module
public import Mathlib.Topology.Maps.Basic
-public import Mathlib.Topology.Baire.Lemmas
/-!
# Open quotient maps
@@ -65,15 +64,6 @@ theorem dense_preimage_iff (h : IsOpenQuotientMap f) {s : Set Y} : Dense (f ⁻
⟨fun hs ↦ h.surjective.denseRange.dense_of_mapsTo h.continuous hs (mapsTo_preimage _ _),
fun hs ↦ hs.preimage h.isOpenMap⟩
-/-- If `f` is an open quotient map and `X` is Baire, then `Y` is Baire. -/
-theorem baireSpace {f : X → Y} [BaireSpace X] (hf : IsOpenQuotientMap f) :
- BaireSpace Y := by
- constructor
- intro u hou hdu
- have := dense_iInter_of_isOpen_nat (fun n => hf.continuous.isOpen_preimage (u n) (hou n))
- (fun n => (IsOpenQuotientMap.dense_preimage_iff hf).mpr (hdu n))
- simp_all [← preimage_iInter, IsOpenQuotientMap.dense_preimage_iff]
-
end IsOpenQuotientMap
theorem Topology.IsInducing.isOpenQuotientMap_of_surjective (ind : IsInducing f)
diff --git a/Mathlib/Topology/Order/IsLUB.lean b/Mathlib/Topology/Order/IsLUB.lean
index 3780b512cae029..f36797e4ef2d73 100644
--- a/Mathlib/Topology/Order/IsLUB.lean
+++ b/Mathlib/Topology/Order/IsLUB.lean
@@ -5,6 +5,7 @@ Authors: Johannes Hölzl, Mario Carneiro, Yury Kudryashov
-/
module
+public import Mathlib.Order.Filter.CountableInter
public import Mathlib.Topology.Order.LeftRightNhds
/-!
From 51dc5fe5cec5c2ef41c1abbf9ca3735314598d4c Mon Sep 17 00:00:00 2001
From: "mathlib-splicebot[bot]"
<261196803+mathlib-splicebot[bot]@users.noreply.github.com>
Date: Wed, 17 Jun 2026 14:21:04 +0000
Subject: [PATCH 0114/1300] feat: isEmbedding_subtypeL (#40716)
This PR was automatically created from PR #39100 by @ADedecker via a [review comment](https://github.com/leanprover-community/mathlib4/pull/39100#discussion_r3428436762) by @ADedecker.
Co-authored-by: ADedecker <48656793+ADedecker@users.noreply.github.com>
---
.../Algebra/Module/ContinuousLinearMap/Restrict.lean | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/Mathlib/Topology/Algebra/Module/ContinuousLinearMap/Restrict.lean b/Mathlib/Topology/Algebra/Module/ContinuousLinearMap/Restrict.lean
index d441ec3c01472a..b143c030d2091a 100644
--- a/Mathlib/Topology/Algebra/Module/ContinuousLinearMap/Restrict.lean
+++ b/Mathlib/Topology/Algebra/Module/ContinuousLinearMap/Restrict.lean
@@ -58,6 +58,14 @@ alias coe_subtypeL' := coe_subtypeL
theorem subtypeL_apply (p : Submodule R M) (x : p) : p.subtypeL x = x := by simp
+theorem isEmbedding_subtype (p : Submodule R M) : Topology.IsEmbedding p.subtype := .subtypeVal
+theorem isEmbedding_subtypeL (p : Submodule R M) : Topology.IsEmbedding p.subtypeL := .subtypeVal
+
+theorem isClosedEmbedding_subtype (p : Submodule R M) (hp : IsClosed (p : Set M)) :
+ Topology.IsClosedEmbedding p.subtype := .subtypeVal hp
+theorem isClosedEmbedding_subtypeL (p : Submodule R M) (hp : IsClosed (p : Set M)) :
+ Topology.IsClosedEmbedding p.subtypeL := .subtypeVal hp
+
@[deprecated range_subtype (since := "2026-05-06")]
theorem range_subtypeL (p : Submodule R M) : (p.subtypeL : p →ₗ[R] M).range = p :=
Submodule.range_subtype _
From 7d46b4c5ac5094a978500cec6f1de5a7550ba64e Mon Sep 17 00:00:00 2001
From: Hannah Scholz <70071345+scholzhannah@users.noreply.github.com>
Date: Wed, 17 Jun 2026 14:21:12 +0000
Subject: [PATCH 0115/1300] chore: rename
`{OpenPartialHomeomorph,PartialEquiv}.map_source''` (#40717)
The new name avoids a double prime and also matches the naming convention.
As suggested [here](https://github.com/leanprover-community/mathlib4/pull/39084#discussion_r3423172247).
---
Mathlib/Geometry/Manifold/ChartedSpace.lean | 2 +-
Mathlib/Logic/Equiv/PartialEquiv.lean | 4 +++-
Mathlib/Topology/OpenPartialHomeomorph/Defs.lean | 4 +++-
3 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/Mathlib/Geometry/Manifold/ChartedSpace.lean b/Mathlib/Geometry/Manifold/ChartedSpace.lean
index da6ab6a1bf0368..c5f1f48b6ac267 100644
--- a/Mathlib/Geometry/Manifold/ChartedSpace.lean
+++ b/Mathlib/Geometry/Manifold/ChartedSpace.lean
@@ -275,7 +275,7 @@ theorem ChartedSpace.locPathConnectedSpace [LocPathConnectedSpace H] : LocPathCo
apply e.symm.image_mem_nhds (by simp [e])
exact pathComponentIn_mem_nhds <| e.image_mem_nhds (mem_chart_source _ _) ht
· refine (isPathConnected_pathComponentIn <| mem_image_of_mem e (mem_of_mem_nhds ht)).image' ?_
- refine e.continuousOn_symm.mono <| subset_trans ?_ e.map_source''
+ refine e.continuousOn_symm.mono <| subset_trans ?_ e.image_source_subset
exact (pathComponentIn_mono <| image_mono inter_subset_right).trans pathComponentIn_subset
· exact (image_mono pathComponentIn_subset).trans
(PartialEquiv.symm_image_image_of_subset_source _ inter_subset_right).subset
diff --git a/Mathlib/Logic/Equiv/PartialEquiv.lean b/Mathlib/Logic/Equiv/PartialEquiv.lean
index 022bfe7a6c1a28..14605e6ca9458c 100644
--- a/Mathlib/Logic/Equiv/PartialEquiv.lean
+++ b/Mathlib/Logic/Equiv/PartialEquiv.lean
@@ -180,9 +180,11 @@ theorem map_source {x : α} (h : x ∈ e.source) : e x ∈ e.target :=
e.map_source' h
/-- Variant of `e.map_source` and `map_source'`, stated for images of subsets of `source`. -/
-lemma map_source'' : e '' e.source ⊆ e.target :=
+lemma image_source_subset : e '' e.source ⊆ e.target :=
fun _ ⟨_, hx, hex⟩ ↦ mem_of_eq_of_mem (id hex.symm) (e.map_source' hx)
+@[deprecated (since := "2026-06-17")] alias map_source'' := image_source_subset
+
@[simp, mfld_simps]
theorem map_target {x : β} (h : x ∈ e.target) : e.symm x ∈ e.source :=
e.map_target' h
diff --git a/Mathlib/Topology/OpenPartialHomeomorph/Defs.lean b/Mathlib/Topology/OpenPartialHomeomorph/Defs.lean
index 8b78eaffe96302..362cbe712da3c6 100644
--- a/Mathlib/Topology/OpenPartialHomeomorph/Defs.lean
+++ b/Mathlib/Topology/OpenPartialHomeomorph/Defs.lean
@@ -144,9 +144,11 @@ theorem map_source {x : X} (h : x ∈ e.source) : e x ∈ e.target :=
e.map_source' h
/-- Variant of `map_source`, stated for images of subsets of `source`. -/
-lemma map_source'' : e '' e.source ⊆ e.target :=
+lemma image_source_subset : e '' e.source ⊆ e.target :=
fun _ ⟨_, hx, hex⟩ ↦ mem_of_eq_of_mem (id hex.symm) (e.map_source' hx)
+@[deprecated (since := "2026-06-17")] alias map_source'' := image_source_subset
+
@[simp, mfld_simps]
theorem map_target {x : Y} (h : x ∈ e.target) : e.symm x ∈ e.source :=
e.map_target' h
From 64f7dc5fc88fdadd768edca49f0a0e2953496ee0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ya=C3=ABl=20Dillies?=
Date: Wed, 17 Jun 2026 15:15:28 +0000
Subject: [PATCH 0116/1300] doc(Algebra/Order): update mentions of
`OrderedSemiring` and friends (#37835)
As of #20676, it was replaced by `IsOrderedRing`. Also deprecate `Rat.coe_int_inj` in favor of `Rat.intCast_inj` and action a TODO in `norm_num`'s code.
Not all docs were straightforward to update.
---
Counterexamples/HomogeneousPrimeNotPrime.lean | 2 +-
.../OrderedCancelAddCommMonoidWithBounds.lean | 6 +-
Mathlib/Algebra/Group/Subgroup/Order.lean | 3 +-
Mathlib/Algebra/Group/ULift.lean | 2 +-
Mathlib/Algebra/Order/AddTorsor.lean | 20 ++---
Mathlib/Algebra/Order/CompleteField.lean | 2 +-
Mathlib/Algebra/Order/Floor/Defs.lean | 3 +-
Mathlib/Algebra/Order/Floor/Ring.lean | 2 +-
Mathlib/Algebra/Order/Floor/Semiring.lean | 2 +-
Mathlib/Algebra/Order/Group/Cone.lean | 5 +-
Mathlib/Algebra/Order/Group/Cyclic.lean | 2 +-
Mathlib/Algebra/Order/GroupWithZero/Defs.lean | 26 +++---
Mathlib/Algebra/Order/Hom/Monoid.lean | 8 +-
.../Algebra/Order/Interval/Set/Monoid.lean | 4 +-
.../Algebra/Order/Monoid/Canonical/Defs.lean | 16 ++--
.../Algebra/Order/Monoid/Unbundled/Defs.lean | 41 +++++----
.../Order/Monoid/Unbundled/ExistsOfLE.lean | 8 +-
Mathlib/Algebra/Order/Nonneg/Field.lean | 4 -
Mathlib/Algebra/Order/Nonneg/Ring.lean | 4 -
Mathlib/Algebra/Order/Positive/Ring.lean | 2 +-
Mathlib/Algebra/Order/Ring/Basic.lean | 7 +-
Mathlib/Algebra/Order/Ring/Cone.lean | 3 +-
Mathlib/Algebra/Order/Ring/Defs.lean | 72 +++------------
.../Algebra/Order/Ring/Unbundled/Basic.lean | 89 +------------------
Mathlib/Algebra/Order/Star/Basic.lean | 4 +-
Mathlib/Algebra/Order/Sub/Basic.lean | 2 +-
Mathlib/Algebra/Order/Sub/WithTop.lean | 4 +-
Mathlib/Algebra/Order/ToIntervalMod.lean | 5 +-
Mathlib/Algebra/Order/WithTop/Untop0.lean | 12 ---
Mathlib/Analysis/Complex/Order.lean | 4 +-
Mathlib/Combinatorics/Pigeonhole.lean | 2 +-
Mathlib/Data/Finsupp/Weight.lean | 4 +-
Mathlib/Data/NNReal/Defs.lean | 2 +-
Mathlib/Data/Nat/Cast/Order/Basic.lean | 14 +--
Mathlib/Data/Nat/Cast/Order/Ring.lean | 10 +--
.../Matrix/Irreducible/Defs.lean | 2 +-
Mathlib/MeasureTheory/Function/LpOrder.lean | 2 +-
.../Measure/Typeclasses/Finite.lean | 2 +-
.../Order/Filter/AtTopBot/Archimedean.lean | 6 +-
Mathlib/Order/Filter/Germ/OrderedMonoid.lean | 2 +-
Mathlib/Order/Interval/Finset/Nat.lean | 2 +-
Mathlib/Order/Interval/Set/Defs.lean | 2 +-
Mathlib/Order/Interval/Set/OrdConnected.lean | 2 +-
Mathlib/RingTheory/GradedAlgebra/Radical.lean | 2 +-
.../RingTheory/HahnSeries/PowerSeries.lean | 5 +-
Mathlib/RingTheory/HahnSeries/Valuation.lean | 3 +-
.../MvPolynomial/WeightedHomogeneous.lean | 4 +-
Mathlib/Tactic/NormNum/Ineq.lean | 18 +---
MathlibTest/positivity.lean | 3 +-
docs/overview.yaml | 2 +-
50 files changed, 139 insertions(+), 314 deletions(-)
diff --git a/Counterexamples/HomogeneousPrimeNotPrime.lean b/Counterexamples/HomogeneousPrimeNotPrime.lean
index a52f267bd992ce..f6ad1b8b76c8ed 100644
--- a/Counterexamples/HomogeneousPrimeNotPrime.lean
+++ b/Counterexamples/HomogeneousPrimeNotPrime.lean
@@ -12,7 +12,7 @@ import Mathlib.RingTheory.GradedAlgebra.Homogeneous.Ideal
# A homogeneous ideal that is homogeneously prime but not prime
In `Ideal.IsHomogeneous.isPrime_of_homogeneous_mem_or_mem`, we assumed that the underlying grading
-is indexed by a `LinearOrderedCancelAddCommMonoid` to prove that a homogeneous ideal is prime
+is indexed by a linearly ordered cancellative monoid to prove that a homogeneous ideal is prime
if and only if it is homogeneously prime. This file shows that even if this assumption isn't
strictly necessary, the assumption of "being cancellative" is. We construct a counterexample where
the underlying indexing set is a `LinearOrderedAddCommMonoid` but is not cancellative and the
diff --git a/Counterexamples/OrderedCancelAddCommMonoidWithBounds.lean b/Counterexamples/OrderedCancelAddCommMonoidWithBounds.lean
index 1eb0e49b8aa8ef..b5346725242b49 100644
--- a/Counterexamples/OrderedCancelAddCommMonoidWithBounds.lean
+++ b/Counterexamples/OrderedCancelAddCommMonoidWithBounds.lean
@@ -7,11 +7,11 @@ import Mathlib.Algebra.Order.Monoid.Defs
import Mathlib.Order.BoundedOrder.Lattice
/-!
-# Do not combine OrderedCancelAddCommMonoid with BoundedOrder
+# Do not combine `IsOrderedCancelAddMonoid` with `BoundedOrder`
-This file shows that combining `OrderedCancelAddCommMonoid` with `BoundedOrder` is not a good idea,
+This file shows that combining `IsOrderedCancelAddMonoid` with `BoundedOrder` is not a good idea,
as such a structure must be trivial (`⊥ = x = ⊤` for all `x`).
-The same applies to any superclasses, e.g. combining `StrictOrderedSemiring` with `CompleteLattice`.
+The same applies to any superclasses, e.g. combining `IsStrictOrderedRing` with `CompleteLattice`.
-/
example {α : Type*} [AddCommMonoid α] [PartialOrder α] [IsOrderedCancelAddMonoid α]
diff --git a/Mathlib/Algebra/Group/Subgroup/Order.lean b/Mathlib/Algebra/Group/Subgroup/Order.lean
index 0db35f85ce7d68..9b821ebe9b7d7a 100644
--- a/Mathlib/Algebra/Group/Subgroup/Order.lean
+++ b/Mathlib/Algebra/Group/Subgroup/Order.lean
@@ -77,7 +77,8 @@ namespace Subgroup
variable {G : Type*}
/-- A subgroup of an ordered group is an ordered group. -/
-@[to_additive /-- An `AddSubgroup` of an `AddOrderedCommGroup` is an `AddOrderedCommGroup`. -/]
+@[to_additive
+/-- An additive subgroup of an additive ordered group is an additive ordered group. -/]
instance toIsOrderedMonoid [CommGroup G] [Preorder G] [IsOrderedMonoid G] (H : Subgroup G) :
IsOrderedMonoid H :=
Function.Injective.isOrderedMonoid Subtype.val (fun _ _ => rfl) .rfl
diff --git a/Mathlib/Algebra/Group/ULift.lean b/Mathlib/Algebra/Group/ULift.lean
index ba97d0e915c396..375f8ee797273c 100644
--- a/Mathlib/Algebra/Group/ULift.lean
+++ b/Mathlib/Algebra/Group/ULift.lean
@@ -136,6 +136,6 @@ instance cancelCommMonoid [CancelCommMonoid α] : CancelCommMonoid (ULift α) :=
instance nontrivial [Nontrivial α] : Nontrivial (ULift α) :=
Equiv.ulift.symm.injective.nontrivial
--- TODO we don't do `OrderedCancelCommMonoid` or `OrderedCommGroup`
+-- TODO: We don't do `IsOrderedCancelMonoid`.
-- We'd need to add instances for `ULift` in `Order.Basic`.
end ULift
diff --git a/Mathlib/Algebra/Order/AddTorsor.lean b/Mathlib/Algebra/Order/AddTorsor.lean
index b52354f9409fbb..edd40c9ab230b6 100644
--- a/Mathlib/Algebra/Order/AddTorsor.lean
+++ b/Mathlib/Algebra/Order/AddTorsor.lean
@@ -32,16 +32,16 @@ an ordered field.
* IsOrderedCancelVAdd : inequalities are preserved and reflected by translation.
## Instances
-* OrderedCommMonoid.toIsOrderedSMul
-* OrderedAddCommMonoid.toIsOrderedVAdd
-* IsOrderedSMul.toCovariantClassLeft
-* IsOrderedVAdd.toCovariantClassLeft
-* IsOrderedCancelSMul.toCancelSMul
-* IsOrderedCancelVAdd.toCancelVAdd
-* OrderedCancelCommMonoid.toIsOrderedCancelSMul
-* OrderedCancelAddCommMonoid.toIsOrderedCancelVAdd
-* IsOrderedCancelSMul.toContravariantClassLeft
-* IsOrderedCancelVAdd.toContravariantClassLeft
+* `IsOrderedMonoid.toIsOrderedSMul`
+* `IsOrderedAddMonoid.toIsOrderedVAdd`
+* `IsOrderedSMul.toCovariantClassLeft`
+* `IsOrderedVAdd.toCovariantClassLeft`
+* `IsOrderedCancelSMul.toCancelSMul`
+* `IsOrderedCancelVAdd.toCancelVAdd`
+* `IsOrderedCancelMonoid.toIsOrderedCancelSMul`
+* `IsOrderedCancelAddMonoid.toIsOrderedCancelVAdd`
+* `IsOrderedCancelSMul.toContravariantClassLeft`
+* `IsOrderedCancelVAdd.toContravariantClassLeft`
## TODO
* (lex) prod instances
diff --git a/Mathlib/Algebra/Order/CompleteField.lean b/Mathlib/Algebra/Order/CompleteField.lean
index 368aabd6624c2a..e0515d95472fab 100644
--- a/Mathlib/Algebra/Order/CompleteField.lean
+++ b/Mathlib/Algebra/Order/CompleteField.lean
@@ -18,7 +18,7 @@ This is `ConditionallyCompleteLinearOrderedField.inducedOrderRingIso`.
Moreover this isomorphism is unique.
We show all conditionally complete linear ordered fields are
-archimedean. We also construct the natural map from a `LinearOrderedField` to such a field.
+archimedean. We also construct the natural map from a linearly ordered field to such a field.
## Main definitions
diff --git a/Mathlib/Algebra/Order/Floor/Defs.lean b/Mathlib/Algebra/Order/Floor/Defs.lean
index ea027109b35e54..64f301a1f3b181 100644
--- a/Mathlib/Algebra/Order/Floor/Defs.lean
+++ b/Mathlib/Algebra/Order/Floor/Defs.lean
@@ -39,8 +39,7 @@ for `nnnorm`.
## TODO
-`LinearOrderedRing`/`LinearOrderedSemiring` can be relaxed to `OrderedRing`/`OrderedSemiring` in
-many lemmas.
+`LinearOrder` can be relaxed to `PartialOrder` in many lemmas.
## Tags
diff --git a/Mathlib/Algebra/Order/Floor/Ring.lean b/Mathlib/Algebra/Order/Floor/Ring.lean
index 43b7f578f2e0d8..79a3481beeae1e 100644
--- a/Mathlib/Algebra/Order/Floor/Ring.lean
+++ b/Mathlib/Algebra/Order/Floor/Ring.lean
@@ -20,7 +20,7 @@ fractional part operator.
## TODO
-`LinearOrderedRing` can be relaxed to `OrderedRing` in many lemmas.
+`LinearOrder` can be relaxed to `PartialOrder` in many lemmas.
## Tags
diff --git a/Mathlib/Algebra/Order/Floor/Semiring.lean b/Mathlib/Algebra/Order/Floor/Semiring.lean
index d024388b60f93d..8af0bf21b086d4 100644
--- a/Mathlib/Algebra/Order/Floor/Semiring.lean
+++ b/Mathlib/Algebra/Order/Floor/Semiring.lean
@@ -15,7 +15,7 @@ This file contains basic results on the natural-valued floor and ceiling functio
## TODO
-`LinearOrderedSemiring` can be relaxed to `OrderedSemiring` in many lemmas.
+`LinearOrder` can be relaxed to `PartialOrder` in many lemmas.
## Tags
diff --git a/Mathlib/Algebra/Order/Group/Cone.lean b/Mathlib/Algebra/Order/Group/Cone.lean
index 851666410a32bc..1156753e2f7abd 100644
--- a/Mathlib/Algebra/Order/Group/Cone.lean
+++ b/Mathlib/Algebra/Order/Group/Cone.lean
@@ -12,9 +12,8 @@ public import Mathlib.Algebra.Order.Monoid.Submonoid
/-!
# Construct ordered groups from groups with a specified positive cone.
-In this file we provide the structure `GroupCone` and the predicate `IsMaxCone`
-that encode axioms of `OrderedCommGroup` and `LinearOrderedCommGroup`
-in terms of the subset of non-negative elements.
+In this file we provide the structure `GroupCone` and the predicate `IsMaxCone` that encode
+the axioms of ordered groups in terms of the subset of non-negative elements.
We also provide constructors that convert between
cones in groups and the corresponding ordered groups.
diff --git a/Mathlib/Algebra/Order/Group/Cyclic.lean b/Mathlib/Algebra/Order/Group/Cyclic.lean
index 5741ab7e3a05ab..e8141a76b1bc79 100644
--- a/Mathlib/Algebra/Order/Group/Cyclic.lean
+++ b/Mathlib/Algebra/Order/Group/Cyclic.lean
@@ -15,7 +15,7 @@ This file contains basic results about cyclic linearly ordered groups and cyclic
linearly ordered groups.
The definitions `LinearOrderedCommGroup.Subgroup.genLTOne` (*resp.*
-`LinearOrderedCommGroup.genLTOone`) yields a generator of a non-trivial subgroup of a linearly
+`LinearOrderedCommGroup.genLTOne`) yields a generator of a non-trivial subgroup of a linearly
ordered commutative group with (*resp.* of a non-trivial linearly ordered commutative group) that
is strictly less than `1`. The corresponding additive definitions are also provided.
-/
diff --git a/Mathlib/Algebra/Order/GroupWithZero/Defs.lean b/Mathlib/Algebra/Order/GroupWithZero/Defs.lean
index 0c6e6304051e23..40e8ee9d4c90cd 100644
--- a/Mathlib/Algebra/Order/GroupWithZero/Defs.lean
+++ b/Mathlib/Algebra/Order/GroupWithZero/Defs.lean
@@ -31,7 +31,7 @@ We then provide statements and instances about these typeclasses not requiring `
or higher on the underlying type – those that do can be found in
`Mathlib/Algebra/Order/GroupWithZero/Unbundled/Basic.lean`.
-Less granular typeclasses like `OrderedAddCommMonoid` and `LinearOrderedField` should be enough for
+Less granular typeclasses like `IsOrderedAddMonoid` and `IsOrderedRing` should be enough for
most purposes, and the system is set up so that they imply the correct granular typeclasses here.
## Implications
@@ -53,10 +53,10 @@ The commonly used implications are:
* `posMulReflectLT_iff_mulPosReflectLT`
Furthermore, the bundled non-granular typeclasses imply the granular ones like so:
-* `OrderedSemiring → PosMulMono`
-* `OrderedSemiring → MulPosMono`
-* `StrictOrderedSemiring → PosMulStrictMono`
-* `StrictOrderedSemiring → MulPosStrictMono`
+* `IsOrderedRing → PosMulMono`
+* `IsOrderedRing → MulPosMono`
+* `IsStrictOrderedRing → PosMulStrictMono`
+* `IsStrictOrderedRing → MulPosStrictMono`
All these are registered as instances, which means that in practice you should not worry about these
implications. However, if you encounter a case where you think a statement is true but not covered
@@ -95,7 +95,7 @@ variable [Mul α] [Zero α] [Preorder α]
namely `a₁ ≤ a₂ → b * a₁ ≤ b * a₂` if `0 ≤ b`.
You should usually not use this very granular typeclass directly, but rather a typeclass like
-`OrderedSemiring`. -/
+`IsOrderedRing`. -/
@[mk_iff] class PosMulMono : Prop where
/-- Do not use this. Use `_root_.mul_le_mul_of_nonneg_left` instead. -/
protected mul_le_mul_of_nonneg_left ⦃a : α⦄ (ha : 0 ≤ a) ⦃b c : α⦄ (hbc : b ≤ c) : a * b ≤ a * c
@@ -104,7 +104,7 @@ You should usually not use this very granular typeclass directly, but rather a t
namely `a₁ < a₂ → b * a₁ < b * a₂` if `0 < b`.
You should usually not use this very granular typeclass directly, but rather a typeclass like
-`StrictOrderedSemiring`. -/
+`IsStrictOrderedRing`. -/
@[mk_iff] class PosMulStrictMono : Prop where
/-- Do not use this. Use `_root_.mul_lt_mul_of_pos_left` instead. -/
protected mul_lt_mul_of_pos_left ⦃a : α⦄ (ha : 0 < a) ⦃b c : α⦄ (hbc : b < c) : a * b < a * c
@@ -113,21 +113,21 @@ You should usually not use this very granular typeclass directly, but rather a t
the left, namely `b * a₁ < b * a₂ → a₁ < a₂` if `0 ≤ b`.
You should usually not use this very granular typeclass directly, but rather a typeclass like
-`LinearOrderedSemiring`. -/
+`IsStrictOrderedRing`. -/
@[mk_iff] class PosMulReflectLT : Prop extends ContravariantClass α≥0 α (fun x y => x * y) (· < ·)
/-- Typeclass for reverse monotonicity of multiplication by positive elements on the left,
namely `b * a₁ ≤ b * a₂ → a₁ ≤ a₂` if `0 < b`.
You should usually not use this very granular typeclass directly, but rather a typeclass like
-`LinearOrderedSemiring`. -/
+`IsStrictOrderedRing`. -/
@[mk_iff] class PosMulReflectLE : Prop extends ContravariantClass α>0 α (fun x y => x * y) (· ≤ ·)
/-- Typeclass for monotonicity of multiplication by nonnegative elements on the right,
namely `a₁ ≤ a₂ → a₁ * b ≤ a₂ * b` if `0 ≤ b`.
You should usually not use this very granular typeclass directly, but rather a typeclass like
-`OrderedSemiring`. -/
+`IsOrderedRing`. -/
@[mk_iff] class MulPosMono : Prop where
/-- Do not use this. Use `_root_.mul_le_mul_of_nonneg_right` instead. -/
protected mul_le_mul_of_nonneg_right ⦃c : α⦄ (hc : 0 ≤ c) ⦃a b : α⦄ (hab : a ≤ b) : a * c ≤ b * c
@@ -136,7 +136,7 @@ You should usually not use this very granular typeclass directly, but rather a t
namely `a₁ < a₂ → a₁ * b < a₂ * b` if `0 < b`.
You should usually not use this very granular typeclass directly, but rather a typeclass like
-`StrictOrderedSemiring`. -/
+`IsStrictOrderedRing`. -/
@[mk_iff] class MulPosStrictMono : Prop where
/-- Do not use this. Use `_root_.mul_lt_mul_of_pos_right` instead. -/
protected mul_lt_mul_of_pos_right ⦃c : α⦄ (hc : 0 < c) ⦃a b : α⦄ (hab : a < b) : a * c < b * c
@@ -145,14 +145,14 @@ You should usually not use this very granular typeclass directly, but rather a t
the right, namely `a₁ * b < a₂ * b → a₁ < a₂` if `0 ≤ b`.
You should usually not use this very granular typeclass directly, but rather a typeclass like
-`LinearOrderedSemiring`. -/
+`IsStrictOrderedRing`. -/
@[mk_iff] class MulPosReflectLT : Prop extends ContravariantClass α≥0 α (fun x y => y * x) (· < ·)
/-- Typeclass for reverse monotonicity of multiplication by positive elements on the right,
namely `a₁ * b ≤ a₂ * b → a₁ ≤ a₂` if `0 < b`.
You should usually not use this very granular typeclass directly, but rather a typeclass like
-`LinearOrderedSemiring`. -/
+`IsStrictOrderedRing`. -/
@[mk_iff] class MulPosReflectLE : Prop extends ContravariantClass α>0 α (fun x y => y * x) (· ≤ ·)
end Abbreviations
diff --git a/Mathlib/Algebra/Order/Hom/Monoid.lean b/Mathlib/Algebra/Order/Hom/Monoid.lean
index 16e48a14aaba0f..e9156ad8f34a22 100644
--- a/Mathlib/Algebra/Order/Hom/Monoid.lean
+++ b/Mathlib/Algebra/Order/Hom/Monoid.lean
@@ -67,7 +67,7 @@ variable {F α β γ δ : Type*}
section AddMonoid
-/-- `α →+o β` is the type of monotone functions `α → β` that preserve the `OrderedAddCommMonoid`
+/-- `α →+o β` is the type of monotone functions `α → β` that preserve the ordered additive monoid
structure.
`OrderAddMonoidHom` is also used for ordered group homomorphisms.
@@ -83,7 +83,7 @@ structure OrderAddMonoidHom (α β : Type*) [Preorder α] [Preorder β] [AddZero
/-- Infix notation for `OrderAddMonoidHom`. -/
infixr:25 " →+o " => OrderAddMonoidHom
-/-- `α ≃+o β` is the type of monotone isomorphisms `α ≃ β` that preserve the `OrderedAddCommMonoid`
+/-- `α ≃+o β` is the type of isomorphisms `α ≃ β` that preserve the ordered additive monoid
structure.
`OrderAddMonoidIso` is also used for ordered group isomorphisms.
@@ -104,7 +104,7 @@ end AddMonoid
section Monoid
-/-- `α →*o β` is the type of functions `α → β` that preserve the `OrderedCommMonoid` structure.
+/-- `α →*o β` is the type of functions `α → β` that preserve the ordered monoid structure.
`OrderMonoidHom` is also used for ordered group homomorphisms.
@@ -139,7 +139,7 @@ def OrderMonoidHomClass.toOrderMonoidHom [OrderHomClass F α β] [MonoidHomClass
instance [OrderHomClass F α β] [MonoidHomClass F α β] : CoeTC F (α →*o β) :=
⟨OrderMonoidHomClass.toOrderMonoidHom⟩
-/-- `α ≃*o β` is the type of isomorphisms `α ≃ β` that preserve the `OrderedCommMonoid` structure.
+/-- `α ≃*o β` is the type of isomorphisms `α ≃ β` that preserve the ordered monoid structure.
`OrderMonoidIso` is also used for ordered group isomorphisms.
diff --git a/Mathlib/Algebra/Order/Interval/Set/Monoid.lean b/Mathlib/Algebra/Order/Interval/Set/Monoid.lean
index 3e3f8e1a066673..d272521e25c660 100644
--- a/Mathlib/Algebra/Order/Interval/Set/Monoid.lean
+++ b/Mathlib/Algebra/Order/Interval/Set/Monoid.lean
@@ -16,8 +16,8 @@ public import Mathlib.Algebra.Order.Monoid.Unbundled.ExistsOfLE
The lemmas in this file state that addition maps intervals bijectively. The typeclass
`ExistsAddOfLE` is defined specifically to make them work when combined with
-`OrderedCancelAddCommMonoid`; the lemmas below therefore apply to all
-`OrderedAddCommGroup`, but also to `ℕ` and `ℝ≥0`, which are not groups.
+`IsOrderedCancelAddMonoid`; the lemmas below therefore apply to all ordered groups,
+but also to `ℕ` and `ℝ≥0`, which are not groups.
-/
public section
diff --git a/Mathlib/Algebra/Order/Monoid/Canonical/Defs.lean b/Mathlib/Algebra/Order/Monoid/Canonical/Defs.lean
index cdc0baaf93f60f..c94ab7346b57cf 100644
--- a/Mathlib/Algebra/Order/Monoid/Canonical/Defs.lean
+++ b/Mathlib/Algebra/Order/Monoid/Canonical/Defs.lean
@@ -23,14 +23,14 @@ universe u
variable {α : Type u}
/-- An ordered additive monoid is `CanonicallyOrderedAdd`
- if the ordering coincides with the subtractibility relation,
- which is to say, `a ≤ b` iff there exists `c` with `b = a + c`.
- This is satisfied by the natural numbers, for example, but not
- the integers or other nontrivial `OrderedAddCommGroup`s.
-
- We have `a ≤ b + a` and `a ≤ a + b` as separate fields. In the commutative case the second field
- is redundant, but in the noncommutative case (satisfied most relevantly by the ordinals), this
- extra field allows us to prove more things without the extra commutativity assumption. -/
+if the ordering coincides with the subtractibility relation,
+which is to say, `a ≤ b` iff there exists `c` with `b = a + c`.
+This is satisfied by the natural numbers, for example, but not
+the integers or other nontrivial ordered groups.
+
+We have `a ≤ b + a` and `a ≤ a + b` as separate fields. In the commutative case the second field
+is redundant, but in the noncommutative case (satisfied most relevantly by the ordinals), this
+extra field allows us to prove more things without the extra commutativity assumption. -/
class CanonicallyOrderedAdd (α : Type*) [Add α] [LE α] : Prop
extends ExistsAddOfLE α where
/-- For any `a` and `b`, `a ≤ a + b` -/
diff --git a/Mathlib/Algebra/Order/Monoid/Unbundled/Defs.lean b/Mathlib/Algebra/Order/Monoid/Unbundled/Defs.lean
index f20efe7851731b..0e4a1637743fee 100644
--- a/Mathlib/Algebra/Order/Monoid/Unbundled/Defs.lean
+++ b/Mathlib/Algebra/Order/Monoid/Unbundled/Defs.lean
@@ -29,9 +29,9 @@ Since `Co(ntra)variantClass` takes as input the operation (typically `(+)` or `(
relation (typically `(≤)` or `(<)`), these are the only two typeclasses that I have used.
The general approach is to formulate the lemma that you are interested in and prove it, with the
-`Ordered[...]` typeclass of your liking. After that, you convert the single typeclass,
-say `[OrderedCancelMonoid M]`, into three typeclasses, e.g.
-`[CancelMonoid M] [PartialOrder M] [CovariantClass M M (Function.swap (*)) (≤)]`
+`IsOrdered[...]` typeclass of your liking. After that, you convert the typeclass,
+say `[IsOrderedCancelMonoid M]`, into whichever typeclasses, e.g.
+`[CovariantClass M M (Function.swap (*)) (≤)]`
and have a go at seeing if the proof still works!
Note that it is possible to combine several `Co(ntra)variantClass` assumptions together.
@@ -54,6 +54,9 @@ typeclass assumptions, since `Function.swap` is slightly better behaved than `fl
However, sometimes as a **non-typeclass** assumption, we prefer `flip (*)` (or `flip (+)`),
as it is easier to use.
+## TODO
+
+This is unergonomic. Inline in `MulLeftMono` and friends.
-/
@[expose] public section
@@ -125,7 +128,7 @@ class ContravariantClass : Prop where
namely `b₁ ≤ b₂ → a * b₁ ≤ a * b₂`.
You should usually not use this very granular typeclass directly, but rather a typeclass like
-`OrderedCommMonoid`. -/
+`IsOrderedMonoid`. -/
abbrev MulLeftMono [Mul M] [LE M] : Prop :=
CovariantClass M M (· * ·) (· ≤ ·)
@@ -133,7 +136,7 @@ abbrev MulLeftMono [Mul M] [LE M] : Prop :=
namely `a₁ ≤ a₂ → a₁ * b ≤ a₂ * b`.
You should usually not use this very granular typeclass directly, but rather a typeclass like
-`OrderedCommMonoid`. -/
+`IsOrderedMonoid`. -/
abbrev MulRightMono [Mul M] [LE M] : Prop :=
CovariantClass M M (swap (· * ·)) (· ≤ ·)
@@ -141,7 +144,7 @@ abbrev MulRightMono [Mul M] [LE M] : Prop :=
namely `b₁ ≤ b₂ → a + b₁ ≤ a + b₂`.
You should usually not use this very granular typeclass directly, but rather a typeclass like
-`OrderedAddCommMonoid`. -/
+`IsOrderedAddMonoid`. -/
abbrev AddLeftMono [Add M] [LE M] : Prop :=
CovariantClass M M (· + ·) (· ≤ ·)
@@ -149,7 +152,7 @@ abbrev AddLeftMono [Add M] [LE M] : Prop :=
namely `a₁ ≤ a₂ → a₁ + b ≤ a₂ + b`.
You should usually not use this very granular typeclass directly, but rather a typeclass like
-`OrderedAddCommMonoid`. -/
+`IsOrderedAddMonoid`. -/
abbrev AddRightMono [Add M] [LE M] : Prop :=
CovariantClass M M (swap (· + ·)) (· ≤ ·)
@@ -159,7 +162,7 @@ attribute [to_additive existing] MulLeftMono MulRightMono
namely `b₁ < b₂ → a * b₁ < a * b₂`.
You should usually not use this very granular typeclass directly, but rather a typeclass like
-`OrderedCommGroup`. -/
+`IsOrderedMonoid`. -/
abbrev MulLeftStrictMono [Mul M] [LT M] : Prop :=
CovariantClass M M (· * ·) (· < ·)
@@ -167,7 +170,7 @@ abbrev MulLeftStrictMono [Mul M] [LT M] : Prop :=
namely `a₁ < a₂ → a₁ * b < a₂ * b`.
You should usually not use this very granular typeclass directly, but rather a typeclass like
-`OrderedCommGroup`. -/
+`IsOrderedMonoid`. -/
abbrev MulRightStrictMono [Mul M] [LT M] : Prop :=
CovariantClass M M (swap (· * ·)) (· < ·)
@@ -175,7 +178,7 @@ abbrev MulRightStrictMono [Mul M] [LT M] : Prop :=
namely `b₁ < b₂ → a + b₁ < a + b₂`.
You should usually not use this very granular typeclass directly, but rather a typeclass like
-`OrderedAddCommGroup`. -/
+`IsOrderedAddMonoid`. -/
abbrev AddLeftStrictMono [Add M] [LT M] : Prop :=
CovariantClass M M (· + ·) (· < ·)
@@ -183,7 +186,7 @@ abbrev AddLeftStrictMono [Add M] [LT M] : Prop :=
namely `a₁ < a₂ → a₁ + b < a₂ + b`.
You should usually not use this very granular typeclass directly, but rather a typeclass like
-`OrderedAddCommGroup`. -/
+`IsOrderedAddMonoid`. -/
abbrev AddRightStrictMono [Add M] [LT M] : Prop :=
CovariantClass M M (swap (· + ·)) (· < ·)
@@ -193,7 +196,7 @@ attribute [to_additive existing] MulLeftStrictMono MulRightStrictMono
namely `a * b₁ < a * b₂ → b₁ < b₂`.
You should usually not use this very granular typeclass directly, but rather a typeclass like
-`OrderedCommGroup`. -/
+`IsOrderedMonoid`. -/
abbrev MulLeftReflectLT [Mul M] [LT M] : Prop :=
ContravariantClass M M (· * ·) (· < ·)
@@ -201,7 +204,7 @@ abbrev MulLeftReflectLT [Mul M] [LT M] : Prop :=
namely `a₁ * b < a₂ * b → a₁ < a₂`.
You should usually not use this very granular typeclass directly, but rather a typeclass like
-`OrderedCommGroup`. -/
+`IsOrderedMonoid`. -/
abbrev MulRightReflectLT [Mul M] [LT M] : Prop :=
ContravariantClass M M (swap (· * ·)) (· < ·)
@@ -209,7 +212,7 @@ abbrev MulRightReflectLT [Mul M] [LT M] : Prop :=
namely `a + b₁ < a + b₂ → b₁ < b₂`.
You should usually not use this very granular typeclass directly, but rather a typeclass like
-`OrderedAddCommGroup`. -/
+`IsOrderedAddMonoid`. -/
abbrev AddLeftReflectLT [Add M] [LT M] : Prop :=
ContravariantClass M M (· + ·) (· < ·)
@@ -217,7 +220,7 @@ abbrev AddLeftReflectLT [Add M] [LT M] : Prop :=
namely `a₁ * b < a₂ * b → a₁ < a₂`.
You should usually not use this very granular typeclass directly, but rather a typeclass like
-`OrderedAddCommGroup`. -/
+`IsOrderedAddMonoid`. -/
abbrev AddRightReflectLT [Add M] [LT M] : Prop :=
ContravariantClass M M (swap (· + ·)) (· < ·)
@@ -227,7 +230,7 @@ attribute [to_additive existing] MulLeftReflectLT MulRightReflectLT
namely `a * b₁ ≤ a * b₂ → b₁ ≤ b₂`.
You should usually not use this very granular typeclass directly, but rather a typeclass like
-`OrderedCancelCommMonoid`. -/
+`IsOrderedCancelMonoid`. -/
class MulLeftReflectLE [Mul M] [LE M] : Prop where
/-- Do not use this. Use `le_of_mul_le_mul_left'` instead. -/
protected le_of_mul_le_mul_left' {a b₁ b₂ : M} : a * b₁ ≤ a * b₂ → b₁ ≤ b₂
@@ -236,7 +239,7 @@ class MulLeftReflectLE [Mul M] [LE M] : Prop where
namely `a₁ * b ≤ a₂ * b → a₁ ≤ a₂`.
You should usually not use this very granular typeclass directly, but rather a typeclass like
-`OrderedCancelCommMonoid`. -/
+`IsOrderedCancelMonoid`. -/
class MulRightReflectLE [Mul M] [LE M] : Prop where
/-- Do not use this. Use `le_of_mul_le_mul_right'` instead. -/
protected le_of_mul_le_mul_right' {b a₁ a₂ : M} : a₁ * b ≤ a₂ * b → a₁ ≤ a₂
@@ -245,7 +248,7 @@ class MulRightReflectLE [Mul M] [LE M] : Prop where
namely `a + b₁ ≤ a + b₂ → b₁ ≤ b₂`.
You should usually not use this very granular typeclass directly, but rather a typeclass like
-`OrderedCancelAddCommMonoid`. -/
+`IsOrderedCancelAddMonoid`. -/
class AddLeftReflectLE [Add M] [LE M] : Prop where
/-- Do not use this. Use `le_of_add_le_add_left` instead. -/
protected le_of_add_le_add_left {a b₁ b₂ : M} : a + b₁ ≤ a + b₂ → b₁ ≤ b₂
@@ -254,7 +257,7 @@ class AddLeftReflectLE [Add M] [LE M] : Prop where
namely `a₁ + b ≤ a₂ + b → a₁ ≤ a₂`.
You should usually not use this very granular typeclass directly, but rather a typeclass like
-`OrderedCancelAddCommMonoid`. -/
+`IsOrderedCancelAddMonoid`. -/
class AddRightReflectLE [Add M] [LE M] : Prop where
/-- Do not use this. Use `le_of_add_le_add_right` instead. -/
protected le_of_add_le_add_right {b a₁ a₂ : M} : a₁ + b ≤ a₂ + b → a₁ ≤ a₂
diff --git a/Mathlib/Algebra/Order/Monoid/Unbundled/ExistsOfLE.lean b/Mathlib/Algebra/Order/Monoid/Unbundled/ExistsOfLE.lean
index 81459510a72dc9..506961d65399eb 100644
--- a/Mathlib/Algebra/Order/Monoid/Unbundled/ExistsOfLE.lean
+++ b/Mathlib/Algebra/Order/Monoid/Unbundled/ExistsOfLE.lean
@@ -22,16 +22,16 @@ public section
universe u
variable {α : Type u}
-/-- An `OrderedAddCommMonoid` with one-sided 'subtraction' in the sense that
+/-- An ordered additive monoid with one-sided 'subtraction' in the sense that
if `a ≤ b`, then there is some `c` for which `a + c = b`. This is a weaker version
-of the condition on canonical orderings defined by `CanonicallyOrderedAddCommMonoid`. -/
+of the condition on canonical orderings defined by `CanonicallyOrderedAdd`. -/
class ExistsAddOfLE (α : Type u) [Add α] [LE α] : Prop where
/-- For `a ≤ b`, there is a `c` so `b = a + c`. -/
exists_add_of_le : ∀ {a b : α}, a ≤ b → ∃ c : α, b = a + c
-/-- An `OrderedCommMonoid` with one-sided 'division' in the sense that
+/-- An ordered monoid with one-sided 'division' in the sense that
if `a ≤ b`, there is some `c` for which `a * c = b`. This is a weaker version
-of the condition on canonical orderings defined by `CanonicallyOrderedCommMonoid`. -/
+of the condition on canonical orderings defined by `CanonicallyOrderedMul`. -/
@[to_additive]
class ExistsMulOfLE (α : Type u) [Mul α] [LE α] : Prop where
/-- For `a ≤ b`, `a` left divides `b` -/
diff --git a/Mathlib/Algebra/Order/Nonneg/Field.lean b/Mathlib/Algebra/Order/Nonneg/Field.lean
index e78440221e8537..3e1f5c8136cdfb 100644
--- a/Mathlib/Algebra/Order/Nonneg/Field.lean
+++ b/Mathlib/Algebra/Order/Nonneg/Field.lean
@@ -18,10 +18,6 @@ This file defines instances and prove some properties about the nonnegative elem
`{x : α // 0 ≤ x}` of an arbitrary type `α`.
This is used to derive algebraic structures on `ℝ≥0` and `ℚ≥0` automatically.
-
-## Main declarations
-
-* `{x : α // 0 ≤ x}` is a `CanonicallyLinearOrderedSemifield` if `α` is a `LinearOrderedField`.
-/
@[expose] public section
diff --git a/Mathlib/Algebra/Order/Nonneg/Ring.lean b/Mathlib/Algebra/Order/Nonneg/Ring.lean
index 3a34a803638f25..7bed53c24a457c 100644
--- a/Mathlib/Algebra/Order/Nonneg/Ring.lean
+++ b/Mathlib/Algebra/Order/Nonneg/Ring.lean
@@ -21,10 +21,6 @@ Currently we only state instances and states some `simp`/`norm_cast` lemmas.
When `α` is `ℝ`, this will give us some properties about `ℝ≥0`.
-## Main declarations
-
-* `{x : α // 0 ≤ x}` is a `CanonicallyLinearOrderedAddCommMonoid` if `α` is a `LinearOrderedRing`.
-
## Implementation Notes
Instead of `{x : α // 0 ≤ x}` we could also use `Set.Ici (0 : α)`, which is definitionally equal.
diff --git a/Mathlib/Algebra/Order/Positive/Ring.lean b/Mathlib/Algebra/Order/Positive/Ring.lean
index cf3eef98b85bcc..a445ed2d6e8bc0 100644
--- a/Mathlib/Algebra/Order/Positive/Ring.lean
+++ b/Mathlib/Algebra/Order/Positive/Ring.lean
@@ -12,7 +12,7 @@ public import Mathlib.Tactic.FastInstance
/-!
# Algebraic structures on the set of positive numbers
-In this file we define various instances (`AddSemigroup`, `OrderedCommMonoid` etc) on the
+In this file we define various instances (`AddSemigroup`, `IsOrderedMonoid` etc) on the
type `{x : R // 0 < x}`. In each case we try to require the weakest possible typeclass
assumptions on `R` but possibly, there is a room for improvements.
-/
diff --git a/Mathlib/Algebra/Order/Ring/Basic.lean b/Mathlib/Algebra/Order/Ring/Basic.lean
index e6787f71924f37..6bd5827d8ae773 100644
--- a/Mathlib/Algebra/Order/Ring/Basic.lean
+++ b/Mathlib/Algebra/Order/Ring/Basic.lean
@@ -96,11 +96,10 @@ def IsNonarchimedean {α : Type*} [Add α] (f : α → R) : Prop := ∀ a b : α
/-!
### Lemmas for canonically linear ordered semirings or linear ordered rings
-The slightly unusual typeclass assumptions `[LinearOrderedSemiring R] [ExistsAddOfLE R]` cover two
+The slightly unusual typeclass assumptions `[IsStrictOrderedRing R] [ExistsAddOfLE R]` cover two
more familiar settings:
-* `[LinearOrderedRing R]`, e.g. `ℤ`, `ℚ` or `ℝ`
-* `[CanonicallyLinearOrderedSemiring R]` (although we don't actually have this typeclass), e.g. `ℕ`,
- `ℚ≥0` or `ℝ≥0`
+* linearly ordered rings, e.g. `ℤ`, `ℚ` or `ℝ`
+* canonically ordered semirings, e.g. `ℕ`, `ℚ≥0` or `ℝ≥0`
-/
variable [ExistsAddOfLE R]
diff --git a/Mathlib/Algebra/Order/Ring/Cone.lean b/Mathlib/Algebra/Order/Ring/Cone.lean
index 6a25aeef9afe6f..087733faa3c575 100644
--- a/Mathlib/Algebra/Order/Ring/Cone.lean
+++ b/Mathlib/Algebra/Order/Ring/Cone.lean
@@ -11,8 +11,7 @@ public import Mathlib.Algebra.Ring.Subsemiring.Order
/-!
# Construct ordered rings from rings with a specified positive cone.
-In this file we provide the structure `RingCone`
-that encodes axioms of `OrderedRing` and `LinearOrderedRing`
+In this file we provide the structure `RingCone` that encodes axioms of ordered rings
in terms of the subset of non-negative elements.
We also provide constructors that convert between
diff --git a/Mathlib/Algebra/Order/Ring/Defs.lean b/Mathlib/Algebra/Order/Ring/Defs.lean
index 45ff82dcc6f8ca..920929518e66b2 100644
--- a/Mathlib/Algebra/Order/Ring/Defs.lean
+++ b/Mathlib/Algebra/Order/Ring/Defs.lean
@@ -34,24 +34,10 @@ For short,
## Typeclasses
-* `OrderedSemiring`: Semiring with a partial order such that `+` and `*` respect `≤`.
-* `StrictOrderedSemiring`: Nontrivial semiring with a partial order such that `+` and `*` respects
- `<`.
-* `OrderedCommSemiring`: Commutative semiring with a partial order such that `+` and `*` respect
- `≤`.
-* `StrictOrderedCommSemiring`: Nontrivial commutative semiring with a partial order such that `+`
- and `*` respect `<`.
-* `OrderedRing`: Ring with a partial order such that `+` respects `≤` and `*` respects `<`.
-* `OrderedCommRing`: Commutative ring with a partial order such that `+` respects `≤` and
- `*` respects `<`.
-* `LinearOrderedSemiring`: Nontrivial semiring with a linear order such that `+` respects `≤` and
- `*` respects `<`.
-* `LinearOrderedCommSemiring`: Nontrivial commutative semiring with a linear order such that `+`
- respects `≤` and `*` respects `<`.
-* `LinearOrderedRing`: Nontrivial ring with a linear order such that `+` respects `≤` and `*`
- respects `<`.
-* `LinearOrderedCommRing`: Nontrivial commutative ring with a linear order such that `+` respects
- `≤` and `*` respects `<`.
+* `IsOrderedRing`: Semiring with a partial order such that addition and multiplication by a
+ nonnegative number are both monotone.
+* `IsStrictOrderedRing`: Nontrivial semiring with a partial order such that addition and
+ multiplication by a positive number are both strictly monotone.
## Hierarchy
@@ -59,49 +45,13 @@ The hardest part of proving order lemmas might be to figure out the correct gene
corresponding typeclass. Here's an attempt at demystifying it. For each typeclass, we list its
immediate predecessors and what conditions are added to each of them.
-* `OrderedSemiring`
- - `OrderedAddCommMonoid` & multiplication & `*` respects `≤`
- - `Semiring` & partial order structure & `+` respects `≤` & `*` respects `≤`
-* `StrictOrderedSemiring`
- - `OrderedCancelAddCommMonoid` & multiplication & `*` respects `<` & nontriviality
- - `OrderedSemiring` & `+` respects `<` & `*` respects `<` & nontriviality
-* `OrderedCommSemiring`
- - `OrderedSemiring` & commutativity of multiplication
- - `CommSemiring` & partial order structure & `+` respects `≤` & `*` respects `<`
-* `StrictOrderedCommSemiring`
- - `StrictOrderedSemiring` & commutativity of multiplication
- - `OrderedCommSemiring` & `+` respects `<` & `*` respects `<` & nontriviality
-* `OrderedRing`
- - `OrderedSemiring` & additive inverses
- - `OrderedAddCommGroup` & multiplication & `*` respects `<`
- - `Ring` & partial order structure & `+` respects `≤` & `*` respects `<`
-* `StrictOrderedRing`
- - `StrictOrderedSemiring` & additive inverses
- - `OrderedSemiring` & `+` respects `<` & `*` respects `<` & nontriviality
-* `OrderedCommRing`
- - `OrderedRing` & commutativity of multiplication
- - `OrderedCommSemiring` & additive inverses
- - `CommRing` & partial order structure & `+` respects `≤` & `*` respects `<`
-* `StrictOrderedCommRing`
- - `StrictOrderedCommSemiring` & additive inverses
- - `StrictOrderedRing` & commutativity of multiplication
- - `OrderedCommRing` & `+` respects `<` & `*` respects `<` & nontriviality
-* `LinearOrderedSemiring`
- - `StrictOrderedSemiring` & totality of the order
- - `LinearOrderedAddCommMonoid` & multiplication & nontriviality & `*` respects `<`
-* `LinearOrderedCommSemiring`
- - `StrictOrderedCommSemiring` & totality of the order
- - `LinearOrderedSemiring` & commutativity of multiplication
-* `LinearOrderedRing`
- - `StrictOrderedRing` & totality of the order
- - `LinearOrderedSemiring` & additive inverses
- - `LinearOrderedAddCommGroup` & multiplication & `*` respects `<`
- - `Ring` & `IsDomain` & linear order structure
-* `LinearOrderedCommRing`
- - `StrictOrderedCommRing` & totality of the order
- - `LinearOrderedRing` & commutativity of multiplication
- - `LinearOrderedCommSemiring` & additive inverses
- - `CommRing` & `IsDomain` & linear order structure
+* `PartialOrder` + `Semiring` + `IsOrderedRing`
+ - `IsOrderedAddMonoid` & multiplication & `*` respects `≤`
+* `PartialOrder` + `Semiring` + `IsStrictOrderedRing`
+ - `IsOrderedCancelAddMonoid` & multiplication & `*` respects `<` & nontriviality
+* `LinearOrder` + `Ring` + `IsOrderedRing`
+ - `IsStrictOrderedRing` & totality of the order
+ - `IsDomain` & linear order structure
-/
public section
diff --git a/Mathlib/Algebra/Order/Ring/Unbundled/Basic.lean b/Mathlib/Algebra/Order/Ring/Unbundled/Basic.lean
index 5a8efc07831acf..3fa36da6ea7b5b 100644
--- a/Mathlib/Algebra/Order/Ring/Unbundled/Basic.lean
+++ b/Mathlib/Algebra/Order/Ring/Unbundled/Basic.lean
@@ -21,97 +21,14 @@ public import Mathlib.Tactic.Tauto
This file develops the basics of ordered (semi)rings in an unbundled fashion for later use with
the bundled classes from `Mathlib/Algebra/Order/Ring/Defs.lean`.
-The set of typeclass variables here comprises
-* an algebraic class (`Semiring`, `CommSemiring`, `Ring`, `CommRing`)
-* an order class (`PartialOrder`, `LinearOrder`)
-* assumptions on how both interact ((strict) monotonicity, canonicity)
-
-For short,
-* "`+` respects `≤`" means "monotonicity of addition"
-* "`+` respects `<`" means "strict monotonicity of addition"
-* "`*` respects `≤`" means "monotonicity of multiplication by a nonnegative number".
-* "`*` respects `<`" means "strict monotonicity of multiplication by a positive number".
-
-## Typeclasses found in `Algebra.Order.Ring.Defs`
-
-* `OrderedSemiring`: Semiring with a partial order such that `+` and `*` respect `≤`.
-* `StrictOrderedSemiring`: Nontrivial semiring with a partial order such that `+` and `*` respects
- `<`.
-* `OrderedCommSemiring`: Commutative semiring with a partial order such that `+` and `*` respect
- `≤`.
-* `StrictOrderedCommSemiring`: Nontrivial commutative semiring with a partial order such that `+`
- and `*` respect `<`.
-* `OrderedRing`: Ring with a partial order such that `+` respects `≤` and `*` respects `<`.
-* `OrderedCommRing`: Commutative ring with a partial order such that `+` respects `≤` and
- `*` respects `<`.
-* `LinearOrderedSemiring`: Nontrivial semiring with a linear order such that `+` respects `≤` and
- `*` respects `<`.
-* `LinearOrderedCommSemiring`: Nontrivial commutative semiring with a linear order such that `+`
- respects `≤` and `*` respects `<`.
-* `LinearOrderedRing`: Nontrivial ring with a linear order such that `+` respects `≤` and `*`
- respects `<`.
-* `LinearOrderedCommRing`: Nontrivial commutative ring with a linear order such that `+` respects
- `≤` and `*` respects `<`.
-* `CanonicallyOrderedCommSemiring`: Commutative semiring with a partial order such that `+`
- respects `≤`, `*` respects `<`, and `a ≤ b ↔ ∃ c, b = a + c`.
-
-## Hierarchy
-
-The hardest part of proving order lemmas might be to figure out the correct generality and its
-corresponding typeclass. Here's an attempt at demystifying it. For each typeclass, we list its
-immediate predecessors and what conditions are added to each of them.
-
-* `OrderedSemiring`
- - `OrderedAddCommMonoid` & multiplication & `*` respects `≤`
- - `Semiring` & partial order structure & `+` respects `≤` & `*` respects `≤`
-* `StrictOrderedSemiring`
- - `OrderedCancelAddCommMonoid` & multiplication & `*` respects `<` & nontriviality
- - `OrderedSemiring` & `+` respects `<` & `*` respects `<` & nontriviality
-* `OrderedCommSemiring`
- - `OrderedSemiring` & commutativity of multiplication
- - `CommSemiring` & partial order structure & `+` respects `≤` & `*` respects `<`
-* `StrictOrderedCommSemiring`
- - `StrictOrderedSemiring` & commutativity of multiplication
- - `OrderedCommSemiring` & `+` respects `<` & `*` respects `<` & nontriviality
-* `OrderedRing`
- - `OrderedSemiring` & additive inverses
- - `OrderedAddCommGroup` & multiplication & `*` respects `<`
- - `Ring` & partial order structure & `+` respects `≤` & `*` respects `<`
-* `StrictOrderedRing`
- - `StrictOrderedSemiring` & additive inverses
- - `OrderedSemiring` & `+` respects `<` & `*` respects `<` & nontriviality
-* `OrderedCommRing`
- - `OrderedRing` & commutativity of multiplication
- - `OrderedCommSemiring` & additive inverses
- - `CommRing` & partial order structure & `+` respects `≤` & `*` respects `<`
-* `StrictOrderedCommRing`
- - `StrictOrderedCommSemiring` & additive inverses
- - `StrictOrderedRing` & commutativity of multiplication
- - `OrderedCommRing` & `+` respects `<` & `*` respects `<` & nontriviality
-* `LinearOrderedSemiring`
- - `StrictOrderedSemiring` & totality of the order
- - `LinearOrderedAddCommMonoid` & multiplication & nontriviality & `*` respects `<`
-* `LinearOrderedCommSemiring`
- - `StrictOrderedCommSemiring` & totality of the order
- - `LinearOrderedSemiring` & commutativity of multiplication
-* `LinearOrderedRing`
- - `StrictOrderedRing` & totality of the order
- - `LinearOrderedSemiring` & additive inverses
- - `LinearOrderedAddCommGroup` & multiplication & `*` respects `<`
- - `Ring` & `IsDomain` & linear order structure
-* `LinearOrderedCommRing`
- - `StrictOrderedCommRing` & totality of the order
- - `LinearOrderedRing` & commutativity of multiplication
- - `LinearOrderedCommSemiring` & additive inverses
- - `CommRing` & `IsDomain` & linear order structure
-
## Generality
Each section is labelled with a corresponding bundled ordered ring typeclass in mind. Mixins for
relating the order structures and ring structures are added as needed.
-TODO: the mixin assumptions can be relaxed in most cases
+## TODO
+The mixin assumptions can be relaxed in most cases.
-/
public section
@@ -581,7 +498,7 @@ theorem mul_nonneg_iff [ExistsAddOfLE R] [MulPosStrictMono R] [PosMulStrictMono
⟨nonneg_and_nonneg_or_nonpos_and_nonpos_of_mul_nonneg, fun h =>
h.elim (and_imp.2 mul_nonneg) (and_imp.2 mul_nonneg_of_nonpos_of_nonpos)⟩
-/-- Out of three elements of a `LinearOrderedRing`, two must have the same sign. -/
+/-- Out of three elements of a linearly ordered semiring, two must have the same sign. -/
theorem mul_nonneg_of_three [ExistsAddOfLE R] [MulPosStrictMono R] [PosMulStrictMono R]
[AddLeftMono R] [AddLeftReflectLE R]
(a b c : R) : 0 ≤ a * b ∨ 0 ≤ b * c ∨ 0 ≤ c * a := by
diff --git a/Mathlib/Algebra/Order/Star/Basic.lean b/Mathlib/Algebra/Order/Star/Basic.lean
index 99fa9e39b070f5..9aba3e1e74e5d3 100644
--- a/Mathlib/Algebra/Order/Star/Basic.lean
+++ b/Mathlib/Algebra/Order/Star/Basic.lean
@@ -29,8 +29,8 @@ rather the entire `≤` relation with `StarOrderedRing.le_iff`. However, notice
`NonUnitalRing`, these are equivalent (see `StarOrderedRing.nonneg_iff` and
`StarOrderedRing.of_nonneg_iff`).
-It is important to note that while a `StarOrderedRing` is an `OrderedAddCommMonoid` it is often
-*not* an `OrderedSemiring`.
+It is important to note that while a `StarOrderedRing` often satisfies `IsOrderedAddMonoid`,
+it usually does *not* satisfy `IsOrderedRing`.
## TODO
diff --git a/Mathlib/Algebra/Order/Sub/Basic.lean b/Mathlib/Algebra/Order/Sub/Basic.lean
index d518c3bd07dab7..9882f36b04bb39 100644
--- a/Mathlib/Algebra/Order/Sub/Basic.lean
+++ b/Mathlib/Algebra/Order/Sub/Basic.lean
@@ -191,7 +191,7 @@ theorem tsub_add_min : a - b + min a b = a := by
rw [← tsub_min, @tsub_add_cancel_of_le]
apply min_le_left
--- `Odd.tsub` requires `CanonicallyLinearOrderedSemiring`, which we don't have
+-- TODO: Should we introduce `Odd.tsub`? It will probably only be used by `ℕ`.
lemma Even.tsub [AddLeftReflectLE α] {m n : α} (hm : Even m) (hn : Even n) :
Even (m - n) := by
obtain ⟨a, rfl⟩ := hm
diff --git a/Mathlib/Algebra/Order/Sub/WithTop.lean b/Mathlib/Algebra/Order/Sub/WithTop.lean
index 4139459a3b5b07..e2805019755413 100644
--- a/Mathlib/Algebra/Order/Sub/WithTop.lean
+++ b/Mathlib/Algebra/Order/Sub/WithTop.lean
@@ -17,8 +17,8 @@ the bottom element is zero.
Note that there is another subtraction on objects of the form `WithTop α` in the file
`Mathlib/Algebra/Order/AddGroupWithTop.lean`, setting `-⊤ = ⊤` as this corresponds to the
-additivization of the usual convention `0⁻¹ = 0` and is relevant in valuation theory. Since this
-other instance is only registered for `LinearOrderedAddCommGroup α` (which doesn't have a bottom
+additivization of the usual convention `0⁻¹ = 0` and is relevant in valuation theory. Since that
+other instance is only registered for `AddCommGroup α` (which doesn't have a bottom
element, unless the group is trivial), this shouldn't create diamonds.
-/
diff --git a/Mathlib/Algebra/Order/ToIntervalMod.lean b/Mathlib/Algebra/Order/ToIntervalMod.lean
index 0ac987cb2d166d..637edc191bba29 100644
--- a/Mathlib/Algebra/Order/ToIntervalMod.lean
+++ b/Mathlib/Algebra/Order/ToIntervalMod.lean
@@ -16,9 +16,8 @@ import Mathlib.GroupTheory.QuotientGroup.ModEq
/-!
# Reducing to an interval modulo its length
-This file defines operations that reduce a number (in an `Archimedean`
-`LinearOrderedAddCommGroup`) to a number in a given interval, modulo the length of that
-interval.
+This file defines operations that reduce a number (in an archimedean linearly ordered abelian group)
+to a number in a given interval, modulo the length of that interval.
## Main definitions
diff --git a/Mathlib/Algebra/Order/WithTop/Untop0.lean b/Mathlib/Algebra/Order/WithTop/Untop0.lean
index 5750f6c6b92df2..8310003823c8c4 100644
--- a/Mathlib/Algebra/Order/WithTop/Untop0.lean
+++ b/Mathlib/Algebra/Order/WithTop/Untop0.lean
@@ -86,18 +86,10 @@ lemma untop₀_neg [AddCommGroup α] : ∀ a : WithTop α, (-a).untop₀ = -a.un
| ⊤ => by simp
| (a : α) => rfl
-/-!
-## Simplifying Lemmas in cases where α is a MulZeroClass
--/
-
@[simp]
lemma untop₀_mul [DecidableEq α] [MulZeroClass α] (a b : WithTop α) :
(a * b).untop₀ = a.untop₀ * b.untop₀ := untopD_zero_mul a b
-/-!
-## Simplifying Lemmas in cases where α is an OrderedAddCommGroup
--/
-
section OrderedAddCommGroup
variable [AddCommGroup α] [PartialOrder α] {a b : WithTop α}
@@ -132,10 +124,6 @@ theorem untop₀_le_untop₀_iff (ha : a ≠ ⊤) (hb : b ≠ ⊤) :
end OrderedAddCommGroup
-/-!
-## Simplifying Lemmas in cases where α is a LinearOrderedAddCommGroup
--/
-
section LinearOrderedAddCommGroup
variable [AddCommGroup α] [LinearOrder α] {a b : WithTop α}
diff --git a/Mathlib/Analysis/Complex/Order.lean b/Mathlib/Analysis/Complex/Order.lean
index a9f974e34436db..179d46a06f6aba 100644
--- a/Mathlib/Analysis/Complex/Order.lean
+++ b/Mathlib/Analysis/Complex/Order.lean
@@ -13,9 +13,9 @@ public import Mathlib.Analysis.Complex.Norm
This order is defined by `z ≤ w ↔ z.re ≤ w.re ∧ z.im = w.im`.
This is a natural order on `ℂ` because, as is well-known, there does not exist an order on `ℂ`
-making it into a `LinearOrderedField`. However, the order described above is the canonical order
+making it into a linearly ordered field. However, the order described above is the canonical order
stemming from the structure of `ℂ` as a ⋆-ring (i.e., it becomes a `StarOrderedRing`). Moreover,
-with this order `ℂ` is a `StrictOrderedCommRing` and the coercion `(↑) : ℝ → ℂ` is an order
+with this order `ℂ` satisfies `IsStrictOrderedRing` and the coercion `(↑) : ℝ → ℂ` is an order
embedding.
This file only provides `Complex.partialOrder` and lemmas about it. Further structural classes are
diff --git a/Mathlib/Combinatorics/Pigeonhole.lean b/Mathlib/Combinatorics/Pigeonhole.lean
index 35207f37b09821..f76c77f931577d 100644
--- a/Mathlib/Combinatorics/Pigeonhole.lean
+++ b/Mathlib/Combinatorics/Pigeonhole.lean
@@ -87,7 +87,7 @@ variations of this theorem.
The principle is formalized in the following way, see
`Finset.exists_lt_sum_fiber_of_maps_to_of_nsmul_lt_sum`: if `f : α → β` is a function which maps all
elements of `s : Finset α` to `t : Finset β` and `#t • b < ∑ x ∈ s, w x`, where `w : α → M` is
-a weight function taking values in a `LinearOrderedCancelAddCommMonoid`, then for
+a weight function taking values in a linearly ordered cancellative monoid, then for
some `y ∈ t`, the sum of the weights of all `x ∈ s` such that `f x = y` is greater than `b`.
There are a few bits we can change in this theorem:
diff --git a/Mathlib/Data/Finsupp/Weight.lean b/Mathlib/Data/Finsupp/Weight.lean
index 19d680c975ef11..4c1fae64aabf15 100644
--- a/Mathlib/Data/Finsupp/Weight.lean
+++ b/Mathlib/Data/Finsupp/Weight.lean
@@ -31,9 +31,9 @@ as well as a function `w : σ → M`. (The important case is `R = ℕ`.)
- `Finsupp.le_weight` says that `f s ≤ f.weight w` when `M = ℕ`
- `Finsupp.le_weight_of_ne_zero` says that `w s ≤ f.weight w`
- for `OrderedAddCommMonoid M`, when `f s ≠ 0` and all `w i` are nonnegative.
+ for `IsOrderedAddMonoid M`, when `f s ≠ 0` and all `w i` are nonnegative.
-- `Finsupp.le_weight_of_ne_zero'` is the same statement for `CanonicallyOrderedAddCommMonoid M`.
+- `Finsupp.le_weight_of_ne_zero'` is the same statement for `CanonicallyOrderedAdd M`.
- `NonTorsionWeight`: all values `w s` are nontorsion in `M`.
diff --git a/Mathlib/Data/NNReal/Defs.lean b/Mathlib/Data/NNReal/Defs.lean
index 41e15f10ecf1e6..5bb1fff0c6d3d4 100644
--- a/Mathlib/Data/NNReal/Defs.lean
+++ b/Mathlib/Data/NNReal/Defs.lean
@@ -24,7 +24,7 @@ a.k.a. the interval `[0, ∞)`. We also define the following operations and stru
complete linear ordered archimedean commutative semifield; we have no typeclass for this in
`mathlib` yet, so we define the following instances instead:
- - `LinearOrderedSemiring ℝ≥0`;
+ - `IsOrderedRing ℝ≥0`;
- `OrderedCommSemiring ℝ≥0`;
- `CanonicallyOrderedAdd ℝ≥0`;
- `LinearOrderedCommGroupWithZero ℝ≥0`;
diff --git a/Mathlib/Data/Nat/Cast/Order/Basic.lean b/Mathlib/Data/Nat/Cast/Order/Basic.lean
index 575cb7470728b0..77c89f5e3e0ca5 100644
--- a/Mathlib/Data/Nat/Cast/Order/Basic.lean
+++ b/Mathlib/Data/Nat/Cast/Order/Basic.lean
@@ -23,12 +23,6 @@ assert_not_exists IsOrderedMonoid
variable {α : Type*}
namespace Nat
-
-section OrderedSemiring
-/- Note: even though the section indicates `OrderedSemiring`, which is the common use case,
-we use a generic collection of instances so that it applies in other settings (e.g., in a
-`StarOrderedRing`, or the `selfAdjoint` or `StarOrderedRing.positive` parts thereof). -/
-
variable [AddMonoidWithOne α] [PartialOrder α]
variable [AddLeftMono α] [ZeroLEOneClass α]
@@ -37,12 +31,12 @@ theorem mono_cast : Monotone (Nat.cast : ℕ → α) :=
monotone_nat_of_le_succ fun n ↦ by
rw [Nat.cast_succ]; exact le_add_of_nonneg_right zero_le_one
-/-- See also `Nat.cast_nonneg`, specialised for an `OrderedSemiring`. -/
+/-- See also `Nat.cast_nonneg`, specialised to `IsOrderedRing`. -/
@[simp low]
theorem cast_nonneg' (n : ℕ) : 0 ≤ (n : α) :=
@Nat.cast_zero α _ ▸ mono_cast (Nat.zero_le n)
-/-- See also `Nat.ofNat_nonneg`, specialised for an `OrderedSemiring`. -/
+/-- See also `Nat.ofNat_nonneg`, specialised to `IsOrderedRing`. -/
@[simp low]
theorem ofNat_nonneg' (n : ℕ) [n.AtLeastTwo] : 0 ≤ (ofNat(n) : α) := cast_nonneg' n
@@ -55,7 +49,7 @@ theorem cast_add_one_pos (n : ℕ) : 0 < (n : α) + 1 := by
convert! (@mono_cast α _).imp (?_ : 1 ≤ n + 1)
<;> simp
-/-- See also `Nat.cast_pos`, specialised for an `OrderedSemiring`. -/
+/-- See also `Nat.cast_pos`, specialised to `IsOrderedRing`. -/
@[simp low]
theorem cast_pos' {n : ℕ} : (0 : α) < n ↔ 0 < n := by cases n <;> simp [cast_add_one_pos]
@@ -153,8 +147,6 @@ theorem ofNat_lt :
(ofNat(m) : α) < (ofNat(n) : α) ↔ (OfNat.ofNat m : ℕ) < OfNat.ofNat n :=
cast_lt
-end OrderedSemiring
-
end Nat
instance [AddMonoidWithOne α] [CharZero α] : Nontrivial α where exists_pair_ne :=
diff --git a/Mathlib/Data/Nat/Cast/Order/Ring.lean b/Mathlib/Data/Nat/Cast/Order/Ring.lean
index 9b99ac171001ed..dd5419a62a310e 100644
--- a/Mathlib/Data/Nat/Cast/Order/Ring.lean
+++ b/Mathlib/Data/Nat/Cast/Order/Ring.lean
@@ -21,11 +21,7 @@ variable {R α : Type*}
namespace Nat
-section OrderedSemiring
-/- Note: even though the section indicates `OrderedSemiring`, which is the common use case,
-we use a generic collection of instances so that it applies in other settings (e.g., in a
-`StarOrderedRing`, or the `selfAdjoint` or `StarOrderedRing.positive` parts thereof). -/
-
+section AddMonoidWithOne
variable [AddMonoidWithOne α] [PartialOrder α]
variable [AddLeftMono α] [ZeroLEOneClass α]
@@ -59,7 +55,7 @@ variable [NeZero (1 : α)]
theorem cast_pos {α} [Semiring α] [PartialOrder α] [IsOrderedRing α] [Nontrivial α] {n : ℕ} :
(0 : α) < n ↔ 0 < n := cast_pos'
-/-- See also `Nat.ofNat_pos`, specialised for an `OrderedSemiring`. -/
+/-- See also `Nat.ofNat_pos`, specialised to `IsOrderedRing`. -/
@[simp low]
theorem ofNat_pos' {n : ℕ} [n.AtLeastTwo] : 0 < (ofNat(n) : α) :=
cast_pos'.mpr (NeZero.pos n)
@@ -73,7 +69,7 @@ theorem ofNat_pos {α} [Semiring α] [PartialOrder α] [IsOrderedRing α] [Nontr
end Nontrivial
-end OrderedSemiring
+end AddMonoidWithOne
/-- A version of `Nat.cast_sub` that works for `ℝ≥0` and `ℚ≥0`. Note that this proof doesn't work
for `ℕ∞` and `ℝ≥0∞`, so we use type-specific lemmas for these types. -/
diff --git a/Mathlib/LinearAlgebra/Matrix/Irreducible/Defs.lean b/Mathlib/LinearAlgebra/Matrix/Irreducible/Defs.lean
index 550c4f6b503a3d..f98961391940e6 100644
--- a/Mathlib/LinearAlgebra/Matrix/Irreducible/Defs.lean
+++ b/Mathlib/LinearAlgebra/Matrix/Irreducible/Defs.lean
@@ -46,7 +46,7 @@ matrix (like powers) into graph-theoretic properties of its quiver (like the exi
## Implementation notes
-Throughout we work over a `LinearOrderedRing R`. Some results require stronger assumptions,
+Throughout we work over a linearly ordered ring `R`. Some results require stronger assumptions,
like `PosMulStrictMono R` or `Nontrivial R`. Some statements expand matrix powers and thus require
`[DecidableEq n]` to reason about finite sums.
diff --git a/Mathlib/MeasureTheory/Function/LpOrder.lean b/Mathlib/MeasureTheory/Function/LpOrder.lean
index a56c96738bcebb..e28f57d227f38a 100644
--- a/Mathlib/MeasureTheory/Function/LpOrder.lean
+++ b/Mathlib/MeasureTheory/Function/LpOrder.lean
@@ -14,7 +14,7 @@ public import Mathlib.MeasureTheory.Function.LpSpace.Basic
## Results
-- `Lp E p μ` is an `OrderedAddCommGroup` when `E` is a `NormedLatticeAddCommGroup`.
+- `Lp E p μ` is an ordered group when `E` is a `NormedLatticeAddCommGroup`.
## TODO
diff --git a/Mathlib/MeasureTheory/Measure/Typeclasses/Finite.lean b/Mathlib/MeasureTheory/Measure/Typeclasses/Finite.lean
index 1f4ea177ffb1d0..fdc5d2827d7b41 100644
--- a/Mathlib/MeasureTheory/Measure/Typeclasses/Finite.lean
+++ b/Mathlib/MeasureTheory/Measure/Typeclasses/Finite.lean
@@ -148,7 +148,7 @@ theorem measureUnivNNReal_pos [IsFiniteMeasure μ] (hμ : μ ≠ 0) : 0 < measur
contrapose! hμ
simpa [measureUnivNNReal_eq_zero, Nat.le_zero] using hμ
-/-- `le_of_add_le_add_left` is normally applicable to `OrderedCancelAddCommMonoid`,
+/-- `le_of_add_le_add_left` is normally applicable to ordered cancellative monoids,
but it holds for measures with the additional assumption that μ is finite. -/
theorem Measure.le_of_add_le_add_left [IsFiniteMeasure μ] (A2 : μ + ν₁ ≤ μ + ν₂) : ν₁ ≤ ν₂ :=
fun S => ENNReal.le_of_add_le_add_left (MeasureTheory.measure_ne_top μ S) (A2 S)
diff --git a/Mathlib/Order/Filter/AtTopBot/Archimedean.lean b/Mathlib/Order/Filter/AtTopBot/Archimedean.lean
index de33d127a733bd..dcafa4e223f82d 100644
--- a/Mathlib/Order/Filter/AtTopBot/Archimedean.lean
+++ b/Mathlib/Order/Filter/AtTopBot/Archimedean.lean
@@ -214,20 +214,20 @@ section LinearOrderedRing
variable [Ring R] [LinearOrder R] [IsStrictOrderedRing R] [Archimedean R]
/-- See also `Filter.Tendsto.atTop_mul_const_of_neg` for a version of this lemma for
-`LinearOrderedField`s which does not require the `Archimedean` assumption. -/
+linearly ordered fields which does not require the `Archimedean` assumption. -/
theorem Tendsto.atTop_mul_const_of_neg' (hr : r < 0) (hf : Tendsto f l atTop) :
Tendsto (fun x => f x * r) l atBot := by
simpa only [tendsto_neg_atTop_iff, mul_neg] using hf.atTop_mul_const' (neg_pos.mpr hr)
/-- See also `Filter.Tendsto.atBot_mul_const` for a version of this lemma for
-`LinearOrderedField`s which does not require the `Archimedean` assumption. -/
+linearly ordered fields which does not require the `Archimedean` assumption. -/
theorem Tendsto.atBot_mul_const' (hr : 0 < r) (hf : Tendsto f l atBot) :
Tendsto (fun x => f x * r) l atBot := by
simp only [← tendsto_neg_atTop_iff, ← neg_mul] at hf ⊢
exact hf.atTop_mul_const' hr
/-- See also `Filter.Tendsto.atBot_mul_const_of_neg` for a version of this lemma for
-`LinearOrderedField`s which does not require the `Archimedean` assumption. -/
+linearly ordered fields which does not require the `Archimedean` assumption. -/
theorem Tendsto.atBot_mul_const_of_neg' (hr : r < 0) (hf : Tendsto f l atBot) :
Tendsto (fun x => f x * r) l atTop := by
simpa only [mul_neg, tendsto_neg_atBot_iff] using hf.atBot_mul_const' (neg_pos.2 hr)
diff --git a/Mathlib/Order/Filter/Germ/OrderedMonoid.lean b/Mathlib/Order/Filter/Germ/OrderedMonoid.lean
index 3500fd9b30b493..e786c7d1fbe922 100644
--- a/Mathlib/Order/Filter/Germ/OrderedMonoid.lean
+++ b/Mathlib/Order/Filter/Germ/OrderedMonoid.lean
@@ -15,7 +15,7 @@ public import Mathlib.Order.Filter.Germ.Basic
For each of the following structures we prove that if `β` has this structure, then so does
`Germ l β`:
-* `OrderedCancelCommMonoid` and `OrderedCancelAddCommMonoid`.
+* `IsOrderedCancelMonoid` and `IsOrderedCancelAddMonoid`.
## Tags
diff --git a/Mathlib/Order/Interval/Finset/Nat.lean b/Mathlib/Order/Interval/Finset/Nat.lean
index 7852bd74f40333..700ddec86f1b7e 100644
--- a/Mathlib/Order/Interval/Finset/Nat.lean
+++ b/Mathlib/Order/Interval/Finset/Nat.lean
@@ -16,7 +16,7 @@ intervals as finsets and fintypes.
## TODO
-Some lemmas can be generalized using `OrderedGroup`, `CanonicallyOrderedMul` or `SuccOrder`
+Some lemmas can be generalized using `IsOrderedAddMonoid`, `CanonicallyOrderedAdd` or `SuccOrder`
and subsequently be moved upstream to `Order.Interval.Finset`.
-/
diff --git a/Mathlib/Order/Interval/Set/Defs.lean b/Mathlib/Order/Interval/Set/Defs.lean
index 5e19c38fbed891..068a144114ac37 100644
--- a/Mathlib/Order/Interval/Set/Defs.lean
+++ b/Mathlib/Order/Interval/Set/Defs.lean
@@ -83,7 +83,7 @@ to_dual_insert_cast Icc := by simp only [and_comm]
/-- We say that a set `s : Set α` is `OrdConnected` if for all `x y ∈ s` it includes the
interval `[[x, y]]`. If `α` is a `DenselyOrdered` `ConditionallyCompleteLinearOrder` with
the `OrderTopology`, then this condition is equivalent to `IsPreconnected s`. If `α` is a
-`LinearOrderedField`, then this condition is also equivalent to `Convex α s`. -/
+linearly ordered field, then this condition is also equivalent to `Convex α s`. -/
class OrdConnected (s : Set α) : Prop where
/-- `s : Set α` is `OrdConnected` if for all `x y ∈ s` it includes the interval `[[x, y]]`. -/
out' ⦃x : α⦄ (hx : x ∈ s) ⦃y : α⦄ (hy : y ∈ s) : Icc x y ⊆ s
diff --git a/Mathlib/Order/Interval/Set/OrdConnected.lean b/Mathlib/Order/Interval/Set/OrdConnected.lean
index 0bad6f3c644c35..47cae5ddfedfa6 100644
--- a/Mathlib/Order/Interval/Set/OrdConnected.lean
+++ b/Mathlib/Order/Interval/Set/OrdConnected.lean
@@ -15,7 +15,7 @@ public import Mathlib.Order.SetNotation
We say that a set `s : Set α` is `OrdConnected` if for all `x y ∈ s` it includes the
interval `[[x, y]]`. If `α` is a `DenselyOrdered` `ConditionallyCompleteLinearOrder` with
the `OrderTopology`, then this condition is equivalent to `IsPreconnected s`. If `α` is a
-`LinearOrderedField`, then this condition is also equivalent to `Convex α s`.
+linearly ordered field, then this condition is also equivalent to `Convex α s`.
In this file we prove that intersection of a family of `OrdConnected` sets is `OrdConnected` and
that all standard intervals are `OrdConnected`.
diff --git a/Mathlib/RingTheory/GradedAlgebra/Radical.lean b/Mathlib/RingTheory/GradedAlgebra/Radical.lean
index bbdea52c8b271b..e83178cf7ff08f 100644
--- a/Mathlib/RingTheory/GradedAlgebra/Radical.lean
+++ b/Mathlib/RingTheory/GradedAlgebra/Radical.lean
@@ -26,7 +26,7 @@ This file contains a proof that the radical of any homogeneous ideal is a homoge
## Implementation details
Throughout this file, the indexing type `ι` of grading is assumed to be a
-`LinearOrderedCancelAddCommMonoid`. This might be stronger than necessary but cancelling
+linearly ordered cancellative monoid. This might be stronger than necessary but cancelling
property is strictly necessary; for a counterexample of how `Ideal.IsHomogeneous.isPrime_iff`
fails for a non-cancellative set see `Counterexamples/HomogeneousPrimeNotPrime.lean`.
diff --git a/Mathlib/RingTheory/HahnSeries/PowerSeries.lean b/Mathlib/RingTheory/HahnSeries/PowerSeries.lean
index 79dff1967dc088..0dec034a06063a 100644
--- a/Mathlib/RingTheory/HahnSeries/PowerSeries.lean
+++ b/Mathlib/RingTheory/HahnSeries/PowerSeries.lean
@@ -85,7 +85,7 @@ theorem coeff_toPowerSeries_symm {f : PowerSeries R} {n : ℕ} :
variable (Γ R) [Semiring Γ] [PartialOrder Γ] [IsStrictOrderedRing Γ]
-/-- Casts a power series as a Hahn series with coefficients from a `StrictOrderedSemiring`. -/
+/-- Casts a power series as a Hahn series with coefficients from a strictly ordered semiring. -/
def ofPowerSeries : PowerSeries R →+* R⟦Γ⟧ :=
(HahnSeries.embDomainRingHom (Nat.castAddMonoidHom Γ) Nat.strictMono_cast.injective fun _ _ =>
Nat.cast_le).comp
@@ -206,8 +206,7 @@ def toPowerSeriesAlg : A⟦ℕ⟧ ≃ₐ[R] PowerSeries A :=
variable (Γ) [Semiring Γ] [PartialOrder Γ] [IsStrictOrderedRing Γ]
-/-- Casting a power series as a Hahn series with coefficients from a `StrictOrderedSemiring`
- is an algebra homomorphism. -/
+/-- Casting a power series as a Hahn series with coefficients from a strictly ordered semiring. -/
@[simps!]
def ofPowerSeriesAlg : PowerSeries A →ₐ[R] A⟦Γ⟧ :=
(HahnSeries.embDomainAlgHom (Nat.castAddMonoidHom Γ) Nat.strictMono_cast.injective fun _ _ =>
diff --git a/Mathlib/RingTheory/HahnSeries/Valuation.lean b/Mathlib/RingTheory/HahnSeries/Valuation.lean
index 046fc3adbab514..349014bca05cff 100644
--- a/Mathlib/RingTheory/HahnSeries/Valuation.lean
+++ b/Mathlib/RingTheory/HahnSeries/Valuation.lean
@@ -10,7 +10,8 @@ public import Mathlib.RingTheory.Valuation.Basic
/-!
# Valuations on Hahn Series rings
-If `Γ` is a `LinearOrderedCancelAddCommMonoid` and `R` is a domain, then the domain `R⟦Γ⟧`
+
+If `Γ` is a linearly ordered cancellative monoid and `R` is a domain, then the domain `R⟦Γ⟧`
admits an additive valuation given by `orderTop`.
## Main Definitions
diff --git a/Mathlib/RingTheory/MvPolynomial/WeightedHomogeneous.lean b/Mathlib/RingTheory/MvPolynomial/WeightedHomogeneous.lean
index d7342312ee467e..2be43ba2d95807 100644
--- a/Mathlib/RingTheory/MvPolynomial/WeightedHomogeneous.lean
+++ b/Mathlib/RingTheory/MvPolynomial/WeightedHomogeneous.lean
@@ -523,8 +523,8 @@ section OrderedAddCommMonoid
variable [AddCommMonoid M] [PartialOrder M]
{w : σ → M} (φ : MvPolynomial σ R)
-/-- If `M` is a canonically `OrderedAddCommMonoid`, then the `weightedHomogeneousComponent`
- of weighted degree `0` of a polynomial is its constant coefficient. -/
+/-- If `M` is canonically ordered, then the `weightedHomogeneousComponent` of weighted degree `0`
+of a polynomial is its constant coefficient. -/
@[simp]
theorem weightedHomogeneousComponent_zero [CanonicallyOrderedAdd M] [IsAddTorsionFree M]
(hw : ∀ i : σ, w i ≠ 0) :
diff --git a/Mathlib/Tactic/NormNum/Ineq.lean b/Mathlib/Tactic/NormNum/Ineq.lean
index 74518ce20e9761..a70b42dde5a358 100644
--- a/Mathlib/Tactic/NormNum/Ineq.lean
+++ b/Mathlib/Tactic/NormNum/Ineq.lean
@@ -22,7 +22,7 @@ namespace Mathlib.Meta.NormNum
variable {u : Level}
-/-- Helper function to synthesize typed `Semiring α` `PartialOrder α` `IsOrderedSemiring α`
+/-- Helper function to synthesize typed `Semiring α` `PartialOrder α` `IsOrderedRing α`
expressions. -/
def inferOrderedSemiring (α : Q(Type u)) : MetaM <|
(_ : Q(Semiring $α)) × (_ : Q(PartialOrder $α)) × Q(IsOrderedRing $α) :=
@@ -33,7 +33,7 @@ def inferOrderedSemiring (α : Q(Type u)) : MetaM <|
return ⟨semiring, partialOrder, isOrderedRing⟩
go <|> throwError "not an ordered semiring"
-/-- Helper function to synthesize typed `Ring α` `PartialOrder α` `IsOrderedSemiring α`
+/-- Helper function to synthesize typed `Ring α` `PartialOrder α` `IsOrderedRing α`
expressions. -/
def inferOrderedRing (α : Q(Type u)) : MetaM <|
(_ : Q(Ring $α)) × (_ : Q(PartialOrder $α)) × Q(IsOrderedRing $α) :=
@@ -240,7 +240,7 @@ where
else if let .some _i ← trySynthInstanceQ q(CharZero $α) then
let r : Q(Nat.ble $na $nb = false) := (q(Eq.refl false) : Expr)
return .isFalse q(isNat_le_false $pa $pb $r)
- else -- Nats can appear in an `OrderedRing` without `CharZero`.
+ else -- Nats can appear in an ordered ring without `CharZero`.
intArm
attribute [local instance] monadLiftOptionMetaM in
@@ -277,11 +277,6 @@ where
let r : Q(decide ($nb ≤ $na) = true) := (q(Eq.refl true) : Expr)
return .isFalse q(isInt_lt_false $pa $pb $r)
let rec nnratArm : MetaM (Result e) := do
- -- We need a division ring with an order, and `LinearOrderedField` is the closest mathlib has.
- /-
- NOTE: after the ordered algebra refactor, this is not true anymore,
- so there may be a better typeclass
- -/
let ⟨_, _, _⟩ ← inferLinearOrderedSemifield α
assumeInstancesCommute
haveI' : $e =Q ($a < $b) := ⟨⟩
@@ -294,11 +289,6 @@ where
let r : Q(decide ($nb * $da ≤ $na * $db) = true) := (q(Eq.refl true) : Expr)
return .isFalse q(isNNRat_lt_false $pa $pb $r)
let rec ratArm : MetaM (Result e) := do
- -- We need a division ring with an order, and `LinearOrderedField` is the closest mathlib has.
- /-
- NOTE: after the ordered algebra refactor, this is not true anymore,
- so there may be a better typeclass
- -/
let ⟨_, _, _i⟩ ← inferLinearOrderedField α
assumeInstancesCommute
haveI' : $e =Q ($a < $b) := ⟨⟩
@@ -327,7 +317,7 @@ where
if let .some _i ← trySynthInstanceQ q(CharZero $α) then
let r : Q(Nat.ble $nb $na = false) := (q(Eq.refl false) : Expr)
return .isTrue q(isNat_lt_true $pa $pb $r)
- else -- Nats can appear in an `OrderedRing` without `CharZero`.
+ else -- Nats can appear in an ordered ring without `CharZero`.
intArm
else
let r : Q(Nat.ble $nb $na = true) := (q(Eq.refl true) : Expr)
diff --git a/MathlibTest/positivity.lean b/MathlibTest/positivity.lean
index 1a5b1a7a6c0f4e..9765bcb3dbfffe 100644
--- a/MathlibTest/positivity.lean
+++ b/MathlibTest/positivity.lean
@@ -545,7 +545,8 @@ example {r : ℝ} (hr : 0 < r) : (0 : EReal) < r := by positivity
example {r : ℝ≥0∞} : (0 : EReal) ≤ r := by positivity
example {r : ℝ≥0∞} (hr : 0 < r) : (0 : EReal) < r := by positivity
--- example {α : Type*} [OrderedRing α] {n : ℤ} : 0 ≤ ((n ^ 2 : ℤ) : α) := by positivity
+-- example {R : Type*} [Ring R] [PartialOrder R] [IsOrderedRing R] {n : ℤ} :
+-- 0 ≤ ((n ^ 2 : ℤ) : R) := by positivity
example {r : ℝ≥0} : 0 ≤ ((r : ℝ) : EReal) := by positivity
example {r : ℝ≥0} : 0 < ((r + 1 : ℝ) : EReal) := by positivity
diff --git a/docs/overview.yaml b/docs/overview.yaml
index ec76c6c11f4918..7d40e124f37b23 100644
--- a/docs/overview.yaml
+++ b/docs/overview.yaml
@@ -69,7 +69,7 @@ General algebra:
localization: 'Localization'
local ring: 'IsLocalRing'
Noetherian ring: 'IsNoetherianRing'
- # ordered ring: 'OrderedRing'
+ ordered ring: 'IsOrderedRing'
Ideals and quotients:
ideal of a commutative ring: 'Ideal'
From 4d472910901f530740174e155a67628929fb1e44 Mon Sep 17 00:00:00 2001
From: Brian Nugent
Date: Wed, 17 Jun 2026 15:15:32 +0000
Subject: [PATCH 0117/1300] feat(CategoryTheory): Functors that preserve the
terminal object are Final (#39994)
Co-authored-by: Brian-Nugent
---
Mathlib/CategoryTheory/Limits/Final.lean | 18 ++++++++++++++++++
.../Sites/CoproductSheafCondition.lean | 3 ++-
2 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/Mathlib/CategoryTheory/Limits/Final.lean b/Mathlib/CategoryTheory/Limits/Final.lean
index f4864fa01f8bd7..40de447a51ef95 100644
--- a/Mathlib/CategoryTheory/Limits/Final.lean
+++ b/Mathlib/CategoryTheory/Limits/Final.lean
@@ -940,6 +940,24 @@ lemma initial_fromPUnit_of_isInitial (hc : Limits.IsInitial c) : (fromPUnit c).I
⟨fun i j ↦ CostructuredArrow.obj_ext _ _ (by cat_disch) (hc.hom_ext _ _)⟩
infer_instance
+instance [HasTerminal C] {D : Type u₂} [Category.{v₂} D] (F : C ⥤ D)
+ [PreservesLimit (Functor.empty.{0} C) F] : F.Final :=
+ have : (fromPUnit.{0} (⊤_ C)).Final := final_fromPUnit_of_isTerminal terminalIsTerminal
+ have : (fromPUnit.{0} (F.obj (⊤_ C))).Final := final_fromPUnit_of_isTerminal
+ (terminalIsTerminal.isTerminalObj F (⊤_ C))
+ have : ((fromPUnit.{0} (⊤_ C)) ⋙ F).Final := final_of_natIso (F := fromPUnit.{0} (F.obj (⊤_ C)))
+ (Discrete.natIso (fun _ => Iso.refl _))
+ final_of_final_comp (fromPUnit.{0} (⊤_ C)) F
+
+instance [HasInitial C] {D : Type u₂} [Category.{v₂} D] (F : C ⥤ D)
+ [PreservesColimit (Functor.empty.{0} C) F] : F.Initial :=
+ have : (fromPUnit.{0} (⊥_ C)).Initial := initial_fromPUnit_of_isInitial initialIsInitial
+ have : (fromPUnit.{0} (F.obj (⊥_ C))).Initial := initial_fromPUnit_of_isInitial
+ (initialIsInitial.isInitialObj F (⊥_ C))
+ have : ((fromPUnit.{0} (⊥_ C)) ⋙ F).Initial := initial_of_natIso
+ (F := fromPUnit.{0} (F.obj (⊥_ C))) (Discrete.natIso (fun _ => Iso.refl _))
+ initial_of_initial_comp (fromPUnit.{0} (⊥_ C)) F
+
end
section
diff --git a/Mathlib/CategoryTheory/Sites/CoproductSheafCondition.lean b/Mathlib/CategoryTheory/Sites/CoproductSheafCondition.lean
index 205ab6da6c2c30..9ebc9725d813d7 100644
--- a/Mathlib/CategoryTheory/Sites/CoproductSheafCondition.lean
+++ b/Mathlib/CategoryTheory/Sites/CoproductSheafCondition.lean
@@ -98,7 +98,8 @@ lemma Presieve.isSheafFor_sigmaDesc_iff {ι : Type*} {X : ι → C} (f : ∀ i,
dsimp [E]; infer_instance
have : PreservesLimit (Discrete.functor fun i ↦ op (E.toPreOneHypercover.Y' i)) F := by
convert! Functor.Initial.preservesLimit_of_comp (Discrete.equivalence <| .sigmaPUnit _).inverse
- assumption
+ · infer_instance
+ · assumption
let equiv := (E.isLimitSigmaOfIsColimitEquiv hc hc' F).nonempty_congr
rwa [isLimit_toPreOneHypercover_type_iff, isLimit_toPreOneHypercover_type_iff,
presieve₀_sigmaOfIsColimit] at equiv
From 5ac74759704aa69e70b00ea2a622efd06aba73df Mon Sep 17 00:00:00 2001
From: teorth <199308+teorth@users.noreply.github.com>
Date: Wed, 17 Jun 2026 15:15:35 +0000
Subject: [PATCH 0118/1300] chore(Analysis/SumIntegralComparisons): golf proofs
(#40655)
Golfed the existing code in `SumIntegralComparisons.lean`. I had also added some additional API lemmas, but this is now done in #40588 and I have removed the redundant additions to simplify the PR.
[](https://gitpod.io/from-referrer/)
Co-authored-by: Terence Tao
---
Mathlib/Analysis/SumIntegralComparisons.lean | 292 ++++++-------------
1 file changed, 88 insertions(+), 204 deletions(-)
diff --git a/Mathlib/Analysis/SumIntegralComparisons.lean b/Mathlib/Analysis/SumIntegralComparisons.lean
index 8b7e0f244ba4bc..d333e36c258610 100644
--- a/Mathlib/Analysis/SumIntegralComparisons.lean
+++ b/Mathlib/Analysis/SumIntegralComparisons.lean
@@ -25,11 +25,11 @@ At the moment it contains several lemmas in this direction, for antitone or mono
## Main Results
* `AntitoneOn.integral_le_sum`: The integral of an antitone function is at most the sum of its
- values at integer steps aligning with the left-hand side of the interval
+ values at integer steps aligning with the left-hand side of the interval.
* `AntitoneOn.sum_le_integral`: The sum of an antitone function along integer steps aligning with
the right-hand side of the interval is at most the integral of the function along that interval
* `MonotoneOn.integral_le_sum`: The integral of a monotone function is at most the sum of its
- values at integer steps aligning with the right-hand side of the interval
+ values at integer steps aligning with the right-hand side of the interval.
* `MonotoneOn.sum_le_integral`: The sum of a monotone function along integer steps aligning with
the left-hand side of the interval is at most the integral of the function along that interval
* `sum_mul_Ico_le_integral_of_monotone_antitone`: the sum of `f i * g i` on an interval is bounded
@@ -44,248 +44,132 @@ analysis, comparison, asymptotics
public section
-
-open Set MeasureTheory MeasureSpace
+open Set MeasureTheory MeasureSpace intervalIntegral
variable {x₀ : ℝ} {a b : ℕ} {f g : ℝ → ℝ}
-lemma sum_Ico_le_integral_of_le
- (hab : a ≤ b) (h : ∀ i ∈ Ico a b, ∀ x ∈ Ico (i : ℝ) (i + 1 : ℕ), f i ≤ g x)
- (hg : IntegrableOn g (Set.Ico a b)) :
- ∑ i ∈ Finset.Ico a b, f i ≤ ∫ x in a..b, g x := by
- have A i (hi : i ∈ Finset.Ico a b) : IntervalIntegrable g volume i (i + 1 : ℕ) := by
+lemma sum_Ico_le_integral_of_le (hab : a ≤ b)
+ (h : ∀ i ∈ Ico a b, ∀ x ∈ Ico (i : ℝ) ↑(i + 1), f i ≤ g x)
+ (hg : IntegrableOn g (Ico a b)) : ∑ i ∈ .Ico a b, f i ≤ ∫ x in a..b, g x := by
+ have A i (hi : i ∈ Finset.Ico a b) : IntervalIntegrable g volume i ↑(i + 1) := by
rw [intervalIntegrable_iff_integrableOn_Ico_of_le (by simp)]
- apply hg.mono _ le_rfl
- rintro x ⟨hx, h'x⟩
- simp only [Finset.mem_Ico, mem_Ico] at hi ⊢
- exact ⟨le_trans (mod_cast hi.1) hx, h'x.trans_le (mod_cast hi.2)⟩
+ simp only [Finset.mem_Ico, ← Nat.add_one_le_iff] at hi
+ rify at hi
+ exact hg.mono (by grind) le_rfl
calc
- ∑ i ∈ Finset.Ico a b, f i
- _ = ∑ i ∈ Finset.Ico a b, (∫ x in (i : ℝ)..(i + 1 : ℕ), f i) := by simp
- _ ≤ ∑ i ∈ Finset.Ico a b, (∫ x in (i : ℝ)..(i + 1 : ℕ), g x) := by
+ _ = ∑ i ∈ .Ico a b, (∫ x in (i : ℝ)..↑(i + 1), f i) := by simp
+ _ ≤ ∑ i ∈ .Ico a b, (∫ x in (i : ℝ)..↑(i + 1), g x) := by
gcongr with i hi
- apply intervalIntegral.integral_mono_on_of_le_Ioo (by simp) (by simp) (A _ hi) (fun x hx ↦ ?_)
+ apply integral_mono_on_of_le_Ioo (by simp) (by simp) (A _ hi) (fun x hx ↦ ?_)
exact h _ (by simpa using hi) _ (Ioo_subset_Ico_self hx)
- _ = ∫ x in a..b, g x := by
- rw [intervalIntegral.sum_integral_adjacent_intervals_Ico (a := fun i ↦ i) hab]
- intro i hi
- exact A _ (by simpa using hi)
+ _ = _ := by rw [sum_integral_adjacent_intervals_Ico (a := (↑·)) hab]; grind
+
+lemma integral_le_sum_Ico_of_le (hab : a ≤ b)
+ (h : ∀ i ∈ Ico a b, ∀ x ∈ Ico (i : ℝ) ↑(i + 1), g x ≤ f i)
+ (hg : IntegrableOn g (Ico a b)) : ∫ x in a..b, g x ≤ ∑ i ∈ .Ico a b, f i := by
+ convert! neg_le_neg (sum_Ico_le_integral_of_le (f := -f) (g := -g) hab
+ (fun i hi x hx ↦ neg_le_neg (h i hi x hx)) hg.neg) <;> simp
-lemma integral_le_sum_Ico_of_le
- (hab : a ≤ b) (h : ∀ i ∈ Ico a b, ∀ x ∈ Ico (i : ℝ) (i + 1 : ℕ), g x ≤ f i)
- (hg : IntegrableOn g (Set.Ico a b)) :
- ∫ x in a..b, g x ≤ ∑ i ∈ Finset.Ico a b, f i := by
- convert!
- neg_le_neg
- (sum_Ico_le_integral_of_le (f := -f) (g := -g) hab (fun i hi x hx ↦ neg_le_neg (h i hi x hx))
- hg.neg) <;> simp
+private theorem AntitoneOn.intervalIntegrable_subset (hf : AntitoneOn f (Icc x₀ (x₀ + a)))
+ (k : ℕ) (hk : k + 1 ≤ a) : IntervalIntegrable f volume (x₀ + k) (x₀ + ↑(k + 1)) := by
+ refine (hf.mono ?_).intervalIntegrable
+ rw [uIcc_of_le (by simp)]
+ apply Icc_subset_Icc <;> simp [-Nat.cast_add, hk]
theorem AntitoneOn.integral_le_sum (hf : AntitoneOn f (Icc x₀ (x₀ + a))) :
- (∫ x in x₀..x₀ + a, f x) ≤ ∑ i ∈ Finset.range a, f (x₀ + i) := by
- have hint : ∀ k : ℕ, k < a → IntervalIntegrable f volume (x₀ + k) (x₀ + (k + 1 : ℕ)) := by
- intro k hk
- refine (hf.mono ?_).intervalIntegrable
- rw [uIcc_of_le]
- · apply Icc_subset_Icc
- · simp only [le_add_iff_nonneg_right, Nat.cast_nonneg]
- · simp only [add_le_add_iff_left, Nat.cast_le, Nat.succ_le_of_lt hk]
- · simp only [add_le_add_iff_left, Nat.cast_le, Nat.le_succ]
- calc
- ∫ x in x₀..x₀ + a, f x = ∑ i ∈ Finset.range a, ∫ x in x₀ + i..x₀ + (i + 1 : ℕ), f x := by
- convert! (intervalIntegral.sum_integral_adjacent_intervals hint).symm
- simp only [Nat.cast_zero, add_zero]
- _ ≤ ∑ i ∈ Finset.range a, ∫ _ in x₀ + i..x₀ + (i + 1 : ℕ), f (x₀ + i) := by
- gcongr with i hi
- have ia : i < a := Finset.mem_range.1 hi
- refine intervalIntegral.integral_mono_on (by simp) (hint _ ia) (by simp) fun x hx => ?_
- apply hf _ _ hx.1
- · simp only [ia.le, mem_Icc, le_add_iff_nonneg_right, Nat.cast_nonneg, add_le_add_iff_left,
- Nat.cast_le, and_self_iff]
- · refine mem_Icc.2 ⟨le_trans (by simp) hx.1, le_trans hx.2 ?_⟩
- simp only [add_le_add_iff_left, Nat.cast_le, Nat.succ_le_of_lt ia]
- _ = ∑ i ∈ Finset.range a, f (x₀ + i) := by simp
+ ∫ x in x₀..x₀ + a, f x ≤ ∑ i ∈ .range a, f (x₀ + i) := calc
+ _ = ∑ i ∈ .range a, ∫ x in x₀ + i..x₀ + ↑(i + 1), f x := by
+ convert! (sum_integral_adjacent_intervals hf.intervalIntegrable_subset).symm
+ simp
+ _ ≤ ∑ i ∈ .range a, ∫ _ in x₀ + i..x₀ + ↑(i + 1), f (x₀ + i) := by
+ gcongr with i hi
+ rw [Finset.mem_range, ← Nat.add_one_le_iff] at hi
+ have := hf.intervalIntegrable_subset _ hi
+ rify at hi this ⊢
+ refine integral_mono_on (by simp) this (by simp) fun _ _ ↦ by apply hf <;> grind
+ _ = _ := by simp
theorem AntitoneOn.integral_le_sum_Ico (hab : a ≤ b) (hf : AntitoneOn f (Set.Icc a b)) :
- (∫ x in a..b, f x) ≤ ∑ x ∈ Finset.Ico a b, f x := by
- rw [(Nat.sub_add_cancel hab).symm, Nat.cast_add]
- conv =>
- congr
- congr
- · skip
- · skip
- rw [add_comm]
- · skip
- · skip
- congr
- congr
- rw [← zero_add a]
+ ∫ x in a..b, f x ≤ ∑ x ∈ .Ico a b, f x := by
+ suffices ∫ x in a..a + ↑(b - a), f x ≤ ∑ x ∈ .Ico (0 + a) (b - a + a), f x by simp_all
rw [← Finset.sum_Ico_add, Nat.Ico_zero_eq_range]
- conv =>
- rhs
- congr
- · skip
- ext
- rw [Nat.cast_add]
- apply AntitoneOn.integral_le_sum
- simp only [hf, hab, Nat.cast_sub, add_sub_cancel]
+ suffices ∫ x in a..a + ↑(b - a), f x ≤ ∑ x ∈ .range (b - a), f (a + x) by simp_all
+ exact AntitoneOn.integral_le_sum (by simp only [hf, hab, Nat.cast_sub, add_sub_cancel])
theorem AntitoneOn.sum_le_integral (hf : AntitoneOn f (Icc x₀ (x₀ + a))) :
- (∑ i ∈ Finset.range a, f (x₀ + (i + 1 : ℕ))) ≤ ∫ x in x₀..x₀ + a, f x := by
- have hint : ∀ k : ℕ, k < a → IntervalIntegrable f volume (x₀ + k) (x₀ + (k + 1 : ℕ)) := by
- intro k hk
- refine (hf.mono ?_).intervalIntegrable
- rw [uIcc_of_le]
- · apply Icc_subset_Icc
- · simp only [le_add_iff_nonneg_right, Nat.cast_nonneg]
- · simp only [add_le_add_iff_left, Nat.cast_le, Nat.succ_le_of_lt hk]
- · simp only [add_le_add_iff_left, Nat.cast_le, Nat.le_succ]
- calc
- (∑ i ∈ Finset.range a, f (x₀ + (i + 1 : ℕ))) =
- ∑ i ∈ Finset.range a, ∫ _ in x₀ + i..x₀ + (i + 1 : ℕ), f (x₀ + (i + 1 : ℕ)) := by simp
- _ ≤ ∑ i ∈ Finset.range a, ∫ x in x₀ + i..x₀ + (i + 1 : ℕ), f x := by
- apply Finset.sum_le_sum fun i hi => ?_
- have ia : i + 1 ≤ a := Finset.mem_range.1 hi
- refine intervalIntegral.integral_mono_on (by simp) (by simp) (hint _ ia) fun x hx => ?_
- apply hf _ _ hx.2
- · refine mem_Icc.2 ⟨le_trans (le_add_of_nonneg_right (Nat.cast_nonneg _)) hx.1,
- le_trans hx.2 ?_⟩
- simp only [Nat.cast_le, add_le_add_iff_left, ia]
- · refine mem_Icc.2 ⟨le_add_of_nonneg_right (Nat.cast_nonneg _), ?_⟩
- simp only [add_le_add_iff_left, Nat.cast_le, ia]
- _ = ∫ x in x₀..x₀ + a, f x := by
- convert! intervalIntegral.sum_integral_adjacent_intervals hint
- simp only [Nat.cast_zero, add_zero]
-
-theorem AntitoneOn.sum_le_integral_Ico (hab : a ≤ b) (hf : AntitoneOn f (Set.Icc a b)) :
- (∑ i ∈ Finset.Ico a b, f (i + 1 : ℕ)) ≤ ∫ x in a..b, f x := by
- rw [(Nat.sub_add_cancel hab).symm, Nat.cast_add]
- conv =>
- congr
- congr
- congr
- rw [← zero_add a]
- · skip
- · skip
- · skip
- rw [add_comm]
- rw [← Finset.sum_Ico_add, Nat.Ico_zero_eq_range]
- conv =>
- lhs
- congr
- congr
- · skip
- ext
- rw [add_assoc, Nat.cast_add]
- apply AntitoneOn.sum_le_integral
- simp only [hf, hab, Nat.cast_sub, add_sub_cancel]
+ ∑ i ∈ .range a, f (x₀ + ↑(i + 1)) ≤ ∫ x in x₀..x₀ + a, f x := calc
+ _ = ∑ i ∈ .range a, ∫ _ in x₀ + i..x₀ + ↑(i + 1), f (x₀ + ↑(i + 1)) := by simp
+ _ ≤ ∑ i ∈ .range a, ∫ x in x₀ + i..x₀ + ↑(i + 1), f x := by
+ gcongr with i hi
+ rw [Finset.mem_range, ← Nat.add_one_le_iff] at hi
+ have := hf.intervalIntegrable_subset _ hi
+ rify at hi this ⊢
+ exact integral_mono_on (by simp) (by simp) this fun _ _ ↦ by apply hf <;> grind
+ _ = _ := by
+ convert! sum_integral_adjacent_intervals hf.intervalIntegrable_subset
+ simp [-Nat.cast_add]
+
+theorem AntitoneOn.sum_le_integral_Ico (hab : a ≤ b) (hf : AntitoneOn f (Icc a b)) :
+ ∑ i ∈ .Ico a b, f ↑(i + 1) ≤ ∫ x in a..b, f x := by
+ suffices ∑ i ∈ .Ico (0 + a) (b - a + a), f ↑(i + 1) ≤ ∫ x in a..a + ↑(b - a), f x by simp_all
+ simp_rw [← Finset.sum_Ico_add, Nat.Ico_zero_eq_range, add_assoc]
+ suffices ∑ x ∈ .range (b - a), f (a + ↑(x + 1)) ≤ ∫ x in a..a + ↑(b - a), f x by simp_all
+ exact AntitoneOn.sum_le_integral (by simp [hf, hab])
theorem MonotoneOn.sum_le_integral (hf : MonotoneOn f (Icc x₀ (x₀ + a))) :
- (∑ i ∈ Finset.range a, f (x₀ + i)) ≤ ∫ x in x₀..x₀ + a, f x := by
+ ∑ i ∈ .range a, f (x₀ + i) ≤ ∫ x in x₀..x₀ + a, f x := by
rw [← neg_le_neg_iff, ← Finset.sum_neg_distrib, ← intervalIntegral.integral_neg]
exact hf.neg.integral_le_sum
theorem MonotoneOn.sum_le_integral_Ico (hab : a ≤ b) (hf : MonotoneOn f (Set.Icc a b)) :
- ∑ x ∈ Finset.Ico a b, f x ≤ ∫ x in a..b, f x := by
+ ∑ x ∈ .Ico a b, f x ≤ ∫ x in a..b, f x := by
rw [← neg_le_neg_iff, ← Finset.sum_neg_distrib, ← intervalIntegral.integral_neg]
exact hf.neg.integral_le_sum_Ico hab
theorem MonotoneOn.integral_le_sum (hf : MonotoneOn f (Icc x₀ (x₀ + a))) :
- (∫ x in x₀..x₀ + a, f x) ≤ ∑ i ∈ Finset.range a, f (x₀ + (i + 1 : ℕ)) := by
+ ∫ x in x₀..x₀ + a, f x ≤ ∑ i ∈ .range a, f (x₀ + ↑(i + 1)) := by
rw [← neg_le_neg_iff, ← Finset.sum_neg_distrib, ← intervalIntegral.integral_neg]
exact hf.neg.sum_le_integral
theorem MonotoneOn.integral_le_sum_Ico (hab : a ≤ b) (hf : MonotoneOn f (Set.Icc a b)) :
- (∫ x in a..b, f x) ≤ ∑ i ∈ Finset.Ico a b, f (i + 1 : ℕ) := by
+ ∫ x in a..b, f x ≤ ∑ i ∈ .Ico a b, f ↑(i + 1) := by
rw [← neg_le_neg_iff, ← Finset.sum_neg_distrib, ← intervalIntegral.integral_neg]
exact hf.neg.sum_le_integral_Ico hab
lemma sum_mul_Ico_le_integral_of_monotone_antitone
(hab : a ≤ b) (hf : MonotoneOn f (Icc a b)) (hg : AntitoneOn g (Icc (a - 1) (b - 1)))
(fpos : 0 ≤ f a) (gpos : 0 ≤ g (b - 1)) :
- ∑ i ∈ Finset.Ico a b, f i * g i ≤ ∫ x in a..b, f x * g (x - 1) := by
+ ∑ i ∈ .Ico a b, f i * g i ≤ ∫ x in a..b, f x * g (x - 1) := by
apply sum_Ico_le_integral_of_le (f := fun x ↦ f x * g x) hab
· intro i hi x hx
- simp only [Nat.cast_add, Nat.cast_one, mem_Ico] at hx hi
- have I0 : (i : ℝ) ≤ b - 1 := by
- simp only [le_sub_iff_add_le]
- norm_cast
- lia
- have I1 : (i : ℝ) ∈ Icc (a - 1 : ℝ) (b - 1) := by
- simp only [mem_Icc, tsub_le_iff_right]
- exact ⟨by norm_cast; lia, I0⟩
- have I2 : x ∈ Icc (a : ℝ) b := by
- refine ⟨le_trans (mod_cast hi.1) hx.1, hx.2.le.trans ?_⟩
- norm_cast
- lia
- apply mul_le_mul
- · apply hf
- · simp only [mem_Icc, Nat.cast_le]
- exact ⟨hi.1, hi.2.le⟩
- · exact I2
- · exact hx.1
- · apply hg
- · simp only [mem_Icc, tsub_le_iff_right, sub_add_cancel]
- refine ⟨le_trans (mod_cast hi.1) hx.1, hx.2.le.trans ?_⟩
- norm_cast
- lia
- · exact I1
- · simpa [sub_le_iff_le_add] using hx.2.le
- · apply gpos.trans
- apply hg I1 (by simp [hab]) I0
- · apply fpos.trans
- apply hf (by simp [hab]) I2
- exact le_trans (mod_cast hi.1) hx.1
- · apply Integrable.mono_measure _ (Measure.restrict_mono_set _ Ico_subset_Icc_self)
- apply Integrable.mul_of_top_left
- · exact hf.integrableOn_isCompact isCompact_Icc
- · apply AntitoneOn.memLp_isCompact isCompact_Icc
- intro x hx y hy hxy
- apply hg
- · simpa using hx
- · simpa using hy
- · simpa using hxy
+ simp only [Nat.cast_add, Nat.cast_one, mem_Ico, ← Nat.add_one_le_iff] at hx hi
+ rify at hi
+ gcongr
+ · grw [gpos]; apply hg <;> grind
+ · grw [fpos]; apply hf <;> grind
+ · apply hf <;> grind
+ · apply hg <;> grind
+ · apply Integrable.mono_measure _ (volume.restrict_mono_set Ico_subset_Icc_self)
+ apply (hf.integrableOn_isCompact isCompact_Icc).mul_of_top_left
+ apply AntitoneOn.memLp_isCompact isCompact_Icc
+ intro _ _ _ _ _
+ apply hg <;> grind
lemma integral_le_sum_mul_Ico_of_antitone_monotone
(hab : a ≤ b) (hf : AntitoneOn f (Icc a b)) (hg : MonotoneOn g (Icc (a - 1) (b - 1)))
(fpos : 0 ≤ f b) (gpos : 0 ≤ g (a - 1)) :
- ∫ x in a..b, f x * g (x - 1) ≤ ∑ i ∈ Finset.Ico a b, f i * g i := by
+ ∫ x in a..b, f x * g (x - 1) ≤ ∑ i ∈ .Ico a b, f i * g i := by
apply integral_le_sum_Ico_of_le (f := fun x ↦ f x * g x) hab
· intro i hi x hx
- simp only [Nat.cast_add, Nat.cast_one, mem_Ico] at hx hi
- have I0 : (i : ℝ) ≤ b - 1 := by
- simp only [le_sub_iff_add_le]
- norm_cast
- lia
- have I1 : (i : ℝ) ∈ Icc (a - 1 : ℝ) (b - 1) := by
- simp only [mem_Icc, tsub_le_iff_right]
- exact ⟨by norm_cast; lia, I0⟩
- have I2 : x ∈ Icc (a : ℝ) b := by
- refine ⟨le_trans (mod_cast hi.1) hx.1, hx.2.le.trans ?_⟩
- norm_cast
- lia
- apply mul_le_mul
- · apply hf
- · simp only [mem_Icc, Nat.cast_le]
- exact ⟨hi.1, hi.2.le⟩
- · exact I2
- · exact hx.1
- · apply hg
- · simp only [mem_Icc, tsub_le_iff_right, sub_add_cancel]
- refine ⟨le_trans (mod_cast hi.1) hx.1, hx.2.le.trans ?_⟩
- norm_cast
- lia
- · exact I1
- · simpa [sub_le_iff_le_add] using hx.2.le
- · apply gpos.trans
- apply hg (by simp [hab]) (by simpa using I2) (by simpa using I2.1)
- · apply fpos.trans
- apply hf ⟨mod_cast hi.1, mod_cast hi.2.le⟩ (by simpa using hab) (mod_cast hi.2.le)
- · apply Integrable.mono_measure _ (Measure.restrict_mono_set _ Ico_subset_Icc_self)
- apply Integrable.mul_of_top_left
- · exact hf.integrableOn_isCompact isCompact_Icc
- · apply MonotoneOn.memLp_isCompact isCompact_Icc
- intro x hx y hy hxy
- apply hg
- · simpa using hx
- · simpa using hy
- · simpa using hxy
+ simp only [Nat.cast_add, Nat.cast_one, mem_Ico, ← Nat.add_one_le_iff] at hx hi
+ rify at hi
+ gcongr
+ · grw [gpos]; apply hg <;> grind
+ · grw [fpos]; apply hf <;> grind
+ · apply hf <;> grind
+ · apply hg <;> grind
+ · apply Integrable.mono_measure _ (volume.restrict_mono_set Ico_subset_Icc_self)
+ apply (hf.integrableOn_isCompact isCompact_Icc).mul_of_top_left
+ apply MonotoneOn.memLp_isCompact isCompact_Icc
+ intro _ _ _ _ _
+ apply hg <;> grind
From 9d11f4f019262236efcde4231f455265d2021f05 Mon Sep 17 00:00:00 2001
From: Pan Lin <58059503+HugLycan@users.noreply.github.com>
Date: Wed, 17 Jun 2026 15:42:38 +0000
Subject: [PATCH 0119/1300] feat(Tactic/Positivity): make positivity work for
types that are not partial orders (#35394)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Make positivity work for types that are not partial orders
Most PositivityExt haven't been updated for non partial order cases yet. They will be updated in the later PR.
`Strictness` now depends on `Option Q(PartialOrder $α)` instead of `Q(PartialOrder $α)`, and the constructors `Strictness.positive`/`Strictness.nonnegative` now have their `Q(PartialOrder $α)` typeclass arguments.
Co-authored-by: Floris van Doorn
---
.../Algebra/Order/AbsoluteValue/Basic.lean | 3 +-
Mathlib/Algebra/Order/Algebra.lean | 6 +-
.../Algebra/Order/BigOperators/Expect.lean | 3 +-
.../Order/BigOperators/Ring/Finset.lean | 3 +-
Mathlib/Algebra/Order/Field/Basic.lean | 39 +-
Mathlib/Algebra/Order/Field/Power.lean | 14 +-
Mathlib/Algebra/Order/Floor/Extended.lean | 6 +-
Mathlib/Algebra/Order/Floor/Ring.lean | 15 +-
Mathlib/Algebra/Order/Interval/Basic.lean | 6 +-
Mathlib/Algebra/Order/Module/Field.lean | 3 +-
Mathlib/Analysis/Complex/Exponential.lean | 3 +-
Mathlib/Analysis/Complex/Order.lean | 13 +-
Mathlib/Analysis/Complex/Trigonometric.lean | 3 +-
.../Complex/UpperHalfPlane/Basic.lean | 6 +-
Mathlib/Analysis/Normed/Group/Basic.lean | 6 +-
Mathlib/Analysis/Real/Sqrt.lean | 10 +-
.../Analysis/SpecialFunctions/Bernstein.lean | 3 +-
.../SpecialFunctions/Gamma/Basic.lean | 5 +-
.../Analysis/SpecialFunctions/Log/Basic.lean | 9 +-
.../Analysis/SpecialFunctions/Pow/NNReal.lean | 28 +-
.../Analysis/SpecialFunctions/Pow/Real.lean | 12 +-
.../Trigonometric/Arctan.lean | 29 +-
.../SpecialFunctions/Trigonometric/Basic.lean | 3 +-
.../Trigonometric/DerivHyp.lean | 3 +-
.../Combinatorics/Enumerative/DyckWord.lean | 5 +-
.../SimpleGraph/Regularity/Bound.lean | 6 +-
.../SimpleGraph/Triangle/Removal.lean | 5 +-
Mathlib/Data/ENNReal/Basic.lean | 8 +-
Mathlib/Data/ENNReal/Real.lean | 5 +-
Mathlib/Data/EReal/Basic.lean | 20 +-
Mathlib/Data/EReal/Inv.lean | 6 +-
Mathlib/Data/EReal/Operations.lean | 6 +-
Mathlib/Data/NNReal/Defs.lean | 15 +-
.../Data/Nat/Factorial/DoubleFactorial.lean | 3 +-
Mathlib/Data/Nat/Totient.lean | 5 +-
Mathlib/Data/Rat/Cast/Order.lean | 12 +-
Mathlib/Geometry/Euclidean/Altitude.lean | 3 +-
.../MeasureTheory/Covering/Besicovitch.lean | 3 +-
.../MeasureTheory/Integral/Bochner/Basic.lean | 3 +-
Mathlib/MeasureTheory/Measure/Real.lean | 3 +-
.../NumberTheory/ArithmeticFunction/Misc.lean | 5 +-
.../NumberTheory/ArithmeticFunction/Zeta.lean | 5 +-
Mathlib/NumberTheory/Height/Basic.lean | 12 +-
Mathlib/NumberTheory/Height/NumberField.lean | 3 +-
.../NumberTheory/Height/Projectivization.lean | 6 +-
Mathlib/NumberTheory/LucasLehmer.lean | 5 +-
Mathlib/NumberTheory/SelbergSieve.lean | 3 +-
Mathlib/Tactic/Positivity/Basic.lean | 376 +++++++++++-------
Mathlib/Tactic/Positivity/Core.lean | 162 +++++---
Mathlib/Tactic/Positivity/Finset.lean | 14 +-
.../Topology/Algebra/InfiniteSum/Order.lean | 3 +-
Mathlib/Topology/MetricSpace/Bounded.lean | 3 +-
Mathlib/Topology/MetricSpace/Pseudo/Defs.lean | 3 +-
MathlibTest/positivity.lean | 27 ++
54 files changed, 612 insertions(+), 354 deletions(-)
diff --git a/Mathlib/Algebra/Order/AbsoluteValue/Basic.lean b/Mathlib/Algebra/Order/AbsoluteValue/Basic.lean
index 6e11dcc3275e68..2438aca9fe70bd 100644
--- a/Mathlib/Algebra/Order/AbsoluteValue/Basic.lean
+++ b/Mathlib/Algebra/Order/AbsoluteValue/Basic.lean
@@ -416,7 +416,8 @@ open Lean Meta Mathlib Meta Positivity Qq in
For performance reasons, we only attempt to apply this when `abv` is a variable.
If it is an explicit function, e.g. `|_|` or `‖_‖`, another extension should apply. -/
@[positivity _]
-meta def Mathlib.Meta.Positivity.evalAbv : PositivityExt where eval {_ _α} _zα _pα e := do
+meta def Mathlib.Meta.Positivity.evalAbv : PositivityExt where eval {_ _α} _zα pα? e := do
+ let some _ := pα? | pure .none
let (.app f a) ← whnfR e | throwError "not abv ·"
if !f.getAppFn.isFVar then
throwError "abv: function is not a variable"
diff --git a/Mathlib/Algebra/Order/Algebra.lean b/Mathlib/Algebra/Order/Algebra.lean
index b2c00ad489a471..c5a1cbd199b481 100644
--- a/Mathlib/Algebra/Order/Algebra.lean
+++ b/Mathlib/Algebra/Order/Algebra.lean
@@ -95,11 +95,12 @@ open Lean Meta Qq Function
/-- Extension for `algebraMap`. -/
@[positivity algebraMap _ _ _]
-meta def evalAlgebraMap : PositivityExt where eval {u β} _zβ _pβ e := do
+meta def evalAlgebraMap : PositivityExt where eval {u β} _zβ pβ? e := do
let ~q(@algebraMap $α _ $instα $instβ $instαβ $a) := e | throwError "not `algebraMap`"
- let pα ← synthInstanceQ q(PartialOrder $α)
+ let pα ← try? <| synthInstanceQ q(PartialOrder $α)
match ← core q(inferInstance) pα a with
| .positive pa =>
+ let some _ := pβ? | pure .none
let _instαSemiring ← synthInstanceQ q(Semiring $α)
let _instαPartialOrder ← synthInstanceQ q(PartialOrder $α)
try
@@ -117,6 +118,7 @@ meta def evalAlgebraMap : PositivityExt where eval {u β} _zβ _pβ e := do
assertInstancesCommute
return .nonnegative q(algebraMap_nonneg $β <| le_of_lt $pa)
| .nonnegative pa =>
+ let some _ := pβ? | pure .none
let _instαSemiring ← synthInstanceQ q(CommSemiring $α)
let _instαPartialOrder ← synthInstanceQ q(PartialOrder $α)
let _instβSemiring ← synthInstanceQ q(Semiring $β)
diff --git a/Mathlib/Algebra/Order/BigOperators/Expect.lean b/Mathlib/Algebra/Order/BigOperators/Expect.lean
index 8f77dc667d84d0..2f7e1bf8a89631 100644
--- a/Mathlib/Algebra/Order/BigOperators/Expect.lean
+++ b/Mathlib/Algebra/Order/BigOperators/Expect.lean
@@ -220,7 +220,8 @@ open scoped BigOperators
attribute [local instance] monadLiftOptionMetaM in
/-- Positivity extension for `Finset.expect`. -/
@[positivity Finset.expect _ _]
-meta def evalFinsetExpect : PositivityExt where eval {u α} zα pα e := do
+meta def evalFinsetExpect : PositivityExt where eval {u α} zα pα? e := do
+ let some pα := pα? | pure .none
match e with
| ~q(@Finset.expect $ι _ $instα $instmod $s $f) =>
let i : Q($ι) ← mkFreshExprMVarQ q($ι) .syntheticOpaque
diff --git a/Mathlib/Algebra/Order/BigOperators/Ring/Finset.lean b/Mathlib/Algebra/Order/BigOperators/Ring/Finset.lean
index b4ccd4aed08988..f4b2836ef9c166 100644
--- a/Mathlib/Algebra/Order/BigOperators/Ring/Finset.lean
+++ b/Mathlib/Algebra/Order/BigOperators/Ring/Finset.lean
@@ -225,9 +225,10 @@ example (s : Finset ℕ) (f : ℕ → ℤ) (hf : ∀ n, 0 ≤ f n) : 0 ≤ s.pro
because `compareHyp` can't look for assumptions behind binders.
-/
@[positivity Finset.prod _ _]
-meta def evalFinsetProd : PositivityExt where eval {u α} zα pα e := do
+meta def evalFinsetProd : PositivityExt where eval {u α} zα pα? e := do
match e with
| ~q(@Finset.prod $ι _ $instα $s $f) =>
+ let some pα := pα? | pure .none
let i : Q($ι) ← mkFreshExprMVarQ q($ι) .syntheticOpaque
have body : Q($α) := Expr.betaRev f #[i]
let rbody ← core zα pα body
diff --git a/Mathlib/Algebra/Order/Field/Basic.lean b/Mathlib/Algebra/Order/Field/Basic.lean
index aadae9becada01..6db675d49c48a0 100644
--- a/Mathlib/Algebra/Order/Field/Basic.lean
+++ b/Mathlib/Algebra/Order/Field/Basic.lean
@@ -733,15 +733,23 @@ lemma zpow_zero_pos {α : Type*} [Semifield α] [PartialOrder α] [IsStrictOrder
/-- The `positivity` extension which identifies expressions of the form `a / b`,
such that `positivity` successfully recognises both `a` and `b`. -/
-@[positivity _ / _] meta def evalDiv : PositivityExt where eval {u α} zα pα e := do
+@[positivity _ / _] meta def evalDiv : PositivityExt where eval {u α} zα pα? e := do
let .app (.app (f : Q($α → $α → $α)) (a : Q($α))) (b : Q($α)) ← withReducible (whnf e)
| throwError "not /"
let _e_eq : $e =Q $f $a $b := ⟨⟩
+ trace[Tactic.positivity.zeroness] "evalDiv: {a} divided by {b}"
+ let _a ← synthInstanceQ q(Semifield $α)
+ let ⟨_f_eq⟩ ← withDefault <| withNewMCtxDepth <| assertDefEqQ q($f) q(HDiv.hDiv)
+ let some pα := pα? |
+ match ← core zα pα? a, ← core zα pα? b with
+ | .nonzero pa, .nonzero pb =>
+ let _a ← synthInstanceQ q(GroupWithZero $α)
+ assumeInstancesCommute
+ pure (.nonzero q(div_ne_zero $pa $pb))
+ | _, _ => pure .none
let _a ← synthInstanceQ q(GroupWithZero $α)
- let _a ← synthInstanceQ q(PartialOrder $α)
let _a ← synthInstanceQ q(PosMulReflectLT $α)
assumeInstancesCommute
- let ⟨_f_eq⟩ ← withDefault <| withNewMCtxDepth <| assertDefEqQ q($f) q(HDiv.hDiv)
let ra ← core zα pα a; let rb ← core zα pα b
match ra, rb with
| .positive pa, .positive pb => pure (.positive q(div_pos $pa $pb))
@@ -756,25 +764,38 @@ such that `positivity` successfully recognises both `a` and `b`. -/
/-- The `positivity` extension which identifies expressions of the form `a⁻¹`,
such that `positivity` successfully recognises `a`. -/
@[positivity _⁻¹]
-meta def evalInv : PositivityExt where eval {u α} zα pα e := do
+meta def evalInv : PositivityExt where eval {u α} zα pα? e := do
let .app (f : Q($α → $α)) (a : Q($α)) ← withReducible (whnf e) | throwError "not ⁻¹"
let _e_eq : $e =Q $f $a := ⟨⟩
+ let _a ← synthInstanceQ q(Semifield $α)
+ let ⟨_f_eq⟩ ← withDefault <| withNewMCtxDepth <| assertDefEqQ q($f) q(Inv.inv)
+ let some _ := pα? |
+ match ← core zα pα? a with
+ | .nonzero pa =>
+ let _a ← synthInstanceQ q(GroupWithZero $α)
+ assumeInstancesCommute
+ pure (.nonzero q(inv_ne_zero $pa))
+ | _ => pure .none
let _a ← synthInstanceQ q(GroupWithZero $α)
let _a ← synthInstanceQ q(PartialOrder $α)
let _a ← synthInstanceQ q(PosMulReflectLT $α)
assumeInstancesCommute
- let ⟨_f_eq⟩ ← withDefault <| withNewMCtxDepth <| assertDefEqQ q($f) q(Inv.inv)
- let ra ← core zα pα a
+ let ra ← core zα pα? a
match ra with
- | .positive pa => pure (.positive q(inv_pos_of_pos $pa))
- | .nonnegative pa => pure (.nonnegative q(inv_nonneg_of_nonneg $pa))
+ | .positive pa =>
+ assumeInstancesCommute
+ pure (.positive q(inv_pos_of_pos $pa))
+ | .nonnegative pa =>
+ assumeInstancesCommute
+ pure (.nonnegative q(inv_nonneg_of_nonneg $pa))
| .nonzero pa => pure (.nonzero q(inv_ne_zero $pa))
| .none => pure .none
/-- The `positivity` extension which identifies expressions of the form `a ^ (0:ℤ)`. -/
@[positivity _ ^ (0 : ℤ), Pow.pow _ (0 : ℤ)]
-meta def evalPowZeroInt : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalPowZeroInt : PositivityExt where eval {u α} _zα pα? e := do
let .app (.app _ (a : Q($α))) _ ← withReducible (whnf e) | throwError "not ^"
+ let some _ := pα? | pure .none
let _a ← synthInstanceQ q(Semifield $α)
let _a ← synthInstanceQ q(LinearOrder $α)
let _a ← synthInstanceQ q(IsStrictOrderedRing $α)
diff --git a/Mathlib/Algebra/Order/Field/Power.lean b/Mathlib/Algebra/Order/Field/Power.lean
index 8f888e8a9c5c2a..921d4a96f7f17f 100644
--- a/Mathlib/Algebra/Order/Field/Power.lean
+++ b/Mathlib/Algebra/Order/Field/Power.lean
@@ -123,8 +123,16 @@ open Lean Meta Qq
/-- The `positivity` extension which identifies expressions of the form `a ^ (b : ℤ)`,
such that `positivity` successfully recognises both `a` and `b`. -/
@[positivity _ ^ (_ : ℤ), Pow.pow _ (_ : ℤ)]
-meta def evalZPow : PositivityExt where eval {u α} zα pα e := do
+meta def evalZPow : PositivityExt where eval {u α} zα pα? e := do
let .app (.app _ (a : Q($α))) (b : Q(ℤ)) ← withReducible (whnf e) | throwError "not ^"
+ let some pα := pα? |
+ match ← core zα pα? a with
+ | .nonzero pa =>
+ let _a ← synthInstanceQ q(GroupWithZero $α)
+ assumeInstancesCommute
+ haveI' : $e =Q $a ^ $b := ⟨⟩
+ pure (.nonzero q(zpow_ne_zero $b $pa))
+ | _ => pure .none
let result ← catchNone do
let _a ← synthInstanceQ q(Field $α)
let _a ← synthInstanceQ q(LinearOrder $α)
@@ -151,11 +159,11 @@ meta def evalZPow : PositivityExt where eval {u α} zα pα e := do
let ra ← core zα pα a
let ofNonneg (pa : Q(0 ≤ $a))
(_oα : Q(Semifield $α)) (_oα : Q(LinearOrder $α)) (_oα : Q(IsStrictOrderedRing $α)) :
- MetaM (Strictness zα pα e) := do
+ MetaM (Strictness zα e pα) := do
haveI' : $e =Q $a ^ $b := ⟨⟩
assumeInstancesCommute
pure (.nonnegative q(zpow_nonneg $pa $b))
- let ofNonzero (pa : Q($a ≠ 0)) (_oα : Q(GroupWithZero $α)) : MetaM (Strictness zα pα e) := do
+ let ofNonzero (pa : Q($a ≠ 0)) (_oα : Q(GroupWithZero $α)) : MetaM (Strictness zα e pα) := do
haveI' : $e =Q $a ^ $b := ⟨⟩
let _a ← synthInstanceQ q(GroupWithZero $α)
assumeInstancesCommute
diff --git a/Mathlib/Algebra/Order/Floor/Extended.lean b/Mathlib/Algebra/Order/Floor/Extended.lean
index e1f38ba80964d0..3708cc1a32b067 100644
--- a/Mathlib/Algebra/Order/Floor/Extended.lean
+++ b/Mathlib/Algebra/Order/Floor/Extended.lean
@@ -256,11 +256,11 @@ alias ⟨_, natCeil_pos⟩ := ENat.ceil_pos
/-- Extension for the `positivity` tactic: `ENat.ceil` is positive if its input is. -/
@[positivity ⌈_⌉ₑ]
-meta def evalENatCeil : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalENatCeil : PositivityExt where eval {u α} _zα pα? e := do
match u, α, e with
| 0, ~q(ℕ∞), ~q(ENat.ceil $r) =>
- assertInstancesCommute
- match ← core q(inferInstance) q(inferInstance) r with
+ let some _ := pα? | pure .none
+ match ← core q(inferInstance) (some q(inferInstance)) r with
| .positive pr =>
assertInstancesCommute
pure (.positive q(natCeil_pos $pr))
diff --git a/Mathlib/Algebra/Order/Floor/Ring.lean b/Mathlib/Algebra/Order/Floor/Ring.lean
index 79a3481beeae1e..3537d313859ee3 100644
--- a/Mathlib/Algebra/Order/Floor/Ring.lean
+++ b/Mathlib/Algebra/Order/Floor/Ring.lean
@@ -50,10 +50,11 @@ theorem int_floor_nonneg_of_pos [Ring α] [LinearOrder α] [FloorRing α] {a :
/-- Extension for the `positivity` tactic: `Int.floor` is nonnegative if its input is. -/
@[positivity ⌊_⌋]
-meta def evalIntFloor : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalIntFloor : PositivityExt where eval {u α} _zα pα? e := do
match u, α, e with
| 0, ~q(ℤ), ~q(@Int.floor $α' $ir $io $j $a) =>
- match ← core q(inferInstance) q(inferInstance) a with
+ let some _ := pα? | pure .none
+ match ← core q(inferInstance) (some q(inferInstance)) a with
| .positive pa =>
assertInstancesCommute
pure (.nonnegative q(int_floor_nonneg_of_pos (α := $α') $pa))
@@ -69,13 +70,14 @@ theorem nat_ceil_pos [Semiring α] [LinearOrder α] [FloorSemiring α] {a : α}
/-- Extension for the `positivity` tactic: `Nat.ceil` is positive if its input is. -/
@[positivity ⌈_⌉₊]
-meta def evalNatCeil : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalNatCeil : PositivityExt where eval {u α} _zα pα? e := do
match u, α, e with
| 0, ~q(ℕ), ~q(@Nat.ceil $α' $ir $io $j $a) =>
+ let some _ := pα? | pure .none
let _i ← synthInstanceQ q(LinearOrder $α')
let _i ← synthInstanceQ q(IsStrictOrderedRing $α')
assertInstancesCommute
- match ← core q(inferInstance) q(inferInstance) a with
+ match ← core q(inferInstance) (some q(inferInstance)) a with
| .positive pa =>
assertInstancesCommute
pure (.positive q(nat_ceil_pos (α := $α') $pa))
@@ -87,10 +89,11 @@ theorem int_ceil_pos [Ring α] [LinearOrder α] [FloorRing α] {a : α} : 0 < a
/-- Extension for the `positivity` tactic: `Int.ceil` is positive/nonnegative if its input is. -/
@[positivity ⌈_⌉]
-meta def evalIntCeil : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalIntCeil : PositivityExt where eval {u α} _zα pα? e := do
match u, α, e with
| 0, ~q(ℤ), ~q(@Int.ceil $α' $ir $io $j $a) =>
- match ← core q(inferInstance) q(inferInstance) a with
+ let some _ := pα? | pure .none
+ match ← core q(inferInstance) (some q(inferInstance)) a with
| .positive pa =>
assertInstancesCommute
pure (.positive q(int_ceil_pos (α := $α') $pa))
diff --git a/Mathlib/Algebra/Order/Interval/Basic.lean b/Mathlib/Algebra/Order/Interval/Basic.lean
index 714243ac6829cd..620b79a1c6fa82 100644
--- a/Mathlib/Algebra/Order/Interval/Basic.lean
+++ b/Mathlib/Algebra/Order/Interval/Basic.lean
@@ -660,9 +660,10 @@ open Lean Meta Qq
/-- Extension for the `positivity` tactic: The length of an interval is always nonnegative. -/
@[positivity NonemptyInterval.length _]
meta def evalNonemptyIntervalLength : PositivityExt where
- eval {u α} _ _ e := do
+ eval {u α} _ pα? e := do
let ~q(@NonemptyInterval.length _ $ig $ipo $a) := e |
throwError "not NonemptyInterval.length"
+ let some _ := pα? | pure .none
let _i ← synthInstanceQ q(IsOrderedAddMonoid $α)
assertInstancesCommute
return .nonnegative q(NonemptyInterval.length_nonneg $a)
@@ -670,8 +671,9 @@ meta def evalNonemptyIntervalLength : PositivityExt where
/-- Extension for the `positivity` tactic: The length of an interval is always nonnegative. -/
@[positivity Interval.length _]
meta def evalIntervalLength : PositivityExt where
- eval {u α} _ _ e := do
+ eval {u α} _ pα? e := do
let ~q(@Interval.length _ $ig $ipo $a) := e | throwError "not Interval.length"
+ let some _ := pα? | pure .none
let _i ← synthInstanceQ q(IsOrderedAddMonoid $α)
assumeInstancesCommute
return .nonnegative q(Interval.length_nonneg $a)
diff --git a/Mathlib/Algebra/Order/Module/Field.lean b/Mathlib/Algebra/Order/Module/Field.lean
index 9e248fcace8a25..f1575550df9058 100644
--- a/Mathlib/Algebra/Order/Module/Field.lean
+++ b/Mathlib/Algebra/Order/Module/Field.lean
@@ -103,7 +103,8 @@ end Module.IsTorsionFree
/-- Positivity extension for scalar multiplication. -/
@[positivity HSMul.hSMul _ _]
-meta def evalSMul : PositivityExt where eval {_u α} zα pα (e : Q($α)) := do
+meta def evalSMul : PositivityExt where eval {_u α} zα pα? (e : Q($α)) := do
+ let some pα := pα? | pure .none
let .app (.app (.app (.app (.app (.app
(.const ``HSMul.hSMul [u1, _, _]) (β : Q(Type u1))) _) _) _)
(a : Q($β))) (b : Q($α)) ← whnfR e | throwError "failed to match hSMul"
diff --git a/Mathlib/Analysis/Complex/Exponential.lean b/Mathlib/Analysis/Complex/Exponential.lean
index 58e394719d257c..9a51b9898ee788 100644
--- a/Mathlib/Analysis/Complex/Exponential.lean
+++ b/Mathlib/Analysis/Complex/Exponential.lean
@@ -692,9 +692,10 @@ open Lean.Meta Qq
/-- Extension for the `positivity` tactic: `Real.exp` is always positive. -/
@[positivity Real.exp _]
-meta def evalExp : PositivityExt where eval {u α} _ _ e := do
+meta def evalExp : PositivityExt where eval {u α} _ pα? e := do
match u, α, e with
| 0, ~q(ℝ), ~q(Real.exp $a) =>
+ let some _ := pα? | pure .none
assertInstancesCommute
pure (.positive q(Real.exp_pos $a))
| _, _, _ => throwError "not Real.exp"
diff --git a/Mathlib/Analysis/Complex/Order.lean b/Mathlib/Analysis/Complex/Order.lean
index 179d46a06f6aba..03bf6ffb6cb7fa 100644
--- a/Mathlib/Analysis/Complex/Order.lean
+++ b/Mathlib/Analysis/Complex/Order.lean
@@ -142,19 +142,12 @@ alias ⟨_, ofReal_ne_zero_of_ne_zero⟩ := ofReal_ne_zero
/-- Extension for the `positivity` tactic: `Complex.ofReal` is positive/nonnegative/nonzero if its
input is. -/
@[positivity Complex.ofReal _, Complex.ofReal _]
-meta def evalComplexOfReal : PositivityExt where eval {u α} _ _ e := do
- -- TODO: Can we avoid duplicating the code?
+meta def evalComplexOfReal : PositivityExt where eval {u α} _ pα? e := do
match u, α, e with
| 0, ~q(ℂ), ~q(Complex.ofReal $a) =>
+ let some _ := pα? | pure .none
assumeInstancesCommute
- match ← core q(inferInstance) q(inferInstance) a with
- | .positive pa => return .positive q(ofReal_pos $pa)
- | .nonnegative pa => return .nonnegative q(ofReal_nonneg $pa)
- | .nonzero pa => return .nonzero q(ofReal_ne_zero_of_ne_zero $pa)
- | _ => return .none
- | 0, ~q(ℂ), ~q(Complex.ofReal $a) =>
- assumeInstancesCommute
- match ← core q(inferInstance) q(inferInstance) a with
+ match ← core q(inferInstance) (some q(inferInstance)) a with
| .positive pa => return .positive q(ofReal_pos $pa)
| .nonnegative pa => return .nonnegative q(ofReal_nonneg $pa)
| .nonzero pa => return .nonzero q(ofReal_ne_zero_of_ne_zero $pa)
diff --git a/Mathlib/Analysis/Complex/Trigonometric.lean b/Mathlib/Analysis/Complex/Trigonometric.lean
index b00c1f90b5c481..44d62339fd1e11 100644
--- a/Mathlib/Analysis/Complex/Trigonometric.lean
+++ b/Mathlib/Analysis/Complex/Trigonometric.lean
@@ -940,9 +940,10 @@ open Lean.Meta Qq
/-- Extension for the `positivity` tactic: `Real.cosh` is always positive. -/
@[positivity Real.cosh _]
-meta def evalCosh : PositivityExt where eval {u α} _ _ e := do
+meta def evalCosh : PositivityExt where eval {u α} _ pα? e := do
match u, α, e with
| 0, ~q(ℝ), ~q(Real.cosh $a) =>
+ let some _ := pα? | pure .none
assertInstancesCommute
return .positive q(Real.cosh_pos $a)
| _, _, _ => throwError "not Real.cosh"
diff --git a/Mathlib/Analysis/Complex/UpperHalfPlane/Basic.lean b/Mathlib/Analysis/Complex/UpperHalfPlane/Basic.lean
index 512ba2046a568b..1ce4914b6dffdb 100644
--- a/Mathlib/Analysis/Complex/UpperHalfPlane/Basic.lean
+++ b/Mathlib/Analysis/Complex/UpperHalfPlane/Basic.lean
@@ -148,18 +148,20 @@ open Lean Meta Qq
/-- Extension for the `positivity` tactic: `UpperHalfPlane.im`. -/
@[positivity UpperHalfPlane.im _]
-meta def evalUpperHalfPlaneIm : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalUpperHalfPlaneIm : PositivityExt where eval {u α} _zα pα? e := do
match u, α, e with
| 0, ~q(ℝ), ~q(UpperHalfPlane.im $a) =>
+ let some _ := pα? | pure .none
assertInstancesCommute
pure (.positive q(@UpperHalfPlane.im_pos $a))
| _, _, _ => throwError "not UpperHalfPlane.im"
/-- Extension for the `positivity` tactic: `UpperHalfPlane.coe`. -/
@[positivity UpperHalfPlane.coe _]
-meta def evalUpperHalfPlaneCoe : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalUpperHalfPlaneCoe : PositivityExt where eval {u α} _zα pα? e := do
match u, α, e with
| 0, ~q(ℂ), ~q(UpperHalfPlane.coe $a) =>
+ let some _ := pα? | pure .none
assertInstancesCommute
pure (.nonzero q(@UpperHalfPlane.ne_zero $a))
| _, _, _ => throwError "not UpperHalfPlane.coe"
diff --git a/Mathlib/Analysis/Normed/Group/Basic.lean b/Mathlib/Analysis/Normed/Group/Basic.lean
index 941ebb349425b5..e9a4d77951abf4 100644
--- a/Mathlib/Analysis/Normed/Group/Basic.lean
+++ b/Mathlib/Analysis/Normed/Group/Basic.lean
@@ -1069,9 +1069,10 @@ open Lean Meta Qq Function
/-- Extension for the `positivity` tactic: multiplicative norms are always nonnegative, and positive
on non-one inputs. -/
@[positivity ‖_‖]
-meta def evalMulNorm : PositivityExt where eval {u α} _ _ e := do
+meta def evalMulNorm : PositivityExt where eval {u α} _ pα? e := do
match u, α, e with
| 0, ~q(ℝ), ~q(@Norm.norm $E $_n $a) =>
+ let some _ := pα? | pure .none
let _seminormedGroup_E ← synthInstanceQ q(SeminormedGroup $E)
assertInstancesCommute
-- Check whether we are in a normed group and whether the context contains a `a ≠ 1` assumption
@@ -1091,9 +1092,10 @@ meta def evalMulNorm : PositivityExt where eval {u α} _ _ e := do
/-- Extension for the `positivity` tactic: additive norms are always nonnegative, and positive
on non-zero inputs. -/
@[positivity ‖_‖]
-meta def evalAddNorm : PositivityExt where eval {u α} _ _ e := do
+meta def evalAddNorm : PositivityExt where eval {u α} _ pα? e := do
match u, α, e with
| 0, ~q(ℝ), ~q(@Norm.norm $E $_n $a) =>
+ let some _ := pα? | pure .none
let _seminormedAddGroup_E ← synthInstanceQ q(SeminormedAddGroup $E)
assertInstancesCommute
-- Check whether we are in a normed group and whether the context contains a `a ≠ 0` assumption
diff --git a/Mathlib/Analysis/Real/Sqrt.lean b/Mathlib/Analysis/Real/Sqrt.lean
index 1d7361a4aaa405..bb2d0f3c36425a 100644
--- a/Mathlib/Analysis/Real/Sqrt.lean
+++ b/Mathlib/Analysis/Real/Sqrt.lean
@@ -312,11 +312,12 @@ open Lean Meta Qq Function
/-- Extension for the `positivity` tactic: a square root of a strictly positive nonnegative real is
positive. -/
@[positivity NNReal.sqrt _]
-meta def evalNNRealSqrt : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalNNRealSqrt : PositivityExt where eval {u α} _zα pα? e := do
+ let some _ := pα? | pure .none
match u, α, e with
| 0, ~q(NNReal), ~q(NNReal.sqrt $a) =>
- let ra ← core q(inferInstance) q(inferInstance) a
assertInstancesCommute
+ let ra ← core q(inferInstance) (some q(inferInstance)) a
match ra with
| .positive pa => pure (.positive q(NNReal.sqrt_pos_of_pos $pa))
| _ => failure -- this case is dealt with by generic nonnegativity of nnreals
@@ -325,11 +326,12 @@ meta def evalNNRealSqrt : PositivityExt where eval {u α} _zα _pα e := do
/-- Extension for the `positivity` tactic: a square root is nonnegative, and is strictly positive if
its input is. -/
@[positivity √_]
-meta def evalSqrt : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalSqrt : PositivityExt where eval {u α} _zα pα? e := do
+ let some _ := pα? | pure .none
match u, α, e with
| 0, ~q(ℝ), ~q(√$a) =>
- let ra ← catchNone <| core q(inferInstance) q(inferInstance) a
assertInstancesCommute
+ let ra ← catchNone <| core q(inferInstance) (some q(inferInstance)) a
match ra with
| .positive pa => pure (.positive q(Real.sqrt_pos_of_pos $pa))
| _ => pure (.nonnegative q(Real.sqrt_nonneg $a))
diff --git a/Mathlib/Analysis/SpecialFunctions/Bernstein.lean b/Mathlib/Analysis/SpecialFunctions/Bernstein.lean
index db4e9c639de726..2e650b4aa8f761 100644
--- a/Mathlib/Analysis/SpecialFunctions/Bernstein.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Bernstein.lean
@@ -80,8 +80,9 @@ open Lean Meta Qq Function
/-- Extension of the `positivity` tactic for Bernstein polynomials: they are always non-negative. -/
@[positivity DFunLike.coe (bernstein _ _) _]
-meta def evalBernstein : PositivityExt where eval {_ _} _zα _pα e := do
+meta def evalBernstein : PositivityExt where eval {_ _} _zα pα? e := do
let .app (.app _coe (.app (.app _ n) ν)) x ← whnfR e | throwError "not bernstein polynomial"
+ let some _ := pα? | pure .none
let p ← mkAppOptM ``bernstein_nonneg #[n, ν, x]
pure (.nonnegative p)
diff --git a/Mathlib/Analysis/SpecialFunctions/Gamma/Basic.lean b/Mathlib/Analysis/SpecialFunctions/Gamma/Basic.lean
index d9657e4d690184..16c34c92f0cf0a 100644
--- a/Mathlib/Analysis/SpecialFunctions/Gamma/Basic.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Gamma/Basic.lean
@@ -472,10 +472,11 @@ lemma integral_rpow_mul_exp_neg_mul_Ioi {a r : ℝ} (ha : 0 < a) (hr : 0 < r) :
open Lean.Meta Qq Mathlib.Meta.Positivity in
/-- The `positivity` extension which identifies expressions of the form `Gamma a`. -/
@[positivity Gamma (_ : ℝ)]
-meta def _root_.Mathlib.Meta.Positivity.evalGamma : PositivityExt where eval {u α} _zα _pα e := do
+meta def _root_.Mathlib.Meta.Positivity.evalGamma : PositivityExt where eval {u α} _zα pα? e := do
match u, α, e with
| 0, ~q(ℝ), ~q(Gamma $a) =>
- match ← core q(inferInstance) q(inferInstance) a with
+ let some _ := pα? | pure .none
+ match ← core q(inferInstance) (some q(inferInstance)) a with
| .positive pa =>
assertInstancesCommute
pure (.positive q(Gamma_pos_of_pos $pa))
diff --git a/Mathlib/Analysis/SpecialFunctions/Log/Basic.lean b/Mathlib/Analysis/SpecialFunctions/Log/Basic.lean
index f64147755f1c5c..a50cc14b759129 100644
--- a/Mathlib/Analysis/SpecialFunctions/Log/Basic.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Log/Basic.lean
@@ -596,27 +596,30 @@ lemma log_nz_of_isRat_neg {n : ℤ} : (NormNum.IsRat e n d) → (decide (n / d <
/-- Extension for the `positivity` tactic: `Real.log` of a natural number is always nonnegative. -/
@[positivity Real.log (Nat.cast _)]
-meta def evalLogNatCast : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalLogNatCast : PositivityExt where eval {u α} _zα pα? e := do
match u, α, e with
| 0, ~q(ℝ), ~q(Real.log (Nat.cast $a)) =>
+ let some _ := pα? | pure .none
assertInstancesCommute
pure (.nonnegative q(Real.log_natCast_nonneg $a))
| _, _, _ => throwError "not Real.log"
/-- Extension for the `positivity` tactic: `Real.log` of an integer is always nonnegative. -/
@[positivity Real.log (Int.cast _)]
-meta def evalLogIntCast : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalLogIntCast : PositivityExt where eval {u α} _zα pα? e := do
match u, α, e with
| 0, ~q(ℝ), ~q(Real.log (Int.cast $a)) =>
+ let some _ := pα? | pure .none
assertInstancesCommute
pure (.nonnegative q(Real.log_intCast_nonneg $a))
| _, _, _ => throwError "not Real.log"
/-- Extension for the `positivity` tactic: `Real.log` of a numeric literal. -/
@[positivity Real.log _]
-meta def evalLogNatLit : PositivityExt where eval {u α} _ _ e := do
+meta def evalLogNatLit : PositivityExt where eval {u α} _ pα? e := do
match u, α, e with
| 0, ~q(ℝ), ~q(Real.log $a) =>
+ let some _ := pα? | pure .none
match ← NormNum.derive a with
| .isNat (_ : Q(AddMonoidWithOne ℝ)) lit p =>
assumeInstancesCommute
diff --git a/Mathlib/Analysis/SpecialFunctions/Pow/NNReal.lean b/Mathlib/Analysis/SpecialFunctions/Pow/NNReal.lean
index c76491e3102c03..ccde81c19c3eca 100644
--- a/Mathlib/Analysis/SpecialFunctions/Pow/NNReal.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Pow/NNReal.lean
@@ -1125,18 +1125,20 @@ open Lean Meta Qq
the base is nonnegative and positive when the base is positive.
This is the `NNReal` analogue of `evalRpow` for `Real`. -/
@[positivity (_ : ℝ≥0) ^ (_ : ℝ)]
-meta def evalNNRealRpow : PositivityExt where eval {u α} _ _ e := do
+meta def evalNNRealRpow : PositivityExt where eval {u α} _ pα? e := do
+ let some _ := pα? | pure .none
match u, α, e with
| 0, ~q(ℝ≥0), ~q($a ^ (0 : ℝ)) =>
assertInstancesCommute
pure (.positive q(NNReal.rpow_zero_pos $a))
| 0, ~q(ℝ≥0), ~q($a ^ ($b : ℝ)) =>
- let ra ← core q(inferInstance) q(inferInstance) a
assertInstancesCommute
+ let ra ← core q(inferInstance) (some q(inferInstance)) a
match ra with
| .positive pa =>
- pure (.positive q(NNReal.rpow_pos $pa))
- | _ => pure (.nonnegative q(zero_le (a := $e)))
+ pure (.positive q(NNReal.rpow_pos $pa))
+ | _ =>
+ pure (.nonnegative q(zero_le (a := $e)))
| _, _, _ => throwError "not NNReal.rpow"
private meta def isFiniteM? (x : Q(ℝ≥0∞)) : MetaM (Option Q($x ≠ (⊤ : ℝ≥0∞))) := do
@@ -1153,24 +1155,26 @@ private meta def isFiniteM? (x : Q(ℝ≥0∞)) : MetaM (Option Q($x ≠ (⊤ :
the base is nonnegative and positive when the base is positive.
This is the `ENNReal` analogue of `evalRpow` for `Real`. -/
@[positivity (_ : ℝ≥0∞) ^ (_ : ℝ)]
-meta def evalENNRealRpow : PositivityExt where eval {u α} _ _ e := do
+meta def evalENNRealRpow : PositivityExt where eval {u α} _ pα? e := do
+ let some _ := pα? | pure .none
match u, α, e with
| 0, ~q(ℝ≥0∞), ~q($a ^ (0 : ℝ)) =>
assertInstancesCommute
pure (.positive q(ENNReal.rpow_zero_pos $a))
| 0, ~q(ℝ≥0∞), ~q($a ^ ($b : ℝ)) =>
- let ra ← core q(inferInstance) q(inferInstance) a
- let rb ← catchNone <| core q(inferInstance) q(inferInstance) b
assertInstancesCommute
+ let ra ← core q(inferInstance) (some q(inferInstance)) a
+ let rb ← catchNone <| core q(inferInstance) (some q(inferInstance)) b
match ra, rb with
| .positive pa, .positive pb =>
- pure (.positive q(ENNReal.rpow_pos_of_nonneg $pa <| le_of_lt $pb))
+ pure (.positive q(ENNReal.rpow_pos_of_nonneg $pa <| le_of_lt $pb))
| .positive pa, .nonnegative pb =>
- pure (.positive q(ENNReal.rpow_pos_of_nonneg $pa $pb))
+ pure (.positive q(ENNReal.rpow_pos_of_nonneg $pa $pb))
| .positive pa, _ =>
- let some ha ← isFiniteM? a | pure <| .nonnegative q(zero_le (a := $e))
- pure <| .positive q(ENNReal.rpow_pos $pa $ha)
- | _, _ => pure <| .nonnegative q(zero_le (a := $e))
+ let some ha ← isFiniteM? a | pure <| .nonnegative q(zero_le (a := $e))
+ pure <| .positive q(ENNReal.rpow_pos $pa $ha)
+ | _, _ =>
+ pure <| .nonnegative q(zero_le (a := $e))
| _, _, _ => throwError "not ENNReal.rpow"
end Mathlib.Meta.Positivity
diff --git a/Mathlib/Analysis/SpecialFunctions/Pow/Real.lean b/Mathlib/Analysis/SpecialFunctions/Pow/Real.lean
index 526196e702c259..b220c5e0e31fdb 100644
--- a/Mathlib/Analysis/SpecialFunctions/Pow/Real.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Pow/Real.lean
@@ -375,7 +375,8 @@ open Lean Meta Qq
/-- Extension for the `positivity` tactic: exponentiation by a real number is positive (namely 1)
when the exponent is zero. The other cases are done in `evalRpow`. -/
@[positivity (_ : ℝ) ^ (0 : ℝ)]
-meta def evalRpowZero : PositivityExt where eval {u α} _ _ e := do
+meta def evalRpowZero : PositivityExt where eval {u α} _ pα? e := do
+ let some _ := pα? | pure .none
match u, α, e with
| 0, ~q(ℝ), ~q($a ^ (0 : ℝ)) =>
assertInstancesCommute
@@ -385,16 +386,17 @@ meta def evalRpowZero : PositivityExt where eval {u α} _ _ e := do
/-- Extension for the `positivity` tactic: exponentiation by a real number is nonnegative when
the base is nonnegative and positive when the base is positive. -/
@[positivity (_ : ℝ) ^ (_ : ℝ)]
-meta def evalRpow : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalRpow : PositivityExt where eval {u α} _zα pα? e := do
+ let some _ := pα? | pure .none
match u, α, e with
| 0, ~q(ℝ), ~q($a ^ ($b : ℝ)) =>
- let ra ← core q(inferInstance) q(inferInstance) a
assertInstancesCommute
+ let ra ← core q(inferInstance) (some q(inferInstance)) a
match ra with
| .positive pa =>
- pure (.positive q(Real.rpow_pos_of_pos $pa $b))
+ pure (.positive q(Real.rpow_pos_of_pos $pa $b))
| .nonnegative pa =>
- pure (.nonnegative q(Real.rpow_nonneg $pa $b))
+ pure (.nonnegative q(Real.rpow_nonneg $pa $b))
| _ => pure .none
| _, _, _ => throwError "not Real.rpow"
diff --git a/Mathlib/Analysis/SpecialFunctions/Trigonometric/Arctan.lean b/Mathlib/Analysis/SpecialFunctions/Trigonometric/Arctan.lean
index 1a81fa75ea4bca..db73145896a607 100644
--- a/Mathlib/Analysis/SpecialFunctions/Trigonometric/Arctan.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Trigonometric/Arctan.lean
@@ -403,19 +403,25 @@ meta def evalRealArctan : PositivityExt where eval {u α} z p e := do
match u, α, e with
| 0, ~q(ℝ), ~q(Real.arctan $a) =>
let ra ← core z p a
- assumeInstancesCommute
match ra with
- | .positive pa => return .positive q(Real.arctan_pos.mpr $pa)
- | .nonnegative na => return .nonnegative q(Real.arctan_nonneg.mpr $na)
- | .nonzero na => return .nonzero q(mt Real.arctan_eq_zero_iff.mp $na)
+ | .positive pa =>
+ assumeInstancesCommute
+ return .positive q(Real.arctan_pos.mpr $pa)
+ | .nonnegative na =>
+ assumeInstancesCommute
+ return .nonnegative q(Real.arctan_nonneg.mpr $na)
+ | .nonzero na =>
+ assumeInstancesCommute
+ return .nonzero q(mt Real.arctan_eq_zero_iff.mp $na)
| .none => return .none
| _ => throwError "not Real.arctan"
/-- Extension for `Real.cos (Real.arctan _)`. -/
@[positivity Real.cos (Real.arctan _)]
-meta def evalRealCosArctan : PositivityExt where eval {u α} _ _ e := do
+meta def evalRealCosArctan : PositivityExt where eval {u α} _ pα? e := do
match u, α, e with
| 0, ~q(ℝ), ~q(Real.cos (Real.arctan $a)) =>
+ let some _ := pα? | pure .none
assumeInstancesCommute
return .positive q(Real.cos_arctan_pos _)
| _ => throwError "not Real.cos (Real.arctan _)"
@@ -425,11 +431,16 @@ meta def evalRealCosArctan : PositivityExt where eval {u α} _ _ e := do
meta def evalRealSinArctan : PositivityExt where eval {u α} z p e := do
match u, α, e with
| 0, ~q(ℝ), ~q(Real.sin (Real.arctan $a)) =>
- assumeInstancesCommute
match ← core z p a with
- | .positive pa => return .positive q(Real.sin_arctan_pos.mpr $pa)
- | .nonnegative na => return .nonnegative q(Real.sin_arctan_nonneg.mpr $na)
- | .nonzero na => return .nonzero q(mt Real.sin_arctan_eq_zero.mp $na)
+ | .positive pa =>
+ assumeInstancesCommute
+ return .positive q(Real.sin_arctan_pos.mpr $pa)
+ | .nonnegative na =>
+ assumeInstancesCommute
+ return .nonnegative q(Real.sin_arctan_nonneg.mpr $na)
+ | .nonzero na =>
+ assumeInstancesCommute
+ return .nonzero q(mt Real.sin_arctan_eq_zero.mp $na)
| .none => return .none
| _ => throwError "not Real.sin (Real.arctan _)"
diff --git a/Mathlib/Analysis/SpecialFunctions/Trigonometric/Basic.lean b/Mathlib/Analysis/SpecialFunctions/Trigonometric/Basic.lean
index f673be94a073fd..6d712d0f1aa57e 100644
--- a/Mathlib/Analysis/SpecialFunctions/Trigonometric/Basic.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Trigonometric/Basic.lean
@@ -177,9 +177,10 @@ open Lean.Meta Qq
/-- Extension for the `positivity` tactic: `π` is always positive. -/
@[positivity Real.pi]
-meta def evalRealPi : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalRealPi : PositivityExt where eval {u α} _zα pα? e := do
match u, α, e with
| 0, ~q(ℝ), ~q(Real.pi) =>
+ let some _ := pα? | pure .none
assertInstancesCommute
pure (.positive q(Real.pi_pos))
| _, _, _ => throwError "not Real.pi"
diff --git a/Mathlib/Analysis/SpecialFunctions/Trigonometric/DerivHyp.lean b/Mathlib/Analysis/SpecialFunctions/Trigonometric/DerivHyp.lean
index 1735eed0bfc822..d4ae1114b1700b 100644
--- a/Mathlib/Analysis/SpecialFunctions/Trigonometric/DerivHyp.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Trigonometric/DerivHyp.lean
@@ -798,7 +798,8 @@ alias ⟨_, sinh_ne_zero_of_ne_zero⟩ := Real.sinh_ne_zero
/-- Extension for the `positivity` tactic: `Real.sinh` is positive/nonnegative/nonzero if its input
is. -/
@[positivity Real.sinh _]
-meta def evalSinh : PositivityExt where eval {u α} _ _ e := do
+meta def evalSinh : PositivityExt where eval {u α} _ pα? e := do
+ let some _ := pα? | pure .none
let zα : Q(Zero ℝ) := q(inferInstance)
let pα : Q(PartialOrder ℝ) := q(inferInstance)
match u, α, e with
diff --git a/Mathlib/Combinatorics/Enumerative/DyckWord.lean b/Mathlib/Combinatorics/Enumerative/DyckWord.lean
index 972ca6dccd153a..0dfcee6466b377 100644
--- a/Mathlib/Combinatorics/Enumerative/DyckWord.lean
+++ b/Mathlib/Combinatorics/Enumerative/DyckWord.lean
@@ -557,11 +557,12 @@ open Lean Meta Qq
/-- Extension for the `positivity` tactic: `p.firstReturn` is positive if `p` is nonzero. -/
@[positivity DyckWord.firstReturn _]
-meta def evalDyckWordFirstReturn : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalDyckWordFirstReturn : PositivityExt where eval {u α} _zα pα? e := do
+ let some _ := pα? | pure .none
match u, α, e with
| 0, ~q(ℕ), ~q(DyckWord.firstReturn $a) =>
- let ra ← core q(inferInstance) q(inferInstance) a
assertInstancesCommute
+ let ra ← core q(inferInstance) (some q(inferInstance)) a
match ra with
| .positive pa => pure (.positive q(DyckWord.firstReturn_pos ($pa).ne'))
| .nonzero pa => pure (.positive q(DyckWord.firstReturn_pos $pa))
diff --git a/Mathlib/Combinatorics/SimpleGraph/Regularity/Bound.lean b/Mathlib/Combinatorics/SimpleGraph/Regularity/Bound.lean
index 4298225b631b62..10020841234a53 100644
--- a/Mathlib/Combinatorics/SimpleGraph/Regularity/Bound.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/Regularity/Bound.lean
@@ -239,9 +239,10 @@ open Lean.Meta Qq
/-- Extension for the `positivity` tactic: `SzemerediRegularity.initialBound` is always positive. -/
@[positivity SzemerediRegularity.initialBound _ _]
-meta def evalInitialBound : PositivityExt where eval {u α} _ _ e := do
+meta def evalInitialBound : PositivityExt where eval {u α} _ pα? e := do
match u, α, e with
| 0, ~q(ℕ), ~q(SzemerediRegularity.initialBound $ε $l) =>
+ let some _ := pα? | pure .none
assertInstancesCommute
pure (.positive q(SzemerediRegularity.initialBound_pos $ε $l))
| _, _, _ => throwError "not initialBound"
@@ -251,9 +252,10 @@ example (ε : ℝ) (l : ℕ) : 0 < SzemerediRegularity.initialBound ε l := by p
/-- Extension for the `positivity` tactic: `SzemerediRegularity.bound` is always positive. -/
@[positivity SzemerediRegularity.bound _ _]
-meta def evalBound : PositivityExt where eval {u α} _ _ e := do
+meta def evalBound : PositivityExt where eval {u α} _ pα? e := do
match u, α, e with
| 0, ~q(ℕ), ~q(SzemerediRegularity.bound $ε $l) =>
+ let some _ := pα? | pure .none
assertInstancesCommute
pure (.positive q(SzemerediRegularity.bound_pos $ε $l))
| _, _, _ => throwError "not bound"
diff --git a/Mathlib/Combinatorics/SimpleGraph/Triangle/Removal.lean b/Mathlib/Combinatorics/SimpleGraph/Triangle/Removal.lean
index bdf4f169a854ea..2e4f7bb17a6c94 100644
--- a/Mathlib/Combinatorics/SimpleGraph/Triangle/Removal.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/Triangle/Removal.lean
@@ -176,10 +176,11 @@ if `ε` is.
This exploits the positivity of the junk value of `triangleRemovalBound ε` for `ε ≥ 1`. -/
@[positivity triangleRemovalBound _]
-meta def evalTriangleRemovalBound : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalTriangleRemovalBound : PositivityExt where eval {u α} _zα pα? e := do
match u, α, e with
| 0, ~q(ℝ), ~q(triangleRemovalBound $ε) =>
- let .positive hε ← core q(inferInstance) q(inferInstance) ε | failure
+ let some _ := pα? | pure .none
+ let .positive hε ← core q(inferInstance) (some q(inferInstance)) ε | failure
assertInstancesCommute
pure (.positive q(triangleRemovalBound_pos $hε))
| _, _, _ => throwError "failed to match on Int.ceil application"
diff --git a/Mathlib/Data/ENNReal/Basic.lean b/Mathlib/Data/ENNReal/Basic.lean
index 40b42ed7d30639..3b71f90289ab75 100644
--- a/Mathlib/Data/ENNReal/Basic.lean
+++ b/Mathlib/Data/ENNReal/Basic.lean
@@ -741,7 +741,8 @@ open Lean Meta Qq
/-- Extension for the `positivity` tactic: `ENNReal.toReal`. -/
@[positivity ENNReal.toReal _]
-meta def evalENNRealtoReal : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalENNRealtoReal : PositivityExt where eval {u α} _zα pα? e := do
+ let some _ := pα? | pure .none
match u, α, e with
| 0, ~q(ℝ), ~q(ENNReal.toReal $a) =>
assertInstancesCommute
@@ -750,11 +751,12 @@ meta def evalENNRealtoReal : PositivityExt where eval {u α} _zα _pα e := do
/-- Extension for the `positivity` tactic: `ENNReal.ofNNReal`. -/
@[positivity ENNReal.ofNNReal _]
-meta def evalENNRealOfNNReal : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalENNRealOfNNReal : PositivityExt where eval {u α} _zα pα? e := do
+ let some _ := pα? | pure .none
match u, α, e with
| 0, ~q(ℝ≥0∞), ~q(ENNReal.ofNNReal $a) =>
- let ra ← core q(inferInstance) q(inferInstance) a
assertInstancesCommute
+ let ra ← core q(inferInstance) (some q(inferInstance)) a
match ra with
| .positive pa => pure <| .positive q(ENNReal.coe_pos.mpr $pa)
| _ => pure .none
diff --git a/Mathlib/Data/ENNReal/Real.lean b/Mathlib/Data/ENNReal/Real.lean
index 4c818edb0ab5b4..6da5409e3594ec 100644
--- a/Mathlib/Data/ENNReal/Real.lean
+++ b/Mathlib/Data/ENNReal/Real.lean
@@ -392,11 +392,12 @@ open Lean Meta Qq
/-- Extension for the `positivity` tactic: `ENNReal.ofReal`. -/
@[positivity ENNReal.ofReal _]
-meta def evalENNRealOfReal : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalENNRealOfReal : PositivityExt where eval {u α} _zα pα? e := do
+ let some _ := pα? | pure .none
match u, α, e with
| 0, ~q(ℝ≥0∞), ~q(ENNReal.ofReal $a) =>
- let ra ← core q(inferInstance) q(inferInstance) a
assertInstancesCommute
+ let ra ← core q(inferInstance) (some q(inferInstance)) a
match ra with
| .positive pa => pure (.positive q(Iff.mpr (@ENNReal.ofReal_pos $a) $pa))
| _ => pure .none
diff --git a/Mathlib/Data/EReal/Basic.lean b/Mathlib/Data/EReal/Basic.lean
index ca18cc4fdf469e..9cbb2d70f53709 100644
--- a/Mathlib/Data/EReal/Basic.lean
+++ b/Mathlib/Data/EReal/Basic.lean
@@ -850,11 +850,12 @@ open Lean Meta Qq Function
/-- Extension for the `positivity` tactic: cast from `ℝ` to `EReal`. -/
@[positivity Real.toEReal _]
-meta def evalRealToEReal : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalRealToEReal : PositivityExt where eval {u α} _zα pα? e := do
+ let some _ := pα? | pure .none
match u, α, e with
| 0, ~q(EReal), ~q(Real.toEReal $a) =>
- let ra ← core q(inferInstance) q(inferInstance) a
assertInstancesCommute
+ let ra ← core q(inferInstance) (some q(inferInstance)) a
match ra with
| .positive pa => pure (.positive q(EReal.coe_pos.2 $pa))
| .nonnegative pa => pure (.nonnegative q(EReal.coe_nonneg.2 $pa))
@@ -864,11 +865,12 @@ meta def evalRealToEReal : PositivityExt where eval {u α} _zα _pα e := do
/-- Extension for the `positivity` tactic: cast from `ℝ≥0∞` to `EReal`. -/
@[positivity ENNReal.toEReal _]
-meta def evalENNRealToEReal : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalENNRealToEReal : PositivityExt where eval {u α} _zα pα? e := do
+ let some _ := pα? | pure .none
match u, α, e with
| 0, ~q(EReal), ~q(ENNReal.toEReal $a) =>
- let ra ← core q(inferInstance) q(inferInstance) a
assertInstancesCommute
+ let ra ← core q(inferInstance) (some q(inferInstance)) a
match ra with
| .positive pa => pure (.positive q(EReal.coe_ennreal_pos.2 $pa))
| .nonzero pa => pure (.positive q(EReal.coe_ennreal_pos_iff_ne_zero.2 $pa))
@@ -881,11 +883,12 @@ We prove that `EReal.toReal x` is nonnegative whenever `x` is nonnegative.
Since `EReal.toReal ⊤ = 0`, we cannot prove a stronger statement,
at least without relying on a tactic like `finiteness`. -/
@[positivity EReal.toReal _]
-meta def evalERealToReal : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalERealToReal : PositivityExt where eval {u α} _zα pα? e := do
+ let some _ := pα? | pure .none
match u, α, e with
| 0, ~q(Real), ~q(EReal.toReal $a) =>
assertInstancesCommute
- match (← core q(inferInstance) q(inferInstance) a).toNonneg with
+ match (← core q(inferInstance) (some q(inferInstance)) a).toNonneg with
| .some pa => pure (.nonnegative q(EReal.toReal_nonneg $pa))
| _ => pure .none
| _, _, _ => throwError "not EReal.toReal"
@@ -897,11 +900,12 @@ and it is nonnegative otherwise.
We cannot deduce any corollaries from `x ≠ 0`, since `EReal.toENNReal x = 0` for `x < 0`.
-/
@[positivity EReal.toENNReal _]
-meta def evalERealToENNReal : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalERealToENNReal : PositivityExt where eval {u α} _zα pα? e := do
+ let some _ := pα? | pure .none
match u, α, e with
| 0, ~q(ENNReal), ~q(EReal.toENNReal $a) =>
assertInstancesCommute
- match ← core q(inferInstance) q(inferInstance) a with
+ match ← core q(inferInstance) (some q(inferInstance)) a with
| .positive pa => pure (.positive q(EReal.toENNReal_pos_iff.2 $pa))
| _ => pure (.nonnegative q(zero_le (a := $e)))
| _, _, _ => throwError "not EReal.toENNReal"
diff --git a/Mathlib/Data/EReal/Inv.lean b/Mathlib/Data/EReal/Inv.lean
index 26d1a3cc43c590..e61ad93ab0efa8 100644
--- a/Mathlib/Data/EReal/Inv.lean
+++ b/Mathlib/Data/EReal/Inv.lean
@@ -551,7 +551,8 @@ open Lean Meta Qq Function
/-- Extension for the `positivity` tactic: inverse of an `EReal`. -/
@[positivity (_⁻¹ : EReal)]
-meta def evalERealInv : PositivityExt where eval {u α} zα pα e := do
+meta def evalERealInv : PositivityExt where eval {u α} zα pα? e := do
+ let some pα := pα? | pure .none
match u, α, e with
| 0, ~q(EReal), ~q($a⁻¹) =>
assertInstancesCommute
@@ -562,7 +563,8 @@ meta def evalERealInv : PositivityExt where eval {u α} zα pα e := do
/-- Extension for the `positivity` tactic: ratio of two `EReal`s. -/
@[positivity (_ / _ : EReal)]
-meta def evalERealDiv : PositivityExt where eval {u α} zα pα e := do
+meta def evalERealDiv : PositivityExt where eval {u α} zα pα? e := do
+ let some pα := pα? | pure .none
match u, α, e with
| 0, ~q(EReal), ~q($a / $b) =>
assertInstancesCommute
diff --git a/Mathlib/Data/EReal/Operations.lean b/Mathlib/Data/EReal/Operations.lean
index 1d3cea1bfb2454..075d47a6994978 100644
--- a/Mathlib/Data/EReal/Operations.lean
+++ b/Mathlib/Data/EReal/Operations.lean
@@ -821,7 +821,8 @@ open Lean Meta Qq Function
/-- Extension for the `positivity` tactic: sum of two `EReal`s. -/
@[positivity (_ + _ : EReal)]
-meta def evalERealAdd : PositivityExt where eval {u α} zα pα e := do
+meta def evalERealAdd : PositivityExt where eval {u α} zα pα? e := do
+ let some pα := pα? | pure .none
match u, α, e with
| 0, ~q(EReal), ~q($a + $b) =>
assertInstancesCommute
@@ -840,7 +841,8 @@ meta def evalERealAdd : PositivityExt where eval {u α} zα pα e := do
/-- Extension for the `positivity` tactic: product of two `EReal`s. -/
@[positivity (_ * _ : EReal)]
-meta def evalERealMul : PositivityExt where eval {u α} zα pα e := do
+meta def evalERealMul : PositivityExt where eval {u α} zα pα? e := do
+ let some pα := pα? | pure .none
match u, α, e with
| 0, ~q(EReal), ~q($a * $b) =>
assertInstancesCommute
diff --git a/Mathlib/Data/NNReal/Defs.lean b/Mathlib/Data/NNReal/Defs.lean
index 5bb1fff0c6d3d4..d1a46797c16806 100644
--- a/Mathlib/Data/NNReal/Defs.lean
+++ b/Mathlib/Data/NNReal/Defs.lean
@@ -1006,11 +1006,12 @@ alias ⟨_, nnreal_coe_pos⟩ := coe_pos
/-- Extension for the `positivity` tactic: cast from `ℝ≥0` to `ℝ`. -/
@[positivity NNReal.toReal _]
-meta def evalNNRealtoReal : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalNNRealtoReal : PositivityExt where eval {u α} _zα pα? e := do
+ let some _ := pα? | pure .none
match u, α, e with
| 0, ~q(ℝ), ~q(NNReal.toReal $a) =>
- let ra ← core q(inferInstance) q(inferInstance) a
assertInstancesCommute
+ let ra ← core q(inferInstance) (some q(inferInstance)) a
match ra with
| .positive pa => pure (.positive q(nnreal_coe_pos $pa))
| _ => pure (.nonnegative q(NNReal.coe_nonneg $a))
@@ -1018,11 +1019,12 @@ meta def evalNNRealtoReal : PositivityExt where eval {u α} _zα _pα e := do
/-- Extension for the `positivity` tactic: `Real.toNNReal` -/
@[positivity Real.toNNReal _]
-meta def evalRealToNNReal : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalRealToNNReal : PositivityExt where eval {u α} _zα pα? e := do
+ let some _ := pα? | pure .none
match u, α, e with
| 0, ~q(ℝ≥0), ~q(Real.toNNReal $a) =>
assertInstancesCommute
- match (← core q(inferInstance) q(inferInstance) a) with
+ match (← core q(inferInstance) (some q(inferInstance)) a) with
| .positive pa => pure (.positive q(toNNReal_pos.mpr $pa))
| _ => failure
| _, _, _ => throwError "not Real.toNNReal"
@@ -1031,11 +1033,12 @@ alias ⟨_, nnabs_pos_of_pos⟩ := Real.nnabs_pos
/-- Extension for the `positivity` tactic: `Real.nnabs` -/
@[positivity Real.nnabs _]
-meta def evalRealNNAbs : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalRealNNAbs : PositivityExt where eval {u α} _zα pα? e := do
+ let some _ := pα? | pure .none
match u, α, e with
| 0, ~q(ℝ≥0), ~q(Real.nnabs $a) =>
assertInstancesCommute
- match (← core q(inferInstance) q(inferInstance) a).toNonzero with
+ match (← core q(inferInstance) (some q(inferInstance)) a).toNonzero with
| some pa => pure (.positive q(nnabs_pos_of_pos $pa))
| _ => failure
| _, _, _ => throwError "not Real.nnabs"
diff --git a/Mathlib/Data/Nat/Factorial/DoubleFactorial.lean b/Mathlib/Data/Nat/Factorial/DoubleFactorial.lean
index 40d3bb7a8ca3f2..0d27087ca77713 100644
--- a/Mathlib/Data/Nat/Factorial/DoubleFactorial.lean
+++ b/Mathlib/Data/Nat/Factorial/DoubleFactorial.lean
@@ -87,7 +87,8 @@ open Lean Meta Qq
/-- Extension for `Nat.doubleFactorial`. -/
@[positivity Nat.doubleFactorial _]
-meta def evalDoubleFactorial : PositivityExt where eval {u α} _ _ e := do
+meta def evalDoubleFactorial : PositivityExt where eval {u α} _ pα? e := do
+ let some _ := pα? | pure .none
match u, α, e with
| 0, ~q(ℕ), ~q(Nat.doubleFactorial $n) =>
assumeInstancesCommute
diff --git a/Mathlib/Data/Nat/Totient.lean b/Mathlib/Data/Nat/Totient.lean
index 9f59ec38d113ca..82b080ac815907 100644
--- a/Mathlib/Data/Nat/Totient.lean
+++ b/Mathlib/Data/Nat/Totient.lean
@@ -446,9 +446,10 @@ open Lean Meta Qq
meta def evalNatTotient : PositivityExt where eval {u α} z p e := do
match u, α, e with
| 0, ~q(ℕ), ~q(Nat.totient $n) =>
- assumeInstancesCommute
match ← core z p n with
- | .positive pa => return .positive q(Nat.totient_pos.mpr $pa)
+ | .positive pa =>
+ assumeInstancesCommute
+ return .positive q(Nat.totient_pos.mpr $pa)
| _ => failure
| _, _, _ => throwError "not Nat.totient"
diff --git a/Mathlib/Data/Rat/Cast/Order.lean b/Mathlib/Data/Rat/Cast/Order.lean
index 24a8ad895fa5f8..935ce8e22c6bcf 100644
--- a/Mathlib/Data/Rat/Cast/Order.lean
+++ b/Mathlib/Data/Rat/Cast/Order.lean
@@ -260,16 +260,18 @@ open Lean Meta Qq Function
/-- Extension for Rat.cast. -/
@[positivity Rat.cast _]
-meta def evalRatCast : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalRatCast : PositivityExt where eval {u α} _zα pα? e := do
let ~q(@Rat.cast _ (_) ($a : ℚ)) := e | throwError "not Rat.cast"
- match ← core q(inferInstance) q(inferInstance) a with
+ match ← core q(inferInstance) (some q(inferInstance)) a with
| .positive pa =>
+ let some _ := pα? | pure .none
let _oα ← synthInstanceQ q(Field $α)
let _oα ← synthInstanceQ q(LinearOrder $α)
let _oα ← synthInstanceQ q(IsStrictOrderedRing $α)
assumeInstancesCommute
return .positive q((Rat.cast_pos (K := $α)).mpr $pa)
| .nonnegative pa =>
+ let some _ := pα? | pure .none
let _oα ← synthInstanceQ q(Field $α)
let _oα ← synthInstanceQ q(LinearOrder $α)
let _oα ← synthInstanceQ q(IsStrictOrderedRing $α)
@@ -284,16 +286,18 @@ meta def evalRatCast : PositivityExt where eval {u α} _zα _pα e := do
/-- Extension for NNRat.cast. -/
@[positivity NNRat.cast _]
-meta def evalNNRatCast : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalNNRatCast : PositivityExt where eval {u α} _zα pα? e := do
let ~q(@NNRat.cast _ (_) ($a : ℚ≥0)) := e | throwError "not NNRat.cast"
- match ← core q(inferInstance) q(inferInstance) a with
+ match ← core q(inferInstance) (some q(inferInstance)) a with
| .positive pa =>
+ let some _ := pα? | pure .none
let _oα ← synthInstanceQ q(Semifield $α)
let _oα ← synthInstanceQ q(LinearOrder $α)
let _oα ← synthInstanceQ q(IsStrictOrderedRing $α)
assumeInstancesCommute
return .positive q((NNRat.cast_pos (K := $α)).mpr $pa)
| _ =>
+ let some _ := pα? | pure .none
let _oα ← synthInstanceQ q(Semifield $α)
let _oα ← synthInstanceQ q(LinearOrder $α)
let _oα ← synthInstanceQ q(IsStrictOrderedRing $α)
diff --git a/Mathlib/Geometry/Euclidean/Altitude.lean b/Mathlib/Geometry/Euclidean/Altitude.lean
index d35a424aec9492..1d100ab3c1c959 100644
--- a/Mathlib/Geometry/Euclidean/Altitude.lean
+++ b/Mathlib/Geometry/Euclidean/Altitude.lean
@@ -260,9 +260,10 @@ lemma height_pos {n : ℕ} [NeZero n] (s : Simplex ℝ P n) (i : Fin (n + 1)) :
open Qq Mathlib.Meta.Positivity in
/-- Extension for the `positivity` tactic: the height of a simplex is always positive. -/
@[positivity height _ _]
-meta def evalHeight : PositivityExt where eval {u α} _ _ e := do
+meta def evalHeight : PositivityExt where eval {u α} _ pα? e := do
match u, α, e with
| 0, ~q(ℝ), ~q(@height $V $P $i1 $i2 $i3 $i4 $n $hn $s $i) =>
+ let some _ := pα? | pure .none
assertInstancesCommute
return .positive q(height_pos $s $i)
| _, _, _ => throwError "not Simplex.height"
diff --git a/Mathlib/MeasureTheory/Covering/Besicovitch.lean b/Mathlib/MeasureTheory/Covering/Besicovitch.lean
index 314885dfedd883..c369663b90f5d7 100644
--- a/Mathlib/MeasureTheory/Covering/Besicovitch.lean
+++ b/Mathlib/MeasureTheory/Covering/Besicovitch.lean
@@ -138,9 +138,10 @@ open Lean Meta Qq
/-- Extension for the `positivity` tactic: `Besicovitch.SatelliteConfig.r`. -/
@[positivity Besicovitch.SatelliteConfig.r _ _]
-meta def evalBesicovitchSatelliteConfigR : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalBesicovitchSatelliteConfigR : PositivityExt where eval {u α} _zα pα? e := do
match u, α, e with
| 0, ~q(ℝ), ~q(@Besicovitch.SatelliteConfig.r $β $inst $N $τ $self $i) =>
+ let some _ := pα? | pure .none
assertInstancesCommute
return .positive q(Besicovitch.SatelliteConfig.rpos $self $i)
| _, _, _ => throwError "not Besicovitch.SatelliteConfig.r"
diff --git a/Mathlib/MeasureTheory/Integral/Bochner/Basic.lean b/Mathlib/MeasureTheory/Integral/Bochner/Basic.lean
index e726c0325904e3..577389d0ba23ef 100644
--- a/Mathlib/MeasureTheory/Integral/Bochner/Basic.lean
+++ b/Mathlib/MeasureTheory/Integral/Bochner/Basic.lean
@@ -1367,7 +1367,8 @@ attribute [local instance] monadLiftOptionMetaM in
This extension only proves non-negativity, strict positivity is more delicate for integration and
requires more assumptions. -/
@[positivity MeasureTheory.integral _ _]
-meta def evalIntegral : PositivityExt where eval {u α} zα pα e := do
+meta def evalIntegral : PositivityExt where eval {u α} zα pα? e := do
+ let some pα := pα? | pure .none
match u, α, e with
| 0, ~q(ℝ), ~q(@MeasureTheory.integral $i ℝ _ $inst2 _ _ $f) =>
let i : Q($i) ← mkFreshExprMVarQ q($i) .syntheticOpaque
diff --git a/Mathlib/MeasureTheory/Measure/Real.lean b/Mathlib/MeasureTheory/Measure/Real.lean
index ebde97eb7f5f44..0d15c02f097b66 100644
--- a/Mathlib/MeasureTheory/Measure/Real.lean
+++ b/Mathlib/MeasureTheory/Measure/Real.lean
@@ -493,8 +493,9 @@ open Lean Meta Qq Function
/-- Extension for the `positivity` tactic: applications of `μ.real` are nonnegative. -/
@[positivity MeasureTheory.Measure.real _ _]
-meta def evalMeasureReal : PositivityExt where eval {_ _} _zα _pα e := do
+meta def evalMeasureReal : PositivityExt where eval {_ _} _zα pα? e := do
let .app (.app _ a) b ← whnfR e | throwError "not measureReal"
+ let some _ := pα? | pure .none
let p ← mkAppOptM ``MeasureTheory.measureReal_nonneg #[none, none, a, b]
pure (.nonnegative p)
diff --git a/Mathlib/NumberTheory/ArithmeticFunction/Misc.lean b/Mathlib/NumberTheory/ArithmeticFunction/Misc.lean
index 6b7c2d9b959adf..0978dd9917eae5 100644
--- a/Mathlib/NumberTheory/ArithmeticFunction/Misc.lean
+++ b/Mathlib/NumberTheory/ArithmeticFunction/Misc.lean
@@ -456,11 +456,12 @@ open Lean Meta Qq
/-- Extension for `ArithmeticFunction.sigma`. -/
@[positivity ArithmeticFunction.sigma _ _]
-meta def evalArithmeticFunctionSigma : PositivityExt where eval {u α} z p e := do
+meta def evalArithmeticFunctionSigma : PositivityExt where eval {u α} z p? e := do
+ let some p := p? | throwError "no PartialOrder instance"
match u, α, e with
| 0, ~q(ℕ), ~q(ArithmeticFunction.sigma $k $n) =>
- let rn ← core z p n
assumeInstancesCommute
+ let rn ← core z p n
match rn with
| .positive pn => return .positive q(Iff.mpr ArithmeticFunction.sigma_pos_iff $pn)
| _ => return .nonnegative q(Nat.zero_le _)
diff --git a/Mathlib/NumberTheory/ArithmeticFunction/Zeta.lean b/Mathlib/NumberTheory/ArithmeticFunction/Zeta.lean
index a7320c6d952dbd..f0595aca62f502 100644
--- a/Mathlib/NumberTheory/ArithmeticFunction/Zeta.lean
+++ b/Mathlib/NumberTheory/ArithmeticFunction/Zeta.lean
@@ -222,11 +222,12 @@ open Lean Meta Qq
/-- Extension for `ArithmeticFunction.zeta`. -/
@[positivity ArithmeticFunction.zeta _]
-meta def evalArithmeticFunctionZeta : PositivityExt where eval {u α} z p e := do
+meta def evalArithmeticFunctionZeta : PositivityExt where eval {u α} z p? e := do
+ let some p := p? | throwError "no PartialOrder instance"
match u, α, e with
| 0, ~q(ℕ), ~q(ArithmeticFunction.zeta $n) =>
- let rn ← core z p n
assumeInstancesCommute
+ let rn ← core z p n
match rn with
| .positive pn => return .positive q(Iff.mpr ArithmeticFunction.zeta_pos $pn)
| _ => return .nonnegative q(Nat.zero_le _)
diff --git a/Mathlib/NumberTheory/Height/Basic.lean b/Mathlib/NumberTheory/Height/Basic.lean
index ccb65f86701f3c..7118e99a9d8893 100644
--- a/Mathlib/NumberTheory/Height/Basic.lean
+++ b/Mathlib/NumberTheory/Height/Basic.lean
@@ -189,18 +189,20 @@ open Lean.Meta Qq Height
/-- Extension for the `positivity` tactic: `Height.mulHeight₁` is always positive. -/
@[positivity Height.mulHeight₁ _]
-meta def evalMulHeight₁ : PositivityExt where eval {u α} _ _ e := do
+meta def evalMulHeight₁ : PositivityExt where eval {u α} _ pα? e := do
match u, α, e with
| 0, ~q(ℝ), ~q(@mulHeight₁ $K $KF $KA $a) =>
+ let some _ := pα? | pure .none
assertInstancesCommute
pure (.positive q(mulHeight₁_pos $a))
| _, _, _ => throwError "not Height.mulHeight₁"
/-- Extension for the `positivity` tactic: `Height.logHeight₁` is always nonnegative. -/
@[positivity Height.logHeight₁ _]
-meta def evalLogHeight₁ : PositivityExt where eval {u α} _ _ e := do
+meta def evalLogHeight₁ : PositivityExt where eval {u α} _ pα? e := do
match u, α, e with
| 0, ~q(ℝ), ~q(@logHeight₁ $K $KF $KA $a) =>
+ let some _ := pα? | pure .none
assertInstancesCommute
pure (.nonnegative q(zero_le_logHeight₁ $a))
| _, _, _ => throwError "not Height.logHeight₁"
@@ -508,9 +510,10 @@ open Lean.Meta Qq Height
/-- Extension for the `positivity` tactic: `Height.mulHeight` is always positive. -/
@[positivity Height.mulHeight _]
-meta def evalMulHeight : PositivityExt where eval {u α} _ _ e := do
+meta def evalMulHeight : PositivityExt where eval {u α} _ pα? e := do
match u, α, e with
| 0, ~q(ℝ), ~q(@mulHeight $K $KF $KA $ι $a) =>
+ let some _ := pα? | pure .none
-- Check whether there is a `Finite` instance for `$ι` around.
match ← trySynthInstanceQ q(Finite $ι) with
| .some _instFinite =>
@@ -521,9 +524,10 @@ meta def evalMulHeight : PositivityExt where eval {u α} _ _ e := do
/-- Extension for the `positivity` tactic: `Height.logHeight` is always nonnegative. -/
@[positivity Height.logHeight _]
-meta def evalLogHeight : PositivityExt where eval {u α} _ _ e := do
+meta def evalLogHeight : PositivityExt where eval {u α} _ pα? e := do
match u, α, e with
| 0, ~q(ℝ), ~q(@logHeight $K $KF $KA $ι $a) =>
+ let some _ := pα? | pure .none
-- Check whether there is a `Finite` instance for `$ι` around.
match ← trySynthInstanceQ q(Finite $ι) with
| .some _instFinite =>
diff --git a/Mathlib/NumberTheory/Height/NumberField.lean b/Mathlib/NumberTheory/Height/NumberField.lean
index 74804fb2a94663..0e3471a25c812c 100644
--- a/Mathlib/NumberTheory/Height/NumberField.lean
+++ b/Mathlib/NumberTheory/Height/NumberField.lean
@@ -194,9 +194,10 @@ open Lean.Meta Qq
/-- Extension for the `positivity` tactic: `Height.totalWeight` is positive for number fields. -/
@[positivity Height.totalWeight _]
-meta def evalHeightTotalWeight : PositivityExt where eval {u α} _ _ e := do
+meta def evalHeightTotalWeight : PositivityExt where eval {u α} _ pα? e := do
match u, α, e with
| 0, ~q(ℕ), ~q(@Height.totalWeight $K $KF $KA) =>
+ let some _ := pα? | pure .none
-- Check whether there is a `NumberField` instance for `$K` around.
match ← trySynthInstanceQ q(NumberField $K) with
| .some _inst =>
diff --git a/Mathlib/NumberTheory/Height/Projectivization.lean b/Mathlib/NumberTheory/Height/Projectivization.lean
index 4251faa9ce7221..0caa26f4437275 100644
--- a/Mathlib/NumberTheory/Height/Projectivization.lean
+++ b/Mathlib/NumberTheory/Height/Projectivization.lean
@@ -83,18 +83,20 @@ open Lean.Meta Qq Projectivization
/-- Extension for the `positivity` tactic: `Projectivization.mulHeight` is always positive. -/
@[positivity Projectivization.mulHeight _]
-meta def evalProjMulHeight : PositivityExt where eval {u α} _ _ e := do
+meta def evalProjMulHeight : PositivityExt where eval {u α} _ pα? e := do
match u, α, e with
| 0, ~q(ℝ), ~q(@mulHeight $K $KF $KA $ι $ιF $a) =>
+ let some _ := pα? | pure .none
assertInstancesCommute
pure (.positive q(mulHeight_pos $a))
| _, _, _ => throwError "not Projectivization.mulHeight"
/-- Extension for the `positivity` tactic: `Projectivization.logHeight` is always nonnegative. -/
@[positivity Projectivization.logHeight _]
-meta def evalProjLogHeight : PositivityExt where eval {u α} _ _ e := do
+meta def evalProjLogHeight : PositivityExt where eval {u α} _ pα? e := do
match u, α, e with
| 0, ~q(ℝ), ~q(@logHeight $K $KF $KA $ι $ιF $a) =>
+ let some _ := pα? | pure .none
assertInstancesCommute
pure (.nonnegative q(logHeight_nonneg $a))
| _, _, _ => throwError "not Projectivization.logHeight"
diff --git a/Mathlib/NumberTheory/LucasLehmer.lean b/Mathlib/NumberTheory/LucasLehmer.lean
index 68b1afc4de2cc0..cfac67c69e8e3b 100644
--- a/Mathlib/NumberTheory/LucasLehmer.lean
+++ b/Mathlib/NumberTheory/LucasLehmer.lean
@@ -78,11 +78,12 @@ alias ⟨_, mersenne_pos_of_pos⟩ := mersenne_pos
/-- Extension for the `positivity` tactic: `mersenne`. -/
@[positivity mersenne _]
-meta def evalMersenne : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalMersenne : PositivityExt where eval {u α} _zα pα? e := do
+ let some _ := pα? | pure .none
match u, α, e with
| 0, ~q(ℕ), ~q(mersenne $a) =>
- let ra ← core q(inferInstance) q(inferInstance) a
assertInstancesCommute
+ let ra ← core q(inferInstance) (some q(inferInstance)) a
match ra with
| .positive pa => pure (.positive q(mersenne_pos_of_pos $pa))
| _ => pure (.nonnegative q(Nat.zero_le (mersenne $a)))
diff --git a/Mathlib/NumberTheory/SelbergSieve.lean b/Mathlib/NumberTheory/SelbergSieve.lean
index d066238a75fe9e..93cfa77d4eb6ff 100644
--- a/Mathlib/NumberTheory/SelbergSieve.lean
+++ b/Mathlib/NumberTheory/SelbergSieve.lean
@@ -89,9 +89,10 @@ open Lean Meta Qq
/-- Extension for the `positivity` tactic: `BoundingSieve.weights`. -/
@[positivity BoundingSieve.weights _ _]
-meta def evalBoundingSieveWeights : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalBoundingSieveWeights : PositivityExt where eval {u α} _zα pα? e := do
match u, α, e with
| 0, ~q(ℝ), ~q(@BoundingSieve.weights $s $n) =>
+ let some _ := pα? | pure .none
assertInstancesCommute
pure (.nonnegative q(BoundingSieve.weights_nonneg $s $n))
| _, _, _ => throwError "not BoundingSieve.weights"
diff --git a/Mathlib/Tactic/Positivity/Basic.lean b/Mathlib/Tactic/Positivity/Basic.lean
index 8edffeca629e60..4e078e63ad35fc 100644
--- a/Mathlib/Tactic/Positivity/Basic.lean
+++ b/Mathlib/Tactic/Positivity/Basic.lean
@@ -57,35 +57,20 @@ end ite
/-- The `positivity` extension which identifies expressions of the form `ite p a b`,
such that `positivity` successfully recognises both `a` and `b`. -/
-@[positivity ite _ _ _] def evalIte : PositivityExt where eval {u α} zα pα e := do
+@[positivity ite _ _ _] def evalIte : PositivityExt where eval {u α} zα pα? e := do
let .app (.app (.app (.app f (p : Q(Prop))) (_ : Q(Decidable $p))) (a : Q($α))) (b : Q($α))
← whnfR e | throwError "not ite"
haveI' : $e =Q ite $p $a $b := ⟨⟩
- let ra ← core zα pα a; let rb ← core zα pα b
+ let ra ← core zα pα? a; let rb ← core zα pα? b
guard <|← withDefault <| withNewMCtxDepth <| isDefEq f q(ite (α := $α))
match ra, rb with
- | .positive pa, .positive pb =>
- pure (.positive q(ite_pos $p $pa $pb))
- | .positive pa, .nonnegative pb =>
- let _b ← synthInstanceQ q(Preorder $α)
- assumeInstancesCommute
- pure (.nonnegative q(ite_nonneg_of_pos_of_nonneg $p $pa $pb))
- | .nonnegative pa, .positive pb =>
- let _b ← synthInstanceQ q(Preorder $α)
- assumeInstancesCommute
- pure (.nonnegative q(ite_nonneg_of_nonneg_of_pos $p $pa $pb))
- | .nonnegative pa, .nonnegative pb =>
- pure (.nonnegative q(ite_nonneg $p $pa $pb))
- | .positive pa, .nonzero pb =>
- let _b ← synthInstanceQ q(Preorder $α)
- assumeInstancesCommute
- pure (.nonzero q(ite_ne_zero_of_pos_of_ne_zero $p $pa $pb))
- | .nonzero pa, .positive pb =>
- let _b ← synthInstanceQ q(Preorder $α)
- assumeInstancesCommute
- pure (.nonzero q(ite_ne_zero_of_ne_zero_of_pos $p $pa $pb))
- | .nonzero pa, .nonzero pb =>
- pure (.nonzero q(ite_ne_zero $p $pa $pb))
+ | .positive pa, .positive pb => pure (.positive q(ite_pos $p $pa $pb))
+ | .positive pa, .nonnegative pb => pure (.nonnegative q(ite_nonneg_of_pos_of_nonneg $p $pa $pb))
+ | .nonnegative pa, .positive pb => pure (.nonnegative q(ite_nonneg_of_nonneg_of_pos $p $pa $pb))
+ | .nonnegative pa, .nonnegative pb => pure (.nonnegative q(ite_nonneg $p $pa $pb))
+ | .positive pa, .nonzero pb => pure (.nonzero q(ite_ne_zero_of_pos_of_ne_zero $p $pa $pb))
+ | .nonzero pa, .positive pb => pure (.nonzero q(ite_ne_zero_of_ne_zero_of_pos $p $pa $pb))
+ | .nonzero pa, .nonzero pb => pure (.nonzero q(ite_ne_zero $p $pa $pb))
| _, _ => pure .none
section LinearOrder
@@ -104,59 +89,80 @@ end LinearOrder
/-- The `positivity` extension which identifies expressions of the form `min a b`,
such that `positivity` successfully recognises both `a` and `b`. -/
-@[positivity min _ _] def evalMin : PositivityExt where eval {u α} zα pα e := do
+@[positivity min _ _] def evalMin : PositivityExt where eval {u α} zα pα? e := do
let .app (.app (f : Q($α → $α → $α)) (a : Q($α))) (b : Q($α)) ← whnfR e | throwError "not min"
let _e_eq : $e =Q $f $a $b := ⟨⟩
let _a ← synthInstanceQ q(LinearOrder $α)
- assumeInstancesCommute
let ⟨_f_eq⟩ ← withDefault <| withNewMCtxDepth <| assertDefEqQ q($f) q(min)
- match ← core zα pα a, ← core zα pα b with
- | .positive pa, .positive pb => pure (.positive q(lt_min $pa $pb))
- | .positive pa, .nonnegative pb => pure (.nonnegative q(le_min_of_lt_of_le $pa $pb))
- | .nonnegative pa, .positive pb => pure (.nonnegative q(le_min_of_le_of_lt $pa $pb))
- | .nonnegative pa, .nonnegative pb => pure (.nonnegative q(le_min $pa $pb))
- | .positive pa, .nonzero pb => pure (.nonzero q(min_ne_of_lt_of_ne $pa $pb))
- | .nonzero pa, .positive pb => pure (.nonzero q(min_ne_of_ne_of_lt $pa $pb))
- | .nonzero pa, .nonzero pb => pure (.nonzero q(min_ne $pa $pb))
+ assumeInstancesCommute
+ match ← core zα pα? a, ← core zα pα? b with
+ | .positive (pα := pα') pa, .positive pb =>
+ assumeInstancesCommute
+ pure (.positive q(lt_min $pa $pb))
+ | .positive (pα := pα') pa, .nonnegative pb =>
+ assumeInstancesCommute
+ pure (.nonnegative q(le_min_of_lt_of_le $pa $pb))
+ | .nonnegative (pα := pα') pa, .positive pb =>
+ assumeInstancesCommute
+ pure (.nonnegative q(le_min_of_le_of_lt $pa $pb))
+ | .nonnegative pa (pα := pα'), .nonnegative pb =>
+ assumeInstancesCommute
+ pure (.nonnegative q(le_min $pa $pb))
+ | .positive pa, .nonzero pb =>
+ assumeInstancesCommute
+ pure (.nonzero q(min_ne_of_lt_of_ne $pa $pb))
+ | .nonzero pa, .positive pb =>
+ assumeInstancesCommute
+ pure (.nonzero q(min_ne_of_ne_of_lt $pa $pb))
+ | .nonzero pa, .nonzero pb => do
+ pure (.nonzero q(min_ne $pa $pb))
| _, _ => pure .none
/-- Extension for the `max` operator. The `max` of two numbers is nonnegative if at least one
is nonnegative, strictly positive if at least one is positive, and nonzero if both are nonzero. -/
-@[positivity max _ _] def evalMax : PositivityExt where eval {u α} zα pα e := do
+@[positivity max _ _] def evalMax : PositivityExt where eval {u α} zα pα? e := do
let .app (.app (f : Q($α → $α → $α)) (a : Q($α))) (b : Q($α)) ← whnfR e | throwError "not max"
let _e_eq : $e =Q $f $a $b := ⟨⟩
let _a ← synthInstanceQ q(LinearOrder $α)
- assumeInstancesCommute
let ⟨_f_eq⟩ ← withDefault <| withNewMCtxDepth <| assertDefEqQ q($f) q(max)
- let result : Strictness zα pα e ← catchNone do
- let ra ← core zα pα a
+ let result : Strictness zα e pα? ← catchNone do
+ let ra ← core zα pα? a
match ra with
- | .positive pa => pure (.positive q(lt_max_of_lt_left $pa))
- | .nonnegative pa => pure (.nonnegative q(le_max_of_le_left $pa))
+ | .positive pa =>
+ assumeInstancesCommute
+ pure (.positive q(lt_max_of_lt_left $pa))
+ | .nonnegative pa =>
+ assumeInstancesCommute
+ pure (.nonnegative q(le_max_of_le_left $pa))
-- If `a ≠ 0`, we might prove `max a b ≠ 0` if `b ≠ 0` but we don't want to evaluate
-- `b` before having ruled out `0 < a`, for performance. So we do that in the second branch
-- of the `orElse'`.
| _ => pure .none
orElse result do
- let rb ← core zα pα b
+ let rb ← core zα pα? b
match rb with
- | .positive pb => pure (.positive q(lt_max_of_lt_right $pb))
- | .nonnegative pb => pure (.nonnegative q(le_max_of_le_right $pb))
+ | .positive pb =>
+ assumeInstancesCommute
+ pure (.positive q(lt_max_of_lt_right $pb))
+ | .nonnegative pb =>
+ assumeInstancesCommute
+ pure (.nonnegative q(le_max_of_le_right $pb))
| .nonzero pb => do
- match ← core zα pα a with
+ match ← core zα pα? a with
| .nonzero pa => pure (.nonzero q(max_ne $pa $pb))
| _ => pure .none
| _ => pure .none
/-- The `positivity` extension which identifies expressions of the form `a + b`,
such that `positivity` successfully recognises both `a` and `b`. -/
-@[positivity _ + _] def evalAdd : PositivityExt where eval {u α} zα pα e := do
+@[positivity _ + _] def evalAdd : PositivityExt where eval {u α} zα pα? e := do
let .app (.app (f : Q($α → $α → $α)) (a : Q($α))) (b : Q($α)) ← whnfR e | throwError "not +"
let _e_eq : $e =Q $f $a $b := ⟨⟩
let _a ← synthInstanceQ q(AddZeroClass $α)
assumeInstancesCommute
let ⟨_f_eq⟩ ← withDefault <| withNewMCtxDepth <| assertDefEqQ q($f) q(HAdd.hAdd)
- let ra ← core zα pα a; let rb ← core zα pα b
+ let ra ← core zα pα? a; let rb ← core zα pα? b
+ let some _pα := pα? | pure .none
match ra, rb with
| .positive pa, .positive pb =>
let _a ← synthInstanceQ q(AddLeftMono $α)
@@ -174,81 +180,106 @@ such that `positivity` successfully recognises both `a` and `b`. -/
/-- The `positivity` extension which identifies expressions of the form `a - b`,
such that there is a local hypothesis `b < a`, `b ≤ a`, `a ≠ b` or `b ≠ a`. -/
-@[positivity _ - _] def evalSub : PositivityExt where eval {u α} _zα pα e := do
+@[positivity _ - _] def evalSub : PositivityExt where eval {u α} _zα pα? e := do
let .app (.app (f : Q($α → $α → $α)) (a : Q($α))) (b : Q($α)) ← whnfR e | throwError "not -"
let _e_eq : $e =Q $f $a $b := ⟨⟩
let _a ← synthInstanceQ q(AddGroup $α)
assumeInstancesCommute
let ⟨_f_eq⟩ ← withDefault <| withNewMCtxDepth <| assertDefEqQ q($f) q(HSub.hSub)
- let mut result := .none
- for decl in ← getLCtx do
- unless decl.isImplementationDetail do
- have e' : Q(Prop) := decl.type
- have p : Q($e') := .fvar decl.fvarId
- result ← orElse result do
- match e' with
- | ~q(@LE.le.{u} $β $le $lo $hi) =>
- let .defEq (_ : $α =Q $β) ← isDefEqQ α β | return .none
- let .defEq _ ← isDefEqQ q($le) q(($pα).toLE) | return .none
- let .defEq (_ : $a =Q $hi) ← isDefEqQ a hi | return .none
- let .defEq (_ : $b =Q $lo) ← isDefEqQ b lo | return .none
- let _ ← synthInstanceQ q(AddRightMono $α)
- return .nonnegative q(sub_nonneg_of_le $p)
- | ~q(@LT.lt.{u} $β $lt $lo $hi) =>
- let .defEq (_ : $α =Q $β) ← isDefEqQ α β | return .none
- let .defEq _ ← isDefEqQ q($lt) q(($pα).toLT) | return .none
- let .defEq (_ : $a =Q $hi) ← isDefEqQ a hi | return .none
- let .defEq (_ : $b =Q $lo) ← isDefEqQ b lo | return .none
- let _i ← synthInstanceQ q(AddRightStrictMono $α)
- assumeInstancesCommute
- return .positive (q(sub_pos_of_lt $p):)
- | ~q(@Ne.{u + 1} $β $lhs $rhs) =>
- let .defEq (_ : $α =Q $β) ← isDefEqQ α β | return .none
- if let .defEq (_ : $a =Q $lhs) ← isDefEqQ a lhs then
- let .defEq (_ : $b =Q $rhs) ← isDefEqQ b rhs | return .none
- return .nonzero (q(sub_ne_zero_of_ne $p):)
- if let .defEq _ ← isDefEqQ a rhs then
- let .defEq _ ← isDefEqQ b lhs | return .none
- return .nonzero (q(sub_ne_zero_of_ne ($p).symm):)
- return .none
- | _ => return .none
- return result
+ match pα? with
+ | some pα =>
+ let mut result := .none
+ for decl in ← getLCtx do
+ unless decl.isImplementationDetail do
+ have e' : Q(Prop) := decl.type
+ have p : Q($e') := .fvar decl.fvarId
+ result ← orElse result do
+ match e' with
+ | ~q(@LE.le.{u} $β $le $lo $hi) =>
+ let .defEq (_ : $α =Q $β) ← isDefEqQ α β | return .none
+ let .defEq _ ← isDefEqQ q($le) q(($pα).toLE) | return .none
+ let .defEq (_ : $a =Q $hi) ← isDefEqQ a hi | return .none
+ let .defEq (_ : $b =Q $lo) ← isDefEqQ b lo | return .none
+ let _ ← synthInstanceQ q(AddRightMono $α)
+ return .nonnegative q(sub_nonneg_of_le $p)
+ | ~q(@LT.lt.{u} $β $lt $lo $hi) =>
+ let .defEq (_ : $α =Q $β) ← isDefEqQ α β | return .none
+ let .defEq _ ← isDefEqQ q($lt) q(($pα).toLT) | return .none
+ let .defEq (_ : $a =Q $hi) ← isDefEqQ a hi | return .none
+ let .defEq (_ : $b =Q $lo) ← isDefEqQ b lo | return .none
+ let _i ← synthInstanceQ q(AddRightStrictMono $α)
+ assumeInstancesCommute
+ return .positive (q(sub_pos_of_lt $p):)
+ | ~q(@Ne.{u + 1} $β $lhs $rhs) =>
+ let .defEq (_ : $α =Q $β) ← isDefEqQ α β | return .none
+ if let .defEq (_ : $a =Q $lhs) ← isDefEqQ a lhs then
+ let .defEq (_ : $b =Q $rhs) ← isDefEqQ b rhs | return .none
+ return .nonzero (q(sub_ne_zero_of_ne $p):)
+ if let .defEq _ ← isDefEqQ a rhs then
+ let .defEq _ ← isDefEqQ b lhs | return .none
+ return .nonzero (q(sub_ne_zero_of_ne ($p).symm):)
+ return .none
+ | _ => return .none
+ return result
+ | none =>
+ let mut result := .none
+ for decl in ← getLCtx do
+ unless decl.isImplementationDetail do
+ have e' : Q(Prop) := decl.type
+ have p : Q($e') := .fvar decl.fvarId
+ result ← orElse result do
+ match e' with
+ | ~q(@Ne.{u + 1} $β $lhs $rhs) =>
+ let .defEq (_ : $α =Q $β) ← isDefEqQ α β | return .none
+ if let .defEq (_ : $a =Q $lhs) ← isDefEqQ a lhs then
+ let .defEq (_ : $b =Q $rhs) ← isDefEqQ b rhs | return .none
+ return .nonzero (q(sub_ne_zero_of_ne $p):)
+ if let .defEq _ ← isDefEqQ a rhs then
+ let .defEq _ ← isDefEqQ b lhs | return .none
+ return .nonzero (q(sub_ne_zero_of_ne ($p).symm):)
+ return .none
+ | _ => return .none
+ return result
/-- The `positivity` extension which identifies expressions of the form `a * b`,
such that `positivity` successfully recognises both `a` and `b`. -/
-@[positivity _ * _] def evalMul : PositivityExt where eval {u α} zα pα e := do
+@[positivity _ * _] def evalMul : PositivityExt where eval {u α} zα pα? e := do
let .app (.app (f : Q($α → $α → $α)) (a : Q($α))) (b : Q($α)) ← whnfR e | throwError "not *"
let _e_eq : $e =Q $f $a $b := ⟨⟩
let _a ← synthInstanceQ q(Mul $α)
let ⟨_f_eq⟩ ← withDefault <| withNewMCtxDepth <| assertDefEqQ q($f) q(HMul.hMul)
- let ra ← core zα pα a; let rb ← core zα pα b
- let tryProveNonzero (pa? : Option Q($a ≠ 0)) (pb? : Option Q($b ≠ 0)) :
- MetaM (Strictness zα pα e) := do
+ let ra ← core zα pα? a; let rb ← core zα pα? b
+ let tryProveNonzero (pα? : Option Q(PartialOrder $α))
+ (pa? : Option Q($a ≠ 0)) (pb? : Option Q($b ≠ 0)) : MetaM (Strictness zα e pα?) := do
let pa ← liftOption pa?
let pb ← liftOption pb?
let _a ← synthInstanceQ q(NoZeroDivisors $α)
pure (.nonzero q(mul_ne_zero $pa $pb))
- let tryProveNonneg (pa? : Option Q(0 ≤ $a)) (pb? : Option Q(0 ≤ $b)) :
- MetaM (Strictness zα pα e) := do
+ let tryProveNonneg (pα : Q(PartialOrder $α)) (pa? : Option Q(0 ≤ $a)) (pb? : Option Q(0 ≤ $b)) :
+ MetaM (Strictness zα e pα) := do
let pa ← liftOption pa?
let pb ← liftOption pb?
let _a ← synthInstanceQ q(MulZeroClass $α)
let _a ← synthInstanceQ q(PosMulMono $α)
assumeInstancesCommute
pure (.nonnegative q(mul_nonneg $pa $pb))
- let tryProvePositive (pa? : Option Q(0 < $a)) (pb? : Option Q(0 < $b)) :
- MetaM (Strictness zα pα e) := do
+ let tryProvePositive (pα : Q(PartialOrder $α)) (pa? : Option Q(0 < $a)) (pb? : Option Q(0 < $b)) :
+ MetaM (Strictness zα e pα) := do
let pa ← liftOption pa?
let pb ← liftOption pb?
let _a ← synthInstanceQ q(MulZeroClass $α)
let _a ← synthInstanceQ q(PosMulStrictMono $α)
assumeInstancesCommute
pure (.positive q(mul_pos $pa $pb))
- let mut result := .none
- result ← orElse result (tryProvePositive ra.toPositive rb.toPositive)
- result ← orElse result (tryProveNonneg ra.toNonneg rb.toNonneg)
- result ← orElse result (tryProveNonzero ra.toNonzero rb.toNonzero)
- return result
+ match pα? with
+ | some pα =>
+ let mut result : Strictness zα e (some pα) := .none
+ result ← orElse result (tryProvePositive pα ra.toPositive rb.toPositive)
+ result ← orElse result (tryProveNonneg pα ra.toNonneg rb.toNonneg)
+ result ← orElse result (tryProveNonzero pα ra.toNonzero rb.toNonzero)
+ return result
+ | none =>
+ return ← catchNone <| tryProveNonzero .none ra.toNonzero rb.toNonzero
lemma int_div_self_pos {a : ℤ} (ha : 0 < a) : 0 < a / a := by
rw [Int.ediv_self ha.ne']; exact zero_lt_one
@@ -264,15 +295,17 @@ lemma int_div_nonneg_of_pos_of_pos {a b : ℤ} (ha : 0 < a) (hb : 0 < b) : 0 ≤
/-- The `positivity` extension which identifies expressions of the form `a / b`,
where `a` and `b` are integers. -/
-@[positivity (_ : ℤ) / (_ : ℤ)] def evalIntDiv : PositivityExt where eval {u α} _ _ e := do
+@[positivity (_ : ℤ) / (_ : ℤ)] def evalIntDiv : PositivityExt where eval {u α} _ pα? e := do
+ let some _ := pα? | throwError "not PartialOrder ℤ"
match u, α, e with
| 0, ~q(ℤ), ~q($a / $b) =>
- let ra ← core q(inferInstance) q(inferInstance) a
- let rb ← core q(inferInstance) q(inferInstance) b
+ let ra ← core q(inferInstance) (some q(inferInstance)) a
+ let rb ← core q(inferInstance) (some q(inferInstance)) b
assertInstancesCommute
match ra, rb with
| .positive (pa : Q(0 < $a)), .positive (pb : Q(0 < $b)) =>
-- Only attempts to prove `0 < a / a`, otherwise falls back to `0 ≤ a / b`
+ let _ := q(int_div_self_pos $pa)
match ← isDefEqQ a b with
| .defEq _ => pure (.positive q(int_div_self_pos $pa))
| .notDefEq => pure (.nonnegative q(int_div_nonneg_of_pos_of_pos $pa $pb))
@@ -289,43 +322,55 @@ theorem pow_zero_pos [Semiring α] [PartialOrder α] [IsOrderedRing α] [Nontriv
(a : α) : 0 < a ^ 0 :=
zero_lt_one.trans_le (pow_zero a).ge
+theorem pow_zero_ne_zero [Semiring α] [Nontrivial α] (a : α) : a ^ 0 ≠ 0 :=
+ pow_zero a ▸ one_ne_zero
+
/-- The `positivity` extension which identifies expressions of the form `a ^ (0 : ℕ)`.
This extension is run in addition to the general `a ^ b` extension (they are overlapping). -/
@[positivity _ ^ (0 : ℕ)]
-meta def evalPowZeroNat : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalPowZeroNat : PositivityExt where eval {u α} _zα pα? e := do
let .app (.app _ (a : Q($α))) _ ← whnfR e | throwError "not ^"
let _a ← synthInstanceQ q(Semiring $α)
- let _a ← synthInstanceQ q(PartialOrder $α)
+ assumeInstancesCommute
+ haveI' : $e =Q $a ^ 0 := ⟨⟩
+ let _a ← synthInstanceQ q(Nontrivial $α)
+ let some _pα := pα? | pure (.nonzero q(pow_zero_ne_zero $a))
let _a ← synthInstanceQ q(IsOrderedRing $α)
- _ ← synthInstanceQ q(Nontrivial $α)
- pure (.positive (q(pow_zero_pos $a) : Expr))
+ pure (.positive q(pow_zero_pos $a))
/-- The `positivity` extension which identifies expressions of the form `a ^ (b : ℕ)`,
such that `positivity` successfully recognises both `a` and `b`. -/
@[positivity _ ^ (_ : ℕ)]
-meta def evalPow : PositivityExt where eval {u α} zα pα e := do
+meta def evalPow : PositivityExt where eval {u α} zα pα? e := do
let .app (.app _ (a : Q($α))) (b : Q(ℕ)) ← whnfR e | throwError "not ^"
- let result ← catchNone do
+ let some pα := pα? | do
+ let _a ← synthInstanceQ q(MonoidWithZero $α)
+ let _a ← synthInstanceQ q(NoZeroDivisors $α)
+ assumeInstancesCommute
+ haveI' : $e =Q $a ^ $b := ⟨⟩
+ let .nonzero nza ← core zα .none a | pure .none
+ pure (.nonzero q(pow_ne_zero $b $nza))
+ let result : Strictness zα e pα ← catchNone do
+ let _a ← synthInstanceQ q(Ring $α)
+ let _a ← synthInstanceQ q(LinearOrder $α)
+ let _a ← synthInstanceQ q(IsStrictOrderedRing $α)
+ assumeInstancesCommute
let .true := b.isAppOfArity ``OfNat.ofNat 3 | throwError "not a ^ n where n is a literal"
let some n := (b.getRevArg! 1).rawNatLit? | throwError "not a ^ n where n is a literal"
guard (n % 2 = 0)
have m : Q(ℕ) := mkRawNatLit (n / 2)
haveI' : $b =Q 2 * $m := ⟨⟩
- let _a ← synthInstanceQ q(Ring $α)
- let _a ← synthInstanceQ q(LinearOrder $α)
- let _a ← synthInstanceQ q(IsStrictOrderedRing $α)
- assumeInstancesCommute
haveI' : $e =Q $a ^ $b := ⟨⟩
pure (.nonnegative q((even_two_mul $m).pow_nonneg $a))
orElse result do
let ra ← core zα pα a
let ofNonneg (pa : Q(0 ≤ $a)) (_rα : Q(Semiring $α)) (_oα : Q(IsOrderedRing $α)) :
- MetaM (Strictness zα pα e) := do
+ MetaM (Strictness zα e (some pα)) := do
haveI' : $e =Q $a ^ $b := ⟨⟩
assumeInstancesCommute
pure (.nonnegative q(pow_nonneg $pa $b))
let ofNonzero (pa : Q($a ≠ 0)) (_rα : Q(Semiring $α)) (_oα : Q(IsOrderedRing $α)) :
- MetaM (Strictness zα pα e) := do
+ MetaM (Strictness zα e (some pα)) := do
haveI' : $e =Q $a ^ $b := ⟨⟩
assumeInstancesCommute
let _a ← synthInstanceQ q(NoZeroDivisors $α)
@@ -358,20 +403,21 @@ theorem abs_pos_of_ne_zero {α : Type*} [AddGroup α] [LinearOrder α]
/-- The `positivity` extension which identifies expressions of the form `|a|`. -/
@[positivity |_|]
-meta def evalAbs : PositivityExt where eval {_u} (α zα pα) (e : Q($α)) := do
+meta def evalAbs : PositivityExt where eval {_u} (α zα pα?) (e : Q($α)) := do
let ~q(@abs _ (_) (_) $a) := e | throwError "not |·|"
+ let some pα' := pα? | pure .none
try
- match ← core zα pα a with
+ match ← core zα (some pα') a with
| .positive pa =>
let pa' ← mkAppM ``abs_pos_of_pos #[pa]
- pure (.positive pa')
+ pure (.positive (pα := pα') pa')
| .nonzero pa =>
let pa' ← mkAppM ``abs_pos_of_ne_zero #[pa]
- pure (.positive pa')
+ pure (.positive (pα := pα') pa')
| _ => throwError "goto catch"
catch _ => do
let pa' ← mkAppM ``abs_nonneg #[a]
- pure (.nonnegative pa')
+ pure (.nonnegative (pα := pα') pa')
theorem int_natAbs_pos {n : ℤ} (hn : 0 < n) : 0 < n.natAbs :=
Int.natAbs_pos.mpr hn.ne'
@@ -381,18 +427,18 @@ Since the output type of `Int.natAbs` is `ℕ`, the nonnegative case is handled
`positivity` tactic.
-/
@[positivity Int.natAbs _]
-meta def evalNatAbs : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalNatAbs : PositivityExt where eval {u α} _zα pα? e := do
+ let some _ := pα? | throwError "not PartialOrder ℕ"
match u, α, e with
| 0, ~q(ℕ), ~q(Int.natAbs $a) =>
let zα' : Q(Zero Int) := q(inferInstance)
let pα' : Q(PartialOrder Int) := q(inferInstance)
+ assertInstancesCommute
let ra ← core zα' pα' a
match ra with
| .positive pa =>
- assertInstancesCommute
pure (.positive q(int_natAbs_pos $pa))
| .nonzero pa =>
- assertInstancesCommute
pure (.positive q(Int.natAbs_pos.mpr $pa))
| .nonnegative _pa =>
pure .none
@@ -403,11 +449,17 @@ meta def evalNatAbs : PositivityExt where eval {u α} _zα _pα e := do
/-- Extension for the `positivity` tactic: `Nat.cast` is always non-negative,
and positive when its input is. -/
@[positivity Nat.cast _]
-meta def evalNatCast : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalNatCast : PositivityExt where eval {u α} _zα pα? e := do
let ~q(@Nat.cast _ (_) ($a : ℕ)) := e | throwError "not Nat.cast"
let zα' : Q(Zero Nat) := q(inferInstance)
- let pα' : Q(PartialOrder Nat) := q(inferInstance)
let (_i1 : Q(AddMonoidWithOne $α)) ← synthInstanceQ q(AddMonoidWithOne $α)
+ let some _pα := pα? | do
+ let (_cz : Q(CharZero $α)) ← synthInstanceQ q(CharZero $α)
+ assumeInstancesCommute
+ match ← core zα' .none a with
+ | .nonzero nza => pure (.nonzero q(Nat.cast_ne_zero.2 $nza))
+ | _ => pure .none
+ let pα' : Q(PartialOrder Nat) := q(inferInstance)
let (_i2 : Q(AddLeftMono $α)) ← synthInstanceQ q(AddLeftMono $α)
let (_i3 : Q(ZeroLEOneClass $α)) ← synthInstanceQ q(ZeroLEOneClass $α)
assumeInstancesCommute
@@ -424,35 +476,36 @@ meta def evalNatCast : PositivityExt where eval {u α} _zα _pα e := do
/-- Extension for the `positivity` tactic: `Int.cast` is positive (resp. non-negative)
if its input is. -/
@[positivity Int.cast _]
-meta def evalIntCast : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalIntCast : PositivityExt where eval {u α} _zα pα? e := do
let ~q(@Int.cast _ (_) ($a : ℤ)) := e | throwError "not Int.cast"
let zα' : Q(Zero Int) := q(inferInstance)
let pα' : Q(PartialOrder Int) := q(inferInstance)
let ra ← core zα' pα' a
- match ra with
- | .positive pa =>
+ match ra, pα? with
+ | .positive pa, some _ =>
let _rα ← synthInstanceQ q(Ring $α)
let _oα ← synthInstanceQ q(IsOrderedRing $α)
let _nt ← synthInstanceQ q(Nontrivial $α)
assumeInstancesCommute
pure (.positive q(Int.cast_pos.mpr $pa))
- | .nonnegative pa =>
+ | .nonnegative pa, some _ =>
let _rα ← synthInstanceQ q(Ring $α)
let _oα ← synthInstanceQ q(IsOrderedRing $α)
let _nt ← synthInstanceQ q(Nontrivial $α)
assumeInstancesCommute
pure (.nonnegative q(Int.cast_nonneg $pa))
- | .nonzero pa =>
+ | .nonzero pa, _ =>
let _oα ← synthInstanceQ q(AddGroupWithOne $α)
let _nt ← synthInstanceQ q(CharZero $α)
assumeInstancesCommute
pure (.nonzero q(Int.cast_ne_zero.mpr $pa))
- | .none =>
+ | _ , _ =>
pure .none
/-- Extension for `Nat.succ`. -/
@[positivity Nat.succ _]
-meta def evalNatSucc : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalNatSucc : PositivityExt where eval {u α} _zα pα? e := do
+ let some _ := pα? | throwError "not PartialOrder ℕ"
match u, α, e with
| 0, ~q(ℕ), ~q(Nat.succ $a) =>
assertInstancesCommute
@@ -461,7 +514,8 @@ meta def evalNatSucc : PositivityExt where eval {u α} _zα _pα e := do
/-- Extension for `PNat.val`. -/
@[positivity PNat.val _]
-meta def evalPNatVal : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalPNatVal : PositivityExt where eval {u α} _zα pα? e := do
+ let some _ := pα? | throwError "not PartialOrder ℕ"
match u, α, e with
| 0, ~q(ℕ), ~q(PNat.val $a) =>
assertInstancesCommute
@@ -470,7 +524,8 @@ meta def evalPNatVal : PositivityExt where eval {u α} _zα _pα e := do
/-- Extension for `Nat.factorial`. -/
@[positivity Nat.factorial _]
-meta def evalFactorial : PositivityExt where eval {u α} _ _ e := do
+meta def evalFactorial : PositivityExt where eval {u α} _ pα? e := do
+ let some _ := pα? | throwError "not PartialOrder ℕ"
match u, α, e with
| 0, ~q(ℕ), ~q(Nat.factorial $a) =>
assertInstancesCommute
@@ -479,7 +534,8 @@ meta def evalFactorial : PositivityExt where eval {u α} _ _ e := do
/-- Extension for `Nat.ascFactorial`. -/
@[positivity Nat.ascFactorial _ _]
-meta def evalAscFactorial : PositivityExt where eval {u α} _ _ e := do
+meta def evalAscFactorial : PositivityExt where eval {u α} _ pα? e := do
+ let some _ := pα? | throwError "not PartialOrder ℕ"
match u, α, e with
| 0, ~q(ℕ), ~q(Nat.ascFactorial ($n + 1) $k) =>
assertInstancesCommute
@@ -496,10 +552,14 @@ meta def evalNatGCD : PositivityExt where eval {u α} z p e := do
| 0, ~q(ℕ), ~q(Nat.gcd $a $b) =>
assertInstancesCommute
match ← core z p a with
- | .positive pa => return .positive q(Nat.gcd_pos_of_pos_left $b $pa)
+ | .positive pa =>
+ assertInstancesCommute
+ return .positive q(Nat.gcd_pos_of_pos_left $b $pa)
| _ =>
match ← core z p b with
- | .positive pb => return .positive q(Nat.gcd_pos_of_pos_right $a $pb)
+ | .positive pb =>
+ assertInstancesCommute
+ return .positive q(Nat.gcd_pos_of_pos_right $a $pb)
| _ => failure
| _, _, _ => throwError "not Nat.gcd"
@@ -508,11 +568,12 @@ meta def evalNatGCD : PositivityExt where eval {u α} z p e := do
meta def evalNatLCM : PositivityExt where eval {u α} z p e := do
match u, α, e with
| 0, ~q(ℕ), ~q(Nat.lcm $a $b) =>
- assertInstancesCommute
match ← core z p a with
| .positive pa =>
+ assertInstancesCommute
match ← core z p b with
| .positive pb =>
+ assertInstancesCommute
return .positive q(Nat.lcm_pos $pa $pb)
| _ => failure
| _ => failure
@@ -523,40 +584,43 @@ meta def evalNatLCM : PositivityExt where eval {u α} z p e := do
meta def evalNatSqrt : PositivityExt where eval {u α} z p e := do
match u, α, e with
| 0, ~q(ℕ), ~q(Nat.sqrt $n) =>
- assumeInstancesCommute
match ← core z p n with
- | .positive pa => return .positive q(Nat.sqrt_pos.mpr $pa)
+ | .positive pa =>
+ assumeInstancesCommute
+ return .positive q(Nat.sqrt_pos.mpr $pa)
| _ => failure
| _, _, _ => throwError "not Nat.sqrt"
/-- Extension for `Int.gcd`.
Uses positivity of the left term, if available, then tries the right term. -/
@[positivity Int.gcd _ _]
-meta def evalIntGCD : PositivityExt where eval {u α} _ _ e := do
+meta def evalIntGCD : PositivityExt where eval {u α} _ pα? e := do
+ let some _ := pα? | throwError "not PartialOrder ℕ"
match u, α, e with
| 0, ~q(ℕ), ~q(Int.gcd $a $b) =>
let z ← synthInstanceQ (q(Zero ℤ) : Q(Type))
let p ← synthInstanceQ (q(PartialOrder ℤ) : Q(Type))
assertInstancesCommute
- match (← catchNone (core z p a)).toNonzero with
+ match (← catchNone (core z (some p) a)).toNonzero z with
| some na => return .positive q(Int.gcd_pos_of_ne_zero_left $b $na)
| none =>
- match (← core z p b).toNonzero with
+ match (← core z (some p) b).toNonzero z with
| some nb => return .positive q(Int.gcd_pos_of_ne_zero_right $a $nb)
| none => failure
| _, _, _ => throwError "not Int.gcd"
/-- Extension for `Int.lcm`. -/
@[positivity Int.lcm _ _]
-meta def evalIntLCM : PositivityExt where eval {u α} _ _ e := do
+meta def evalIntLCM : PositivityExt where eval {u α} _ pα? e := do
+ let some _ := pα? | throwError "not PartialOrder ℕ"
match u, α, e with
| 0, ~q(ℕ), ~q(Int.lcm $a $b) =>
let z ← synthInstanceQ (q(Zero ℤ) : Q(Type))
let p ← synthInstanceQ (q(PartialOrder ℤ) : Q(Type))
assertInstancesCommute
- match (← core z p a).toNonzero with
+ match (← core z (some p) a).toNonzero z with
| some na =>
- match (← core z p b).toNonzero with
+ match (← core z (some p) b).toNonzero z with
| some nb => return .positive q(Int.lcm_pos $na $nb)
| _ => failure
| _ => failure
@@ -571,21 +635,25 @@ alias ⟨_, NNRat.num_ne_zero_of_ne_zero⟩ := num_ne_zero
/-- The `positivity` extension which identifies expressions of the form `NNRat.num q`,
such that `positivity` successfully recognises `q`. -/
@[positivity NNRat.num _]
-meta def evalNNRatNum : PositivityExt where eval {u α} _ _ e := do
+meta def evalNNRatNum : PositivityExt where eval {u α} _ pα? e := do
+ let some _ := pα? | throwError "not PartialOrder ℕ"
match u, α, e with
| 0, ~q(ℕ), ~q(NNRat.num $a) =>
let zα : Q(Zero ℚ≥0) := q(inferInstance)
let pα : Q(PartialOrder ℚ≥0) := q(inferInstance)
+ trace[Tactic.positivity] "I'm evalNNRatNum: {e}"
assumeInstancesCommute
match ← core zα pα a with
- | .positive pa => return .positive q(NNRat.num_pos_of_pos $pa)
+ | .positive pa =>
+ return .positive q(NNRat.num_pos_of_pos $pa)
| .nonzero pa => return .nonzero q(NNRat.num_ne_zero_of_ne_zero $pa)
| _ => return .none
| _, _, _ => throwError "not NNRat.num"
/-- The `positivity` extension which identifies expressions of the form `Rat.den a`. -/
@[positivity NNRat.den _]
-meta def evalNNRatDen : PositivityExt where eval {u α} _ _ e := do
+meta def evalNNRatDen : PositivityExt where eval {u α} _ pα? e := do
+ let some _ := pα? | throwError "not PartialOrder ℕ"
match u, α, e with
| 0, ~q(ℕ), ~q(NNRat.den $a) =>
assumeInstancesCommute
@@ -609,22 +677,26 @@ alias ⟨_, num_ne_zero_of_ne_zero⟩ := num_ne_zero
/-- The `positivity` extension which identifies expressions of the form `Rat.num a`,
such that `positivity` successfully recognises `a`. -/
@[positivity Rat.num _]
-meta def evalRatNum : PositivityExt where eval {u α} _ _ e := do
+meta def evalRatNum : PositivityExt where eval {u α} _ pα? e := do
+ let some _ := pα? | throwError "not PartialOrder ℤ"
match u, α, e with
| 0, ~q(ℤ), ~q(Rat.num $a) =>
let zα : Q(Zero ℚ) := q(inferInstance)
let pα : Q(PartialOrder ℚ) := q(inferInstance)
assumeInstancesCommute
match ← core zα pα a with
- | .positive pa => pure <| .positive q(num_pos_of_pos $pa)
- | .nonnegative pa => pure <| .nonnegative q(num_nonneg_of_nonneg $pa)
+ | .positive pa =>
+ pure <| .positive q(num_pos_of_pos $pa)
+ | .nonnegative pa =>
+ pure <| .nonnegative q(num_nonneg_of_nonneg $pa)
| .nonzero pa => pure <| .nonzero q(num_ne_zero_of_ne_zero $pa)
| .none => pure .none
| _, _ => throwError "not Rat.num"
/-- The `positivity` extension which identifies expressions of the form `Rat.den a`. -/
@[positivity Rat.den _]
-meta def evalRatDen : PositivityExt where eval {u α} _ _ e := do
+meta def evalRatDen : PositivityExt where eval {u α} _ pα? e := do
+ let some _ := pα? | throwError "not PartialOrder ℕ"
match u, α, e with
| 0, ~q(ℕ), ~q(Rat.den $a) =>
assumeInstancesCommute
@@ -633,7 +705,8 @@ meta def evalRatDen : PositivityExt where eval {u α} _ _ e := do
/-- Extension for `posPart`. `a⁺` is always nonnegative, and positive if `a` is. -/
@[positivity _⁺]
-meta def evalPosPart : PositivityExt where eval {u α} zα pα e := do
+meta def evalPosPart : PositivityExt where eval {u α} zα pα? e := do
+ let some pα := pα? | pure .none
match e with
| ~q(@posPart _ $instαpospart $a) =>
let _instαlat ← synthInstanceQ q(Lattice $α)
@@ -643,13 +716,15 @@ meta def evalPosPart : PositivityExt where eval {u α} zα pα e := do
-- `.none`) here sometimes. See e.g. the first test for `posPart`. This is why we need
-- `catchNone`
match ← catchNone (core zα pα a) with
- | .positive pf => return .positive q(posPart_pos $pf)
+ | .positive pf =>
+ return .positive q(posPart_pos $pf)
| _ => return .nonnegative q(posPart_nonneg $a)
| _ => throwError "not `posPart`"
/-- Extension for `negPart`. `a⁻` is always nonnegative. -/
@[positivity _⁻]
-meta def evalNegPart : PositivityExt where eval {u α} _ _ e := do
+meta def evalNegPart : PositivityExt where eval {u α} _ pα? e := do
+ let some _ := pα? | pure .none
match e with
| ~q(@negPart _ $instαnegpart $a) =>
let _instαlat ← synthInstanceQ q(Lattice $α)
@@ -660,7 +735,8 @@ meta def evalNegPart : PositivityExt where eval {u α} _ _ e := do
/-- Extension for the `positivity` tactic: nonnegative maps take nonnegative values. -/
@[positivity DFunLike.coe _ _]
-meta def evalMap : PositivityExt where eval {_ β} _ _ e := do
+meta def evalMap : PositivityExt where eval {_ β} _ pβ? e := do
+ let some _ := pβ? | pure .none
let .app (.app _ f) a ← whnfR e
| throwError "not ↑f · where f is of NonnegHomClass"
let pa ← mkAppOptM ``apply_nonneg #[none, none, β, none, none, none, none, f, a]
diff --git a/Mathlib/Tactic/Positivity/Core.lean b/Mathlib/Tactic/Positivity/Core.lean
index f669b02420923e..45a3faaa15fde1 100644
--- a/Mathlib/Tactic/Positivity/Core.lean
+++ b/Mathlib/Tactic/Positivity/Core.lean
@@ -54,37 +54,36 @@ lemma ne_of_ne_of_eq' {α : Sort*} {a c b : α} (hab : (a : α) ≠ c) (hbc : a
namespace Mathlib.Meta.Positivity
-variable {u : Level} {α : Q(Type u)} (zα : Q(Zero $α)) (pα : Q(PartialOrder $α))
+variable {u : Level} {α : Q(Type u)} (zα : Q(Zero $α))
/-- The result of `positivity` running on an expression `e` of type `α`. -/
-inductive Strictness (e : Q($α)) where
- | positive (pf : Q(0 < $e))
- | nonnegative (pf : Q(0 ≤ $e))
- | nonzero (pf : Q($e ≠ 0))
- | none
- deriving Repr
+inductive Strictness (e : Q($α)) : Option Q(PartialOrder $α) → Type where
+ | positive {pα : Q(PartialOrder $α)} (pf : Q(0 < $e)) : Strictness e pα
+ | nonnegative {pα : Q(PartialOrder $α)} (pf : Q(0 ≤ $e)) : Strictness e pα
+ | nonzero {pα?} (pf : Q($e ≠ 0)) : Strictness e pα?
+ | none {pα?} : Strictness e pα?
/-- Gives a generic description of the `positivity` result. -/
-def Strictness.toString {e : Q($α)} : Strictness zα pα e → String
+def Strictness.toString {e pα?} : Strictness zα e pα? → String
| positive _ => "positive"
| nonnegative _ => "nonnegative"
| nonzero _ => "nonzero"
| none => "none"
/-- Extract a proof that `e` is positive, if possible, from `Strictness` information about `e`. -/
-def Strictness.toPositive {e} : Strictness zα pα e → Option Q(0 < $e)
+def Strictness.toPositive {e pα} : Strictness zα e (some pα) → Option Q(0 < $e)
| .positive pf => some pf
| _ => .none
/-- Extract a proof that `e` is nonnegative, if possible, from `Strictness` information about `e`.
-/
-def Strictness.toNonneg {e} : Strictness zα pα e → Option Q(0 ≤ $e)
+def Strictness.toNonneg {e pα} : Strictness zα e (some pα) → Option Q(0 ≤ $e)
| .positive pf => some q(le_of_lt $pf)
| .nonnegative pf => some pf
| _ => .none
/-- Extract a proof that `e` is nonzero, if possible, from `Strictness` information about `e`. -/
-def Strictness.toNonzero {e} : Strictness zα pα e → Option Q($e ≠ 0)
+def Strictness.toNonzero {e pα?} : Strictness zα e pα? → Option Q($e ≠ 0)
| .positive pf => some q(ne_of_gt $pf)
| .nonzero pf => some pf
| _ => .none
@@ -92,8 +91,8 @@ def Strictness.toNonzero {e} : Strictness zα pα e → Option Q($e ≠ 0)
/-- An extension for `positivity`. -/
structure PositivityExt where
/-- Attempts to prove an expression `e : α` is `>0`, `≥0`, or `≠0`. -/
- eval {u : Level} {α : Q(Type u)} (zα : Q(Zero $α)) (pα : Q(PartialOrder $α)) (e : Q($α)) :
- MetaM (Strictness zα pα e)
+ eval {u : Level} {α : Q(Type u)} (zα : Q(Zero $α)) (pα? : Option Q(PartialOrder $α)) (e : Q($α)) :
+ MetaM (Strictness zα e pα?)
/-- Read a `positivity` extension from a declaration of the right type. -/
def mkPositivityExt (n : Name) : ImportM PositivityExt := do
@@ -213,24 +212,25 @@ lemma nz_of_isRat {n : ℤ} {d : ℕ} [Ring A] [LinearOrder A] [IsStrictOrderedR
rw [eq]
exact ne_iff_lt_or_gt.2 (Or.inl neg)
-variable {zα pα} in
+variable {zα} in
/-- Converts a `MetaM Strictness` which can fail
into one that never fails and returns `.none` instead. -/
-def catchNone {e : Q($α)} (t : MetaM (Strictness zα pα e)) : MetaM (Strictness zα pα e) :=
+def catchNone {e pα?} (t : MetaM (Strictness zα e pα?)) : MetaM (Strictness zα e pα?) :=
try t catch e =>
trace[Tactic.positivity.failure] "{e.toMessageData}"
pure .none
-variable {zα pα} in
+variable {zα} in
/-- Converts a `MetaM Strictness` which can return `.none`
into one which never returns `.none` but fails instead. -/
-def throwNone {e : Q($α)} (t : MetaM (Strictness zα pα e)) : MetaM (Strictness zα pα e) := do
+def throwNone {e pα?} (t : MetaM (Strictness zα e pα?)) : MetaM (Strictness zα e pα?) := do
match ← t with
| .none => throwError "Strictness result was `{.ofConstName ``Strictness.none}`."
| r => pure r
/-- Attempts to prove a `Strictness` result when `e` evaluates to a literal number. -/
-def normNumPositivity (e : Q($α)) : MetaM (Strictness zα pα e) := catchNone do
+def normNumPositivity (pα : Q(PartialOrder $α)) (e : Q($α))
+ : MetaM (Strictness zα e (some pα)) := catchNone do
match ← NormNum.derive e with
| .isBool .. => failure
| .isNat _ lit p =>
@@ -312,7 +312,7 @@ def normNumPositivity (e : Q($α)) : MetaM (Strictness zα pα e) := catchNone d
pure (.nonnegative q(nonneg_of_isRat $p $w))
/-- Attempts to prove that `e ≥ 0` using `zero_le` in a `CanonicallyOrderedAdd` monoid. -/
-def positivityCanon (e : Q($α)) : MetaM (Strictness zα pα e) := do
+def positivityCanon (pα : Q(PartialOrder $α)) (e : Q($α)) : MetaM (Strictness zα e (some pα)) := do
let _add ← synthInstanceQ q(AddMonoid $α)
let _le ← synthInstanceQ q(PartialOrder $α)
let _i ← synthInstanceQ q(CanonicallyOrderedAdd $α)
@@ -320,21 +320,24 @@ def positivityCanon (e : Q($α)) : MetaM (Strictness zα pα e) := do
pure (.nonnegative q(zero_le (a := $e)))
/-- A variation on `assumption` when the hypothesis is `lo ≤ e` where `lo` is a numeral. -/
-def compareHypLE (lo e : Q($α)) (p₂ : Q($lo ≤ $e)) : MetaM (Strictness zα pα e) := do
+def compareHypLE (pα : Q(PartialOrder $α)) (lo e : Q($α)) (p₂ : Q($lo ≤ $e))
+ : MetaM (Strictness zα e pα) := do
match ← normNumPositivity zα pα lo with
| .positive p₁ => pure (.positive q(lt_of_lt_of_le $p₁ $p₂))
| .nonnegative p₁ => pure (.nonnegative q(le_trans $p₁ $p₂))
| _ => pure .none
/-- A variation on `assumption` when the hypothesis is `lo < e` where `lo` is a numeral. -/
-def compareHypLT (lo e : Q($α)) (p₂ : Q($lo < $e)) : MetaM (Strictness zα pα e) := do
+def compareHypLT (pα : Q(PartialOrder $α)) (lo e : Q($α)) (p₂ : Q($lo < $e)) :
+ MetaM (Strictness zα e pα) := do
match ← normNumPositivity zα pα lo with
| .positive p₁ => pure (.positive q(lt_trans $p₁ $p₂))
| .nonnegative p₁ => pure (.positive q(lt_of_le_of_lt $p₁ $p₂))
| _ => pure .none
/-- A variation on `assumption` when the hypothesis is `x = e` where `x` is a numeral. -/
-def compareHypEq (e x : Q($α)) (p₂ : Q($x = $e)) : MetaM (Strictness zα pα e) := do
+def compareHypEq (pα : Q(PartialOrder $α)) (e x : Q($α)) (p₂ : Q($x = $e)) :
+ MetaM (Strictness zα e pα) := do
match ← normNumPositivity zα pα x with
| .positive p₁ => pure (.positive q(lt_of_lt_of_eq $p₁ $p₂))
| .nonnegative p₁ => pure (.nonnegative q(le_of_le_of_eq $p₁ $p₂))
@@ -346,7 +349,8 @@ initialize registerTraceClass `Tactic.positivity.failure
/-- A variation on `assumption` which checks if the hypothesis `ldecl` is `a [≤/=] e`
where `a` is a numeral. -/
-def compareHyp (e : Q($α)) (ldecl : LocalDecl) : MetaM (Strictness zα pα e) := do
+def compareHyp (pα : Q(PartialOrder $α)) (e : Q($α)) (ldecl : LocalDecl) :
+ MetaM (Strictness zα e pα) := do
have e' : Q(Prop) := ldecl.type
let p : Q($e') := .fvar ldecl.fvarId
match e' with
@@ -378,7 +382,7 @@ def compareHyp (e : Q($α)) (ldecl : LocalDecl) : MetaM (Strictness zα pα e) :
match rhs with
| ~q(0) => pure <| .nonnegative q(ge_of_eq $p)
| _ => compareHypEq zα pα e rhs q(Eq.symm $p)
- | ~q(@Ne.{u + 1} $α' $lhs $rhs) =>
+ | ~q(@Ne.{u+1} $α' $lhs $rhs) =>
let .defEq (_ : $α =Q $α') ← isDefEqQ α α' | pure .none
match lhs, rhs with
| ~q(0), _ =>
@@ -390,13 +394,30 @@ def compareHyp (e : Q($α)) (ldecl : LocalDecl) : MetaM (Strictness zα pα e) :
| _, _ => pure .none
| _ => pure .none
-variable {zα pα} in
+/-- A variation on `assumption` when the hypothesis is `e ≠ 0` or `0 ≠ e`. -/
+def compareHypNonzero {pα?} (e : Q($α)) (ldecl : LocalDecl) : MetaM (Strictness zα e pα?) := do
+ have e' : Q(Prop) := ldecl.type
+ let p : Q($e') := .fvar ldecl.fvarId
+ match e' with
+ | ~q(@Ne.{u+1} $α' $lhs $rhs) =>
+ let .defEq (_ : $α =Q $α') ← isDefEqQ α α' | pure .none
+ match lhs, rhs with
+ | ~q(0), _ =>
+ let .defEq _ ← isDefEqQ e rhs | pure .none
+ pure <| .nonzero q(Ne.symm $p)
+ | _, ~q(0) =>
+ let .defEq _ ← isDefEqQ e lhs | pure .none
+ pure <| .nonzero q($p)
+ | _, _ => pure .none
+ | _ => pure .none
+
+variable {zα} in
/-- The main combinator which combines multiple `positivity` results.
It assumes `t₁` has already been run for a result, and runs `t₂` and takes the best result.
It will skip `t₂` if `t₁` is already a proof of `.positive`, and can also combine
`.nonnegative` and `.nonzero` to produce a `.positive` result. -/
-def orElse {e : Q($α)} (t₁ : Strictness zα pα e) (t₂ : MetaM (Strictness zα pα e)) :
- MetaM (Strictness zα pα e) := do
+def orElse {pα?} {e : Q($α)} (t₁ : Strictness zα e pα?) (t₂ : MetaM (Strictness zα e pα?)) :
+ MetaM (Strictness zα e pα?) := do
match t₁ with
| .none => catchNone t₂
| p@(.positive _) => pure p
@@ -412,24 +433,41 @@ def orElse {e : Q($α)} (t₁ : Strictness zα pα e) (t₂ : MetaM (Strictness
| _ => pure (.nonzero p₁)
/-- Run each registered `positivity` extension on an expression, returning a `NormNum.Result`. -/
-def core (e : Q($α)) : MetaM (Strictness zα pα e) := do
+def core (pα? : Option Q(PartialOrder $α)) (e : Q($α)) : MetaM (Strictness zα e pα?) := do
let mut result := .none
trace[Tactic.positivity] "trying to prove positivity of {e}"
for ext in ← (positivityExt.getState (← getEnv)).2.getMatch e do
try
- result ← orElse result <| ext.eval zα pα e
+ result ← orElse result <| ext.eval zα pα? e
catch err =>
trace[Tactic.positivity] "{e} failed: {err.toMessageData}"
- result ← orElse result <| normNumPositivity zα pα e
- result ← orElse result <| positivityCanon zα pα e
- if let .positive _ := result then
+ trace[Tactic.positivity] "current result from positivity extensions: {result.toString}"
+ match pα? with
+ | some pα =>
+ trace[Tactic.positivity] "{α} has some {pα}"
+ result ← orElse result <| normNumPositivity zα pα e
+ trace[Tactic.positivity] "current result from normNum: {result.toString}"
+ result ← orElse result <| positivityCanon zα pα e
+ trace[Tactic.positivity] "current result from canonicity: {result.toString}"
+ if let .positive _ := result then
+ trace[Tactic.positivity] "{e} => {result.toString}"
+ return result
+ for ldecl in ← getLCtx do
+ if !ldecl.isImplementationDetail then
+ result ← orElse result <| compareHyp zα pα e ldecl
+ trace[Tactic.positivity] "{e} => {result.toString}"
+ throwNone (pure result)
+ | .none =>
+ trace[Tactic.positivity] "{α} has no PartialOrder"
+ if let .nonzero _ := result then
+ trace[Tactic.positivity] "{e} => {result.toString}"
+ return result
+ for ldecl in ← getLCtx do
+ if !ldecl.isImplementationDetail then
+ result ← orElse result <| compareHypNonzero zα e ldecl
trace[Tactic.positivity] "{e} => {result.toString}"
- return result
- for ldecl in ← getLCtx do
- if !ldecl.isImplementationDetail then
- result ← orElse result <| compareHyp zα pα e ldecl
- trace[Tactic.positivity] "{e} => {result.toString}"
- throwNone (pure result)
+ throwNone (pure result)
+
private inductive OrderRel : Type
| le : OrderRel -- `0 ≤ a`
@@ -446,8 +484,9 @@ inequality was established) together with the proof as an expression. -/
def bestResult (e : Expr) : MetaM (Bool × Expr) := do
let ⟨u, α, _⟩ ← inferTypeQ' e
let zα ← synthInstanceQ q(Zero $α)
- let pα ← synthInstanceQ q(PartialOrder $α)
- match ← try? (Meta.Positivity.core zα pα e) with
+ let pα? ← try? <| synthInstanceQ q(PartialOrder $α)
+ assumeInstancesCommute
+ match ← try? (Meta.Positivity.core zα pα? e) with
| some (.positive pf) => pure (true, pf)
| some (.nonnegative pf) => pure (false, pf)
| _ => throwError "could not establish the nonnegativity of {e}"
@@ -464,28 +503,33 @@ or fails. -/
def solve (t : Q(Prop)) : MetaM Expr := do
let rest {u : Level} (α : Q(Type u)) z e (relDesired : OrderRel) : MetaM Expr := do
let zα ← synthInstanceQ q(Zero $α)
- assumeInstancesCommute
let .true ← isDefEq z q(0 : $α) | throwError "not a positivity goal"
- let pα ← synthInstanceQ q(PartialOrder $α)
- assumeInstancesCommute
- let r ← catchNone <| Meta.Positivity.core zα pα e
+ let pα? ← try? <| synthInstanceQ q(PartialOrder $α)
+ let r ← catchNone <| Meta.Positivity.core zα pα? e
let throw (a b : String) : MetaM Expr := throwError
"failed to prove {a}, but it would be possible to prove {b} if desired"
- let p ← show MetaM Expr from match relDesired, r with
- | .lt, .positive p
- | .le, .nonnegative p
- | .ne, .nonzero p => pure p
- | .le, .positive p => pure q(le_of_lt $p)
- | .ne, .positive p => pure q(ne_of_gt $p)
- | .ne', .positive p => pure q(ne_of_lt $p)
- | .ne', .nonzero p => pure q(Ne.symm $p)
- | .lt, .nonnegative _ => throw "strict positivity" "nonnegativity"
- | .lt, .nonzero _ => throw "strict positivity" "nonzeroness"
- | .le, .nonzero _ => throw "nonnegativity" "nonzeroness"
- | .ne, .nonnegative _
- | .ne', .nonnegative _ => throw "nonzeroness" "nonnegativity"
- | _, .none => throwError "failed to prove positivity/nonnegativity/nonzeroness"
- pure p
+ if let some _ := pα? then
+ match relDesired, r with
+ | .lt, .positive p
+ | .le, .nonnegative p
+ | .ne, .nonzero p => pure p
+ | .le, .positive p => pure q(le_of_lt $p)
+ | .ne, .positive p => pure q(ne_of_gt $p)
+ | .ne', .positive p => pure q(ne_of_lt $p)
+ | .ne', .nonzero p => pure q(Ne.symm $p)
+ | .lt, .nonnegative _ => throw "strict positivity" "nonnegativity"
+ | .lt, .nonzero _ => throw "strict positivity" "nonzeroness"
+ | .le, .nonzero _ => throw "nonnegativity" "nonzeroness"
+ | .ne, .nonnegative _
+ | .ne', .nonnegative _ => throw "nonzeroness" "nonnegativity"
+ | _, .none => throwError "failed to prove positivity/nonnegativity/nonzeroness"
+ else
+ match relDesired, r with
+ | .ne, .nonzero p => pure p
+ | .ne', .nonzero p => pure q(Ne.symm $p)
+ | .lt, .nonzero _ => throw "strict positivity" "nonzeroness"
+ | .le, .nonzero _ => throw "nonnegativity" "nonzeroness"
+ | _, _ => throwError "failed to prove nonzeroness"
match t with
| ~q(@LE.le $α $_a $z $e) => rest α z e .le
| ~q(@LT.lt $α $_a $z $e) => rest α z e .lt
diff --git a/Mathlib/Tactic/Positivity/Finset.lean b/Mathlib/Tactic/Positivity/Finset.lean
index 7e1d3c40ce0b72..50fb47d5c610e6 100644
--- a/Mathlib/Tactic/Positivity/Finset.lean
+++ b/Mathlib/Tactic/Positivity/Finset.lean
@@ -27,9 +27,10 @@ open Qq Lean Meta Finset
It calls `Mathlib.Meta.proveFinsetNonempty` to attempt proving that the finset is nonempty. -/
@[positivity Finset.card _]
-meta def evalFinsetCard : PositivityExt where eval {u α} _ _ e := do
+meta def evalFinsetCard : PositivityExt where eval {u α} _ pα? e := do
match u, α, e with
| 0, ~q(ℕ), ~q(Finset.card $s) =>
+ let some _ := pα? | pure .none
let some ps ← proveFinsetNonempty s | return .none
assertInstancesCommute
return .positive q(Finset.Nonempty.card_pos $ps)
@@ -37,9 +38,10 @@ meta def evalFinsetCard : PositivityExt where eval {u α} _ _ e := do
/-- Extension for `Fintype.card`. `Fintype.card α` is positive if `α` is nonempty. -/
@[positivity Fintype.card _]
-meta def evalFintypeCard : PositivityExt where eval {u α} _ _ e := do
+meta def evalFintypeCard : PositivityExt where eval {u α} _ pα? e := do
match u, α, e with
| 0, ~q(ℕ), ~q(@Fintype.card $β $instβ) =>
+ let some _ := pα? | pure .none
let instβno ← synthInstanceQ q(Nonempty $β)
assumeInstancesCommute
return .positive q(@Fintype.card_pos $β $instβ $instβno)
@@ -49,9 +51,10 @@ meta def evalFintypeCard : PositivityExt where eval {u α} _ _ e := do
It calls `Mathlib.Meta.proveFinsetNonempty` to attempt proving that the finset is nonempty. -/
@[positivity Finset.dens _]
-meta def evalFinsetDens : PositivityExt where eval {u 𝕜} _ _ e := do
+meta def evalFinsetDens : PositivityExt where eval {u 𝕜} _ pα? e := do
match u, 𝕜, e with
| 0, ~q(ℚ≥0), ~q(@Finset.dens $α $instα $s) =>
+ let some _ := pα? | pure .none
let some ps ← proveFinsetNonempty s | return .none
assumeInstancesCommute
return .positive q(@Nonempty.dens_pos $α $instα $s $ps)
@@ -68,12 +71,13 @@ example (s : Finset ℕ) (f : ℕ → ℤ) (hf : ∀ n, 0 ≤ f n) : 0 ≤ s.sum
because `compareHyp` can't look for assumptions behind binders.
-/
@[positivity Finset.sum _ _]
-meta def evalFinsetSum : PositivityExt where eval {u α} zα pα e := do
+meta def evalFinsetSum : PositivityExt where eval {u α} zα pα? e := do
match e with
| ~q(@Finset.sum $ι _ $instα $s $f) =>
let i : Q($ι) ← mkFreshExprMVarQ q($ι) .syntheticOpaque
have body : Q($α) := .betaRev f #[i]
- let rbody ← core zα pα body
+ let rbody ← core zα pα? body
+ let some pα := pα? | pure .none -- TODO: the case without PartialOrder
let p_pos : Option Q(0 < $e) := ← (do
let .positive pbody := rbody | pure none -- Fail if the body is not provably positive
let some ps ← proveFinsetNonempty s | pure none
diff --git a/Mathlib/Topology/Algebra/InfiniteSum/Order.lean b/Mathlib/Topology/Algebra/InfiniteSum/Order.lean
index dc8f2ac255a83e..ddd26a88733d28 100644
--- a/Mathlib/Topology/Algebra/InfiniteSum/Order.lean
+++ b/Mathlib/Topology/Algebra/InfiniteSum/Order.lean
@@ -371,7 +371,8 @@ attribute [local instance] monadLiftOptionMetaM in
This extension only proves non-negativity, strict positivity is more delicate for infinite sums and
requires more assumptions. -/
@[positivity tsum _]
-meta def evalTsum : PositivityExt where eval {u α} zα pα e := do
+meta def evalTsum : PositivityExt where eval {u α} zα pα? e := do
+ let some pα := pα? | pure .none
match e with
| ~q(@tsum _ $ι $instCommMonoid $instTopSpace $f $L) =>
lambdaBoundedTelescope f 1 fun args (body : Q($α)) => do
diff --git a/Mathlib/Topology/MetricSpace/Bounded.lean b/Mathlib/Topology/MetricSpace/Bounded.lean
index 1e1351befa0e17..50d0a217cdaf41 100644
--- a/Mathlib/Topology/MetricSpace/Bounded.lean
+++ b/Mathlib/Topology/MetricSpace/Bounded.lean
@@ -589,9 +589,10 @@ open Lean Meta Qq Function
/-- Extension for the `positivity` tactic: the diameter of a set is always nonnegative. -/
@[positivity Metric.diam _]
-meta def evalDiam : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalDiam : PositivityExt where eval {u α} _zα pα? e := do
match u, α, e with
| 0, ~q(ℝ), ~q(@Metric.diam _ $inst $s) =>
+ let some _ := pα? | pure .none
assertInstancesCommute
pure (.nonnegative q(Metric.diam_nonneg))
| _, _, _ => throwError "not ‖ · ‖"
diff --git a/Mathlib/Topology/MetricSpace/Pseudo/Defs.lean b/Mathlib/Topology/MetricSpace/Pseudo/Defs.lean
index e3694f57974cec..334e1ae713a618 100644
--- a/Mathlib/Topology/MetricSpace/Pseudo/Defs.lean
+++ b/Mathlib/Topology/MetricSpace/Pseudo/Defs.lean
@@ -257,9 +257,10 @@ open Lean Meta Qq Function
/-- Extension for the `positivity` tactic: distances are nonnegative. -/
@[positivity Dist.dist _ _]
-meta def evalDist : PositivityExt where eval {u α} _zα _pα e := do
+meta def evalDist : PositivityExt where eval {u α} _zα pα? e := do
match u, α, e with
| 0, ~q(ℝ), ~q(@Dist.dist $β $inst $a $b) =>
+ let some _ := pα? | pure .none
let _inst ← synthInstanceQ q(PseudoMetricSpace $β)
assertInstancesCommute
pure (.nonnegative q(dist_nonneg))
diff --git a/MathlibTest/positivity.lean b/MathlibTest/positivity.lean
index 9765bcb3dbfffe..a680ddf744befd 100644
--- a/MathlibTest/positivity.lean
+++ b/MathlibTest/positivity.lean
@@ -633,3 +633,30 @@ example [Semiring S] [PartialOrder S] [IsOrderedRing S] [Semiring R]
(abv : R → S) [IsAbsoluteValue abv] (x : R) :
0 ≤ abv x := by
positivity
+
+/- ## Nonzeroness -/
+
+example {α : Type*} [Zero α] {a : α} (ha : a ≠ 0) : a ≠ 0 := by positivity
+example {α : Type*} [Zero α] {a : α} (ha : a ≠ 0) : 0 ≠ a := by positivity
+example {α : Type*} [Zero α] {a : α} (ha : 0 ≠ a) : a ≠ 0 := by positivity
+
+example {α : Type*} [Semifield α] {x : α} (hx : x ≠ 0) : x⁻¹ ≠ 0 := by positivity
+example {α : Type*} [Semifield α] {x y : α} (hx : x ≠ 0) (hy : y ≠ 0) : x / y ≠ 0 := by positivity
+
+example {α : Type*} [MonoidWithZero α] [NoZeroDivisors α] {x : α} (hx : x ≠ 0) (n : ℕ) :
+ x ^ n ≠ 0 := by positivity
+
+example {α : Type*} [MonoidWithZero α] [NoZeroDivisors α] {x y : α}
+ (hx : x ≠ 0) (hy : y ≠ 0) : x * y ≠ 0 := by positivity
+
+example {α : Type*} [AddMonoidWithOne α] [CharZero α] {n : ℕ} (hn : n ≠ 0) :
+ (n : α) ≠ 0 := by positivity
+example {α : Type*} [AddGroupWithOne α] [CharZero α] {z : ℤ} (hz : z ≠ 0) :
+ (z : α) ≠ 0 := by positivity
+example {α : Type*} [DivisionRing α] [CharZero α] {q : ℚ} (hq : q ≠ 0) :
+ (q : α) ≠ 0 := by positivity
+
+example {α : Type*} [Semiring α] [Nontrivial α] (a : α) : a ^ 0 ≠ 0 := by positivity
+
+example {α : Type*} [AddGroup α] {a b : α} (ha : a ≠ b) : 0 ≠ b - a := by positivity
+example {α : Type*} [AddGroup α] {a b : α} (ha : a ≠ b) : 0 ≠ a - b := by positivity
From e1d1de3bbb575ceb968a895a3462d5a9ca4b22c9 Mon Sep 17 00:00:00 2001
From: Jovan Gerbscheid <56355248+JovanGerb@users.noreply.github.com>
Date: Wed, 17 Jun 2026 15:42:42 +0000
Subject: [PATCH 0120/1300] feat(Convert): less aggressive congruence (#38071)
This PR tries to make `convert` behave more predictably, disabling some of the aggressive congruence steps that `congr!` does. In particular, when the two sides have a different head constants, then we should not use congruence on these applications.
Co-authored-by: Jon Eugster
---
Mathlib/Analysis/Complex/Exponential.lean | 2 +-
Mathlib/Data/Fin/Tuple/Basic.lean | 9 ++---
Mathlib/Data/List/Perm/Basic.lean | 3 +-
Mathlib/Tactic/Convert.lean | 49 +++++------------------
MathlibTest/Tactic/Convert/Basic.lean | 12 +-----
5 files changed, 19 insertions(+), 56 deletions(-)
diff --git a/Mathlib/Analysis/Complex/Exponential.lean b/Mathlib/Analysis/Complex/Exponential.lean
index 9a51b9898ee788..798b4ef9df2d96 100644
--- a/Mathlib/Analysis/Complex/Exponential.lean
+++ b/Mathlib/Analysis/Complex/Exponential.lean
@@ -95,7 +95,7 @@ variable (x y : ℂ)
theorem exp_zero : exp 0 = 1 := by
rw [exp]
refine lim_eq_of_equiv_const fun ε ε0 => ⟨1, fun j hj => ?_⟩
- convert! (config := .unfoldSameFun) ε0 -- ε0 : ε > 0 but goal is _ < ε
+ convert ε0.lt
rcases j with - | j
· exact absurd hj (not_le_of_gt zero_lt_one)
· dsimp [exp']
diff --git a/Mathlib/Data/Fin/Tuple/Basic.lean b/Mathlib/Data/Fin/Tuple/Basic.lean
index 8dda5f5800debb..cc05c5f49d6741 100644
--- a/Mathlib/Data/Fin/Tuple/Basic.lean
+++ b/Mathlib/Data/Fin/Tuple/Basic.lean
@@ -400,9 +400,8 @@ theorem append_castAdd_natAdd {f : Fin (m + n) → α} :
/-- Splitting a dependent finite sequence v into an initial part and a final part,
and then concatenating these components, produces an identical sequence. -/
-theorem addCases_castAdd_natAdd {γ : Fin (m + n) → Sort*} (v : ∀ i, γ i) :
- addCases (fun i ↦ v (castAdd n i)) (fun j ↦ v (natAdd m j)) = v := by
- ext i
+theorem addCases_castAdd_natAdd {γ : Fin (m + n) → Sort*} (v : ∀ i, γ i) (i : Fin (m + n)) :
+ addCases (fun i ↦ v (castAdd n i)) (fun j ↦ v (natAdd m j)) i = v i := by
cases i using addCases <;> simp
theorem append_comp_sumElim {xs : Fin m → α} {ys : Fin n → α} :
@@ -802,8 +801,8 @@ theorem forall_fin_add_pi {γ : Fin (m + n) → Sort*} {P : (∀ i, γ i) → Pr
(∀ (vₘ : ∀ i, γ (castAdd n i)) (vₙ : ∀ j, γ (natAdd m j)), P (addCases vₘ vₙ)) where
mp hv vm vn := hv (addCases vm vn)
mpr h v := by
- convert! h (fun i => v (castAdd n i)) (fun j => v (natAdd m j))
- exact (addCases_castAdd_natAdd v).symm
+ convert h (fun i => v (castAdd n i)) (fun j => v (natAdd m j))
+ exact (addCases_castAdd_natAdd v _).symm
lemma exists_iff_castSucc {P : Fin (n + 1) → Prop} :
(∃ i, P i) ↔ P (last n) ∨ ∃ i : Fin n, P i.castSucc where
diff --git a/Mathlib/Data/List/Perm/Basic.lean b/Mathlib/Data/List/Perm/Basic.lean
index c1bc8b988698fa..d15c400a41f037 100644
--- a/Mathlib/Data/List/Perm/Basic.lean
+++ b/Mathlib/Data/List/Perm/Basic.lean
@@ -164,7 +164,8 @@ end Rel
lemma count_eq_count_filter_add [DecidableEq α] (P : α → Prop) [DecidablePred P]
(l : List α) (a : α) :
count a l = count a (l.filter P) + count a (l.filter (¬ P ·)) := by
- convert! countP_eq_countP_filter_add l _ P
+ unfold count
+ convert countP_eq_countP_filter_add l _ P
simp only [decide_not]
theorem Perm.foldl_eq {f : β → α → β} {l₁ l₂ : List α} [rcomm : RightCommutative f] (p : l₁ ~ l₂) :
diff --git a/Mathlib/Tactic/Convert.lean b/Mathlib/Tactic/Convert.lean
index c023a7dfb9e737..eb74ff924a4cec 100644
--- a/Mathlib/Tactic/Convert.lean
+++ b/Mathlib/Tactic/Convert.lean
@@ -24,7 +24,7 @@ e : Prime (2 * n + 1)
⊢ Prime (n + n + 1)
```
-the tactic `convert e using 2` will change the goal to
+the tactic `convert e` will change the goal to
```lean
⊢ n + n = 2 * n
@@ -32,14 +32,6 @@ the tactic `convert e using 2` will change the goal to
In this example, the new goal can be solved using `ring`.
-The `using 2` indicates it should iterate the congruence algorithm up to two times,
-where `convert e` would use an unrestricted number of iterations and lead to two
-impossible goals: `⊢ HAdd.hAdd = HMul.hMul` and `⊢ n = 2`.
-
-A variant configuration is `convert (config := .unfoldSameFun) e`, which only equates function
-applications for the same function (while doing so at the higher `default` transparency).
-This gives the same goal of `⊢ n + n = 2 * n` without needing `using 2`.
-
The `convert` tactic applies congruence lemmas eagerly before reducing,
therefore it can fail in cases where `exact` succeeds:
```lean
@@ -47,7 +39,7 @@ def p (n : ℕ) := True
example (h : p 0) : p 1 := by exact h -- succeeds
example (h : p 0) : p 1 := by convert h -- fails, with leftover goal `1 = 0`
```
-Limiting the depth of recursion can help with this. For example, `convert h using 1` will work
+Limiting the depth of recursion can help with this. For example, `convert h using 0` will work
in this case.
The syntax `convert ← e` will reverse the direction of the new goals
@@ -85,21 +77,12 @@ between `Convert.CheapConfig` and `Convert.ExpensiveConfig` based on other flags
-/
structure Convert.CheapConfig extends Congr!.Config where
postTransparency := .reducible
+ partialApp := false
+ sameFun := true
/-- Internal elaborator for `Convert.CheapConfig`: use `Convert.elabConfig` instead. -/
declare_config_elab Convert.elabCheapConfig Convert.CheapConfig
-/-- A configuration option that makes `convert` do the sorts of aggressive unfoldings that `congr`
-does while also similarly preventing `convert` from considering partial applications or congruences
-between different functions being applied.
-
-Note that `convert (config := .unfoldSameFun)` and `convert! (config := .unfoldSameFun)`
-currently do the same thing since `.unfoldSameFun` runs at default transparency always.
-This may change in the future, if `convert!` affects other options too.
--/
-abbrev Convert.CheapConfig.unfoldSameFun : Convert.CheapConfig :=
- { Congr!.Config.unfoldSameFun with }
-
/-- Configuration for the `convert!` family of tactics.
This is `Convert.CheapConfig` (used by `convert` without exclamation mark) with different,
more aggressive, defaults.
@@ -113,26 +96,14 @@ example the following call runs at `.instances` transparency.
convert! (postTransparency := .instances)
```
-/
-structure Convert.ExpensiveConfig extends Convert.CheapConfig where
+structure Convert.ExpensiveConfig extends Congr!.Config where
-- TODO: also enable this in the future?
-- preTransparency := .default
-- transparency := .default
- postTransparency := .default
/-- Internal elaborator for `Convert.ExpensiveConfig`: use `Convert.elabConfig` instead. -/
declare_config_elab Convert.elabExpensiveConfig Convert.ExpensiveConfig
-/-- A configuration option that makes `convert!` do the sorts of aggressive unfoldings that `congr`
-does while also similarly preventing `convert!` from considering partial applications or congruences
-between different functions being applied.
-
-Note that `convert (config := .unfoldSameFun)` and `convert! (config := .unfoldSameFun)`
-currently do the same thing since `.unfoldSameFun` runs at default transparency always.
-This may change in the future, if `convert!` affects other options too.
--/
-abbrev Convert.ExpensiveConfig.unfoldSameFun : Convert.ExpensiveConfig :=
- { Congr!.Config.unfoldSameFun with }
-
/-- Configuration elaborator for the `convert`/`convert!` family of tactics.
If `expensive` is true, we're elaborating for `convert!`, and will configure to run at default
@@ -201,9 +172,10 @@ pattern-matched, like `rintro` would, using the `with` keyword.
See also `convert_to t`, where `t` specifies the expected type, instead of a proof term of type `t`.
In other words, `convert_to t` works like `convert (?_ : t)`. Both tactics use the same options.
-* `convert! e` uses default transparency, rather than reducible, when solving side goals.
+* `convert! e` uses default transparency, rather than reducible, when solving side goals, and
+ it tries to apply congruence even if the two expressions do not have the same head constant.
* `convert ← e` creates equality goals in the opposite direction (with the goal type on the right).
-* `convert e using n`, where `n` is a positive numeral, controls the depth with which congruence is
+* `convert e using n`, where `n` is a numeral, controls the depth with which congruence is
applied. For example, if the main goal is `⊢ Prime (n + n + 1)` and `e : Prime (2 * n + 1)`, then
`convert e using 2` results in one goal, `⊢ n + n = 2 * n`, and `convert e using 3` (or more)
results in two (impossible) goals `⊢ HAdd.hAdd = HMul.hMul` and `⊢ n = 2`.
@@ -216,10 +188,9 @@ In other words, `convert_to t` works like `convert (?_ : t)`. Both tactics use t
Examples:
```lean
--- `convert using` controls the depth of congruence.
example {n : ℕ} (e : Prime (2 * n + 1)) :
Prime (n + n + 1) := by
- convert e using 2
+ convert e
-- One goal: ⊢ n + n = 2 * n
ring
@@ -234,7 +205,7 @@ example (h : p 0) : p 1 := by
-- `convert with` names introduced variables.
example (p q : Nat → Prop) (h : ∀ ε > 0, p ε) :
∀ ε > 0, q ε := by
- convert h using 2 with ε hε
+ convert h with ε hε
-- Goal now looks like:
-- hε : ε > 0
-- ⊢ q ε ↔ p ε
diff --git a/MathlibTest/Tactic/Convert/Basic.lean b/MathlibTest/Tactic/Convert/Basic.lean
index e49fab8836a4ad..6c41f450e271e8 100644
--- a/MathlibTest/Tactic/Convert/Basic.lean
+++ b/MathlibTest/Tactic/Convert/Basic.lean
@@ -61,7 +61,7 @@ end convert_to
example (prime : Nat → Prop) (n : Nat) (h : prime (2 * n + 1)) :
prime (n + n + 1) := by
- convert h
+ convert! h
· guard_target = (HAdd.hAdd : Nat → Nat → Nat) = HMul.hMul
exact test_sorry
· guard_target = n = 2
@@ -69,15 +69,7 @@ example (prime : Nat → Prop) (n : Nat) (h : prime (2 * n + 1)) :
example (prime : Nat → Prop) (n : Nat) (h : prime (2 * n + 1)) :
prime (n + n + 1) := by
- convert (config := .unfoldSameFun) h
- guard_target = n + n = 2 * n
- exact test_sorry
-
--- `convert! (config := .unfoldSameFun)` does the same thing as `convert (config := .unfoldSameFun)`
--- (at least for now)
-example (prime : Nat → Prop) (n : Nat) (h : prime (2 * n + 1)) :
- prime (n + n + 1) := by
- convert! (config := .unfoldSameFun) h
+ convert h
guard_target = n + n = 2 * n
exact test_sorry
From e24b844979be3369da7481a9ab864a3528f326ab Mon Sep 17 00:00:00 2001
From: Michael Rothgang <10105016+grunweg@users.noreply.github.com>
Date: Wed, 17 Jun 2026 15:42:45 +0000
Subject: [PATCH 0121/1300] chore: add deprecations for
`Data/Set/{Accumulate,Dissipate}` (#40676)
Follow-up to #40003.
---
Mathlib.lean | 2 ++
Mathlib/Data/Set/Accumulate.lean | 5 +++++
Mathlib/Data/Set/Dissipate.lean | 5 +++++
3 files changed, 12 insertions(+)
create mode 100644 Mathlib/Data/Set/Accumulate.lean
create mode 100644 Mathlib/Data/Set/Dissipate.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 57d9493e5cebd8..28715711f082d3 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -4315,6 +4315,7 @@ public import Mathlib.Data.Seq.Basic
public import Mathlib.Data.Seq.Computation
public import Mathlib.Data.Seq.Defs
public import Mathlib.Data.Seq.Parallel
+public import Mathlib.Data.Set.Accumulate
public import Mathlib.Data.Set.Basic
public import Mathlib.Data.Set.BoolIndicator
public import Mathlib.Data.Set.BooleanAlgebra
@@ -4325,6 +4326,7 @@ public import Mathlib.Data.Set.Constructions
public import Mathlib.Data.Set.Countable
public import Mathlib.Data.Set.Defs
public import Mathlib.Data.Set.Disjoint
+public import Mathlib.Data.Set.Dissipate
public import Mathlib.Data.Set.Enumerate
public import Mathlib.Data.Set.Equitable
public import Mathlib.Data.Set.Finite.Basic
diff --git a/Mathlib/Data/Set/Accumulate.lean b/Mathlib/Data/Set/Accumulate.lean
new file mode 100644
index 00000000000000..6645dd738d08eb
--- /dev/null
+++ b/Mathlib/Data/Set/Accumulate.lean
@@ -0,0 +1,5 @@
+module -- shake: keep-all
+
+public import Mathlib.Order.SetAccumulate
+
+deprecated_module (since := "2026-06-10")
diff --git a/Mathlib/Data/Set/Dissipate.lean b/Mathlib/Data/Set/Dissipate.lean
new file mode 100644
index 00000000000000..672b1257912b06
--- /dev/null
+++ b/Mathlib/Data/Set/Dissipate.lean
@@ -0,0 +1,5 @@
+module -- shake: keep-all
+
+public import Mathlib.Order.SetDissipate
+
+deprecated_module (since := "2026-06-10")
From 1ce228d453951997c60c821c7738e71fa0db0142 Mon Sep 17 00:00:00 2001
From: Michael Rothgang <10105016+grunweg@users.noreply.github.com>
Date: Wed, 17 Jun 2026 16:39:00 +0000
Subject: [PATCH 0122/1300] feat: metric connections (#36299)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This file defines what it means for a connection on a Riemannian vector bundle `(V, g)` to be *compatible* with the metric `g`. Namely, the differentiated metric tensor `∇ g` (defined by `(X, σ, τ) ↦ X g(σ, τ) - g(∇_X σ, τ) - g(σ, ∇_X τ)`) should vanish on all differentiable vector fields `X` and differentiable sections `σ`, `τ`.
From the path towards the Levi-Civita connection and Riemannian geometry.
Co-authored-by: Heather Macbeth [25316162+hrmacbeth@users.noreply.github.com](mailto:25316162+hrmacbeth@users.noreply.github.com)
Co-authored-by: Patrick Massot [patrickmassot@free.fr](mailto:patrickmassot@free.fr)
Co-authored-by: Heather Macbeth <25316162+hrmacbeth@users.noreply.github.com>
Co-authored-by: sgouezel
Co-authored-by: Patrick Massot
---
Mathlib.lean | 1 +
.../CovariantDerivative/Metric.lean | 182 ++++++++++++++++++
.../VectorBundle/MDifferentiable.lean | 25 ++-
3 files changed, 205 insertions(+), 3 deletions(-)
create mode 100644 Mathlib/Geometry/Manifold/VectorBundle/CovariantDerivative/Metric.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 28715711f082d3..631fc974d6988a 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -4665,6 +4665,7 @@ public import Mathlib.Geometry.Manifold.Submersion
public import Mathlib.Geometry.Manifold.VectorBundle.Basic
public import Mathlib.Geometry.Manifold.VectorBundle.ContMDiffSection
public import Mathlib.Geometry.Manifold.VectorBundle.CovariantDerivative.Basic
+public import Mathlib.Geometry.Manifold.VectorBundle.CovariantDerivative.Metric
public import Mathlib.Geometry.Manifold.VectorBundle.CovariantDerivative.Torsion
public import Mathlib.Geometry.Manifold.VectorBundle.FiberwiseLinear
public import Mathlib.Geometry.Manifold.VectorBundle.Hom
diff --git a/Mathlib/Geometry/Manifold/VectorBundle/CovariantDerivative/Metric.lean b/Mathlib/Geometry/Manifold/VectorBundle/CovariantDerivative/Metric.lean
new file mode 100644
index 00000000000000..740d390849c3eb
--- /dev/null
+++ b/Mathlib/Geometry/Manifold/VectorBundle/CovariantDerivative/Metric.lean
@@ -0,0 +1,182 @@
+/-
+Copyright (c) 2025 Michael Rothgang. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Patrick Massot, Michael Rothgang, Heather Macbeth
+-/
+module
+
+public import Mathlib.Geometry.Manifold.VectorBundle.CovariantDerivative.Basic
+public import Mathlib.Geometry.Manifold.VectorBundle.Riemannian
+public import Mathlib.Geometry.Manifold.MFDeriv.NormedSpace
+
+/-! # Metric connections
+
+This file defines connections on a Riemannian vector bundle which are compatible with the ambient
+metric. A bundled connection `∇` on a Riemannian vector bundle `(V, g)` is compatible with the
+metric `g` if and only if the differentiated metric tensor `∇ g` (defined by
+`(X, σ, τ) ↦ 𝓛_X g(σ, τ) - g(∇_X σ, τ) - g(σ, ∇_X τ)`) vanishes on all differentiable vector fields
+`X` and differentiable sections `σ`, `τ`.
+
+## Main definitions and results
+
+* `CovariantDerivative.derivMetricTensor`: the tensor
+ `(X, σ, τ) ↦ 𝓛_X g(σ, τ) - g(∇_X σ, τ) - g(σ, ∇_X τ)` defining when a connection `∇` on a
+ Riemannian vector bundle `(V, g)` is compatible with the metric `g`.
+* `CovariantDerivative.derivMetricTensor_apply` and
+ `CovariantDerivative.derivMetricTensor_apply_eq_extend` give formulas for applying
+ the compatibility tensor at `x` to vector fields and sections which are differentiable at `x`,
+ resp. to extensions of tangent vectors and sections at `x` to differentiable vector fields and
+ sections near `x`.
+* `CovariantDerivative.IsMetricCompatible`: predicate for a connection to be metric, namely that
+ `∇` is metric iff its `derivMetricTensor` vanishes
+
+## TODO
+
+* When Mathlib has a notion of parallel transport, prove the equivalence of
+ `CovariantDerivative.IsMetricCompatible` with the characterisation that parallel transport be an
+ isometry.
+
+* Given connections on bundles `V` and `W`, there is an induced connnection on the bundle
+ `Hom(V, W)`. When this induced connection has been defined in Mathlib, rephrase the definition of
+ `CovariantDerivative.derivMetricTensor`, to be simply the covariant derivative of the
+ metric tensor (considered as a section of `Hom(V, Hom(V, ℝ))`).
+
+-/
+open Bundle NormedSpace
+open scoped Manifold ContDiff
+
+variable
+ -- Let `M` be a real manifold modeled on `(E, H)`
+ {E : Type*} [NormedAddCommGroup E] [NormedSpace ℝ E]
+ {H : Type*} [TopologicalSpace H] (I : ModelWithCorners ℝ E H)
+ {M : Type*} [TopologicalSpace M] [ChartedSpace H M]
+ -- Let `V` be a bundle over `M` with standard fiber `F`.
+ {F : Type*} [NormedAddCommGroup F] [NormedSpace ℝ F]
+ {V : M → Type*} [TopologicalSpace (TotalSpace F V)]
+ [∀ x, NormedAddCommGroup (V x)] [∀ x, InnerProductSpace ℝ (V x)] [FiberBundle F V]
+
+/-! # Compatible connections
+
+A connection on `V` is compatible with the metric on `V` iff `𝓛_X ⟨σ, τ⟩ = ⟨∇_X σ, τ⟩ + ⟨σ, ∇_X τ⟩`
+holds for all sufficiently nice vector fields `X` on `M` and sections `σ`, `τ` of `V`.
+The left hand side is the Lie derivative of the function `⟨σ, τ⟩` w.r.t. the vector field `X`:
+its value at `x` is `df(X x)`, where `f := ⟨σ, τ⟩` (ie. `X` is seen a derivation on the algebra
+of functions on the base manifold acting on the function `⟨σ, τ⟩`).
+In our definition, we ask for this identity to hold at each `x : M`, whenever `X`, `σ` and `τ` are
+differentiable at `x`.
+-/
+
+variable {σ σ' σ'' τ τ' τ'' : Π x : M, V x}
+
+local notation "⟪" σ ", " τ "⟫" => fun x ↦ inner ℝ (σ x) (τ x)
+
+namespace CovariantDerivative
+
+-- Let `cov` be a covariant derivative on `V`.
+variable (cov : CovariantDerivative I F V)
+
+/-- Local notation for a covariant derivative on a vector bundle acting on a vector field and a
+section. -/
+local syntax "∇" term:arg term : term
+local macro_rules | `(∇ $X $σ) => `(fun (x : M) ↦ cov $σ x ($X x))
+
+/-- The function defining the compatibility tensor for `∇` w.r.t. `g`:
+prefer using `derivMetricTensor` instead -/
+noncomputable def derivMetricTensorAux (σ τ : Π x : M, V x) (x : M) : TangentSpace I x →L[ℝ] ℝ :=
+ d% ⟪σ, τ⟫ x - innerSL ℝ (τ x) ∘L cov σ x - innerSL ℝ (σ x) ∘L cov τ x
+
+@[simp]
+lemma derivMetricTensorAux_apply (σ τ : Π x : M, V x) {x : M} (X₀ : TangentSpace I x) :
+ derivMetricTensorAux I cov σ τ x X₀ =
+ d% ⟪σ, τ⟫ x X₀ - inner ℝ (cov σ x X₀) (τ x) - inner ℝ (σ x) (cov τ x X₀) := by
+ rw [real_inner_comm]
+ rfl
+
+-- From now on, assume `V` is a vector bundle endowed with a `C¹` Riemannian metric.
+variable [VectorBundle ℝ F V] [IsContMDiffRiemannianBundle I 1 F V] {x : M}
+
+theorem tensorial_derivMetricTensorAux₁ (τ : Π x, V x) (hτ : MDiffAt (T% τ) x) :
+ TensorialAt I F (derivMetricTensorAux I cov · τ x) x where
+ smul hf hσ := by
+ ext X₀
+ simp [mvfderiv_fun_mul hf (hσ.inner_bundle hτ),
+ cov.isCovariantDerivativeOn.leibniz hσ hf, inner_add_left, inner_smul_left]
+ ring
+ add hσ hσ' := by
+ ext X₀
+ simp [mvfderiv_fun_add (hσ.inner_bundle hτ) (hσ'.inner_bundle hτ),
+ cov.isCovariantDerivativeOn.add hσ hσ', inner_add_left]
+ abel
+
+theorem tensorial_derivMetricTensorAux₂ (σ : Π x, V x) (hσ : MDiffAt (T% σ) x) :
+ TensorialAt I F (derivMetricTensorAux I cov σ · x) x where
+ smul hf hτ := by
+ ext X₀
+ simp [mvfderiv_fun_mul hf (hσ.inner_bundle hτ),
+ cov.isCovariantDerivativeOn.leibniz hτ hf, inner_add_right, inner_smul_right]
+ ring
+ add hτ hτ' := by
+ ext X₀
+ simp [mvfderiv_fun_add (hσ.inner_bundle hτ) (hσ.inner_bundle hτ'),
+ cov.isCovariantDerivativeOn.add hτ hτ', inner_add_right]
+ abel
+
+variable {I} [ContMDiffVectorBundle 1 F V I] in
+/-- The tensor `(X, σ, τ) ↦ X g(σ, τ) - g(∇_X σ, τ) - g(σ, ∇_X τ)` defining when a connection
+`∇` on a Riemannian bundle `(M, V)` is compatible with the metric `g`. -/
+public noncomputable def derivMetricTensor [FiniteDimensional ℝ F] (x : M) :
+ V x →L[ℝ] V x →L[ℝ] (TangentSpace I x →L[ℝ] ℝ) :=
+ TensorialAt.mkHom₂ (derivMetricTensorAux I cov · · x) _
+ (tensorial_derivMetricTensorAux₁ I cov) (tensorial_derivMetricTensorAux₂ I cov)
+
+variable {X : Π x : M, TangentSpace I x}
+
+variable {I} [ContMDiffVectorBundle 1 F V I] in
+public theorem derivMetricTensor_apply [FiniteDimensional ℝ F] (x : M)
+ (hσ : MDiffAt (T% σ) x) (hτ : MDiffAt (T% τ) x) :
+ cov.derivMetricTensor x (σ x) (τ x) (X x) =
+ d% ⟪σ, τ⟫ x (X x) - ⟪∇ X σ, τ⟫ x - ⟪σ, ∇ X τ⟫ x := by
+ unfold derivMetricTensor
+ rw [TensorialAt.mkHom₂_apply _ _ hσ hτ, derivMetricTensorAux_apply]
+
+variable {I} [ContMDiffVectorBundle 1 F V I] in
+public theorem derivMetricTensor_apply_eq_extend [FiniteDimensional ℝ F]
+ (X₀ : TangentSpace I x) (σ₀ τ₀ : V x) :
+ cov.derivMetricTensor x σ₀ τ₀ X₀ =
+ d% ⟪(FiberBundle.extend F σ₀), (FiberBundle.extend F τ₀)⟫ x X₀
+ - inner ℝ (cov (FiberBundle.extend F σ₀) x X₀) τ₀
+ - inner ℝ σ₀ (cov (FiberBundle.extend F τ₀) x X₀) := by
+ simp [derivMetricTensor, TensorialAt.mkHom₂_apply_eq_extend]
+
+variable {I} [ContMDiffVectorBundle 1 F V I] in
+/-- Predicate saying that a connection `∇` on a Riemannian bundle `(V, g)` is compatible with the
+ambient metric, i.e. for all differentiable vector fields `X` on `M` and sections `σ` and `τ` of
+`V`, we have `X ⟨σ, τ⟩ = ⟨∇_X σ, τ⟩ + ⟨σ, ∇_X τ⟩`. -/
+public def IsMetricCompatible [FiniteDimensional ℝ F] : Prop := derivMetricTensor cov = 0
+
+variable {I} [ContMDiffVectorBundle 1 F V I]
+
+variable {cov} in
+public lemma IsMetricCompatible.mvfderiv_inner_eq [FiniteDimensional ℝ F]
+ (hcov : cov.IsMetricCompatible) {x : M} (X : Π x, TangentSpace I x) {σ τ : (x : M) → V x}
+ (hσ : MDiffAt (T% σ) x) (hτ : MDiffAt (T% τ) x) :
+ d% ⟪σ, τ⟫ x (X x) = ⟪∇ X σ, τ⟫ x + ⟪σ, ∇ X τ⟫ x := by
+ have H := congr($hcov x (σ x) (τ x) (X x))
+ simp [derivMetricTensor_apply _ _ hσ hτ] at H
+ linear_combination H
+
+variable [IsManifold I 1 M]
+
+public lemma isMetricCompatible_iff [FiniteDimensional ℝ F] :
+ cov.IsMetricCompatible ↔ ∀ {x : M} {X : Π x, TangentSpace I x} {σ τ : (x : M) → V x},
+ MDiffAt (T% X) x → MDiffAt (T% σ) x → MDiffAt (T% τ) x →
+ d% ⟪σ, τ⟫ x (X x) = ⟪∇ X σ, τ⟫ x + ⟪σ, ∇ X τ⟫ x := by
+ refine ⟨fun hcov x X σ τ hX ↦ hcov.mvfderiv_inner_eq X, fun h ↦ ?_⟩
+ ext1 x
+ apply VectorBundle.injective_eval_mdifferentiableAt_sec I F; ext1 σ; ext1 hσ
+ apply VectorBundle.injective_eval_mdifferentiableAt_sec I F; ext1 τ; ext1 hτ
+ apply VectorBundle.injective_eval_mdifferentiableAt_sec I E (TangentSpace I); ext X hX
+ simp (disch := assumption) [derivMetricTensor_apply]
+ linear_combination h hX hσ hτ
+
+end CovariantDerivative
diff --git a/Mathlib/Geometry/Manifold/VectorBundle/MDifferentiable.lean b/Mathlib/Geometry/Manifold/VectorBundle/MDifferentiable.lean
index ac9b4aab75ccf4..b47e572fc3d9b8 100644
--- a/Mathlib/Geometry/Manifold/VectorBundle/MDifferentiable.lean
+++ b/Mathlib/Geometry/Manifold/VectorBundle/MDifferentiable.lean
@@ -17,9 +17,8 @@ import Mathlib.Geometry.Manifold.Notation
public section
-open Bundle Set OpenPartialHomeomorph ContinuousLinearMap Pretrivialization Filter
-
-open scoped Manifold Bundle Topology
+open Bundle Set ContinuousLinearMap Pretrivialization Filter
+open scoped Manifold Topology
section
@@ -717,5 +716,25 @@ lemma mdifferentiableAt_extend {x : M} (σ₀ : V x) :
MDiffAt (T% (extend F σ₀)) x :=
(contMDiffAt_extend' (k := 1) I F σ₀).mdifferentiableAt one_ne_zero
+variable (V) in
+lemma _root_.VectorBundle.injective_eval_mdifferentiableAt_sec [∀ x, Module 𝕜 (V x)]
+ (W : Type*) [AddCommGroup W] [Module 𝕜 W] [TopologicalSpace W] (x : M) :
+ Function.Injective
+ (fun A : V x →L[𝕜] W ↦
+ fun (Z : Π x, V x) (_ : MDiffAt (T% Z) x) ↦ A (Z x)) := by
+ intro X X' h
+ ext σ₀
+ simpa using congr($h (extend F σ₀) (mdifferentiableAt_extend ..))
+
+variable (V) in
+lemma _root_.VectorBundle.injective_eval_contMDiffAt_sec {n : WithTop ℕ∞} [∀ x, Module 𝕜 (V x)]
+ (W : Type*) [AddCommGroup W] [Module 𝕜 W] [TopologicalSpace W] (x : M) :
+ Function.Injective
+ (fun A : V x →L[𝕜] W ↦
+ fun (Z : Π x, V x) (_ : CMDiffAt n (T% Z) x) ↦ A (Z x)) := by
+ intro X X' h
+ ext σ₀
+ simpa using congr($h (extend F σ₀) (contMDiffAt_extend' ..))
+
end FiberBundle
end extend
From 2ebce3689ad34d0d826baf7191226aefba799996 Mon Sep 17 00:00:00 2001
From: Marcelo Lynch
Date: Wed, 17 Jun 2026 16:39:03 +0000
Subject: [PATCH 0123/1300] ci(cache): consolidate cache fetching into one
action, warmed from master (#40678)
Consolidates the build job's cache fetch into a single `get-cache` action. It used to be a 3-step sequence (a cold-cache probe, a landrun verify, then a parent-commit warmup plus the fetch); now one action runs a single HEAD-scoped `cache get`, optionally warmed from a free master snapshot. Two things come out of that:
- HEAD-scoped fetch: dropping the parent-commit warmup (which checked out other SHAs and read their cache) means a run reads only its own scope plus the trusted master snapshot, matching the per-commit scope from #40035.
- Warmed from master: master's `.ltar` set is otherwise re-read from the paid Azure cache on every cold runner. The master `push` build in `build.yml` now publishes it once as a free `cache-snapshot` artifact, pruned to that commit's set and reusing what is already on disk, so it adds no Azure read. The action seeds `~/.cache/mathlib` from the snapshot at the PR's merge-base (its unchanged files hash identically there), else the newest one at-or-before it, else the latest, before fetching, so the unchanged-from-master oleans come from GitHub rather than Azure.
The snapshot download is trust-pinned to master push runs and fail-safe (any problem falls back to a plain Azure `cache get`), and fork-safe because `pull_request_target` runs the base branch's workflow and actions rather than the PR's. Each run logs a warm/cold count. `bors.yml` and `ci_dev.yml` gain read-only `actions: read`. Snapshot retention is 14 days, and `.ltar` are input-hash-keyed, so a stale base falls back gracefully.
---
.github/actions/get-cache/action.yml | 95 ++++++++++++++++++++
.github/workflows/bors.yml | 1 +
.github/workflows/build.yml | 2 +
.github/workflows/build_fork.yml | 7 +-
.github/workflows/build_template.yml | 126 ++++++++-------------------
.github/workflows/ci_dev.yml | 1 +
6 files changed, 138 insertions(+), 94 deletions(-)
create mode 100644 .github/actions/get-cache/action.yml
diff --git a/.github/actions/get-cache/action.yml b/.github/actions/get-cache/action.yml
new file mode 100644
index 00000000000000..ca5d3a1a2846f7
--- /dev/null
+++ b/.github/actions/get-cache/action.yml
@@ -0,0 +1,95 @@
+# Get this commit's oleans, in two phases:
+# 1. Warm the cache from the `cache-snapshot` GitHub artifact (canonical repo only).
+# 2. Fetch this commit's oleans from the remote cache with the trusted master-built binary.
+# The fetch is HEAD-scoped (reads only this commit's own cache scope). The warm is fail-safe
+# (any failure → just the remote fetch) and its source is hardcoded, so nothing can redirect
+# the download off the trusted master pipeline.
+name: Get cache
+description: Get this commit's oleans into the local cache.
+inputs:
+ working_directory:
+ description: The lake project to fetch the cache for (e.g. the checked-out PR branch).
+ required: true
+ cache_bin:
+ description: Path to the trusted `cache` binary, relative to `working_directory`.
+ required: true
+runs:
+ using: composite
+ steps:
+ # 1. Warm cache from the GitHub artifact. Resolve which snapshot to use: the one built
+ # at this commit's merge-base with master (its unchanged files hash identically
+ # there), else the newest still-retained one at-or-before it, else the latest (a
+ # merge-base older than retention, or any failure, lands here). Canonical repo only.
+ - name: Resolve cache snapshot
+ id: resolve
+ if: ${{ github.repository == 'leanprover-community/mathlib4' }}
+ shell: bash
+ env:
+ GH_TOKEN: ${{ github.token }}
+ HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
+ run: |
+ set -uo pipefail
+ runs="repos/leanprover-community/mathlib4/actions/workflows/build.yml/runs?branch=master&event=push&status=success"
+
+ # Each helper prints a matching successful master-push run-id, or empty.
+ run_at() { gh api "${runs}&head_sha=$1" --jq '.workflow_runs[0].id // empty' 2>/dev/null || true; }
+ latest_run() { gh api "${runs}&per_page=1" --jq '.workflow_runs[0].id // empty' 2>/dev/null || true; }
+ # newest run created at-or-before date $1, but not older than cutoff $2
+ newest_before() {
+ gh api "${runs}&per_page=100" 2>/dev/null | jq -r --arg d "$1" --arg c "$2" \
+ '[.workflow_runs[] | select(.created_at <= $d and ($c == "" or .created_at >= $c))][0].id // empty' \
+ 2>/dev/null || true
+ }
+
+ # This PR's merge-base with master (+ its commit date), and the cutoff below which
+ # snapshots have expired (retention ~14d).
+ mb_info=$(gh api "repos/leanprover-community/mathlib4/compare/master...${HEAD_SHA}" \
+ --jq '.merge_base_commit | "\(.sha) \(.commit.committer.date)"' 2>/dev/null || true)
+ read -r mb mb_date <<< "${mb_info}"
+ cutoff=$(date -u -d '13 days ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || true)
+
+ # Prefer the merge-base's snapshot (while still retained), then the newest one
+ # before it, then the latest of all.
+ run_id=""
+ if [[ -n "${mb}" && ( -z "${cutoff}" || "${mb_date}" > "${cutoff}" ) ]]; then
+ run_id=$(run_at "${mb}")
+ [[ -z "${run_id}" ]] && run_id=$(newest_before "${mb_date}" "${cutoff}")
+ fi
+ [[ -z "${run_id}" ]] && run_id=$(latest_run)
+
+ echo "Resolved cache-snapshot run_id: '${run_id}' (merge-base: ${mb:-unknown})"
+ echo "run_id=${run_id}" >> "$GITHUB_OUTPUT"
+
+ - name: Warm cache from GitHub artifact
+ if: ${{ steps.resolve.outputs.run_id != '' }}
+ continue-on-error: true # fail-safe: fall back to the remote fetch (step 2)
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: cache-snapshot
+ path: /home/lean/.cache/mathlib
+ repository: leanprover-community/mathlib4
+ run-id: ${{ steps.resolve.outputs.run_id }}
+ github-token: ${{ github.token }}
+
+ # 2. Fetch this commit's oleans from the remote cache with the trusted `cache` binary
+ # (outside landrun). Runs on every repo; the warm above just gives the canonical
+ # repo a local head start. HEAD-scoped: reads only this commit's own cache scope.
+ - name: Fetch cache from remote
+ shell: bash
+ env:
+ WORKDIR: ${{ inputs.working_directory }}
+ CACHE_BIN: ${{ inputs.cache_bin }}
+ CACHE_REPO: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
+ run: |
+ set -eo pipefail
+ cd "${WORKDIR}"
+ rm -rf .lake/build/lib/lean/Mathlib
+ log="${RUNNER_TEMP:-/tmp}/cache-get.log"
+ # --repo so fork PRs also read their own repo-namespaced cache (master is read flat,
+ # so this still gets the master bulk); for in-repo runs it resolves to the same repo.
+ "${CACHE_BIN}" --repo="${CACHE_REPO}" get 2>&1 | tee "${log}"
+ # Warmth = how much the snapshot covered HEAD: files already cached locally
+ # (just decompressed) vs downloaded from Azure. Parsed best-effort from the log.
+ warm=$(grep -oE 'Decompressing [0-9]+ already-cached' "${log}" | grep -oE '[0-9]+' | head -1 || true)
+ cold=$(grep -oE 'Attempting to download [0-9]+' "${log}" | grep -oE '[0-9]+' | head -1 || true)
+ echo "Cache warmth: ${warm:-0} already-cached (warm) / ${cold:-0} downloaded from Azure (cold)"
diff --git a/.github/workflows/bors.yml b/.github/workflows/bors.yml
index b6fc3e849b538c..a9eaf8fd68886f 100644
--- a/.github/workflows/bors.yml
+++ b/.github/workflows/bors.yml
@@ -16,6 +16,7 @@ concurrency:
permissions:
contents: read
id-token: write
+ actions: read # download master artifacts (tools-bin, cache-snapshot)
pull-requests: write # Only allow PR comments/labels
# All other permissions are implicitly 'none'
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 120bd6ae6ba196..db56bf6f261e22 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -70,5 +70,7 @@ jobs:
&& 'cache-upload-forks'
|| ''
}}
+ # the single producer: the master `push` build
+ publish_cache: ${{ github.repository == 'leanprover-community/mathlib4' && github.event_name == 'push' && github.ref == 'refs/heads/master' }}
runs_on: pr
secrets: inherit
diff --git a/.github/workflows/build_fork.yml b/.github/workflows/build_fork.yml
index addc86f66e546c..36a435275dc222 100644
--- a/.github/workflows/build_fork.yml
+++ b/.github/workflows/build_fork.yml
@@ -9,10 +9,9 @@ on:
- 'staging*.tmp'
- 'nolints'
paths-ignore:
- # pull_request_target uses the workflow from the target branch:
- # PR changes under this directory won't affect this run, so
- # running it is just wasteful
- - '.github/workflows/**'
+ # pull_request_target runs the workflow and its actions from the target branch, not
+ # the PR, so PR changes under these dirs can't affect the run — triggering is wasteful.
+ - '.github/**'
concurrency:
# label each workflow run; only the latest with each label will run
diff --git a/.github/workflows/build_template.yml b/.github/workflows/build_template.yml
index 5b0676dd6245e7..511e19544781d5 100644
--- a/.github/workflows/build_template.yml
+++ b/.github/workflows/build_template.yml
@@ -33,6 +33,12 @@ on:
# federated credential is scoped to; it must match the writer `cache_application_id`.
type: string
required: true
+ publish_cache:
+ # On a successful build, publish this run's `.ltar` set as `cache-snapshot`.
+ # Set by callers ONLY for the master-`push` build — the single producer.
+ type: boolean
+ required: false
+ default: false
env:
# Disable Lake's automatic fetching of cloud builds.
@@ -51,7 +57,6 @@ jobs:
archive-outcome: ${{ steps.archive.outcome }}
counterexamples-outcome: ${{ steps.counterexamples.outcome }}
cache-staging-has-files: ${{ steps.cache_staging_check.outputs.has_files }}
- get-cache-outcome: ${{ steps.get.outcome }}
lint-outcome: ${{ steps.lint.outcome }}
mk_all-outcome: ${{ steps.mk_all.outcome }}
noisy-outcome: ${{ steps.noisy.outcome }}
@@ -313,95 +318,12 @@ jobs:
echo "✅ All inputRevs in lake-manifest.json are valid"
fi
- - name: get cache (1/3 - setup and initial fetch)
- id: get_cache_part1_setup
- shell: bash # only runs `cache get` from `tools-branch`, so doesn't need to be inside landrun
- run: |
- cd pr-branch
- echo "Removing old Mathlib build directories prior to cache fetch..."
- rm -rf .lake/build/lib/lean/Mathlib
-
- # Fail quickly if the cache is completely cold, by checking for Mathlib.Init
- echo "Attempting to fetch olean for Mathlib/Init.lean from cache..."
- ../tools-branch/.lake/build/bin/cache get Mathlib/Init.lean
-
- - name: get cache (2/3 - test Mathlib.Init cache)
- id: get_cache_part2_test
- continue-on-error: true # Allow workflow to proceed to Part 3 to check outcome
- # This step uses the job's default shell, which is landrun-wrapped bash
- run: |
- cd pr-branch
-
- echo "Attempting: lake build --no-build -v Mathlib.Init (this runs under landrun)"
- lake build --no-build -v Mathlib.Init
-
- - name: get cache (3/3 - finalize cache operation)
- id: get
- shell: bash # only runs git and `cache get` from `tools-branch`, so doesn't need to be inside landrun
- env:
- BEFORE_SHA: ${{ github.event.before || '' }}
- BASE_SHA: ${{ github.event.pull_request.base.sha || '' }}
- CACHE_REPO: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
- run: |
- cd pr-branch
- if [[ "${{ steps.get_cache_part2_test.outcome }}" != "success" ]]; then
- echo "WARNING: 'lake build --no-build -v Mathlib.Init' failed."
- echo "No cache for 'Mathlib.Init' available or it could not be prepared."
- exit 0
- fi
-
- ORIG_SHA="$(git rev-parse HEAD)"
- PREV_SHA=""
- PREV_SHA_SOURCE=""
-
- # When a ref is newly created, github.event.before can be all-zero.
- if [[ "$BEFORE_SHA" =~ ^0{40}$ ]]; then
- BEFORE_SHA=""
- fi
-
- if [[ -n "$BEFORE_SHA" ]]; then
- PREV_SHA="$BEFORE_SHA"
- PREV_SHA_SOURCE="github.event.before"
- elif [[ -n "$BASE_SHA" ]]; then
- PREV_SHA="$BASE_SHA"
- PREV_SHA_SOURCE="pull_request.base.sha"
- else
- PREV_SHA="$(git rev-parse --verify --quiet HEAD^ || true)"
- if [[ -n "$PREV_SHA" ]]; then
- PREV_SHA_SOURCE="HEAD^"
- fi
- fi
-
- if [[ -n "$PREV_SHA" ]]; then
- # cf. https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Mathlib.20has.20moved.20to.20the.20new.20module.20system/near/563452000
- echo "Warming up cache using previous commit: $PREV_SHA (source: $PREV_SHA_SOURCE)"
- if git cat-file -e "$PREV_SHA^{commit}" 2>/dev/null || git fetch --no-tags --depth=1 origin "$PREV_SHA"; then
- # Skip warmup if the previous commit uses a different toolchain
- PREV_TOOLCHAIN=$(git show "$PREV_SHA:lean-toolchain" 2>/dev/null || true)
- if [[ "$PREV_TOOLCHAIN" != "$(cat lean-toolchain)" ]]; then
- echo "Previous commit $PREV_SHA uses a different toolchain ($PREV_TOOLCHAIN); skipping warmup."
- else
- git checkout "$PREV_SHA"
- ../tools-branch/.lake/build/bin/cache get
- # Run again with --repo, to ensure we actually get the oleans.
- ../tools-branch/.lake/build/bin/cache --repo="$CACHE_REPO" get
-
- echo "Switching back to branch head"
- git checkout "$ORIG_SHA"
- fi
- else
- echo "Could not fetch $PREV_SHA; skipping parent warmup cache fetch."
- fi
- else
- echo "No previous commit candidate found; skipping parent warmup cache fetch."
- fi
-
- echo "Fetching all remaining cache..."
-
- ../tools-branch/.lake/build/bin/cache get
-
- # Run again with --repo, to ensure we actually get the oleans.
- ../tools-branch/.lake/build/bin/cache --repo="$CACHE_REPO" get
+ # Get this commit's oleans into the local cache — see the action for the steps.
+ - name: Get cache
+ uses: ./workflow-actions/.github/actions/get-cache
+ with:
+ working_directory: pr-branch
+ cache_bin: ../tools-branch/.lake/build/bin/cache
- name: update {Mathlib, Tactic, Counterexamples, Archive}.lean
id: mk_all
@@ -522,6 +444,30 @@ jobs:
name: cache-staging
path: cache-staging/
+ # Prune to this commit's `.ltar` set so the published snapshot is exactly master's
+ # current cache (the local dir also holds the previous snapshot it warmed from).
+ - name: prune local cache to this commit's set
+ if: ${{ inputs.publish_cache && steps.build.outcome == 'success' }}
+ continue-on-error: true # best-effort; never fail the build
+ shell: bash # runs the trusted tools-branch `cache` binary, so no landrun needed
+ run: |
+ cd pr-branch
+ ../tools-branch/.lake/build/bin/cache clean
+
+ # Publish this run's pruned `.ltar` set as `cache-snapshot` for other runs to warm
+ # from. Already on disk (no Azure egress) and already zstd-compressed (skip
+ # recompression); retention covers how far back a PR's merge-base can be matched.
+ - name: upload cache snapshot warming artifact
+ if: ${{ inputs.publish_cache && steps.build.outcome == 'success' }}
+ continue-on-error: true # best-effort; never fail the build
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: cache-snapshot
+ path: /home/lean/.cache/mathlib/*.ltar
+ compression-level: 0
+ retention-days: 14
+ if-no-files-found: warn
+
- name: Check if building Archive or Counterexamples failed
if: steps.archive.outcome == 'failure' || steps.counterexamples.outcome == 'failure'
run: |
diff --git a/.github/workflows/ci_dev.yml b/.github/workflows/ci_dev.yml
index c2bd37019314c1..5b394f44cd2d88 100644
--- a/.github/workflows/ci_dev.yml
+++ b/.github/workflows/ci_dev.yml
@@ -28,6 +28,7 @@ on:
permissions:
contents: read
id-token: write
+ actions: read # read-only: download master artifacts (tools-bin, cache-snapshot)
# By default let's remove this permission (which is present in the other build pipelines)
# from the CI experimentation runs to avoid unwitting side effects
# pull-requests: write
From 60d8e4f973821226de51b3688df20433325c7f58 Mon Sep 17 00:00:00 2001
From: Sabrina Jewson <58880148+SabrinaJewson@users.noreply.github.com>
Date: Wed, 17 Jun 2026 17:44:29 +0000
Subject: [PATCH 0124/1300] feat(Order): add conversions from `Std` order
typeclasses to Mathlib ones (#37718)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
`{Preorder, PartialOrder, LinearOrder}.ofStd` exist to facilitate convenient translation from `Std` order typeclasses to Mathlib ones.
The design is modelled closely after [`Init.Data.Order.PackageFactories`](https://leanprover-community.github.io/mathlib4_docs/Init/Data/Order/PackageFactories.html) (`Std.PreorderPackage` is equivalent-ish to Mathlib’s `Preorder`, and same for partial and linear orders). The `OfStdArgs` types allow conveniently bundling a whole bunch of default arguments together in a way that allows one default argument set to `extends` another.
Co-authored-by: SabrinaJewson
---
Mathlib.lean | 1 +
Mathlib/Order/Std.lean | 289 ++++++++++++++++++++++++++++++++++++
MathlibTest/OrderOfStd.lean | 101 +++++++++++++
3 files changed, 391 insertions(+)
create mode 100644 Mathlib/Order/Std.lean
create mode 100644 MathlibTest/OrderOfStd.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 631fc974d6988a..a7c7d7b349bc80 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -6164,6 +6164,7 @@ public import Mathlib.Order.SetDissipate
public import Mathlib.Order.SetIsMax
public import Mathlib.Order.SetNotation
public import Mathlib.Order.Shrink
+public import Mathlib.Order.Std
public import Mathlib.Order.Sublattice
public import Mathlib.Order.Sublocale
public import Mathlib.Order.SuccPred.Archimedean
diff --git a/Mathlib/Order/Std.lean b/Mathlib/Order/Std.lean
new file mode 100644
index 00000000000000..cc4f6df8be5f5f
--- /dev/null
+++ b/Mathlib/Order/Std.lean
@@ -0,0 +1,289 @@
+/-
+Copyright (c) 2026 Sabrina Jewson. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Sabrina Jewson
+-/
+module
+
+public import Mathlib.Order.Defs.LinearOrder
+
+/-!
+# Converting Std order typeclasses into Mathlib ones
+
+This file provides factories for creating Mathlib order typeclasses (`PartialOrder`, `LinearOrder`)
+from Std ones.
+
+When all instances are present, the factories may be used without arguments:
+
+```lean
+instance : LinearOrder X := .ofStd X
+```
+
+Otherwise, it may be necessary to provide some instances manually:
+
+```lean
+instance : LinearOrder X := .ofStd X
+ { lawfulOrderOrd := sorry }
+```
+
+When existing instances of typeclasses exist, they will be preferred; otherwise, they will be
+generated automatically.
+
+## Implementation notes
+
+This module's job is to take a bunch of autoparams filled in with all the ways we could synthesize
+order typeclasses from `Std` and bundle them into a Mathlib order typeclass. We also need
+inheritance of sets of autoparams, because `PartialOrder.ofStd` takes all the autoparams of
+`Preorder.ofStd` and then some.
+
+To achieve this, each set of autoparams becomes a structure called `OfStdArgs` (thereby allowing it
+to extend existing `OfStdArgs` structures), and the synthesization scripts are in the default field
+values of this type. For the user, a simple `{}` will synthesize all fields of the structure with
+tactics, while individual fields that fail to synthesize may be specified using `{ field := … }`.
+
+The autoparam tactic scripts prioritize existing instances if they exist, to prevent diamonds,
+and in such cases require extra `Prop` typeclasses to ensure everyone agrees on the order.
+Otherwise, those agreement typeclasses can be synthesized with tactics automatically.
+
+Often, we write `let := someField` in the type of the structure fields, followed by `extract_lets`
+in the tactic body. This is done to make sure that `someField` is an instance that can be found by
+typeclass search in the tactic body: in the case where `someField` is generated by some means other
+than typeclass inference, it does not exist as a local variable otherwise.
+-/
+
+public section
+
+/-- Arguments for `Preorder.ofStd`; see that function for details. -/
+structure Preorder.OfStdArgs (α : Type*) where
+ /-- The `LE` instance of the order. -/
+ le : LE α := by
+ first
+ | infer_instance
+ | exact LE.ofOrd _
+ | fail "failed to infer `LE` instance; \
+ make sure you have an `LE` or `Ord` instance"
+ /-- The `LT` instance of the order. -/
+ lt :
+ let := le
+ LT α := by
+ extract_lets
+ first
+ | infer_instance
+ | exact ⟨fun a b ↦ a ≤ b ∧ ¬b ≤ a⟩
+ /-- `a < b` is equivalent to `a ≤ b ∧ ¬b ≤ a`. -/
+ lawfulOrderLT :
+ let := le; let := lt
+ Std.LawfulOrderLT α := by
+ extract_lets
+ first
+ /- Try the case where `LT` is synthesized from `LE` first so the error message points to the
+ lack of an instance and not the lack of a definitional equality. -/
+ | exact ⟨fun _ _ ↦ _root_.Iff.rfl⟩
+ | infer_instance
+ /-- ≤ forms a preorder. -/
+ isPreorder :
+ let := le
+ Std.IsPreorder α := by
+ extract_lets
+ first
+ | infer_instance
+ | exact _root_.Std.IsLinearPreorder.of_ord.toIsPreorder
+ | fail "failed to infer `Std.IsPreorder` instance; \
+ make sure you have an `Std.IsPreorder` instance \
+ or `Std.LawfulOrderOrd` and `Std.TransOrd` instances"
+
+/-- Create a `Preorder` from a type satisfying `Std.IsPreorder`.
+
+If an `LE` instance exists, either an `Std.IsPreorder` instance must exist, or there must be an
+`Ord` instance together with `Std.LawfulOrderOrd` and `Std.TransOrd` instances.
+
+If no `LE` instance exists, it can be generated from `Ord` and `Std.TransOrd` instances.
+
+If an `LT` instance exists, an `Std.LawfulOrderLT` instance must exist also; otherwise, a suitable
+`LT` instance will be generated. -/
+@[expose, implicit_reducible]
+def Preorder.ofStd (α : Type*) (args : OfStdArgs α := by exact {}) : Preorder α where
+ toLE := args.le
+ toLT := args.lt
+ le_refl := args.isPreorder.le_refl
+ le_trans := args.isPreorder.le_trans
+ lt_iff_le_not_ge := args.lawfulOrderLT.lt_iff
+
+/-- Arguments for `PartialOrder.ofStd`; see that function for details. -/
+structure PartialOrder.OfStdArgs (α : Type*) extends toPreorderArgs : Preorder.OfStdArgs α where
+ /-- ≤ forms a partial order. -/
+ isPartialOrder :
+ let := le
+ Std.IsPartialOrder α := by
+ extract_lets
+ first
+ | infer_instance
+ | exact _root_.Std.IsLinearOrder.of_ord.toIsPartialOrder
+ | fail "failed to infer `Std.IsPartialOrder` instance; \
+ make sure you have an `Std.IsPartialOrder` instance \
+ or `LawfulOrderOrd`, `LawfulEqOrd` and `TransOrd` instances"
+
+/-- Create a `PartialOrder` from a type satisfying `Std.IsPartialOrder`.
+
+If an `LE` instance exists, either an `Std.IsPartialOrder` instance must exist, or there must be an
+`Ord` instance together with `Std.LawfulOrderOrd`, `Std.LawfulEqOrd`, and `Std.TransOrd` instances.
+
+If no `LE` instance exists, it can be generated from `Ord`, `Std.LawfulEqOrd`, and `Std.TransOrd`
+instances.
+
+If an `LT` instance exists, an `Std.LawfulOrderLT` instance must exist also; otherwise, a suitable
+`LT` instance will be generated. -/
+@[expose, implicit_reducible]
+def PartialOrder.ofStd (α : Type*) (args : OfStdArgs α := by exact {}) : PartialOrder α where
+ toPreorder := .ofStd α args.toPreorderArgs
+ le_antisymm := args.isPartialOrder.le_antisymm
+
+/- Although Batteries provides that `compareOfLessAndEq` satisfies `LawfulLECmp`, there is
+unfortunately no link between that and `LawfulOrderCmp` even though they are essentially the same
+thing. -/
+theorem Std.LawfulOrderCmp.compareOfLessAndEq (α : Type*)
+ [LE α] [LT α] [LawfulOrderLT α] [IsLinearOrder α] [DecidableEq α] [DecidableLT α] :
+ LawfulOrderCmp (fun a b : α ↦ compareOfLessAndEq a b) :=
+ let : Ord α := ⟨fun a b : α ↦ _root_.compareOfLessAndEq a b⟩
+ { isLE_compare _ _ :=
+ have : DecidableLE α := fun _ _ ↦ Classical.propDecidable _
+ isLE_compareOfLessAndEq Std.le_antisymm Std.not_le (fun _ _ ↦ Std.le_total)
+ isGE_compare _ _ :=
+ have : DecidableLE α := fun _ _ ↦ Classical.propDecidable _
+ isGE_compareOfLessAndEq Std.le_antisymm Std.not_le (fun _ _ ↦ Std.le_total) }
+
+/-- Arguments for `LinearOrder.ofStd`; see that function for details. -/
+structure LinearOrder.OfStdArgs (α : Type*) extends
+ toPartialOrderArgs : PartialOrder.OfStdArgs α where
+ /-- ≤ forms a linear order. -/
+ isLinearOrder :
+ let := le
+ Std.IsLinearOrder α := by
+ extract_lets
+ first
+ | infer_instance
+ | exact _root_.Std.IsLinearOrder.of_ord
+ | fail "failed to infer `Std.IsLinearOrder` instance; \
+ make sure you have an `Std.IsLinearOrder` instance \
+ or `LawfulOrderOrd`, `LawfulEqOrd` and `TransOrd` instances"
+ /-- ≤ is decidable. -/
+ decidableLE : DecidableLE α := by
+ first
+ | infer_instance
+ | exact _root_.DecidableLE.ofOrd _
+ | fail "failed to infer `DecidableLE` instance; \
+ make sure you have a `DecidableLE` instance \
+ or a `LawfulOrderOrd` instance"
+ /-- = is decidable. This can always be automatically derived from `decidableLE`. -/
+ decidableEq :
+ let := toPartialOrderArgs; let := decidableLE
+ DecidableEq α := by
+ extract_lets _ toPartialOrderArgs
+ first
+ | infer_instance
+ | exact @_root_.decidableEqOfDecidableLE _ (.ofStd _ toPartialOrderArgs) _
+ /-- < is decidable. This can always be automatically derived from `decidableLE`. -/
+ decidableLT :
+ let := toPreorderArgs; let := decidableLE
+ DecidableLT α := by
+ extract_lets _ toPreorderArgs
+ first
+ | infer_instance
+ | exact @_root_.decidableLTOfDecidableLE _ (.ofStd _ toPreorderArgs) _
+ /-- The `Min` instance of the order. This can always be automatically derived. -/
+ min :
+ let := le; let := decidableLE
+ Min α := by
+ extract_lets
+ first
+ | infer_instance
+ | exact _root_.Min.leftLeaningOfLE _
+ /-- The `Max` instance of the order. This can always be automatically derived. -/
+ max :
+ let := le; let := decidableLE
+ Max α := by
+ extract_lets
+ first
+ | infer_instance
+ | exact _root_.Max.leftLeaningOfLE _
+ /-- `min a b` is equivalent to `if a ≤ b then a else b`. -/
+ lawfulOrderLeftLeaningMin : Std.LawfulOrderLeftLeaningMin α := by infer_instance
+ /-- `max a b` is equivalent to `if b ≤ a then a else b`. -/
+ lawfulOrderLeftLeaningMax : Std.LawfulOrderLeftLeaningMax α := by infer_instance
+ /-- The `Ord` instance of the order. This can always be automatically derived. -/
+ ord :
+ let := lt; let := decidableEq; let := decidableLT
+ Ord α := by
+ extract_lets
+ first
+ | infer_instance
+ | exact ⟨fun a b ↦ _root_.compareOfLessAndEq a b⟩
+ /-- `Ord` is compatible with ≤. -/
+ lawfulOrderOrd :
+ let := le; let := lt; let := lawfulOrderLT; let := isLinearOrder
+ let := decidableEq; let := decidableLT
+ Std.LawfulOrderOrd α := by
+ extract_lets
+ first
+ /- Try the case where `Ord` is synthesized from `compareOfLessAndEq` first so the error message
+ points to the lack of an instance and not the lack of a definitional equality. -/
+ | exact _root_.Std.LawfulOrderCmp.compareOfLessAndEq _
+ | infer_instance
+
+/-- Create a `LinearOrder` from a type satisfying `Std.IsLinearOrder`.
+
+If an `LE` instance exists, either an `Std.IsLinearOrder` instance must exist, or there must be an
+`Ord` instance together with `Std.LawfulOrderOrd`, `Std.LawfulEqOrd`, and `Std.TransOrd` instances.
+
+If no `LE` instance exists, it can be generated from `Ord`, `Std.LawfulEqOrd`, and `Std.TransOrd`
+instances.
+
+If an `LT` instance exists, an `Std.LawfulOrderLT` instance must exist also; otherwise, a suitable
+`LT` instance will be generated.
+
+If a `DecidableLE` instance exists, it will be used. Otherwise, it can be generated from an `Ord`
+instance.
+
+If `DecidableEq` and `DecidableLT` instances exist, they will be used. Otherwise, they will be
+generated from the `DecidableLE` instance.
+
+If `Min` and `Max` instances exist, they will be used, in which case the user must provide
+`Std.LawfulOrderLeftLeaningMin` or `Std.LawfulOrderLeftLeaningMax` respectively. Otherwise, they
+will be generated.
+
+If an `Ord` instance exists, it will be used, in which case the user must provide an
+`Std.LawfulOrderOrd` instance. Otherwise, it will be generated. -/
+@[expose, implicit_reducible]
+def LinearOrder.ofStd (α : Type*) (args : OfStdArgs α := by exact {}) : LinearOrder α :=
+ let := args.le
+ let := args.lt
+ have := args.lawfulOrderLT
+ have := args.isLinearOrder
+ let := args.decidableLE
+ have := args.lawfulOrderLeftLeaningMin
+ have := args.lawfulOrderLeftLeaningMax
+ { toPartialOrder := .ofStd _ args.toPartialOrderArgs
+ le_total := args.isLinearOrder.le_total
+ toDecidableLE := args.decidableLE
+ toDecidableEq := args.decidableEq
+ toDecidableLT := args.decidableLT
+ toMin := args.min
+ toMax := args.max
+ min_def _ _ := Std.min_eq_if
+ max_def a b := by
+ rw [Std.max_eq_if]
+ split
+ · split
+ · exact Std.le_antisymm ‹_› ‹_›
+ · rfl
+ case _ h => rw [if_pos (Std.le_of_lt (Std.not_le.mp h))]
+ toOrd := args.ord
+ compare_eq_compareOfLessAndEq a b := by
+ let := args.ord
+ have := args.lawfulOrderOrd
+ rw [compareOfLessAndEq]
+ split_ifs
+ case _ => rwa [Std.compare_eq_lt]
+ case _ => rwa [Std.compare_eq_iff_eq]
+ case _ h h' =>
+ exact Std.compare_eq_gt.mpr <| Std.lt_of_le_of_ne (Std.not_lt.mp h) (Ne.symm h') }
diff --git a/MathlibTest/OrderOfStd.lean b/MathlibTest/OrderOfStd.lean
new file mode 100644
index 00000000000000..543d1185512d61
--- /dev/null
+++ b/MathlibTest/OrderOfStd.lean
@@ -0,0 +1,101 @@
+module
+import Mathlib.Order.Std
+
+namespace PreorderFromLE
+
+def X := Nat deriving LE, Std.IsPreorder
+instance h : Preorder X := .ofStd X {}
+example : h.toLE = instLEX := rfl
+example {a b : X} : h.lt a b ↔ instLEX.le a b ∧ ¬instLEX.le b a := Iff.rfl
+
+end PreorderFromLE
+
+namespace PreorderFromLELT
+
+def X := Nat deriving LE, LT, Std.LawfulOrderLT, Std.IsPreorder
+attribute [irreducible] instLEX instLTX
+instance h : Preorder X := .ofStd X {}
+example : h.toLE = instLEX := rfl
+example : h.toLT = instLTX := rfl
+
+end PreorderFromLELT
+
+namespace PreorderFromOrd
+
+def X := Nat deriving Ord, Std.TransOrd
+instance h : Preorder X := .ofStd X {}
+example {a b} : h.le a b ↔ (instOrdX.compare a b).isLE := Iff.rfl
+example {a b} : h.lt a b ↔ (instOrdX.compare a b).isLE ∧ ¬(instOrdX.compare b a).isLE := Iff.rfl
+
+end PreorderFromOrd
+
+namespace PartialOrderFromLE
+
+def X := Nat deriving LE, Std.IsPartialOrder
+instance h : PartialOrder X := .ofStd X {}
+example : h.toLE = instLEX := rfl
+
+end PartialOrderFromLE
+
+namespace LinearOrderFromLE
+
+def X := Nat deriving LE, Std.IsLinearOrder, DecidableLE
+instance h : LinearOrder X := .ofStd X {}
+example : h.toLE = instLEX := rfl
+example {a b} : h.toOrd.compare a b = compareOfLessAndEq a b := rfl
+
+end LinearOrderFromLE
+
+-- If `DecidableEq`, `DecidableLT`, `Min` and `Max` instances exist, they are preserved.
+namespace DecidableMinMaxInstances
+
+def X := Nat
+deriving
+ LE, LT, Std.LawfulOrderLT, Std.IsLinearOrder,
+ DecidableLE, DecidableEq, DecidableLT,
+ Min, Max, Std.LawfulOrderLeftLeaningMin, Std.LawfulOrderLeftLeaningMax
+attribute [irreducible] instDecidableEqX instDecidableLTX instMinX instMaxX
+instance h : LinearOrder X := .ofStd X {}
+example : h.toDecidableEq = instDecidableEqX := rfl
+example : h.toDecidableLT = instDecidableLTX := rfl
+example : h.toMin = instMinX := rfl
+example : h.toMax = instMaxX := rfl
+
+end DecidableMinMaxInstances
+
+-- Generate `LE` from `Ord`
+namespace FromOrd
+
+def X := Nat deriving Ord, Std.TransOrd, Std.LawfulEqOrd
+attribute [irreducible] instOrdX
+instance h : LinearOrder X := .ofStd X
+example : h.toOrd = instOrdX := rfl
+example {a b} : h.le a b ↔ (instOrdX.compare a b).isLE := Iff.rfl
+example {a b} : h.lt a b ↔ (instOrdX.compare a b).isLE ∧ ¬(instOrdX.compare b a).isLE := Iff.rfl
+example : h.toMin = .leftLeaningOfLE X := rfl
+example : h.toMax = .leftLeaningOfLE X := rfl
+
+end FromOrd
+
+-- Transfer the properties of `Ord` over to the properties of `LE`
+namespace FromLEOrdViaOrd
+
+def X := Nat deriving LE, Ord, Std.TransOrd, Std.LawfulOrderOrd, Std.LawfulEqOrd
+attribute [irreducible] instLEX instOrdX
+instance h : LinearOrder X := .ofStd X
+example : h.toLE = instLEX := rfl
+example : h.toOrd = instOrdX := rfl
+
+end FromLEOrdViaOrd
+
+-- Transfer the properties of `LE` over to the properties of `Ord`
+namespace FromLEOrdViaLE
+
+def X := Nat deriving LE, Std.IsLinearOrder, DecidableLE, Ord, Std.LawfulOrderOrd
+attribute [irreducible] instLEX instDecidableLEX instOrdX
+instance h : LinearOrder X := .ofStd X
+example : h.toLE = instLEX := rfl
+example : h.toDecidableLE = instDecidableLEX := rfl
+example : h.toOrd = instOrdX := rfl
+
+end FromLEOrdViaLE
From 83a621b2967b53d00417992c770f6ffc232771ba Mon Sep 17 00:00:00 2001
From: Hannah Scholz <70071345+scholzhannah@users.noreply.github.com>
Date: Wed, 17 Jun 2026 17:44:31 +0000
Subject: [PATCH 0125/1300] feat: generalize `OpenPartialHomeomorph.Defs` file
to `PartialHomeomorph` (#39084)
Add `PartialHomeomorph`, which generalises `OpenPartialHomoemorph` by dropping the condition that the source and target be open. In other words, an `OpenPartialHomeomorph` is a `PartialHomeomorph` that additionally has open source and target.
This generalisation should find uses for manifolds (extended charts are `PartialHomeomorph`s, but their target is not open for manifolds with boundary). For CW complexes, we will want a `ClosedPartialHomeomorph`: this allows re-using results as much as possible.
This PR adds the basic definitions; future PRs will add additional material: #39071 indicates what the eventual result may be.
Zulip discussion: [#mathlib4 > Generalizing `PartialHomeomorph`?](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Generalizing.20.60PartialHomeomorph.60.3F/with/536896273)
---
Mathlib.lean | 1 +
.../Geometry/Manifold/ContMDiffMFDeriv.lean | 4 +-
.../Geometry/Manifold/IsManifold/Basic.lean | 2 +-
.../Geometry/Manifold/LocalDiffeomorph.lean | 2 +-
.../Manifold/LocalSourceTargetProperty.lean | 2 +-
.../CanonicalEmbedding/NormLeOne.lean | 7 +-
.../Topology/FiberBundle/Trivialization.lean | 4 +-
.../Topology/OpenPartialHomeomorph/Basic.lean | 4 +-
.../OpenPartialHomeomorph/Composition.lean | 9 +-
.../OpenPartialHomeomorph/Constructions.lean | 6 +-
.../Topology/OpenPartialHomeomorph/Defs.lean | 49 ++--
.../OpenPartialHomeomorph/IsImage.lean | 5 +-
Mathlib/Topology/PartialHomeomorph/Defs.lean | 226 ++++++++++++++++++
13 files changed, 277 insertions(+), 44 deletions(-)
create mode 100644 Mathlib/Topology/PartialHomeomorph/Defs.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index a7c7d7b349bc80..d03479050e91f0 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -8038,6 +8038,7 @@ public import Mathlib.Topology.Order.T5
public import Mathlib.Topology.Order.UpperLowerSetTopology
public import Mathlib.Topology.Order.WithTop
public import Mathlib.Topology.Partial
+public import Mathlib.Topology.PartialHomeomorph.Defs
public import Mathlib.Topology.PartitionOfUnity
public import Mathlib.Topology.Path
public import Mathlib.Topology.Perfect
diff --git a/Mathlib/Geometry/Manifold/ContMDiffMFDeriv.lean b/Mathlib/Geometry/Manifold/ContMDiffMFDeriv.lean
index fe370981f4d96c..d5747cf0dbe93d 100644
--- a/Mathlib/Geometry/Manifold/ContMDiffMFDeriv.lean
+++ b/Mathlib/Geometry/Manifold/ContMDiffMFDeriv.lean
@@ -455,7 +455,7 @@ lemma contMDiff_equivTangentBundleProd_symm :
filter_upwards [chart_source_mem_nhds (ModelProd (ModelProd H E) (ModelProd H' E')) (a, b)]
with p hp
-- now we have to check that the original map coincides locally with `pM` read in target chart.
- simp only [prodChartedSpace_chartAt, OpenPartialHomeomorph.prod_toPartialEquiv,
+ simp only [prodChartedSpace_chartAt, OpenPartialHomeomorph.prod_toPartialHomeomorph,
PartialEquiv.prod_source, mem_prod, TangentBundle.mem_chart_source_iff] at hp
let φ (x : E) := I ((chartAt H a.proj) ((chartAt H p.1.proj).symm (I.symm x)))
have D0 : DifferentiableWithinAt 𝕜 φ (Set.range I) (I ((chartAt H p.1.proj) p.1.proj)) := by
@@ -494,7 +494,7 @@ lemma contMDiff_equivTangentBundleProd_symm :
filter_upwards [chart_source_mem_nhds (ModelProd (ModelProd H E) (ModelProd H' E')) (a, b)]
with p hp
-- now we have to check that the original map coincides locally with `pM'` read in target chart.
- simp only [prodChartedSpace_chartAt, OpenPartialHomeomorph.prod_toPartialEquiv,
+ simp only [prodChartedSpace_chartAt, OpenPartialHomeomorph.prod_toPartialHomeomorph,
PartialEquiv.prod_source, mem_prod, TangentBundle.mem_chart_source_iff] at hp
let φ (x : E') := I' ((chartAt H' b.proj) ((chartAt H' p.2.proj).symm (I'.symm x)))
have D0 : DifferentiableWithinAt 𝕜 φ (Set.range I') (I' ((chartAt H' p.2.proj) p.2.proj)) := by
diff --git a/Mathlib/Geometry/Manifold/IsManifold/Basic.lean b/Mathlib/Geometry/Manifold/IsManifold/Basic.lean
index 79619e7d1384c9..11fbcf447b60bd 100644
--- a/Mathlib/Geometry/Manifold/IsManifold/Basic.lean
+++ b/Mathlib/Geometry/Manifold/IsManifold/Basic.lean
@@ -751,7 +751,7 @@ theorem contDiffGroupoid_prod {I : ModelWithCorners 𝕜 E H} {I' : ModelWithCor
e.prod e' ∈ contDiffGroupoid n (I.prod I') := by
obtain ⟨he, he_symm⟩ := he
obtain ⟨he', he'_symm⟩ := he'
- constructor <;> simp only [PartialEquiv.prod_source, OpenPartialHomeomorph.prod_toPartialEquiv,
+ constructor <;> simp only [OpenPartialHomeomorph.prod_toPartialHomeomorph,
contDiffPregroupoid]
· have h3 := ContDiffOn.prodMap he he'
rw [← I.image_eq, ← I'.image_eq, prod_image_image_eq] at h3
diff --git a/Mathlib/Geometry/Manifold/LocalDiffeomorph.lean b/Mathlib/Geometry/Manifold/LocalDiffeomorph.lean
index 6edbe4e85b53d5..db32ceb65a874e 100644
--- a/Mathlib/Geometry/Manifold/LocalDiffeomorph.lean
+++ b/Mathlib/Geometry/Manifold/LocalDiffeomorph.lean
@@ -105,7 +105,7 @@ namespace PartialDiffeomorph
variable (Φ : PartialDiffeomorph I J M N n)
/-- A partial diffeomorphism is also a local homeomorphism. -/
-@[expose, simps toPartialEquiv]
+@[expose, simps toPartialHomeomorph_toPartialEquiv]
def toOpenPartialHomeomorph : OpenPartialHomeomorph M N where
toPartialEquiv := Φ.toPartialEquiv
open_source := Φ.open_source
diff --git a/Mathlib/Geometry/Manifold/LocalSourceTargetProperty.lean b/Mathlib/Geometry/Manifold/LocalSourceTargetProperty.lean
index eba67d29639cd2..ab1e4a4b92d4d4 100644
--- a/Mathlib/Geometry/Manifold/LocalSourceTargetProperty.lean
+++ b/Mathlib/Geometry/Manifold/LocalSourceTargetProperty.lean
@@ -238,7 +238,7 @@ lemma prodMap [IsManifold I n M] [IsManifold I' n M'] [IsManifold J n N] [IsMani
(domChart_mem_maximalAtlas hf) (domChart_mem_maximalAtlas hg)
· apply IsManifold.mem_maximalAtlas_prod
(codChart_mem_maximalAtlas hf) (codChart_mem_maximalAtlas hg)
- · simp only [OpenPartialHomeomorph.prod_toPartialEquiv, PartialEquiv.prod_source,
+ · simp only [OpenPartialHomeomorph.prod_toPartialHomeomorph, PartialEquiv.prod_source,
preimage_prod_map_prod]
exact prod_mono hf.source_subset_preimage_source hg.source_subset_preimage_source
· exact h hf.property hg.property
diff --git a/Mathlib/NumberTheory/NumberField/CanonicalEmbedding/NormLeOne.lean b/Mathlib/NumberTheory/NumberField/CanonicalEmbedding/NormLeOne.lean
index f3b6b6f2488b12..7da4f53aa81781 100644
--- a/Mathlib/NumberTheory/NumberField/CanonicalEmbedding/NormLeOne.lean
+++ b/Mathlib/NumberTheory/NumberField/CanonicalEmbedding/NormLeOne.lean
@@ -248,12 +248,13 @@ variable (K)
theorem expMap_source :
expMap.source = (Set.univ : Set (realSpace K)) := by
- simp_rw [expMap, OpenPartialHomeomorph.pi_toPartialEquiv, PartialEquiv.pi_source, expMap_single,
- Set.pi_univ Set.univ]
+ simp_rw [expMap, OpenPartialHomeomorph.pi_toPartialHomeomorph,
+ PartialEquiv.pi_source, expMap_single, Set.pi_univ Set.univ]
theorem expMap_target :
expMap.target = Set.univ.pi fun (_ : InfinitePlace K) ↦ Set.Ioi 0 := by
- simp_rw [expMap, OpenPartialHomeomorph.pi_toPartialEquiv, PartialEquiv.pi_target, expMap_single]
+ simp_rw [expMap, OpenPartialHomeomorph.pi_toPartialHomeomorph,
+ PartialEquiv.pi_target, expMap_single]
theorem injective_expMap :
Function.Injective (expMap : realSpace K → realSpace K) :=
diff --git a/Mathlib/Topology/FiberBundle/Trivialization.lean b/Mathlib/Topology/FiberBundle/Trivialization.lean
index a3b14ce26719a8..38133c78000fab 100644
--- a/Mathlib/Topology/FiberBundle/Trivialization.lean
+++ b/Mathlib/Topology/FiberBundle/Trivialization.lean
@@ -412,8 +412,8 @@ initialize_simps_projections Trivialization (toFun → apply, invFun → symm_ap
theorem toPretrivialization_injective :
Function.Injective fun e : Trivialization F proj => e.toPretrivialization := fun e e' h => by
ext1
- exacts [OpenPartialHomeomorph.toPartialEquiv_injective
- (congr_arg Pretrivialization.toPartialEquiv h), congr_arg Pretrivialization.baseSet h]
+ exacts [OpenPartialHomeomorph.toPartialEquiv_injective congr(Pretrivialization.toPartialEquiv $h),
+ congr(Pretrivialization.baseSet $h)]
@[simp, mfld_simps]
theorem coe_coe : ⇑e.toOpenPartialHomeomorph = e :=
diff --git a/Mathlib/Topology/OpenPartialHomeomorph/Basic.lean b/Mathlib/Topology/OpenPartialHomeomorph/Basic.lean
index d7b7543fc806ee..bb87598e2874a5 100644
--- a/Mathlib/Topology/OpenPartialHomeomorph/Basic.lean
+++ b/Mathlib/Topology/OpenPartialHomeomorph/Basic.lean
@@ -122,7 +122,7 @@ theorem isOpen_image_iff_of_subset_source {s : Set X} (hs : s ⊆ e.source) :
/-- A `PartialEquiv` with continuous open forward map and open source is a
`OpenPartialHomeomorph`. -/
-@[simps toPartialEquiv]
+@[simps toPartialHomeomorph]
def ofContinuousOpenRestrict (e : PartialEquiv X Y) (hc : ContinuousOn e e.source)
(ho : IsOpenMap (e.source.restrict e)) (hs : IsOpen e.source) : OpenPartialHomeomorph X Y where
toPartialEquiv := e
@@ -145,7 +145,7 @@ theorem coe_ofContinuousOpenRestrict_symm (e : PartialEquiv X Y) (hc : Continuou
/-- A `PartialEquiv` with continuous open forward map and open source is a
`OpenPartialHomeomorph`. -/
-@[simps! toPartialEquiv]
+@[simps! toPartialHomeomorph]
def ofContinuousOpen (e : PartialEquiv X Y) (hc : ContinuousOn e e.source) (ho : IsOpenMap e)
(hs : IsOpen e.source) : OpenPartialHomeomorph X Y :=
ofContinuousOpenRestrict e hc (ho.restrict hs) hs
diff --git a/Mathlib/Topology/OpenPartialHomeomorph/Composition.lean b/Mathlib/Topology/OpenPartialHomeomorph/Composition.lean
index 943cdfd79060bf..b60b59954a009f 100644
--- a/Mathlib/Topology/OpenPartialHomeomorph/Composition.lean
+++ b/Mathlib/Topology/OpenPartialHomeomorph/Composition.lean
@@ -37,7 +37,7 @@ variable (e' : OpenPartialHomeomorph Y Z)
/-- Composition of two open partial homeomorphisms when the target of the first and the source of
the second coincide. -/
-@[simps! apply symm_apply toPartialEquiv, simps! -isSimp source target]
+@[simps! apply symm_apply toPartialHomeomorph, simps! -isSimp source target]
protected def trans' (h : e.target = e'.source) : OpenPartialHomeomorph X Z where
toPartialEquiv := PartialEquiv.trans' e.toPartialEquiv e'.toPartialEquiv h
open_source := e.open_source
@@ -103,11 +103,11 @@ theorem trans_assoc (e'' : OpenPartialHomeomorph Z Z') :
@[simp, mfld_simps]
theorem trans_refl : e.trans (OpenPartialHomeomorph.refl Y) = e :=
- toPartialEquiv_injective e.1.trans_refl
+ toPartialHomeomorph_injective (PartialHomeomorph.toPartialEquiv_injective e.1.trans_refl)
@[simp, mfld_simps]
theorem refl_trans : (OpenPartialHomeomorph.refl X).trans e = e :=
- toPartialEquiv_injective e.1.refl_trans
+ toPartialHomeomorph_injective (PartialHomeomorph.toPartialEquiv_injective e.1.refl_trans)
theorem trans_ofSet {s : Set Y} (hs : IsOpen s) : e.trans (ofSet s hs) = e.restr (e ⁻¹' s) :=
OpenPartialHomeomorph.ext _ _ (fun _ => rfl) (fun _ => rfl) <| by
@@ -197,7 +197,8 @@ variable (e : X ≃ₜ Y) (e' : Y ≃ₜ Z)
@[simp, mfld_simps]
theorem trans_toOpenPartialHomeomorph : (e.trans e').toOpenPartialHomeomorph =
e.toOpenPartialHomeomorph.trans e'.toOpenPartialHomeomorph :=
- OpenPartialHomeomorph.toPartialEquiv_injective <| Equiv.trans_toPartialEquiv _ _
+ OpenPartialHomeomorph.toPartialHomeomorph_injective <|
+ PartialHomeomorph.toPartialEquiv_injective <| Equiv.trans_toPartialEquiv _ _
/-- Precompose an open partial homeomorphism with a homeomorphism.
We modify the source and target to have better definitional behavior. -/
diff --git a/Mathlib/Topology/OpenPartialHomeomorph/Constructions.lean b/Mathlib/Topology/OpenPartialHomeomorph/Constructions.lean
index b1543e614aa23f..7da001d1a1cebe 100644
--- a/Mathlib/Topology/OpenPartialHomeomorph/Constructions.lean
+++ b/Mathlib/Topology/OpenPartialHomeomorph/Constructions.lean
@@ -76,7 +76,7 @@ section Prod
/-- The product of two open partial homeomorphisms, as an open partial homeomorphism on the product
space. -/
-@[simps! (attr := mfld_simps) -fullyApplied toPartialEquiv apply,
+@[simps! (attr := mfld_simps) -fullyApplied toPartialHomeomorph apply,
simps! -isSimp source target symm_apply]
def prod (eX : OpenPartialHomeomorph X X') (eY : OpenPartialHomeomorph Y Y') :
OpenPartialHomeomorph (X × Y) (X' × Y') where
@@ -137,7 +137,7 @@ variable {ι : Type*} [Finite ι] {X Y : ι → Type*} [∀ i, TopologicalSpace
[∀ i, TopologicalSpace (Y i)] (ei : ∀ i, OpenPartialHomeomorph (X i) (Y i))
/-- The product of a finite family of `OpenPartialHomeomorph`s. -/
-@[simps! toPartialEquiv apply symm_apply]
+@[simps! toPartialHomeomorph apply symm_apply]
def pi : OpenPartialHomeomorph (∀ i, X i) (∀ i, Y i) where
toPartialEquiv := PartialEquiv.pi fun i => (ei i).toPartialEquiv
open_source := isOpen_set_pi finite_univ fun i _ => (ei i).open_source
@@ -164,7 +164,7 @@ To ensure the maps `toFun` and `invFun` are inverse of each other on the new `so
the definition assumes that the sets `s` and `t` are related both by `e.is_image` and `e'.is_image`.
To ensure that the new maps are continuous on `source`/`target`, it also assumes that `e.source` and
`e'.source` meet `frontier s` on the same set and `e x = e' x` on this intersection. -/
-@[simps! -fullyApplied toPartialEquiv apply]
+@[simps! -fullyApplied toPartialHomeomorph apply]
def piecewise (e e' : OpenPartialHomeomorph X Y) (s : Set X) (t : Set Y) [∀ x, Decidable (x ∈ s)]
[∀ y, Decidable (y ∈ t)] (H : e.IsImage s t) (H' : e'.IsImage s t)
(Hs : e.source ∩ frontier s = e'.source ∩ frontier s)
diff --git a/Mathlib/Topology/OpenPartialHomeomorph/Defs.lean b/Mathlib/Topology/OpenPartialHomeomorph/Defs.lean
index 362cbe712da3c6..a8f90bd94cf6df 100644
--- a/Mathlib/Topology/OpenPartialHomeomorph/Defs.lean
+++ b/Mathlib/Topology/OpenPartialHomeomorph/Defs.lean
@@ -5,8 +5,7 @@ Authors: Sébastien Gouëzel
-/
module
-public import Mathlib.Logic.Equiv.PartialEquiv
-public import Mathlib.Topology.ContinuousOn
+public import Mathlib.Topology.PartialHomeomorph.Defs
/-!
# Partial homeomorphisms: definitions
@@ -17,7 +16,7 @@ This file defines homeomorphisms between open subsets of topological spaces. An
Additionally, we require that these sets are open, and that the functions are continuous on them.
Equivalently, they are homeomorphisms there.
-As in equivs, we register a coercion to functions, and we use `e x` and `e.symm x` throughout
+As for `Equiv`s, we register a coercion to functions, and we use `e x` and `e.symm x` throughout
instead of `e.toFun x` and `e.invFun x`.
## Main definitions
@@ -52,11 +51,9 @@ variable {X X' : Type*} {Y Y' : Type*} {Z Z' : Type*}
/-- Partial homeomorphisms, defined on open subsets of the space -/
structure OpenPartialHomeomorph (X : Type*) (Y : Type*) [TopologicalSpace X]
- [TopologicalSpace Y] extends PartialEquiv X Y where
+ [TopologicalSpace Y] extends PartialHomeomorph X Y where
open_source : IsOpen source
open_target : IsOpen target
- continuousOn_toFun : ContinuousOn toFun source
- continuousOn_invFun : ContinuousOn invFun target
namespace OpenPartialHomeomorph
@@ -65,8 +62,7 @@ variable (e : OpenPartialHomeomorph X Y)
/-! Basic properties; inverse (symm instance) -/
section Basic
/-- Coercion of an open partial homeomorphisms to a function. We don't use `e.toFun` because it is
-actually `e.toPartialEquiv.toFun`, so `simp` will apply lemmas about `toPartialEquiv`.
-While we may want to switch to this behavior later, doing it mid-port will break a lot of proofs. -/
+actually `e.toPartialEquiv.toFun`, so `simp` will apply lemmas about `toPartialEquiv`. -/
@[coe] def toFun' : X → Y := e.toFun
/-- Coercion of an `OpenPartialHomeomorph` to function.
@@ -77,11 +73,9 @@ instance : CoeFun (OpenPartialHomeomorph X Y) fun _ => X → Y :=
/-- The inverse of an open partial homeomorphism -/
@[symm]
protected def symm : OpenPartialHomeomorph Y X where
- toPartialEquiv := e.toPartialEquiv.symm
+ toPartialHomeomorph := e.toPartialHomeomorph.symm
open_source := e.open_target
open_target := e.open_source
- continuousOn_toFun := e.continuousOn_invFun
- continuousOn_invFun := e.continuousOn_toFun
/-- See Note [custom simps projection]. We need to specify this projection explicitly in this case,
because it is a composition of multiple projections. -/
@@ -100,21 +94,25 @@ theorem continuousOn_symm : ContinuousOn e.symm e.target :=
@[simp, mfld_simps]
theorem coe_mk (e : PartialEquiv X Y) (h₁ h₂ h₃ h₄) :
- (OpenPartialHomeomorph.mk e h₁ h₂ h₃ h₄ : X → Y) = e :=
+ (OpenPartialHomeomorph.mk (.mk e h₁ h₂) h₃ h₄ : X → Y) = e :=
rfl
@[deprecated (since := "2026-05-20")] alias mk_coe := coe_mk
@[simp, mfld_simps]
theorem coe_mk_symm (e : PartialEquiv X Y) (h₁ h₂ h₃ h₄) :
- ((OpenPartialHomeomorph.mk e h₁ h₂ h₃ h₄).symm : Y → X) = e.symm :=
+ ((OpenPartialHomeomorph.mk (.mk e h₁ h₂) h₃ h₄).symm : Y → X) = e.symm :=
rfl
@[deprecated (since := "2026-05-20")] alias mk_coe_symm := coe_mk_symm
+theorem toPartialHomeomorph_injective :
+ Injective (toPartialHomeomorph : OpenPartialHomeomorph X Y → PartialHomeomorph X Y)
+ | ⟨_, _, _⟩, ⟨_, _, _⟩, rfl => rfl
+
theorem toPartialEquiv_injective :
- Injective (toPartialEquiv : OpenPartialHomeomorph X Y → PartialEquiv X Y)
- | ⟨_, _, _, _, _⟩, ⟨_, _, _, _, _⟩, rfl => rfl
+ Injective (fun f ↦ f.toPartialEquiv : OpenPartialHomeomorph X Y → PartialEquiv X Y) :=
+ PartialHomeomorph.toPartialEquiv_injective.comp toPartialHomeomorph_injective
/- Register a few simp lemmas to make sure that `simp` puts the application of a local
homeomorphism in its normal form, i.e., in terms of its coercion to a function. -/
@@ -143,6 +141,14 @@ theorem coe_toPartialEquiv_symm : (e.toPartialEquiv.symm : Y → X) = e.symm :=
theorem map_source {x : X} (h : x ∈ e.source) : e x ∈ e.target :=
e.map_source' h
+@[simp, mfld_simps]
+theorem coe_toPartialHomeomorph : (e.toPartialHomeomorph : X → Y) = e :=
+ rfl
+
+@[simp, mfld_simps]
+theorem coe_toPartialHomeomorph_symm : (e.toPartialHomeomorph.symm : Y → X) = e.symm :=
+ rfl
+
/-- Variant of `map_source`, stated for images of subsets of `source`. -/
lemma image_source_subset : e '' e.source ⊆ e.target :=
fun _ ⟨_, hx, hex⟩ ↦ mem_of_eq_of_mem (id hex.symm) (e.map_source' hx)
@@ -192,15 +198,13 @@ end Basic
/-- Interpret a `Homeomorph` as an `OpenPartialHomeomorph` by restricting it
to an open set `s` in the domain and to `t` in the codomain. -/
-@[simps! -fullyApplied apply symm_apply toPartialEquiv,
+@[simps! -fullyApplied apply symm_apply toPartialHomeomorph,
simps! -isSimp source target]
def _root_.Homeomorph.toOpenPartialHomeomorphOfImageEq (e : X ≃ₜ Y) (s : Set X) (hs : IsOpen s)
(t : Set Y) (h : e '' s = t) : OpenPartialHomeomorph X Y where
- toPartialEquiv := e.toPartialEquivOfImageEq s t h
+ toPartialHomeomorph := e.toPartialHomeomorphOfImageEq s t h
open_source := hs
open_target := by simpa [← h]
- continuousOn_toFun := e.continuous.continuousOn
- continuousOn_invFun := e.symm.continuous.continuousOn
/-- A homeomorphism induces an open partial homeomorphism on the whole space -/
@[simps! (attr := mfld_simps) -fullyApplied]
@@ -211,11 +215,9 @@ def _root_.Homeomorph.toOpenPartialHomeomorph (e : X ≃ₜ Y) : OpenPartialHome
/-- Replace `toPartialEquiv` field to provide better definitional equalities. -/
def replacePartialEquiv (e : OpenPartialHomeomorph X Y) (e' : PartialEquiv X Y)
(h : e.toPartialEquiv = e') : OpenPartialHomeomorph X Y where
- toPartialEquiv := e'
+ toPartialHomeomorph := e.toPartialHomeomorph.replacePartialEquiv e' h
open_source := h ▸ e.open_source
open_target := h ▸ e.open_target
- continuousOn_toFun := h ▸ e.continuousOn_toFun
- continuousOn_invFun := h ▸ e.continuousOn_invFun
@[deprecated (since := "2026-05-19")] alias replaceEquiv := replacePartialEquiv
@@ -234,7 +236,8 @@ called `EqOnSource`. -/
@[ext]
protected theorem ext (e' : OpenPartialHomeomorph X Y) (h : ∀ x, e x = e' x)
(hinv : ∀ x, e.symm x = e'.symm x) (hs : e.source = e'.source) : e = e' :=
- toPartialEquiv_injective (PartialEquiv.ext h hinv hs)
+ toPartialHomeomorph_injective
+ (PartialHomeomorph.ext e.toPartialHomeomorph e'.toPartialHomeomorph h hinv hs)
@[simp, mfld_simps]
theorem symm_toPartialEquiv : e.symm.toPartialEquiv = e.toPartialEquiv.symm :=
diff --git a/Mathlib/Topology/OpenPartialHomeomorph/IsImage.lean b/Mathlib/Topology/OpenPartialHomeomorph/IsImage.lean
index 4046c9bec712cd..fe21e40169ee0f 100644
--- a/Mathlib/Topology/OpenPartialHomeomorph/IsImage.lean
+++ b/Mathlib/Topology/OpenPartialHomeomorph/IsImage.lean
@@ -171,7 +171,7 @@ theorem isOpen_iff (h : e.IsImage s t) : IsOpen (e.source ∩ s) ↔ IsOpen (e.t
h.preimage_eq' ▸ e.isOpen_inter_preimage hs⟩
/-- Restrict an `OpenPartialHomeomorph` to a pair of corresponding open sets. -/
-@[simps! -fullyApplied apply symm_apply toPartialEquiv]
+@[simps! -fullyApplied apply symm_apply toPartialHomeomorph]
def restr (h : e.IsImage s t) (hs : IsOpen (e.source ∩ s)) : OpenPartialHomeomorph X Y where
toPartialEquiv := h.toPartialEquiv.restr
open_source := hs
@@ -315,7 +315,8 @@ theorem eqOnSource_iff (e e' : OpenPartialHomeomorph X Y) :
/-- `EqOnSource` is an equivalence relation. -/
instance eqOnSourceSetoid : Setoid (OpenPartialHomeomorph X Y) :=
- { PartialEquiv.eqOnSourceSetoid.comap toPartialEquiv with r := EqOnSource }
+ { PartialEquiv.eqOnSourceSetoid.comap
+ (fun x ↦ (toPartialHomeomorph x).toPartialEquiv) with r := EqOnSource }
theorem eqOnSource_refl : e ≈ e := Setoid.refl _
diff --git a/Mathlib/Topology/PartialHomeomorph/Defs.lean b/Mathlib/Topology/PartialHomeomorph/Defs.lean
new file mode 100644
index 00000000000000..ab78f5be70780c
--- /dev/null
+++ b/Mathlib/Topology/PartialHomeomorph/Defs.lean
@@ -0,0 +1,226 @@
+/-
+Copyright (c) 2019 Sébastien Gouëzel. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Sébastien Gouëzel
+-/
+module
+
+public import Mathlib.Logic.Equiv.PartialEquiv
+public import Mathlib.Topology.ContinuousOn
+
+/-!
+# Partial homeomorphisms: definitions
+
+This file defines homeomorphisms between subsets of topological spaces. An element `e` of
+`PartialHomeomorph X Y` is an extension of `PartialEquiv X Y`, i.e., it is a pair of functions
+`e.toFun` and `e.invFun`, inverse of each other on the sets `e.source` and `e.target`.
+Additionally, we require that the functions are continuous on them. Equivalently, they are
+homeomorphisms there.
+
+As for `Equiv`s, we register a coercion to functions, and we use `e x` and `e.symm x` throughout
+instead of `e.toFun x` and `e.invFun x`.
+
+## Main definitions
+
+This file is intentionally kept small; many other constructions of, and lemmas about,
+partial homeomorphisms can be found in other files under `Mathlib/Topology/PartialHomeomorph/`.
+
+* `Homeomorph.toPartialHomeomorph`: associating a partial homeomorphism to a
+ homeomorphism, with `source = target = Set.univ`;
+* `PartialHomeomorph.symm`: the inverse of a partial homeomorphism
+
+## Implementation notes
+
+Most statements are copied from their `PartialEquiv` versions, although some care is required.
+
+For design notes, see `PartialEquiv.lean`.
+
+### Local coding conventions
+
+If a lemma deals with the intersection of a set with either source or target of a `PartialEquiv`,
+then it should use `e.source ∩ s` or `e.target ∩ t`, not `s ∩ e.source` or `t ∩ e.target`.
+-/
+
+@[expose] public section
+
+open Function Set Filter Topology
+
+variable {X X' : Type*} {Y Y' : Type*} {Z Z' : Type*}
+ [TopologicalSpace X] [TopologicalSpace X'] [TopologicalSpace Y] [TopologicalSpace Y']
+ [TopologicalSpace Z] [TopologicalSpace Z']
+
+/-- Partial homeomorphisms, defined on subsets of the space -/
+structure PartialHomeomorph (X : Type*) (Y : Type*) [TopologicalSpace X]
+ [TopologicalSpace Y] extends PartialEquiv X Y where
+ continuousOn_toFun : ContinuousOn toFun source
+ continuousOn_invFun : ContinuousOn invFun target
+
+namespace PartialHomeomorph
+
+variable (e : PartialHomeomorph X Y)
+
+/-! Basic properties; inverse (symm instance) -/
+section Basic
+/-- Coercion of a partial homeomorphisms to a function. We don't use `e.toFun` because it is
+actually `e.toPartialEquiv.toFun`, so `simp` will apply lemmas about `toPartialEquiv`. -/
+@[coe] def toFun' : X → Y := e.toFun
+
+/-- Coercion of a `PartialHomeomorph` to function.
+Note that a `PartialHomeomorph` is not `DFunLike`. -/
+instance : CoeFun (PartialHomeomorph X Y) fun _ => X → Y :=
+ ⟨fun e => e.toFun'⟩
+
+/-- The inverse of a partial homeomorphism -/
+@[symm]
+protected def symm : PartialHomeomorph Y X where
+ toPartialEquiv := e.toPartialEquiv.symm
+ continuousOn_toFun := e.continuousOn_invFun
+ continuousOn_invFun := e.continuousOn_toFun
+
+/-- See Note [custom simps projection]. We need to specify this projection explicitly in this case,
+ because it is a composition of multiple projections. -/
+def Simps.apply (e : PartialHomeomorph X Y) : X → Y := e
+
+/-- See Note [custom simps projection] -/
+def Simps.symm_apply (e : PartialHomeomorph X Y) : Y → X := e.symm
+
+initialize_simps_projections PartialHomeomorph (toFun → apply, invFun → symm_apply)
+
+protected theorem continuousOn : ContinuousOn e e.source :=
+ e.continuousOn_toFun
+
+theorem continuousOn_symm : ContinuousOn e.symm e.target :=
+ e.continuousOn_invFun
+
+@[simp]
+theorem coe_mk (e : PartialEquiv X Y) (h₁ h₂) : (PartialHomeomorph.mk e h₁ h₂ : X → Y) = e := rfl
+
+@[simp]
+theorem coe_mk_symm (e : PartialEquiv X Y) (h₁ h₂) :
+ ((PartialHomeomorph.mk e h₁ h₂).symm : Y → X) = e.symm :=
+ rfl
+
+theorem toPartialEquiv_injective :
+ Injective (toPartialEquiv : PartialHomeomorph X Y → PartialEquiv X Y)
+ | ⟨_, _, _⟩, ⟨_, _, _⟩, rfl => rfl
+
+/- Register a few simp lemmas to make sure that `simp` puts the application of a local
+homeomorphism in its normal form, i.e., in terms of its coercion to a function. -/
+@[simp]
+theorem toFun_eq_coe (e : PartialHomeomorph X Y) : e.toFun = e :=
+ rfl
+
+@[simp]
+theorem invFun_eq_coe (e : PartialHomeomorph X Y) : e.invFun = e.symm :=
+ rfl
+
+@[simp]
+theorem coe_toPartialEquiv : (e.toPartialEquiv : X → Y) = e :=
+ rfl
+
+@[simp]
+theorem coe_toPartialEquiv_symm : (e.toPartialEquiv.symm : Y → X) = e.symm :=
+ rfl
+
+@[simp]
+theorem map_source {x : X} (h : x ∈ e.source) : e x ∈ e.target :=
+ e.map_source' h
+
+/-- Variant of `map_source`, stated in terms of subsets. -/
+lemma image_source_subset : e '' e.source ⊆ e.target :=
+ fun _ ⟨_, hx, hex⟩ ↦ mem_of_eq_of_mem (id hex.symm) (e.map_source' hx)
+
+@[simp]
+theorem map_target {x : Y} (h : x ∈ e.target) : e.symm x ∈ e.source :=
+ e.map_target' h
+
+@[simp]
+theorem left_inv {x : X} (h : x ∈ e.source) : e.symm (e x) = x :=
+ e.left_inv' h
+
+@[simp]
+theorem right_inv {x : Y} (h : x ∈ e.target) : e (e.symm x) = x :=
+ e.right_inv' h
+
+theorem eq_symm_apply {x : X} {y : Y} (hx : x ∈ e.source) (hy : y ∈ e.target) :
+ x = e.symm y ↔ e x = y :=
+ e.toPartialEquiv.eq_symm_apply hx hy
+
+protected theorem mapsTo : MapsTo e e.source e.target := fun _ => e.map_source
+
+protected theorem mapsTo_symm : MapsTo e.symm e.target e.source :=
+ e.symm.mapsTo
+
+protected theorem leftInvOn : LeftInvOn e.symm e e.source := fun _ => e.left_inv
+
+protected theorem rightInvOn : RightInvOn e.symm e e.target := fun _ => e.right_inv
+
+protected theorem invOn : InvOn e.symm e e.source e.target :=
+ ⟨e.leftInvOn, e.rightInvOn⟩
+
+protected theorem injOn : InjOn e e.source :=
+ e.leftInvOn.injOn
+
+protected theorem bijOn : BijOn e e.source e.target :=
+ e.invOn.bijOn e.mapsTo e.mapsTo_symm
+
+protected theorem surjOn : SurjOn e e.source e.target :=
+ e.bijOn.surjOn
+
+end Basic
+
+/-- Interpret a `Homeomorph` as a `PartialHomeomorph` by restricting it
+to a set `s` in the domain and to `t` in the codomain. -/
+@[simps! -fullyApplied apply symm_apply toPartialEquiv,
+ simps! -isSimp source target]
+def _root_.Homeomorph.toPartialHomeomorphOfImageEq (e : X ≃ₜ Y) (s : Set X)
+ (t : Set Y) (h : e '' s = t) : PartialHomeomorph X Y where
+ toPartialEquiv := e.toPartialEquivOfImageEq s t h
+ continuousOn_toFun := e.continuous.continuousOn
+ continuousOn_invFun := e.symm.continuous.continuousOn
+
+/-- A homeomorphism induces a partial homeomorphism on the whole space -/
+@[simps! -fullyApplied]
+def _root_.Homeomorph.toPartialHomeomorph (e : X ≃ₜ Y) : PartialHomeomorph X Y :=
+ e.toPartialHomeomorphOfImageEq univ univ <| by rw [image_univ, e.surjective.range_eq]
+
+/-- Replace `toPartialEquiv` field to provide better definitional equalities. -/
+def replacePartialEquiv (e : PartialHomeomorph X Y) (e' : PartialEquiv X Y)
+ (h : e.toPartialEquiv = e') : PartialHomeomorph X Y where
+ toPartialEquiv := e'
+ continuousOn_toFun := h ▸ e.continuousOn_toFun
+ continuousOn_invFun := h ▸ e.continuousOn_invFun
+
+theorem replacePartialEquiv_eq_self (e' : PartialEquiv X Y)
+ (h : e.toPartialEquiv = e') : e.replacePartialEquiv e' h = e := by
+ cases e
+ subst e'
+ rfl
+
+/-- Two partial homeomorphisms are equal when they have equal `toFun`, `invFun` and `source`.
+It is not sufficient to have equal `toFun` and `source`, as this only determines `invFun` on
+the target. This would only be true for a weaker notion of equality, arguably the right one,
+called `EqOnSource`. -/
+@[ext]
+protected theorem ext (e' : PartialHomeomorph X Y) (h : ∀ x, e x = e' x)
+ (hinv : ∀ x, e.symm x = e'.symm x) (hs : e.source = e'.source) : e = e' :=
+ toPartialEquiv_injective (PartialEquiv.ext h hinv hs)
+
+@[simp]
+theorem symm_toPartialEquiv : e.symm.toPartialEquiv = e.toPartialEquiv.symm :=
+ rfl
+
+-- The following lemmas are already simp via `PartialEquiv`
+theorem symm_source : e.symm.source = e.target :=
+ rfl
+
+theorem symm_target : e.symm.target = e.source :=
+ rfl
+
+@[simp] theorem symm_symm : e.symm.symm = e := rfl
+
+theorem symm_bijective : Function.Bijective
+ (PartialHomeomorph.symm : PartialHomeomorph X Y → PartialHomeomorph Y X) :=
+ Function.bijective_iff_has_inverse.mpr ⟨_, symm_symm, symm_symm⟩
+
+end PartialHomeomorph
From c7943cc789d6f1fcd90b124812b946f766a44abe Mon Sep 17 00:00:00 2001
From: Eric Wieser <425260+eric-wieser@users.noreply.github.com>
Date: Wed, 17 Jun 2026 19:45:24 +0000
Subject: [PATCH 0126/1300] chore: remove stray line break (#40719)
---
Mathlib/GroupTheory/Congruence/Hom.lean | 1 -
1 file changed, 1 deletion(-)
diff --git a/Mathlib/GroupTheory/Congruence/Hom.lean b/Mathlib/GroupTheory/Congruence/Hom.lean
index 0e04ceff448309..0d95b7350e7028 100644
--- a/Mathlib/GroupTheory/Congruence/Hom.lean
+++ b/Mathlib/GroupTheory/Congruence/Hom.lean
@@ -40,7 +40,6 @@ namespace Con
section Mul
variable {F} [Mul M] [Mul N] [Mul P] [FunLike F M N] [MulHomClass F M N]
-
/-- The natural homomorphism from a magma to its quotient by a congruence relation. -/
@[to_additive (attr := simps) /-- The natural homomorphism from an additive magma to its quotient by
an additive congruence relation. -/]
From 54d47da30ce1fec48772370361cdaea2587c1af0 Mon Sep 17 00:00:00 2001
From: "Thomas R. Murrills" <68410468+thorimur@users.noreply.github.com>
Date: Wed, 17 Jun 2026 20:47:16 +0000
Subject: [PATCH 0127/1300] chore: always include comment when importing header
linter explicitly (#40729)
This PR makes sure we always includes the comment
```
-- Import this linter explicitly to ensure that
-- this file has a valid copyright header and module docstring.
```
whenever `Mathlib.Tactic.Linter.Header` is imported explicitly.
Some files already had this comment, and this PR just makes sure all of them do.
We also normalize whitespace in adjacent `shake` annotations while we're at it.
We could check for this in lint-style, but it would be easier after #40347.
---
Mathlib/Lean/Elab/InfoTree.lean | 2 +-
Mathlib/Lean/Environment.lean | 2 +-
Mathlib/Lean/Expr/Basic.lean | 2 +-
Mathlib/Tactic/Linter/AuxLemma.lean | 2 ++
Mathlib/Tactic/Linter/DocString.lean | 2 ++
Mathlib/Tactic/Linter/EmptyLine.lean | 3 +++
Mathlib/Tactic/Linter/FlexibleLinter.lean | 2 ++
Mathlib/Tactic/Linter/TacticDocumentation.lean | 2 ++
Mathlib/Tactic/Linter/UnusedInstancesInType.lean | 2 +-
Mathlib/Tactic/Linter/Whitespace.lean | 2 ++
Mathlib/Tactic/TypeStar.lean | 2 +-
Mathlib/Util/ParseCommand.lean | 2 +-
12 files changed, 19 insertions(+), 6 deletions(-)
diff --git a/Mathlib/Lean/Elab/InfoTree.lean b/Mathlib/Lean/Elab/InfoTree.lean
index 9755b678eea600..cca33050a3b0e2 100644
--- a/Mathlib/Lean/Elab/InfoTree.lean
+++ b/Mathlib/Lean/Elab/InfoTree.lean
@@ -11,7 +11,7 @@ public import Lean.Meta.TryThis
public import Batteries.Tactic.Lint.Misc
-- Import this linter explicitly to ensure that
-- this file has a valid copyright header and module docstring.
-import Mathlib.Tactic.Linter.Header --shake: keep
+import Mathlib.Tactic.Linter.Header -- shake: keep
public import Batteries.Tactic.Lint.Basic
import Lean.Elab.Term.TermElabM
diff --git a/Mathlib/Lean/Environment.lean b/Mathlib/Lean/Environment.lean
index aed6331c83adf0..5afff12cfbe458 100644
--- a/Mathlib/Lean/Environment.lean
+++ b/Mathlib/Lean/Environment.lean
@@ -8,7 +8,7 @@ module
public import Lean.Environment
-- Import this linter explicitly to ensure that
-- this file has a valid copyright header and module docstring.
-import Mathlib.Tactic.Linter.Header --shake: keep
+import Mathlib.Tactic.Linter.Header -- shake: keep
/-!
# Additional utilities for `Lean.Environment`
diff --git a/Mathlib/Lean/Expr/Basic.lean b/Mathlib/Lean/Expr/Basic.lean
index 91f9bd6a9666f3..0be1b3683d9f16 100644
--- a/Mathlib/Lean/Expr/Basic.lean
+++ b/Mathlib/Lean/Expr/Basic.lean
@@ -8,7 +8,7 @@ module
-- Import this linter explicitly to ensure that
-- this file has a valid copyright header and module docstring.
-import Mathlib.Tactic.Linter.Header --shake: keep
+import Mathlib.Tactic.Linter.Header -- shake: keep
public import Lean.Meta.AppBuilder
public import Lean.Meta.Match.MatcherInfo
public import Lean.Meta.Transform
diff --git a/Mathlib/Tactic/Linter/AuxLemma.lean b/Mathlib/Tactic/Linter/AuxLemma.lean
index a329619c0e265c..964759aa94aae7 100644
--- a/Mathlib/Tactic/Linter/AuxLemma.lean
+++ b/Mathlib/Tactic/Linter/AuxLemma.lean
@@ -6,6 +6,8 @@ Authors: Kim Morrison
module
public meta import Lean.Elab.Command
+-- Import this linter explicitly to ensure that
+-- this file has a valid copyright header and module docstring.
public meta import Mathlib.Tactic.Linter.Header -- shake: keep
/-!
diff --git a/Mathlib/Tactic/Linter/DocString.lean b/Mathlib/Tactic/Linter/DocString.lean
index 37045c86f2197d..8b3182d24d9ca4 100644
--- a/Mathlib/Tactic/Linter/DocString.lean
+++ b/Mathlib/Tactic/Linter/DocString.lean
@@ -5,6 +5,8 @@ Authors: Michael Rothgang, Damiano Testa
-/
module
+-- Import this linter explicitly to ensure that
+-- this file has a valid copyright header and module docstring.
public meta import Mathlib.Tactic.Linter.Header -- shake: keep
public meta import Std.Data.Iterators.Combinators.Zip
public import Lean.Parser.Command
diff --git a/Mathlib/Tactic/Linter/EmptyLine.lean b/Mathlib/Tactic/Linter/EmptyLine.lean
index d86bfce93400c4..2a927a380c2a54 100644
--- a/Mathlib/Tactic/Linter/EmptyLine.lean
+++ b/Mathlib/Tactic/Linter/EmptyLine.lean
@@ -5,6 +5,9 @@ Authors: Damiano Testa
-/
module
+
+-- Import this linter explicitly to ensure that
+-- this file has a valid copyright header and module docstring.
public meta import Mathlib.Tactic.Linter.Header -- shake: keep
public import Lean.Parser.Command
diff --git a/Mathlib/Tactic/Linter/FlexibleLinter.lean b/Mathlib/Tactic/Linter/FlexibleLinter.lean
index 0246c939ff4119..b7de42cc5db889 100644
--- a/Mathlib/Tactic/Linter/FlexibleLinter.lean
+++ b/Mathlib/Tactic/Linter/FlexibleLinter.lean
@@ -9,6 +9,8 @@ public meta import Lean.Elab.Command
public meta import Lean.Elab.Tactic.Simp
public meta import Lean.Meta.Tactic.TryThis
public meta import Lean.Server.InfoUtils
+-- Import this linter explicitly to ensure that
+-- this file has a valid copyright header and module docstring.
public meta import Mathlib.Tactic.Linter.Header -- shake: keep
public import Lean.Parser.Term
diff --git a/Mathlib/Tactic/Linter/TacticDocumentation.lean b/Mathlib/Tactic/Linter/TacticDocumentation.lean
index 315f35673e7945..bd0874afb9d480 100644
--- a/Mathlib/Tactic/Linter/TacticDocumentation.lean
+++ b/Mathlib/Tactic/Linter/TacticDocumentation.lean
@@ -7,6 +7,8 @@ module
public meta import Lean.Elab.Tactic.Doc
public meta import Lean.Parser.Tactic.Doc
+-- Import this linter explicitly to ensure that
+-- this file has a valid copyright header and module docstring.
public import Mathlib.Tactic.Linter.Header -- shake: keep
public import Batteries.Tactic.Lint.Basic
public import Lean.Elab.Tactic.Doc
diff --git a/Mathlib/Tactic/Linter/UnusedInstancesInType.lean b/Mathlib/Tactic/Linter/UnusedInstancesInType.lean
index 72e03512cdafc3..4a6ec648aff168 100644
--- a/Mathlib/Tactic/Linter/UnusedInstancesInType.lean
+++ b/Mathlib/Tactic/Linter/UnusedInstancesInType.lean
@@ -11,7 +11,7 @@ public meta import Mathlib.Lean.Elab.InfoTree
public meta import Lean.Linter.Basic
-- Import this linter explicitly to ensure that
-- this file has a valid copyright header and module docstring.
-public import Mathlib.Tactic.Linter.Header --shake: keep
+public import Mathlib.Tactic.Linter.Header -- shake: keep
public import Batteries.Tactic.Lint.Basic
public import Batteries.Tactic.Lint.Misc
diff --git a/Mathlib/Tactic/Linter/Whitespace.lean b/Mathlib/Tactic/Linter/Whitespace.lean
index 054bff27fa7fb1..16a175642c7990 100644
--- a/Mathlib/Tactic/Linter/Whitespace.lean
+++ b/Mathlib/Tactic/Linter/Whitespace.lean
@@ -5,6 +5,8 @@ Authors: Damiano Testa
-/
module
+-- Import this linter explicitly to ensure that
+-- this file has a valid copyright header and module docstring.
public import Mathlib.Tactic.Linter.Header -- shake: keep
/-!
diff --git a/Mathlib/Tactic/TypeStar.lean b/Mathlib/Tactic/TypeStar.lean
index f80f6490f307ec..9ad0340489994d 100644
--- a/Mathlib/Tactic/TypeStar.lean
+++ b/Mathlib/Tactic/TypeStar.lean
@@ -7,7 +7,7 @@ module
-- Import this linter explicitly to ensure that
-- this file has a valid copyright header and module docstring.
-public import Mathlib.Tactic.Linter.Header --shake: keep
+public import Mathlib.Tactic.Linter.Header -- shake: keep
/-!
# Support for `Sort*` and `Type*`.
diff --git a/Mathlib/Util/ParseCommand.lean b/Mathlib/Util/ParseCommand.lean
index ec932c087b98fc..3feb0438ac9028 100644
--- a/Mathlib/Util/ParseCommand.lean
+++ b/Mathlib/Util/ParseCommand.lean
@@ -8,7 +8,7 @@ module
public meta import Lean.Elab.Command
-- Import this linter explicitly to ensure that
-- this file has a valid copyright header and module docstring.
-public meta import Mathlib.Tactic.Linter.Header --shake: keep
+public meta import Mathlib.Tactic.Linter.Header -- shake: keep
/-!
# `#parse` -- a command to parse text and log outputs
From dba82c485d7efa056a35e699878b3951dcdc0583 Mon Sep 17 00:00:00 2001
From: Marcelo Lynch
Date: Wed, 17 Jun 2026 21:42:41 +0000
Subject: [PATCH 0128/1300] ci(cache): fall back to latest cache-snapshot when
the resolved run has none (#40734)
The resolved master run can predate the `cache-snapshot` artifact (or have an expired one), making the warm step's `download-artifact` log a spurious "Artifact not found" error. Verify the artifact exists first; otherwise fall back to the latest master run that has one, and skip the warm if none does.
---
.github/actions/get-cache/action.yml | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/.github/actions/get-cache/action.yml b/.github/actions/get-cache/action.yml
index ca5d3a1a2846f7..00bfcdaf0550be 100644
--- a/.github/actions/get-cache/action.yml
+++ b/.github/actions/get-cache/action.yml
@@ -40,6 +40,13 @@ runs:
'[.workflow_runs[] | select(.created_at <= $d and ($c == "" or .created_at >= $c))][0].id // empty' \
2>/dev/null || true
}
+ # True if run-id $1 actually carries a `cache-snapshot` artifact. Older runs
+ # predate the feature and artifacts expire, so a successful run is not enough.
+ # Filters server-side by name, but re-checks in jq in case `name` is ignored.
+ has_snapshot() {
+ [[ -n "$(gh api "repos/leanprover-community/mathlib4/actions/runs/$1/artifacts?name=cache-snapshot&per_page=100" \
+ --jq '.artifacts[] | select(.name == "cache-snapshot") | .id' 2>/dev/null | head -1 || true)" ]]
+ }
# This PR's merge-base with master (+ its commit date), and the cutoff below which
# snapshots have expired (retention ~14d).
@@ -57,6 +64,19 @@ runs:
fi
[[ -z "${run_id}" ]] && run_id=$(latest_run)
+ # The resolved run may carry no `cache-snapshot` artifact: an older merge-base
+ # predating the feature, or one whose artifact already expired (older-than-today
+ # runs often won't have one). Rather than let the download step hard-error on a
+ # missing artifact, confirm it's present; if not, fall back to the latest master
+ # run, and warm only if that one has it.
+ if [[ -n "${run_id}" ]] && ! has_snapshot "${run_id}"; then
+ echo "Run ${run_id} has no cache-snapshot artifact; falling back to latest master run."
+ run_id=$(latest_run)
+ if [[ -n "${run_id}" ]] && ! has_snapshot "${run_id}"; then
+ run_id=""
+ fi
+ fi
+
echo "Resolved cache-snapshot run_id: '${run_id}' (merge-base: ${mb:-unknown})"
echo "run_id=${run_id}" >> "$GITHUB_OUTPUT"
From 9a9483a92959bc92bd6a60176dd1fe597298c1f8 Mon Sep 17 00:00:00 2001
From: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com>
Date: Thu, 18 Jun 2026 02:43:13 +0000
Subject: [PATCH 0129/1300] =?UTF-8?q?feat(Combinatorics/SimpleGraph/Paths)?=
=?UTF-8?q?:=20`p.IsPath=20=E2=86=92=20p.dropLast.IsPath`=20(#40667)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
We have this for `tail`/`take`/`drop`/`takeUntil`/`dropUntil`/`reverse`/`copy`/`concat` but not for `dropLast`.
---
Mathlib/Combinatorics/SimpleGraph/Paths.lean | 3 +++
1 file changed, 3 insertions(+)
diff --git a/Mathlib/Combinatorics/SimpleGraph/Paths.lean b/Mathlib/Combinatorics/SimpleGraph/Paths.lean
index 52bdf1c73ab472..a5813a65160535 100644
--- a/Mathlib/Combinatorics/SimpleGraph/Paths.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/Paths.lean
@@ -358,6 +358,9 @@ lemma IsPath.tail {p : G.Walk u v} (hp : p.IsPath) : p.tail.IsPath := by
| cons hadj p =>
simp_all [Walk.isPath_def]
+theorem IsPath.dropLast (hp : p.IsPath) : p.dropLast.IsPath :=
+ hp.take _
+
/-- There exists a trail of maximal length in a non-empty graph on finite edges. -/
lemma exists_isTrail_forall_isTrail_length_le_length (G : SimpleGraph V) [N : Nonempty V]
[Finite G.edgeSet] :
From 014c1563dc2c952488b6acfd3fac97ee588f0c6d Mon Sep 17 00:00:00 2001
From: Jack McCarthy <37917934+Deicyde@users.noreply.github.com>
Date: Thu, 18 Jun 2026 06:53:39 +0000
Subject: [PATCH 0130/1300] doc: add wikidata attributes (#40682)
This PR adds a batch of 25 `@[wikidata]` attributes.
Claude helped generate the list of crossrefs (by scanning Wikidata + Mathlib). Comments are generated by [crossref-report](https://github.com/jcommelin/mathlib-crossref-report) and Wikilean.
See https://wikilean.jackmccarthy.org/review?pr=40682 for reviewer UI.
---
Mathlib/Combinatorics/SimpleGraph/Basic.lean | 3 ++-
Mathlib/Data/Countable/Defs.lean | 3 ++-
Mathlib/Geometry/Manifold/IsManifold/Basic.lean | 2 ++
Mathlib/GroupTheory/Solvable.lean | 3 ++-
Mathlib/LinearAlgebra/Matrix/Defs.lean | 2 ++
Mathlib/MeasureTheory/MeasurableSpace/Defs.lean | 3 ++-
Mathlib/NumberTheory/DirichletCharacter/Basic.lean | 2 ++
Mathlib/NumberTheory/LSeries/RiemannZeta.lean | 2 ++
Mathlib/NumberTheory/Padics/PadicNumbers.lean | 2 ++
Mathlib/NumberTheory/Real/Irrational.lean | 2 ++
Mathlib/Probability/CDF.lean | 2 ++
Mathlib/Probability/Distributions/Gaussian/Real.lean | 2 ++
Mathlib/Probability/Distributions/Poisson/Basic.lean | 2 ++
Mathlib/RingTheory/Coprime/Basic.lean | 2 ++
Mathlib/RingTheory/PowerSeries/Basic.lean | 2 ++
Mathlib/Topology/Defs/Basic.lean | 4 +++-
Mathlib/Topology/MetricSpace/Pseudo/Defs.lean | 2 ++
Mathlib/Topology/UniformSpace/UniformConvergence.lean | 2 ++
18 files changed, 37 insertions(+), 5 deletions(-)
diff --git a/Mathlib/Combinatorics/SimpleGraph/Basic.lean b/Mathlib/Combinatorics/SimpleGraph/Basic.lean
index 6d2f1c5de950af..d1b86b494d8c74 100644
--- a/Mathlib/Combinatorics/SimpleGraph/Basic.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/Basic.lean
@@ -11,6 +11,7 @@ public import Mathlib.Data.Rel
public import Mathlib.Data.Set.Finite.Basic
public import Mathlib.Data.Sym.Sym2
public import Mathlib.Order.CompleteBooleanAlgebra
+public import Mathlib.Tactic.CrossRefAttribute
/-!
# Simple graphs
@@ -88,7 +89,7 @@ The relation describes which pairs of vertices are adjacent.
There is exactly one edge for every pair of adjacent vertices;
see `SimpleGraph.edgeSet` for the corresponding edge set.
-/
-@[ext, aesop safe constructors (rule_sets := [SimpleGraph])]
+@[ext, aesop safe constructors (rule_sets := [SimpleGraph]), wikidata Q141488]
structure SimpleGraph (V : Type u) where
/-- The adjacency relation of a simple graph. -/
Adj : V → V → Prop
diff --git a/Mathlib/Data/Countable/Defs.lean b/Mathlib/Data/Countable/Defs.lean
index 2a85f6ab177fb3..e3b5df28b7d149 100644
--- a/Mathlib/Data/Countable/Defs.lean
+++ b/Mathlib/Data/Countable/Defs.lean
@@ -8,6 +8,7 @@ module
public import Mathlib.Data.Finite.Defs
public import Mathlib.Data.Bool.Basic
public import Mathlib.Data.Subtype
+public import Mathlib.Tactic.CrossRefAttribute
public import Mathlib.Tactic.MkIffOfInductiveProp
/-!
@@ -36,7 +37,7 @@ variable {α : Sort u} {β : Sort v}
-/
/-- A type `α` is countable if there exists an injective map `α → ℕ`. -/
-@[mk_iff countable_iff_exists_injective]
+@[mk_iff countable_iff_exists_injective, wikidata Q66707394]
class Countable (α : Sort u) : Prop where
/-- A type `α` is countable if there exists an injective map `α → ℕ`. -/
exists_injective_nat' : ∃ f : α → ℕ, Injective f
diff --git a/Mathlib/Geometry/Manifold/IsManifold/Basic.lean b/Mathlib/Geometry/Manifold/IsManifold/Basic.lean
index 11fbcf447b60bd..5dc7b8202b1ab3 100644
--- a/Mathlib/Geometry/Manifold/IsManifold/Basic.lean
+++ b/Mathlib/Geometry/Manifold/IsManifold/Basic.lean
@@ -10,6 +10,7 @@ public import Mathlib.Analysis.Normed.Module.Convex
public import Mathlib.Analysis.RCLike.TangentCone
public import Mathlib.Data.Bundle
public import Mathlib.Geometry.Manifold.HasGroupoid
+public import Mathlib.Tactic.CrossRefAttribute
/-!
# `C^n` manifolds (possibly with boundary or corners)
@@ -1070,6 +1071,7 @@ variable (M) in
-- is empty if the base manifold is empty
/-- The tangent bundle to a manifold, as a Sigma type. Defined in terms of
`Bundle.TotalSpace` to be able to put a suitable topology on it. -/
+@[wikidata Q746550]
abbrev TangentBundle := Bundle.TotalSpace E (TangentSpace I : M → Type _)
end TangentSpace
diff --git a/Mathlib/GroupTheory/Solvable.lean b/Mathlib/GroupTheory/Solvable.lean
index a72d318f20ef0e..9ec89956631467 100644
--- a/Mathlib/GroupTheory/Solvable.lean
+++ b/Mathlib/GroupTheory/Solvable.lean
@@ -10,6 +10,7 @@ public import Mathlib.GroupTheory.Abelianization.Defs
public import Mathlib.GroupTheory.Perm.ViaEmbedding
public import Mathlib.GroupTheory.Subgroup.Simple
public import Mathlib.SetTheory.Cardinal.Order
+public import Mathlib.Tactic.CrossRefAttribute
/-!
# Solvable Groups
@@ -102,7 +103,7 @@ variable (G)
/-- A group `G` is solvable if its derived series is eventually trivial. We use this definition
because it's the most convenient one to work with. -/
-@[mk_iff isSolvable_def]
+@[mk_iff isSolvable_def, wikidata Q759832]
class IsSolvable : Prop where
/-- A group `G` is solvable if its derived series is eventually trivial. -/
solvable : ∃ n : ℕ, derivedSeries G n = ⊥
diff --git a/Mathlib/LinearAlgebra/Matrix/Defs.lean b/Mathlib/LinearAlgebra/Matrix/Defs.lean
index 4654757566793a..73ad6a744b33d9 100644
--- a/Mathlib/LinearAlgebra/Matrix/Defs.lean
+++ b/Mathlib/LinearAlgebra/Matrix/Defs.lean
@@ -7,6 +7,7 @@ module
public import Mathlib.Algebra.Module.Pi
public import Mathlib.Logic.Nontrivial.Basic
+public import Mathlib.Tactic.CrossRefAttribute
/-!
# Matrices
@@ -50,6 +51,7 @@ universe u u' v w
/-- `Matrix m n R` is the type of matrices with entries in `R`, whose rows are indexed by `m`
and whose columns are indexed by `n`. -/
+@[wikidata Q44337]
def Matrix (m : Type u) (n : Type u') (α : Type v) : Type max u u' v :=
m → n → α
diff --git a/Mathlib/MeasureTheory/MeasurableSpace/Defs.lean b/Mathlib/MeasureTheory/MeasurableSpace/Defs.lean
index a7e126d997a52b..d7965599471eca 100644
--- a/Mathlib/MeasureTheory/MeasurableSpace/Defs.lean
+++ b/Mathlib/MeasureTheory/MeasurableSpace/Defs.lean
@@ -7,6 +7,7 @@ module
public import Mathlib.Data.Set.Countable
public import Mathlib.Order.ConditionallyCompleteLattice.Basic
+public import Mathlib.Tactic.CrossRefAttribute
public import Mathlib.Tactic.FunProp.Attr
public import Mathlib.Tactic.Measurability
@@ -489,7 +490,7 @@ end MeasurableSpace
/-- A function `f` between measurable spaces is measurable if the preimage of every
measurable set is measurable. -/
-@[fun_prop]
+@[fun_prop, wikidata Q516776]
def Measurable [MeasurableSpace α] [MeasurableSpace β] (f : α → β) : Prop :=
∀ ⦃t : Set β⦄, MeasurableSet t → MeasurableSet (f ⁻¹' t)
diff --git a/Mathlib/NumberTheory/DirichletCharacter/Basic.lean b/Mathlib/NumberTheory/DirichletCharacter/Basic.lean
index 39d32385a7820e..2e86d6e8cf4339 100644
--- a/Mathlib/NumberTheory/DirichletCharacter/Basic.lean
+++ b/Mathlib/NumberTheory/DirichletCharacter/Basic.lean
@@ -8,6 +8,7 @@ module
public import Mathlib.Algebra.Group.EvenFunction
public import Mathlib.Data.ZMod.Units
public import Mathlib.NumberTheory.MulChar.Basic
+public import Mathlib.Tactic.CrossRefAttribute
/-!
# Dirichlet Characters
@@ -35,6 +36,7 @@ dirichlet character, multiplicative character
-/
/-- The type of Dirichlet characters of level `n`. -/
+@[wikidata Q1063579]
abbrev DirichletCharacter (R : Type*) [CommMonoidWithZero R] (n : ℕ) := MulChar (ZMod n) R
open MulChar
diff --git a/Mathlib/NumberTheory/LSeries/RiemannZeta.lean b/Mathlib/NumberTheory/LSeries/RiemannZeta.lean
index 276c7e7c8635c2..fdff2c94adcfd8 100644
--- a/Mathlib/NumberTheory/LSeries/RiemannZeta.lean
+++ b/Mathlib/NumberTheory/LSeries/RiemannZeta.lean
@@ -7,6 +7,7 @@ module
public import Mathlib.NumberTheory.LSeries.HurwitzZeta
public import Mathlib.Analysis.PSeriesComplex
+public import Mathlib.Tactic.CrossRefAttribute
/-!
# Definition of the Riemann zeta function
@@ -179,6 +180,7 @@ theorem riemannZeta_one_sub {s : ℂ} (hs : ∀ n : ℕ, s ≠ -n) (hs' : s ≠
/-- A formal statement of the **Riemann hypothesis** – constructing a term of this type is worth a
million dollars. -/
+@[wikidata Q205966]
def RiemannHypothesis : Prop :=
∀ (s : ℂ) (_ : riemannZeta s = 0) (_ : ¬∃ n : ℕ, s = -2 * (n + 1)) (_ : s ≠ 1), s.re = 1 / 2
diff --git a/Mathlib/NumberTheory/Padics/PadicNumbers.lean b/Mathlib/NumberTheory/Padics/PadicNumbers.lean
index 937a475b3d77ec..a01f31fae82778 100644
--- a/Mathlib/NumberTheory/Padics/PadicNumbers.lean
+++ b/Mathlib/NumberTheory/Padics/PadicNumbers.lean
@@ -8,6 +8,7 @@ module
public import Mathlib.RingTheory.Valuation.Basic
public import Mathlib.NumberTheory.Padics.PadicNorm
public import Mathlib.Analysis.Normed.Field.Lemmas
+public import Mathlib.Tactic.CrossRefAttribute
public import Mathlib.Tactic.Peel
public import Mathlib.Topology.MetricSpace.Ultra.Basic
@@ -526,6 +527,7 @@ end PadicSeq
/-- The `p`-adic numbers `ℚ_[p]` are the Cauchy completion of `ℚ` with respect to the `p`-adic norm.
-/
+@[wikidata Q311627]
def Padic (p : ℕ) [Fact p.Prime] :=
CauSeq.Completion.Cauchy (padicNorm p)
deriving Zero, One, Add, Neg, Sub, Mul, Div, AddCommGroup, Ring, CommRing, Field, Inhabited
diff --git a/Mathlib/NumberTheory/Real/Irrational.lean b/Mathlib/NumberTheory/Real/Irrational.lean
index 16b276aef33605..763756b8560f64 100644
--- a/Mathlib/NumberTheory/Real/Irrational.lean
+++ b/Mathlib/NumberTheory/Real/Irrational.lean
@@ -10,6 +10,7 @@ public import Mathlib.Data.Nat.Prime.Int
public import Mathlib.Data.Rat.Sqrt
public import Mathlib.Analysis.Real.Sqrt
public import Mathlib.RingTheory.Algebraic.Basic
+public import Mathlib.Tactic.CrossRefAttribute
public import Mathlib.Tactic.IntervalCases
/-!
@@ -32,6 +33,7 @@ but this only works if you `unseal Nat.sqrt.iter in` before the theorem where yo
open Rat Real
/-- A real number is irrational if it is not equal to any rational number. -/
+@[wikidata Q607728]
def Irrational (x : ℝ) :=
x ∉ Set.range ((↑) : ℚ → ℝ)
diff --git a/Mathlib/Probability/CDF.lean b/Mathlib/Probability/CDF.lean
index c145e7ffde0855..52656abe68af9d 100644
--- a/Mathlib/Probability/CDF.lean
+++ b/Mathlib/Probability/CDF.lean
@@ -6,6 +6,7 @@ Authors: Rémy Degenne
module
public import Mathlib.Probability.Kernel.Disintegration.CondCDF
+public import Mathlib.Tactic.CrossRefAttribute
/-!
# Cumulative distribution function of a real probability measure
@@ -51,6 +52,7 @@ namespace ProbabilityTheory
/-- Cumulative distribution function of a real measure. The definition currently makes sense only
for probability measures. In that case, it satisfies `cdf μ x = μ.real (Iic x)` (see
`ProbabilityTheory.cdf_eq_real`). -/
+@[wikidata Q386228]
noncomputable
def cdf (μ : Measure ℝ) : StieltjesFunction ℝ :=
condCDF ((dirac Unit.unit).prod μ) Unit.unit
diff --git a/Mathlib/Probability/Distributions/Gaussian/Real.lean b/Mathlib/Probability/Distributions/Gaussian/Real.lean
index 0f4143516c8f8e..9b7b05e1bbfa7b 100644
--- a/Mathlib/Probability/Distributions/Gaussian/Real.lean
+++ b/Mathlib/Probability/Distributions/Gaussian/Real.lean
@@ -8,6 +8,7 @@ module
public import Mathlib.Analysis.SpecialFunctions.Gaussian.FourierTransform
public import Mathlib.Probability.HasLaw
public import Mathlib.Probability.Moments.MGFAnalytic
+public import Mathlib.Tactic.CrossRefAttribute
/-!
# Gaussian distributions over ℝ
@@ -216,6 +217,7 @@ end GaussianPDF
section GaussianReal
/-- A Gaussian distribution on `ℝ` with mean `μ` and variance `v`. -/
+@[wikidata Q133871]
noncomputable
def gaussianReal (μ : ℝ) (v : ℝ≥0) : Measure ℝ :=
if v = 0 then Measure.dirac μ else volume.withDensity (gaussianPDF μ v)
diff --git a/Mathlib/Probability/Distributions/Poisson/Basic.lean b/Mathlib/Probability/Distributions/Poisson/Basic.lean
index 71b10edb13dd25..9b92070095b9a6 100644
--- a/Mathlib/Probability/Distributions/Poisson/Basic.lean
+++ b/Mathlib/Probability/Distributions/Poisson/Basic.lean
@@ -8,6 +8,7 @@ module
public import Mathlib.MeasureTheory.Measure.CharacteristicFunction.Basic
public import Mathlib.Probability.HasLaw
public import Mathlib.Probability.ProbabilityMassFunction.Basic
+public import Mathlib.Tactic.CrossRefAttribute
import Mathlib.LinearAlgebra.Complex.FiniteDimensional
@@ -35,6 +36,7 @@ open scoped NNReal Nat
namespace ProbabilityTheory
/-- The poisson measure with rate `r : ℝ≥0` as a measure over `ℕ`. -/
+@[wikidata Q205692]
noncomputable
def poissonMeasure (r : ℝ≥0) : Measure ℕ :=
Measure.sum (fun n ↦ ENNReal.ofReal (exp (-r) * r ^ n / (n)!) • (.dirac n))
diff --git a/Mathlib/RingTheory/Coprime/Basic.lean b/Mathlib/RingTheory/Coprime/Basic.lean
index 4aed62561adf65..2b88d64f35a22c 100644
--- a/Mathlib/RingTheory/Coprime/Basic.lean
+++ b/Mathlib/RingTheory/Coprime/Basic.lean
@@ -11,6 +11,7 @@ public import Mathlib.Algebra.GroupWithZero.Associated
public import Mathlib.Algebra.Ring.Divisibility.Basic
public import Mathlib.Algebra.Ring.Hom.Defs
public import Mathlib.Logic.Basic
+public import Mathlib.Tactic.CrossRefAttribute
public import Mathlib.Tactic.Ring
/-!
@@ -40,6 +41,7 @@ variable {R : Type u} [CommSemiring R] (x y z w : R)
/-- The proposition that `x` and `y` are coprime, defined to be the existence of `a` and `b` such
that `a * x + b * y = 1`. Note that elements with no common divisors are not necessarily coprime,
e.g., the multivariate polynomials `x₁` and `x₂` are not coprime. -/
+@[wikidata Q104752]
def IsCoprime : Prop :=
∃ a b, a * x + b * y = 1
diff --git a/Mathlib/RingTheory/PowerSeries/Basic.lean b/Mathlib/RingTheory/PowerSeries/Basic.lean
index 3cd109ae347e5d..b9c69c2a9e360c 100644
--- a/Mathlib/RingTheory/PowerSeries/Basic.lean
+++ b/Mathlib/RingTheory/PowerSeries/Basic.lean
@@ -9,6 +9,7 @@ public import Mathlib.Algebra.CharP.Defs
public import Mathlib.Algebra.Polynomial.AlgebraMap
public import Mathlib.Algebra.Polynomial.Basic
public import Mathlib.RingTheory.MvPowerSeries.Basic
+public import Mathlib.Tactic.CrossRefAttribute
public import Mathlib.Tactic.MoveAdd
public import Mathlib.Algebra.MvPolynomial.Equiv
public import Mathlib.RingTheory.Ideal.Basic
@@ -55,6 +56,7 @@ noncomputable section
open Finset (antidiagonal mem_antidiagonal)
/-- Formal power series over a coefficient type `R` -/
+@[wikidata Q1003025]
abbrev PowerSeries (R : Type*) :=
MvPowerSeries Unit R
diff --git a/Mathlib/Topology/Defs/Basic.lean b/Mathlib/Topology/Defs/Basic.lean
index c2976b51382d49..25731ef39ec5b2 100644
--- a/Mathlib/Topology/Defs/Basic.lean
+++ b/Mathlib/Topology/Defs/Basic.lean
@@ -7,6 +7,7 @@ module
public import Mathlib.Order.SetNotation
public import Mathlib.Tactic.Continuity
+public import Mathlib.Tactic.CrossRefAttribute
public import Mathlib.Tactic.FunProp
public import Mathlib.Tactic.MkIffOfInductiveProp
public import Mathlib.Data.Nat.Notation
@@ -90,6 +91,7 @@ section Defs
variable [TopologicalSpace X] [TopologicalSpace Y] {s t : Set X}
/-- `IsOpen s` means that `s` is open in the ambient topological space on `X` -/
+@[wikidata Q213363]
def IsOpen : Set X → Prop := TopologicalSpace.IsOpen
@[simp] theorem isOpen_univ : IsOpen (univ : Set X) := TopologicalSpace.isOpen_univ
@@ -148,7 +150,7 @@ def DenseRange {α : Type*} (f : α → X) := Dense (range f)
/-- A function between topological spaces is continuous if the preimage
of every open set is open. Registered as a structure to make sure it is not unfolded by Lean. -/
-@[fun_prop]
+@[fun_prop, wikidata Q170058]
structure Continuous (f : X → Y) : Prop where
/-- The preimage of an open set under a continuous function is an open set. Use `IsOpen.preimage`
instead. -/
diff --git a/Mathlib/Topology/MetricSpace/Pseudo/Defs.lean b/Mathlib/Topology/MetricSpace/Pseudo/Defs.lean
index 334e1ae713a618..aabb1cb2b5e4d6 100644
--- a/Mathlib/Topology/MetricSpace/Pseudo/Defs.lean
+++ b/Mathlib/Topology/MetricSpace/Pseudo/Defs.lean
@@ -7,6 +7,7 @@ module
public import Mathlib.Data.ENNReal.Real
public import Mathlib.Tactic.Bound.Attribute
+public import Mathlib.Tactic.CrossRefAttribute
public import Mathlib.Topology.Bornology.Basic
public import Mathlib.Topology.EMetricSpace.Defs
public import Mathlib.Topology.UniformSpace.Basic
@@ -365,6 +366,7 @@ namespace Metric
variable {x y z : α} {δ ε ε₁ ε₂ : ℝ} {s : Set α}
/-- `ball x ε` is the set of all points `y` with `dist y x < ε` -/
+@[wikidata Q838611]
def ball (x : α) (ε : ℝ) : Set α :=
{ y | dist y x < ε }
diff --git a/Mathlib/Topology/UniformSpace/UniformConvergence.lean b/Mathlib/Topology/UniformSpace/UniformConvergence.lean
index 4cd72c2a9f15e8..283de8651863b5 100644
--- a/Mathlib/Topology/UniformSpace/UniformConvergence.lean
+++ b/Mathlib/Topology/UniformSpace/UniformConvergence.lean
@@ -5,6 +5,7 @@ Authors: Sébastien Gouëzel
-/
module
+public import Mathlib.Tactic.CrossRefAttribute
public import Mathlib.Topology.UniformSpace.Cauchy
/-!
@@ -110,6 +111,7 @@ theorem tendstoUniformlyOn_iff_tendsto :
/-- A sequence of functions `Fₙ` converges uniformly to a limiting function `f` with respect to a
filter `p` if, for any entourage of the diagonal `u`, one has `p`-eventually
`(f x, Fₙ x) ∈ u` for all `x`. -/
+@[wikidata Q1411887]
def TendstoUniformly (F : ι → α → β) (f : α → β) (p : Filter ι) :=
∀ u ∈ 𝓤 β, ∀ᶠ n in p, ∀ x : α, (f x, F n x) ∈ u
From b1bc198f96e5c034c0fe49b89efa03e4067f9644 Mon Sep 17 00:00:00 2001
From: Jovan Gerbscheid <56355248+JovanGerb@users.noreply.github.com>
Date: Thu, 18 Jun 2026 07:34:46 +0000
Subject: [PATCH 0131/1300] chore: remove `using` clause from `convert`
(#40738)
The new `convert` is less aggressive so in many cases, the `using` clause is not needed anymore. In this PR I manually removed some of these. There are many more left to remove, ideally using some automation.
---
Archive/Imo/Imo1959Q2.lean | 2 +-
Archive/Imo/Imo2024Q3.lean | 6 +++---
Archive/Imo/Imo2024Q6.lean | 4 ++--
Archive/MinimalSheffer.lean | 2 +-
Archive/MiuLanguage/DecisionSuf.lean | 4 ++--
.../AscendingDescendingSequences.lean | 2 +-
.../CliffordAlgebraNotInjective.lean | 2 +-
Counterexamples/Phillips.lean | 2 +-
.../ZeroDivisorsInAddMonoidAlgebras.lean | 2 +-
.../Algebra/Spectrum/Quasispectrum.lean | 10 +++++-----
Mathlib/Algebra/GCDMonoid/Basic.lean | 2 +-
Mathlib/Algebra/Group/ForwardDiff.lean | 2 +-
Mathlib/Algebra/Jordan/Basic.lean | 2 +-
Mathlib/Algebra/Lie/Basic.lean | 2 +-
Mathlib/Algebra/Lie/Weights/IsSimple.lean | 2 +-
Mathlib/Algebra/Lie/Weights/RootSystem.lean | 2 +-
Mathlib/Algebra/Module/Submodule/Union.lean | 2 +-
.../Order/BigOperators/Group/Finset.lean | 2 +-
Mathlib/Algebra/Order/CauSeq/Basic.lean | 4 ++--
Mathlib/Algebra/Order/Field/Basic.lean | 10 +++++-----
Mathlib/Algebra/Order/Floor/Ring.lean | 4 ++--
.../Algebra/Order/Module/HahnEmbedding.lean | 2 +-
Mathlib/Algebra/Order/Ring/Archimedean.lean | 2 +-
.../Polynomial/Degree/IsMonicOfDegree.lean | 2 +-
Mathlib/Algebra/Polynomial/EraseLead.lean | 2 +-
Mathlib/Algebra/Polynomial/Laurent.lean | 2 +-
Mathlib/Algebra/Polynomial/RuleOfSigns.lean | 2 +-
Mathlib/Algebra/QuadraticDiscriminant.lean | 2 +-
Mathlib/Algebra/Regular/SMul.lean | 2 +-
Mathlib/Algebra/Ring/Divisibility/Basic.lean | 2 +-
Mathlib/Algebra/Ring/Idempotent.lean | 2 +-
Mathlib/Algebra/Ring/Subring/Basic.lean | 2 +-
Mathlib/Algebra/TrivSqZeroExt/Basic.lean | 2 +-
Mathlib/AlgebraicGeometry/AffineScheme.lean | 2 +-
.../AffineTransitionLimit.lean | 2 +-
.../IdealSheaf/Subscheme.lean | 4 ++--
Mathlib/AlgebraicGeometry/Limits.lean | 2 +-
.../AlgebraicGeometry/Morphisms/Affine.lean | 6 +++---
.../Morphisms/QuasiCompact.lean | 2 +-
.../Morphisms/QuasiFinite.lean | 2 +-
.../AlgebraicGeometry/Morphisms/Separated.lean | 2 +-
.../Morphisms/UnderlyingMap.lean | 2 +-
.../ProjectiveSpectrum/Proper.lean | 8 ++++----
.../ProjectiveSpectrum/Scheme.lean | 12 ++++++------
Mathlib/Analysis/Analytic/Basic.lean | 18 +++++++++---------
Mathlib/Analysis/Analytic/Binomial.lean | 12 ++++++------
Mathlib/Analysis/Analytic/OfScalars.lean | 2 +-
Mathlib/Analysis/Asymptotics/Lemmas.lean | 2 +-
Mathlib/Analysis/CStarAlgebra/Spectrum.lean | 2 +-
.../Calculus/ContDiff/Convolution.lean | 2 +-
Mathlib/Analysis/Calculus/Deriv/Inv.lean | 4 ++--
Mathlib/Analysis/Calculus/Deriv/ZPow.lean | 2 +-
.../Analysis/Calculus/FDeriv/Symmetric.lean | 2 +-
Mathlib/Analysis/Calculus/LogDeriv.lean | 2 +-
Mathlib/Analysis/Calculus/Taylor.lean | 4 ++--
Mathlib/Analysis/Complex/CoveringMap.lean | 4 ++--
Mathlib/Analysis/Complex/Exponential.lean | 4 ++--
Mathlib/Analysis/Complex/HasPrimitives.lean | 4 ++--
Mathlib/Analysis/Complex/Poisson.lean | 2 +-
Mathlib/Analysis/Complex/TaylorSeries.lean | 2 +-
.../ValueDistribution/FirstMainTheorem.lean | 2 +-
Mathlib/Analysis/ConstantSpeed.lean | 4 ++--
Mathlib/Analysis/Convex/Basic.lean | 2 +-
Mathlib/Analysis/Convex/Deriv.lean | 4 ++--
Mathlib/Analysis/Convex/PathConnected.lean | 2 +-
Mathlib/Analysis/Convex/Segment.lean | 4 ++--
Mathlib/Analysis/Distribution/Sobolev.lean | 2 +-
.../Analysis/Distribution/TemperateGrowth.lean | 2 +-
Mathlib/Analysis/Fourier/AddCircle.lean | 4 ++--
.../FunctionalSpaces/SobolevInequality.lean | 2 +-
Mathlib/Analysis/InnerProductSpace/Basic.lean | 2 +-
.../Analysis/InnerProductSpace/LinearPMap.lean | 2 +-
.../Analysis/InnerProductSpace/NormPow.lean | 2 +-
Mathlib/Analysis/InnerProductSpace/OfNorm.lean | 6 +++---
.../Analysis/InnerProductSpace/Orthogonal.lean | 2 +-
.../InnerProductSpace/Projection/Basic.lean | 2 +-
.../Projection/Reflection.lean | 2 +-
.../Projection/Submodule.lean | 4 ++--
Mathlib/Analysis/MeanInequalities.lean | 13 ++++++-------
Mathlib/Analysis/MeanInequalitiesPow.lean | 2 +-
Mathlib/Analysis/Meromorphic/Basic.lean | 8 ++++----
Mathlib/Analysis/Normed/Algebra/Spectrum.lean | 2 +-
Mathlib/Analysis/Normed/Group/Basic.lean | 2 +-
Mathlib/Analysis/SumOverResidueClass.lean | 2 +-
84 files changed, 142 insertions(+), 143 deletions(-)
diff --git a/Archive/Imo/Imo1959Q2.lean b/Archive/Imo/Imo1959Q2.lean
index 808f53a5984f23..2ac53efe02d11f 100644
--- a/Archive/Imo/Imo1959Q2.lean
+++ b/Archive/Imo/Imo1959Q2.lean
@@ -60,7 +60,7 @@ theorem sqrt_two_mul_sub_one_le_one : sqrt (2 * x - 1) ≤ 1 ↔ x ≤ 1 := by
theorem isGood_iff_eq_sqrt_two (hx : x ∈ Icc (1 / 2) 1) : IsGood x A ↔ A = sqrt 2 := by
have : sqrt (2 * x - 1) ≤ 1 := sqrt_two_mul_sub_one_le_one.2 hx.2
simp only [isGood_iff, hx.1, abs_sub_comm _ (1 : ℝ), abs_of_nonneg (sub_nonneg.2 this), and_true]
- suffices 2 = A * sqrt 2 ↔ A = sqrt 2 by convert! this using 2; ring
+ suffices 2 = A * sqrt 2 ↔ A = sqrt 2 by convert this; ring
rw [← div_eq_iff, div_sqrt, eq_comm]
positivity
diff --git a/Archive/Imo/Imo2024Q3.lean b/Archive/Imo/Imo2024Q3.lean
index ea807ad1b99c00..2c51652ba0ffd4 100644
--- a/Archive/Imo/Imo2024Q3.lean
+++ b/Archive/Imo/Imo2024Q3.lean
@@ -938,7 +938,7 @@ lemma exists_a_apply_add_eq : ∃ b c, 0 < c ∧ ∀ n, b < n →
have := hc.p_pos (N' a N + 2 * (b + 2))
rcases hc.even_p (by lia) (hs (b + 2)) with ⟨_, _⟩
lia
- · convert! hc.apply_add_p_eq (by lia) (hs n) using 3
+ · convert hc.apply_add_p_eq (by lia) (hs n)
rcases hc.even_p (by lia) (hs n) with ⟨_, ht⟩
simp [ht, ← two_mul]
@@ -949,8 +949,8 @@ theorem result {a : ℕ → ℕ} {N : ℕ} (h : Condition a N) :
obtain ⟨b, c, hc, hbc⟩ := h.exists_a_apply_add_eq a N
obtain ⟨t, _⟩ | ⟨t, _⟩ := Nat.even_or_odd (Condition.N' a N)
· refine .inl ⟨c, Condition.N' a N / 2 + b + 1, hc, fun m hm ↦ ?_⟩
- convert! hbc (m - t) (by lia) using 1 <;> dsimp only <;> congr <;> lia
+ convert hbc (m - t) (by lia) <;> lia
· refine .inr ⟨c, Condition.N' a N / 2 + b + 1, hc, fun m hm ↦ ?_⟩
- convert! hbc (m - t) (by lia) using 1 <;> dsimp only <;> congr 1 <;> lia
+ convert hbc (m - t) (by lia) using 2 <;> lia
end Imo2024Q3
diff --git a/Archive/Imo/Imo2024Q6.lean b/Archive/Imo/Imo2024Q6.lean
index 422898498003a3..84fc79eb2061d0 100644
--- a/Archive/Imo/Imo2024Q6.lean
+++ b/Archive/Imo/Imo2024Q6.lean
@@ -57,7 +57,7 @@ lemma Aquaesulian.injective : Function.Injective f := by
lemma Aquaesulian.apply_zero : f 0 = 0 := by
refine h.injective ?_
- convert! h.apply_apply_add 0 using 1 <;> simp
+ convert h.apply_apply_add 0 <;> simp
@[simp]
lemma Aquaesulian.apply_neg_apply_add (x : G) : f (-(f x)) + x = 0 := by
@@ -80,7 +80,7 @@ lemma Aquaesulian.apply_neg_of_apply_eq {x₁ x₂ : G} (hx : f x₁ = x₂) : f
lemma Aquaesulian.apply_neg_eq_neg_iff {x₁ x₂ : G} : f (-x₂) = -x₁ ↔ f x₁ = x₂ := by
refine ⟨fun hn ↦ ?_, h.apply_neg_of_apply_eq⟩
- convert! h.apply_neg_of_apply_eq hn <;> rw [neg_neg]
+ convert h.apply_neg_of_apply_eq hn <;> rw [neg_neg]
lemma Aquaesulian.pair_lemma {x u v : G} (huv : u ≠ v) (hx : f x = u ∨ f u = x)
(hy : f x = v ∨ f v = x) : f x = v ∨ f x = u := by
diff --git a/Archive/MinimalSheffer.lean b/Archive/MinimalSheffer.lean
index 329aa377c84033..19f9bbef6f1d99 100644
--- a/Archive/MinimalSheffer.lean
+++ b/Archive/MinimalSheffer.lean
@@ -121,7 +121,7 @@ lemma sup_le (h₁ : a ≤ c) (h₂ : b ≤ c) : aᶜ | bᶜ ≤ c := by
rw [h₂]
have l1 := (abba (aᶜ | (b | c)) (b | c | c)).symm
rw [comm _ (aᶜ | _), ← le_def] at l1
- convert! l1 using 1
+ convert l1
have l2 := veroff (b | c) c a
rw [comm _ a, ← h₁, comm, comm _ aᶜ] at l2
nth_rw 1 [l2, comm (b | c) c, comm b, veroff]
diff --git a/Archive/MiuLanguage/DecisionSuf.lean b/Archive/MiuLanguage/DecisionSuf.lean
index e4b1c5f4d9f5fd..5cc67b253b8bfc 100644
--- a/Archive/MiuLanguage/DecisionSuf.lean
+++ b/Archive/MiuLanguage/DecisionSuf.lean
@@ -194,7 +194,7 @@ theorem der_replicate_I_of_mod3 (c : ℕ) (h : c % 3 = 1 ∨ c % 3 = 2) :
replicate ((2 ^ m - c) / 3) U ++ replicate ((2 ^ m - c) / 3 % 2) U) := by
apply
der_cons_replicate_I_replicate_U_append_of_der_cons_replicate_I_append c ((2 ^ m - c) / 3) h
- convert! hw₂ using 4
+ convert hw₂
-- now we must show `c + 3 * ((2 ^ m - c) / 3) = 2 ^ m`
rw [Nat.mul_div_cancel']
· exact add_tsub_cancel_of_le hm.1
@@ -220,7 +220,7 @@ example (c : ℕ) (h : c % 3 = 1 ∨ c % 3 = 2) : Derivable (M :: replicate c I)
replicate ((2 ^ m - c) / 3) U ++ replicate ((2 ^ m - c) / 3 % 2) U) := by
apply
der_cons_replicate_I_replicate_U_append_of_der_cons_replicate_I_append c ((2 ^ m - c) / 3) h
- convert! hw₂ using 4
+ convert hw₂
-- now we must show `c + 3 * ((2 ^ m - c) / 3) = 2 ^ m`
rw [Nat.mul_div_cancel']
· exact add_tsub_cancel_of_le hm.1
diff --git a/Archive/Wiedijk100Theorems/AscendingDescendingSequences.lean b/Archive/Wiedijk100Theorems/AscendingDescendingSequences.lean
index ec92034f0ad98c..b9480c2046155a 100644
--- a/Archive/Wiedijk100Theorems/AscendingDescendingSequences.lean
+++ b/Archive/Wiedijk100Theorems/AscendingDescendingSequences.lean
@@ -91,7 +91,7 @@ private lemma maxIncSequencesTo_lt {i j : α} (hij : i < j) (hfij : f i < f j) :
exact (hti.2 hx).trans_lt hij
refine ⟨insert j t, ?_, ?_, ?_⟩
next =>
- convert! hti.insert j using 1
+ convert hti.insert j
next => simp
next => rw [max_eq_left hij.le]
next =>
diff --git a/Counterexamples/CliffordAlgebraNotInjective.lean b/Counterexamples/CliffordAlgebraNotInjective.lean
index 1d52202222ca50..f47f319b0e9f62 100644
--- a/Counterexamples/CliffordAlgebraNotInjective.lean
+++ b/Counterexamples/CliffordAlgebraNotInjective.lean
@@ -234,7 +234,7 @@ theorem quot_obv : α • x' - β • y' - γ • z' = 0 := by
dsimp only [gen]
simp_rw [← map_smul, ← map_sub, ← Submodule.Quotient.mk_smul _ (_ : K),
← Submodule.Quotient.mk_sub]
- convert! LinearMap.map_zero _ using 2
+ convert LinearMap.map_zero _
rw [Submodule.Quotient.mk_eq_zero]
simp +decide [sub_zero]
diff --git a/Counterexamples/Phillips.lean b/Counterexamples/Phillips.lean
index 026e8594775262..1fc73e0eda9341 100644
--- a/Counterexamples/Phillips.lean
+++ b/Counterexamples/Phillips.lean
@@ -360,7 +360,7 @@ theorem discretePart_apply (f : BoundedAdditiveMeasure α) (s : Set α) :
theorem continuousPart_apply_eq_zero_of_countable (f : BoundedAdditiveMeasure α) (s : Set α)
(hs : s.Countable) : f.continuousPart s = 0 := by
simp only [continuousPart, restrict_apply]
- convert! f.apply_countable s hs using 2
+ convert f.apply_countable s hs
ext x
simp [and_comm]
diff --git a/Counterexamples/ZeroDivisorsInAddMonoidAlgebras.lean b/Counterexamples/ZeroDivisorsInAddMonoidAlgebras.lean
index 4a9c213dd49407..03e41b3f218856 100644
--- a/Counterexamples/ZeroDivisorsInAddMonoidAlgebras.lean
+++ b/Counterexamples/ZeroDivisorsInAddMonoidAlgebras.lean
@@ -97,7 +97,7 @@ theorem zero_divisors_of_torsion {R A} [Nontrivial R] [Ring R] [AddMonoid A] (a
(nsmul_ne_zero_of_lt_addOrderOf one_ne_zero (Nat.succ_le_iff.mp o2))
simp only [a0, single_eq_of_ne', Ne, not_false_iff]
· simpa only [single_eq_same] using zero_ne_one
- · convert! Commute.geom_sum₂_mul (R := AddMonoidAlgebra R A) _ (addOrderOf a) using 3
+ · convert Commute.geom_sum₂_mul (R := AddMonoidAlgebra R A) _ (addOrderOf a)
· rw [single_zero_one, one_pow, mul_one]
· rw [single_pow, one_pow, addOrderOf_nsmul_eq_zero, single_zero_one, one_pow, sub_self]
· simp only [single_zero_one, Commute.one_right]
diff --git a/Mathlib/Algebra/Algebra/Spectrum/Quasispectrum.lean b/Mathlib/Algebra/Algebra/Spectrum/Quasispectrum.lean
index 352921912f0081..01711a7aa8dadb 100644
--- a/Mathlib/Algebra/Algebra/Spectrum/Quasispectrum.lean
+++ b/Mathlib/Algebra/Algebra/Spectrum/Quasispectrum.lean
@@ -156,13 +156,13 @@ def unitsFstOne_mulEquiv_quasiregular : unitsFstOne R A ≃* (PreQuasiregular A)
{ val := 1 + PreQuasiregular.equiv.symm x.val
inv := 1 + PreQuasiregular.equiv.symm x⁻¹.val
val_inv := by
- convert! congr((1 + $(inv_add_add_mul_eq_zero x) : Unitization R A)) using 1
+ convert congr((1 + $(inv_add_add_mul_eq_zero x) : Unitization R A))
· simp only [mul_one, PreQuasiregular.equiv_symm_apply, one_mul, mul_add,
add_mul, inr_add, inr_mul]
abel
· simp only [inr_zero, add_zero]
inv_val := by
- convert! congr((1 + $(add_inv_add_mul_eq_zero x) : Unitization R A)) using 1
+ convert congr((1 + $(add_inv_add_mul_eq_zero x) : Unitization R A))
· simp only [mul_one, PreQuasiregular.equiv_symm_apply, one_mul, mul_add,
add_mul, inr_add, inr_mul]
abel
@@ -216,8 +216,8 @@ lemma IsQuasiregular.isUnit_one_add {R : Type*} [Semiring R] {x : R} (hx : IsQua
IsUnit (1 + x) := by
obtain ⟨y, hy₁, hy₂⟩ := isQuasiregular_iff.mp hx
refine ⟨⟨1 + x, 1 + y, ?_, ?_⟩, rfl⟩
- · convert! congr(1 + $(hy₁)) using 1 <;> [noncomm_ring; simp]
- · convert! congr(1 + $(hy₂)) using 1 <;> [noncomm_ring; simp]
+ · convert congr(1 + $(hy₁)) <;> [noncomm_ring; simp]
+ · convert congr(1 + $(hy₂)) <;> [noncomm_ring; simp]
lemma isQuasiregular_iff_isUnit {R : Type*} [Ring R] {x : R} :
IsQuasiregular x ↔ IsUnit (1 + x) := by
@@ -229,7 +229,7 @@ lemma isQuasiregular_iff_isUnit {R : Type*} [Ring R] {x : R} :
case' h.right => have := congr($(hx.val_inv_mul) - 1)
all_goals
rw [← sub_add_cancel (↑hx.unit⁻¹ : R) 1, sub_self] at this
- convert! this using 1
+ convert this
noncomm_ring
-- interestingly, this holds even in the semiring case.
diff --git a/Mathlib/Algebra/GCDMonoid/Basic.lean b/Mathlib/Algebra/GCDMonoid/Basic.lean
index 5c5fb38a783f39..2bafbe6eb818b4 100644
--- a/Mathlib/Algebra/GCDMonoid/Basic.lean
+++ b/Mathlib/Algebra/GCDMonoid/Basic.lean
@@ -624,7 +624,7 @@ theorem isUnit_gcd_of_eq_mul_gcd {α : Type*} [CommMonoidWithZero α] [GCDMonoid
IsUnit (gcd x' y') := by
rw [← associated_one_iff_isUnit]
refine Associated.of_mul_left ?_ (Associated.refl <| gcd x y) h
- convert! (gcd_mul_left' (gcd x y) x' y').symm using 1
+ convert (gcd_mul_left' (gcd x y) x' y').symm
rw [← ex, ← ey, mul_one]
theorem extract_gcd {α : Type*} [CommMonoidWithZero α] [GCDMonoid α] (x y : α) :
diff --git a/Mathlib/Algebra/Group/ForwardDiff.lean b/Mathlib/Algebra/Group/ForwardDiff.lean
index 2c95d310a551fa..90a86ed78e6758 100644
--- a/Mathlib/Algebra/Group/ForwardDiff.lean
+++ b/Mathlib/Algebra/Group/ForwardDiff.lean
@@ -151,7 +151,7 @@ theorem fwdDiff_iter_eq_sum_shift (f : M → G) (n : ℕ) (y : M) :
rw [← coe_fwdDiffₗ, this, ← Module.End.pow_apply]
-- use binomial theorem `Commute.add_pow` to expand this
have : Commute (shiftₗ M G h) (-1) := (Commute.one_right _).neg_right
- convert! congr_fun (LinearMap.congr_fun (this.add_pow n) f) y using 3
+ convert congr_fun (LinearMap.congr_fun (this.add_pow n) f) y
· simp only [sub_eq_add_neg]
· rw [LinearMap.sum_apply, sum_apply]
congr 1 with k
diff --git a/Mathlib/Algebra/Jordan/Basic.lean b/Mathlib/Algebra/Jordan/Basic.lean
index 937a2df977f828..55dfe8ff8daaae 100644
--- a/Mathlib/Algebra/Jordan/Basic.lean
+++ b/Mathlib/Algebra/Jordan/Basic.lean
@@ -165,7 +165,7 @@ theorem two_nsmul_lie_lmul_lmul_add_eq_lie_lmul_lmul_add [IsCommJordan A] (a b :
2 • (⁅L a, L (a * b)⁆ + ⁅L b, L (b * a)⁆) = ⁅L (a * a), L b⁆ + ⁅L (b * b), L a⁆ := by
suffices 2 • ⁅L a, L (a * b)⁆ + 2 • ⁅L b, L (b * a)⁆ + ⁅L b, L (a * a)⁆ + ⁅L a, L (b * b)⁆ = 0 by
rwa [← sub_eq_zero, ← sub_sub, sub_eq_add_neg, sub_eq_add_neg, lie_skew, lie_skew, nsmul_add]
- convert! (commute_lmul_lmul_sq (a + b)).lie_eq using 1
+ convert (commute_lmul_lmul_sq (a + b)).lie_eq
simp only [add_mul, mul_add, map_add, lie_add, add_lie, mul_comm b a,
(commute_lmul_lmul_sq a).lie_eq, (commute_lmul_lmul_sq b).lie_eq, zero_add, add_zero, two_smul]
abel
diff --git a/Mathlib/Algebra/Lie/Basic.lean b/Mathlib/Algebra/Lie/Basic.lean
index 71797fc47faf5f..e90318b3481721 100644
--- a/Mathlib/Algebra/Lie/Basic.lean
+++ b/Mathlib/Algebra/Lie/Basic.lean
@@ -121,7 +121,7 @@ lemma lie_swap_lie [Bracket L₂ L₁] [AddCommGroup M] [IsLieTower L₁ L₂ M]
(x : L₁) (y : L₂) (m : M) : ⁅⁅x, y⁆, m⁆ = -⁅⁅y, x⁆, m⁆ := by
have h1 := leibniz_lie x y m
have h2 := leibniz_lie y x m
- convert! congr($h1.symm - $h2) using 1 <;> simp only [add_sub_cancel_right, sub_add_cancel_right]
+ convert congr($h1.symm - $h2) <;> simp only [add_sub_cancel_right, sub_add_cancel_right]
end IsLieTower
diff --git a/Mathlib/Algebra/Lie/Weights/IsSimple.lean b/Mathlib/Algebra/Lie/Weights/IsSimple.lean
index 46c7955c7d3236..fedb3e7ceb88fa 100644
--- a/Mathlib/Algebra/Lie/Weights/IsSimple.lean
+++ b/Mathlib/Algebra/Lie/Weights/IsSimple.lean
@@ -324,7 +324,7 @@ private theorem chi_not_in_q_aux (h_chi_not_in_q : ↑χ ∉ q) :
rw [hi] at h_equiv
exact h_chi_not_in_q (h_equiv.mpr (by
rw [hj, Weight.toLinear_neg]
- convert! q.smul_mem (-1) hαq using 1
+ convert q.smul_mem (-1) hαq
rw [neg_smul, one_smul]))
obtain ⟨i, hi⟩ := exists_root_index χ (Weight.coe_toLinear_ne_zero_iff.mp w_chi)
obtain ⟨j, hj⟩ := exists_root_index α hα₀
diff --git a/Mathlib/Algebra/Lie/Weights/RootSystem.lean b/Mathlib/Algebra/Lie/Weights/RootSystem.lean
index b5674c3c1754bf..15e5b3055a9dff 100644
--- a/Mathlib/Algebra/Lie/Weights/RootSystem.lean
+++ b/Mathlib/Algebra/Lie/Weights/RootSystem.lean
@@ -274,7 +274,7 @@ lemma chainTopCoeff_zero_right [Nontrivial L] (hα : α.IsNonZero) :
obtain ⟨k, hk⟩ : ∃ k : K, k • f =
(toEnd K L L f ^ (chainTopCoeff α (0 : Weight K H L) + 1)) x := by
have : (toEnd K L L f ^ (chainTopCoeff α (0 : Weight K H L) + 1)) x ∈ rootSpace H (-α) := by
- convert! toEnd_pow_apply_mem hf hx (chainTopCoeff α (0 : Weight K H L) + 1) using 2
+ convert toEnd_pow_apply_mem hf hx (chainTopCoeff α (0 : Weight K H L) + 1)
rw [coe_chainTop', Weight.coe_zero, add_zero, succ_nsmul',
add_assoc, smul_neg, neg_add_cancel, add_zero]
simpa using! (finrank_eq_one_iff_of_nonzero' ⟨f, hf⟩ (by simpa using! isSl2.f_ne_zero)).mp
diff --git a/Mathlib/Algebra/Module/Submodule/Union.lean b/Mathlib/Algebra/Module/Submodule/Union.lean
index 1573543bc065ef..8a1c1deb6ef30c 100644
--- a/Mathlib/Algebra/Module/Submodule/Union.lean
+++ b/Mathlib/Algebra/Module/Submodule/Union.lean
@@ -79,7 +79,7 @@ lemma Submodule.iUnion_ssubset_of_forall_ne_top_of_card_lt (s : Finset ι) (p :
obtain ⟨z₁, -, z₂, -, h⟩ := exists_ne_map_eq_of_encard_lt_of_maps_to (by simpa) hf'
exact ⟨z₁, z₂, h⟩
replace ht : y ∈ p k := by
- have : (t₁ - t₂) • y ∈ p k := by convert! sub_mem ht₁ ht₂ using 1; module
+ have : (t₁ - t₂) • y ∈ p k := by convert sub_mem ht₁ ht₂; module
refine ((p k).smul_mem_iff ?_).mp this
rwa [sub_ne_zero]
replace ht : x ∈ p k := by convert sub_mem ht₁ ((p k).smul_mem t₁ ht); simp
diff --git a/Mathlib/Algebra/Order/BigOperators/Group/Finset.lean b/Mathlib/Algebra/Order/BigOperators/Group/Finset.lean
index 1da58707e8c8cc..cf94a12bd7304e 100644
--- a/Mathlib/Algebra/Order/BigOperators/Group/Finset.lean
+++ b/Mathlib/Algebra/Order/BigOperators/Group/Finset.lean
@@ -677,7 +677,7 @@ alias finset_sum_eq_sup_iff_disjoint := finsetSum_eq_sup_iff_disjoint
theorem sup_powerset_len [DecidableEq α] (x : Multiset α) :
(Finset.sup (Finset.range (card x + 1)) fun k => x.powersetCard k) = x.powerset := by
- convert! bind_powerset_len x using 1
+ convert bind_powerset_len x
rw [Multiset.bind, Multiset.join, ← Finset.range_val, ← Finset.sum_eq_multiset_sum]
exact
Eq.symm (finsetSum_eq_sup_iff_disjoint.mpr fun _ _ _ _ h => pairwise_disjoint_powersetCard x h)
diff --git a/Mathlib/Algebra/Order/CauSeq/Basic.lean b/Mathlib/Algebra/Order/CauSeq/Basic.lean
index 6d999ed68ba29d..c3f8165c29f6db 100644
--- a/Mathlib/Algebra/Order/CauSeq/Basic.lean
+++ b/Mathlib/Algebra/Order/CauSeq/Basic.lean
@@ -631,7 +631,7 @@ instance : LE (CauSeq α abs) :=
theorem lt_of_lt_of_eq {f g h : CauSeq α abs} (fg : f < g) (gh : g ≈ h) : f < h :=
show Pos (h - f) by
- convert! pos_add_limZero fg (neg_limZero gh) using 1
+ convert pos_add_limZero fg (neg_limZero gh)
simp
theorem lt_of_eq_of_lt {f g h : CauSeq α abs} (fg : f ≈ g) (gh : g < h) : f < h := by
@@ -640,7 +640,7 @@ theorem lt_of_eq_of_lt {f g h : CauSeq α abs} (fg : f ≈ g) (gh : g < h) : f <
theorem lt_trans {f g h : CauSeq α abs} (fg : f < g) (gh : g < h) : f < h :=
show Pos (h - f) by
- convert! add_pos fg gh using 1
+ convert add_pos fg gh
simp
theorem lt_irrefl {f : CauSeq α abs} : ¬f < f
diff --git a/Mathlib/Algebra/Order/Field/Basic.lean b/Mathlib/Algebra/Order/Field/Basic.lean
index 6db675d49c48a0..ca4d841b0657e8 100644
--- a/Mathlib/Algebra/Order/Field/Basic.lean
+++ b/Mathlib/Algebra/Order/Field/Basic.lean
@@ -217,10 +217,10 @@ theorem inv_strictAntiOn : StrictAntiOn (fun x : α => x⁻¹) (Set.Ioi 0) := fu
(inv_lt_inv₀ hy hx).2 xy
theorem inv_pow_le_inv_pow_of_le (a1 : 1 ≤ a) {m n : ℕ} (mn : m ≤ n) : (a ^ n)⁻¹ ≤ (a ^ m)⁻¹ := by
- convert! one_div_pow_le_one_div_pow_of_le a1 mn using 1 <;> simp
+ convert one_div_pow_le_one_div_pow_of_le a1 mn <;> simp
theorem inv_pow_lt_inv_pow_of_lt (a1 : 1 < a) {m n : ℕ} (mn : m < n) : (a ^ n)⁻¹ < (a ^ m)⁻¹ := by
- convert! one_div_pow_lt_one_div_pow_of_lt a1 mn using 1 <;> simp
+ convert one_div_pow_lt_one_div_pow_of_lt a1 mn <;> simp
theorem inv_pow_anti (a1 : 1 ≤ a) : Antitone fun n : ℕ => (a ^ n)⁻¹ := fun _ _ =>
inv_pow_le_inv_pow_of_le a1
@@ -236,11 +236,11 @@ theorem le_iff_forall_one_lt_le_mul₀ {α : Type*}
· simp_rw [zero_mul] at h
exact h 2 one_lt_two
refine le_of_forall_gt_imp_ge_of_dense fun x hbx => ?_
- convert! h (x / b) ((one_lt_div hb).mpr hbx)
+ convert h (x / b) ((one_lt_div hb).mpr hbx)
rw [mul_div_cancel₀ _ hb.ne']
theorem div_nat_le_self_of_nonnneg (ha : 0 ≤ a) (n : ℕ) : a / n ≤ a :=
- if h : n = 0 then by simpa [h] using ha
+ if h : n = 0 then by simpa [h]
else div_le_self ha (n.one_le_cast_iff_ne_zero.mpr h)
theorem div_nat_lt_self_of_pos_of_two_le (ha : 0 < a) {n : ℕ} (hn : 2 ≤ n) : a / n < a :=
@@ -538,7 +538,7 @@ theorem sub_one_div_inv_le_two (a2 : 2 ≤ a) : (1 - 1 / a)⁻¹ ≤ 2 := by
-- move `1 / a` to the left and `2⁻¹` to the right.
rw [le_sub_iff_add_le, add_comm, ← le_sub_iff_add_le]
-- take inverses on both sides and use the assumption `2 ≤ a`.
- convert! (one_div a).le.trans (inv_anti₀ zero_lt_two a2) using 1
+ convert (one_div a).le.trans (inv_anti₀ zero_lt_two a2)
-- show `1 - 1 / 2 = 1 / 2`.
-- show `1 - 1 / 2 = 1 / 2`.
diff --git a/Mathlib/Algebra/Order/Floor/Ring.lean b/Mathlib/Algebra/Order/Floor/Ring.lean
index 3537d313859ee3..a7b86a5d802241 100644
--- a/Mathlib/Algebra/Order/Floor/Ring.lean
+++ b/Mathlib/Algebra/Order/Floor/Ring.lean
@@ -671,7 +671,7 @@ theorem ceil_sub_intCast (a : R) (z : ℤ) : ⌈a - z⌉ = ⌈a⌉ - z :=
@[simp]
theorem ceil_sub_natCast (a : R) (n : ℕ) : ⌈a - n⌉ = ⌈a⌉ - n := by
- convert! ceil_sub_intCast a n using 1
+ convert ceil_sub_intCast a n
simp
@[simp]
@@ -769,7 +769,7 @@ lemma ceil_div_ceil_inv_sub_one (ha : 1 ≤ a) : ⌈⌈(a - 1)⁻¹⌉ / a⌉ =
refine le_antisymm (ceil_le.2 <| div_le_self (by positivity) ha.le) <| ?_
rw [le_ceil_iff, sub_lt_comm, div_eq_mul_inv, ← mul_one_sub,
← lt_div_iff₀ (sub_pos.2 <| inv_lt_one_of_one_lt₀ ha)]
- convert! ceil_lt_add_one (R := k) _ using 1
+ convert ceil_lt_add_one (R := k) _
field
lemma ceil_lt_mul (hb : 1 < b) (hba : ⌈(b - 1)⁻¹⌉ / b < a) : ⌈a⌉ < b * a := by
diff --git a/Mathlib/Algebra/Order/Module/HahnEmbedding.lean b/Mathlib/Algebra/Order/Module/HahnEmbedding.lean
index 8bbb9bd0db33c6..e9101fdda6f68b 100644
--- a/Mathlib/Algebra/Order/Module/HahnEmbedding.lean
+++ b/Mathlib/Algebra/Order/Module/HahnEmbedding.lean
@@ -545,7 +545,7 @@ theorem isWF_support_evalCoeff [IsOrderedAddMonoid R] [Archimedean R] (x : M) :
have hmem' (n : ℕ) : seq n ∈ (ofLex (f.val y)).coeff.support := by
specialize hmem n
rw [Function.mem_support] at ⊢ hmem
- convert! hmem using 1
+ convert hmem
refine (f.evalCoeff_eq ((ball_strictAnti K).antitone ?_ hy)).symm
simpa using hanti.antitone (show 0 ≤ n by simp)
obtain hwf := (ofLex (f.val y)).isWF_support
diff --git a/Mathlib/Algebra/Order/Ring/Archimedean.lean b/Mathlib/Algebra/Order/Ring/Archimedean.lean
index 6bc0565a98cf90..ad71ce19de201b 100644
--- a/Mathlib/Algebra/Order/Ring/Archimedean.lean
+++ b/Mathlib/Algebra/Order/Ring/Archimedean.lean
@@ -62,7 +62,7 @@ private theorem mk_mul_le_of_le {x₁ y₁ x₂ y₂ : R} (hx : mk x₁ ≤ mk x
obtain ⟨m, hm⟩ := hx
obtain ⟨n, hn⟩ := hy
use m * n
- convert! mul_le_mul hm hn (abs_nonneg _) (nsmul_nonneg (abs_nonneg _) _) using 1 <;>
+ convert mul_le_mul hm hn (abs_nonneg _) (nsmul_nonneg (abs_nonneg _) _) <;>
simp_rw [ArchimedeanOrder.val_of, abs_mul]
ring
diff --git a/Mathlib/Algebra/Polynomial/Degree/IsMonicOfDegree.lean b/Mathlib/Algebra/Polynomial/Degree/IsMonicOfDegree.lean
index 0acdfb7850f3fb..9ab22b4f95c7cc 100644
--- a/Mathlib/Algebra/Polynomial/Degree/IsMonicOfDegree.lean
+++ b/Mathlib/Algebra/Polynomial/Degree/IsMonicOfDegree.lean
@@ -267,7 +267,7 @@ lemma IsMonicOfDegree.of_dvd_add {a b r : R[X]} {m n : ℕ} (hmn : n ≤ m) (ha
lemma IsMonicOfDegree.of_dvd_sub {a b r : R[X]} {m n : ℕ} (hmn : n ≤ m) (ha : IsMonicOfDegree a m)
(hb : IsMonicOfDegree b n) (hr : r.natDegree < m) (h : b ∣ a - r) :
∃ q : R[X], IsMonicOfDegree q (m - n) ∧ a = q * b + r := by
- convert! ha.of_dvd_add hmn hb ?_ h using 4 with q
+ convert ha.of_dvd_add hmn hb ?_ h with q
· rw [sub_neg_eq_add]
· rwa [natDegree_neg]
diff --git a/Mathlib/Algebra/Polynomial/EraseLead.lean b/Mathlib/Algebra/Polynomial/EraseLead.lean
index ce8b63e2e27be0..54af338b94faea 100644
--- a/Mathlib/Algebra/Polynomial/EraseLead.lean
+++ b/Mathlib/Algebra/Polynomial/EraseLead.lean
@@ -327,7 +327,7 @@ theorem induction_with_natDegree_le (motive : R[X] → Prop) (N : ℕ) (zero : m
| succ c hc =>
rw [← eraseLead_add_C_mul_X_pow f]
cases c
- · convert! C_mul_pow f.natDegree f.leadingCoeff ?_ df using 1
+ · convert C_mul_pow f.natDegree f.leadingCoeff ?_ df
· convert! zero_add (C (leadingCoeff f) * X ^ f.natDegree)
rw [← card_support_eq_zero, card_support_eraseLead' hf]
· rw [leadingCoeff_ne_zero, Ne, ← card_support_eq_zero, hf]
diff --git a/Mathlib/Algebra/Polynomial/Laurent.lean b/Mathlib/Algebra/Polynomial/Laurent.lean
index e3b674a50ed46d..c0c049fb4ab09e 100644
--- a/Mathlib/Algebra/Polynomial/Laurent.lean
+++ b/Mathlib/Algebra/Polynomial/Laurent.lean
@@ -375,7 +375,7 @@ theorem reduce_to_polynomial_of_mul_T (f : R[T;T⁻¹]) {Q : R[T;T⁻¹] → Pro
induction f using LaurentPolynomial.induction_on_mul_T with | _ f n
induction n with
| zero => simpa only [Nat.cast_zero, neg_zero, T_zero, mul_one] using Qf _
- | succ n hn => convert! QT _ _; simpa using hn
+ | succ n hn => convert QT _ _; simpa
section Support
diff --git a/Mathlib/Algebra/Polynomial/RuleOfSigns.lean b/Mathlib/Algebra/Polynomial/RuleOfSigns.lean
index 6a3f307dd88e4d..7cddfd744d4758 100644
--- a/Mathlib/Algebra/Polynomial/RuleOfSigns.lean
+++ b/Mathlib/Algebra/Polynomial/RuleOfSigns.lean
@@ -209,7 +209,7 @@ lemma signVariations_eraseLead_mul_X_sub_C (hη : 0 < η) (hP₀ : 0 < leadingCo
lemma succ_signVariations_X_sub_C_mul_monomial {d c} (hc : c ≠ 0) (hη : 0 < η) :
(monomial d c).signVariations + 1 ≤ ((X - C η) * monomial d c).signVariations := by
have h₁ : nextCoeff ((X - C η) * monomial d c) = -(η * c) := by
- convert! coeff_mul_monomial (X - C η) d 0 c using 1
+ convert coeff_mul_monomial (X - C η) d 0 c
· simp [hc, nextCoeff, natDegree_mul (X_sub_C_ne_zero η)]
· simp
have h₂ : eraseLead ((X - C η) * monomial d c) ≠ 0 := by
diff --git a/Mathlib/Algebra/QuadraticDiscriminant.lean b/Mathlib/Algebra/QuadraticDiscriminant.lean
index d468d87f32ef32..3b663b49b48ba3 100644
--- a/Mathlib/Algebra/QuadraticDiscriminant.lean
+++ b/Mathlib/Algebra/QuadraticDiscriminant.lean
@@ -139,7 +139,7 @@ theorem discrim_le_zero (h : ∀ x : K, 0 ≤ a * (x * x) + b * x + c) : discrim
linarith
-- if a > 0
· have ha' : 0 ≤ 4 * a := mul_nonneg zero_le_four ha.le
- convert! neg_nonpos.2 (mul_nonneg ha' (h (-b / (2 * a)))) using 1
+ convert neg_nonpos.2 (mul_nonneg ha' (h (-b / (2 * a))))
field
lemma discrim_le_zero_of_nonpos (h : ∀ x : K, a * (x * x) + b * x + c ≤ 0) : discrim a b c ≤ 0 :=
diff --git a/Mathlib/Algebra/Regular/SMul.lean b/Mathlib/Algebra/Regular/SMul.lean
index 3d343ced450708..4ba3c9dbceed3a 100644
--- a/Mathlib/Algebra/Regular/SMul.lean
+++ b/Mathlib/Algebra/Regular/SMul.lean
@@ -224,7 +224,7 @@ variable {G : Type*} [Group G]
of the inverse given by groups, since there is no `LeftCancelSMul` typeclass. -/
theorem isSMulRegular_of_group [MulAction G R] (g : G) : IsSMulRegular R g := by
intro x y h
- convert! congr_arg (g⁻¹ • ·) h using 1 <;> simp [← smul_assoc]
+ convert congr_arg (g⁻¹ • ·) h <;> simp [← smul_assoc]
end Group
diff --git a/Mathlib/Algebra/Ring/Divisibility/Basic.lean b/Mathlib/Algebra/Ring/Divisibility/Basic.lean
index 1dbced56baea73..05bb2b130d1cc7 100644
--- a/Mathlib/Algebra/Ring/Divisibility/Basic.lean
+++ b/Mathlib/Algebra/Ring/Divisibility/Basic.lean
@@ -182,7 +182,7 @@ variable [NonUnitalCommRing α]
theorem dvd_mul_sub_mul {k a b x y : α} (hab : k ∣ a - b) (hxy : k ∣ x - y) :
k ∣ a * x - b * y := by
- convert! dvd_add (hxy.mul_left a) (hab.mul_right y) using 1
+ convert dvd_add (hxy.mul_left a) (hab.mul_right y)
rw [mul_sub_left_distrib, mul_sub_right_distrib]
simp only [sub_eq_add_neg, add_assoc, neg_add_cancel_left]
diff --git a/Mathlib/Algebra/Ring/Idempotent.lean b/Mathlib/Algebra/Ring/Idempotent.lean
index 5e11e9f0f17344..05999cf2e63959 100644
--- a/Mathlib/Algebra/Ring/Idempotent.lean
+++ b/Mathlib/Algebra/Ring/Idempotent.lean
@@ -143,7 +143,7 @@ theorem sub_iff [NonUnitalRing R] [IsAddTorsionFree R] {p q : R}
simp_rw [sub_mul, add_mul, mul_assoc, hq.eq, add_sub_cancel_left, ← mul_assoc] at h2
exact h2.symm.trans h1
rw [hpq.eq, and_self, ← nsmul_right_inj (by simp : 2 ≠ 0), ← zero_add (2 • p)]
- convert! congrArg (· + 2 • p) h using 1
+ convert congrArg (· + 2 • p) h
simp [sub_mul, mul_sub, hp.eq, hpq.eq, two_nsmul, sub_add, sub_sub]
end IsIdempotentElem
diff --git a/Mathlib/Algebra/Ring/Subring/Basic.lean b/Mathlib/Algebra/Ring/Subring/Basic.lean
index fa665fc64c8c3a..381b16158620ac 100644
--- a/Mathlib/Algebra/Ring/Subring/Basic.lean
+++ b/Mathlib/Algebra/Ring/Subring/Basic.lean
@@ -1166,5 +1166,5 @@ end Subring
theorem AddSubgroup.int_mul_mem {G : AddSubgroup R} (k : ℤ) {g : R} (h : g ∈ G) :
(k : R) * g ∈ G := by
- convert! AddSubgroup.zsmul_mem G h k using 1
+ convert AddSubgroup.zsmul_mem G h k
rw [zsmul_eq_mul]
diff --git a/Mathlib/Algebra/TrivSqZeroExt/Basic.lean b/Mathlib/Algebra/TrivSqZeroExt/Basic.lean
index d9a220b6c71c85..d1559a84faf99a 100644
--- a/Mathlib/Algebra/TrivSqZeroExt/Basic.lean
+++ b/Mathlib/Algebra/TrivSqZeroExt/Basic.lean
@@ -790,7 +790,7 @@ protected theorem inv_one : (1 : tsze R M)⁻¹ = (1 : tsze R M) := by
rw [← inl_one, TrivSqZeroExt.inv_inl, inv_one]
protected theorem inv_mul_cancel {x : tsze R M} (hx : fst x ≠ 0) : x⁻¹ * x = 1 := by
- convert! mul_left_eq_one _ _ (_root_.inv_mul_cancel₀ hx) using 2
+ convert mul_left_eq_one _ _ (_root_.inv_mul_cancel₀ hx)
ext <;> simp
variable [SMulCommClass R Rᵐᵒᵖ M]
diff --git a/Mathlib/AlgebraicGeometry/AffineScheme.lean b/Mathlib/AlgebraicGeometry/AffineScheme.lean
index 5c6067430c560d..06bc6b0c1d65fa 100644
--- a/Mathlib/AlgebraicGeometry/AffineScheme.lean
+++ b/Mathlib/AlgebraicGeometry/AffineScheme.lean
@@ -807,7 +807,7 @@ theorem isLocalization_stalk' (y : PrimeSpectrum Γ(X, U)) (hy : hU.fromSpec y
(S := X.presheaf.stalk (hU.fromSpec y)) _ y.asIdeal.primeCompl _
(TopCat.Presheaf.algebra_section_stalk X.presheaf ⟨hU.fromSpec y, hy⟩) _ _
(asIso <| hU.fromSpec.stalkMap y).commRingCatIsoToRingEquiv).mpr
- convert! StructureSheaf.IsLocalization.to_stalk Γ(X, U) y using 1
+ convert StructureSheaf.IsLocalization.to_stalk Γ(X, U) y
delta IsLocalization.AtPrime StructureSheaf.stalkAlgebra
congr!
simp [RingHom.algebraMap_toAlgebra, ← CommRingCat.hom_comp, IsAffineOpen.fromSpec_app_self]
diff --git a/Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean b/Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean
index c12b8899b8151a..105ba3a560eaad 100644
--- a/Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean
+++ b/Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean
@@ -749,7 +749,7 @@ lemma exists_appTop_map_eq_zero_of_isLimit [∀ {i j} (f : i ⟶ j), IsAffineHom
have (j : Over i) : IsAffine ((opensDiagram D i U).obj j) := hU.preimage (D.map _)
obtain ⟨j, f, hj⟩ := exists_appTop_map_eq_zero_of_isAffine_of_isLimit _ _
(isLimitOpensCone D c hc i U) (.mk (𝟙 i)) (((opensDiagramι D i U).app _).appTop s) (by
- convert! congr((c.pt.presheaf.map (homOfLE le_top).op).hom $hs) using 1
+ convert congr((c.pt.presheaf.map (homOfLE le_top).op).hom $hs)
· simp [Scheme.Hom.app_eq_appLE, Scheme.Hom.resLE_appLE, ← ConcreteCategory.comp_apply]; rfl
· simp)
refine ⟨U, hU, hxU, j.left, j.hom, ?_⟩
diff --git a/Mathlib/AlgebraicGeometry/IdealSheaf/Subscheme.lean b/Mathlib/AlgebraicGeometry/IdealSheaf/Subscheme.lean
index ef44e2202e0812..d767fc855ecdee 100644
--- a/Mathlib/AlgebraicGeometry/IdealSheaf/Subscheme.lean
+++ b/Mathlib/AlgebraicGeometry/IdealSheaf/Subscheme.lean
@@ -174,7 +174,7 @@ lemma ideal_le_ker_glueDataObjι (U V : X.affineOpens) :
simp only [Scheme.Hom.comp_app, Scheme.Opens.ι_app, Scheme.homOfLE_app, ← Functor.map_comp_assoc,
Scheme.Hom.app_eq _ H, Scheme.Opens.toScheme_presheaf_map, ← Functor.map_comp, Category.assoc]
simp only [CommRingCat.hom_comp, RingHom.comp_apply]
- convert! RingHom.map_zero _ using 2
+ convert RingHom.map_zero _
rw [← RingHom.mem_ker, ker_glueDataObjι_appTop, ← Ideal.mem_comap, Ideal.comap_comap,
← CommRingCat.hom_comp]
simp only [homOfLE_leOfHom, Scheme.Hom.comp_base,
@@ -703,7 +703,7 @@ def Hom.toImage : X ⟶ f.image :=
@[reassoc (attr := simp)]
lemma Hom.toImage_imageι :
f.toImage ≫ f.imageι = f := by
- convert! f.toImageAux_spec using 2
+ convert f.toImageAux_spec
exact Scheme.Hom.copyBase_eq _ _ _
instance [QuasiCompact f] : IsDominant f.toImage where
diff --git a/Mathlib/AlgebraicGeometry/Limits.lean b/Mathlib/AlgebraicGeometry/Limits.lean
index 7d557c41b1f71f..a832ac5e0c047b 100644
--- a/Mathlib/AlgebraicGeometry/Limits.lean
+++ b/Mathlib/AlgebraicGeometry/Limits.lean
@@ -664,7 +664,7 @@ private lemma IsAffineOpen.iSup_of_disjoint_aux [Finite ι] {U : ι → X.Opens}
(hU : ∀ i, IsAffineOpen (U i)) (hU' : Pairwise (Disjoint on U)) :
IsAffineOpen (iSup U) := by
have := isOpenImmersion_sigmaDesc _ (fun i ↦ (U i).ι)
- (fun i j e ↦ by convert! hU' e using 0; simp [← Opens.coe_disjoint])
+ (fun i j e ↦ by convert hU' e; simp [← Opens.coe_disjoint])
convert! isAffineOpen_opensRange (Sigma.desc fun i ↦ (U i).ι)
· ext
simp [(sigmaMk _).symm.exists_congr_left, ← Scheme.Hom.comp_apply, Scheme.Opens.exists_toScheme]
diff --git a/Mathlib/AlgebraicGeometry/Morphisms/Affine.lean b/Mathlib/AlgebraicGeometry/Morphisms/Affine.lean
index fad81099650161..e2615dd23bd633 100644
--- a/Mathlib/AlgebraicGeometry/Morphisms/Affine.lean
+++ b/Mathlib/AlgebraicGeometry/Morphisms/Affine.lean
@@ -253,14 +253,14 @@ lemma isAffineHom_of_isInducing
exact ⟨⟨U', hU'⟩ ⊓ U, inf_le_right, Opens.ext (by simpa [e] using hVU)⟩
obtain ⟨r, hrU', hxr⟩ := hU.exists_basicOpen_le ⟨f x, hxV⟩ hxU
refine ⟨_, hxr, hU.basicOpen r, ?_⟩
- convert! hV.basicOpen (f.app _ (Y.presheaf.map (homOfLE hU'U).op r)) using 1
+ convert hV.basicOpen (f.app _ (Y.presheaf.map (homOfLE hU'U).op r))
simp only [Scheme.preimage_basicOpen, ← CommRingCat.comp_apply, f.naturality]
simpa using ((Opens.map f.base).map (homOfLE hrU')).le
· obtain ⟨_, ⟨U, hU, rfl⟩, hyU, hU'⟩ :=
Y.isBasis_affineOpens.exists_subset_of_mem_open hy hf₂.isOpen_compl
rw [Set.subset_compl_iff_disjoint_right, ← Set.preimage_eq_empty_iff] at hU'
refine ⟨U, hyU, hU, ?_⟩
- convert! isAffineOpen_bot _
+ convert isAffineOpen_bot _
exact Opens.ext hU'
lemma IsAffineOpen.isCompact_pullback_inf {X Y Z : Scheme.{u}} {f : X ⟶ Z} {g : Y ⟶ Z}
@@ -303,7 +303,7 @@ theorem diagonal_isAffine_iff_forall_isAffineOpen_inf [IsAffine Y] (f : X ⟶ Y)
exact .of_isIso this.isoPullback.hom
· introv H h₁ h₂
have : IsAffineOpen (pullback.fst f₁ f₂ ≫ f₁).opensRange := by
- convert! H _ _ (isAffineOpen_opensRange f₁) (isAffineOpen_opensRange f₂) using 1
+ convert! H _ _ (isAffineOpen_opensRange f₁) (isAffineOpen_opensRange f₂)
exact Opens.ext (IsOpenImmersion.range_pullback_to_base_of_left _ _)
change IsAffine _ at this
exact .of_isIso (pullback.fst f₁ f₂ ≫ f₁).isoOpensRange.hom
diff --git a/Mathlib/AlgebraicGeometry/Morphisms/QuasiCompact.lean b/Mathlib/AlgebraicGeometry/Morphisms/QuasiCompact.lean
index 8d498a091cc989..26d5fefa8013e3 100644
--- a/Mathlib/AlgebraicGeometry/Morphisms/QuasiCompact.lean
+++ b/Mathlib/AlgebraicGeometry/Morphisms/QuasiCompact.lean
@@ -84,7 +84,7 @@ theorem isCompact_and_isOpen_iff_finite_and_eq_biUnion_affineOpens {U : Set X} :
theorem isCompact_iff_finite_and_eq_biUnion_affineOpens {U : X.Opens} :
IsCompact (X := X) U ↔ ∃ s : Set X.affineOpens, s.Finite ∧ U = ⨆ i ∈ s, (i : X.Opens) := by
- convert! isCompact_and_isOpen_iff_finite_and_eq_biUnion_affineOpens (U := U.1) using 4 with s
+ convert isCompact_and_isOpen_iff_finite_and_eq_biUnion_affineOpens (U := U.1) with s
· simp [U.isOpen]
· convert! SetLike.coe_injective.eq_iff.symm; simp
diff --git a/Mathlib/AlgebraicGeometry/Morphisms/QuasiFinite.lean b/Mathlib/AlgebraicGeometry/Morphisms/QuasiFinite.lean
index fbe4adfc521fb3..2e85b370116bd2 100644
--- a/Mathlib/AlgebraicGeometry/Morphisms/QuasiFinite.lean
+++ b/Mathlib/AlgebraicGeometry/Morphisms/QuasiFinite.lean
@@ -232,7 +232,7 @@ nonrec lemma LocallyQuasiFinite.of_fiberToSpecResidueField
let g : (X.affineCover.f i ≫ f).fiber x ⟶ f.fiber x :=
pullback.map _ _ _ _ (X.affineCover.f i) (𝟙 _) (𝟙 _) (by simp) (by simp)
have : IsClosedImmersion g := .of_isPreimmersion _ (isClosed_discrete _)
- convert! (inferInstance : LocallyQuasiFinite <| g ≫ f.fiberToSpecResidueField _) using 1
+ convert (inferInstance : LocallyQuasiFinite <| g ≫ f.fiberToSpecResidueField _)
simp [g, Hom.fiberToSpecResidueField]
obtain ⟨S, rfl⟩ := hX
obtain ⟨φ, rfl⟩ := Spec.map_surjective f
diff --git a/Mathlib/AlgebraicGeometry/Morphisms/Separated.lean b/Mathlib/AlgebraicGeometry/Morphisms/Separated.lean
index 062845c2c55847..853b0ae33aa695 100644
--- a/Mathlib/AlgebraicGeometry/Morphisms/Separated.lean
+++ b/Mathlib/AlgebraicGeometry/Morphisms/Separated.lean
@@ -133,7 +133,7 @@ instance [IsSeparated g] :
rw [← MorphismProperty.cancel_left_of_respectsIso @IsClosedImmersion (pullback.fst f (𝟙 Y))]
rw [← MorphismProperty.cancel_right_of_respectsIso @IsClosedImmersion _
(pullback.congrHom rfl (Category.id_comp g)).inv]
- convert! (inferInstance : IsClosedImmersion (pullback.mapDesc f (𝟙 _) g)) using 1
+ convert (inferInstance : IsClosedImmersion (pullback.mapDesc f (𝟙 _) g))
ext : 1 <;> simp [pullback.condition]
end IsSeparated
diff --git a/Mathlib/AlgebraicGeometry/Morphisms/UnderlyingMap.lean b/Mathlib/AlgebraicGeometry/Morphisms/UnderlyingMap.lean
index e71b9335da7474..f505798c3260fe 100644
--- a/Mathlib/AlgebraicGeometry/Morphisms/UnderlyingMap.lean
+++ b/Mathlib/AlgebraicGeometry/Morphisms/UnderlyingMap.lean
@@ -258,7 +258,7 @@ lemma IsDominant.of_comp_of_isOpenImmersion
IsDominant f := by
rw [isDominant_iff, DenseRange] at H ⊢
simp only [Scheme.Hom.comp_base, TopCat.coe_comp, Set.range_comp] at H
- convert! H.preimage g.isOpenEmbedding.isOpenMap using 1
+ convert H.preimage g.isOpenEmbedding.isOpenMap
rw [Set.preimage_image_eq _ g.isOpenEmbedding.injective]
lemma Opens.isDominant_ι {U : X.Opens} (hU : Dense (X := X) U) : IsDominant U.ι :=
diff --git a/Mathlib/AlgebraicGeometry/ProjectiveSpectrum/Proper.lean b/Mathlib/AlgebraicGeometry/ProjectiveSpectrum/Proper.lean
index 7bbc9d9c03b233..10c2bdfa34cc70 100644
--- a/Mathlib/AlgebraicGeometry/ProjectiveSpectrum/Proper.lean
+++ b/Mathlib/AlgebraicGeometry/ProjectiveSpectrum/Proper.lean
@@ -53,17 +53,17 @@ lemma lift_awayMapₐ_awayMapₐ_surjective {d e : ℕ} {f : A} (hf : f ∈ 𝒜
let x0 : NumDenSameDeg 𝒜 (.powers f) :=
{ deg := j * (d * (e + 1))
num := ⟨a * g ^ (j * (d - 1)), by
- convert! SetLike.mul_mem_graded ha (SetLike.pow_mem_graded _ hg) using 2
+ convert SetLike.mul_mem_graded ha (SetLike.pow_mem_graded _ hg)
rw [this]
cases d
· contradiction
· simp; ring⟩
- den := ⟨f ^ (j * (e + 1)), by convert! SetLike.pow_mem_graded _ hf using 2; ring⟩
+ den := ⟨f ^ (j * (e + 1)), by convert SetLike.pow_mem_graded _ hf; ring⟩
den_mem := ⟨_,rfl⟩ }
let y0 : NumDenSameDeg 𝒜 (.powers g) :=
{ deg := j * (d * e)
- num := ⟨f ^ (j * e), by convert! SetLike.pow_mem_graded _ hf using 2; ring⟩
- den := ⟨g ^ (j * d), by convert! SetLike.pow_mem_graded _ hg using 2; ring⟩
+ num := ⟨f ^ (j * e), by convert SetLike.pow_mem_graded _ hf; ring⟩
+ den := ⟨g ^ (j * d), by convert SetLike.pow_mem_graded _ hg; ring⟩
den_mem := ⟨_,rfl⟩ }
use mk x0 ⊗ₜ mk y0
ext
diff --git a/Mathlib/AlgebraicGeometry/ProjectiveSpectrum/Scheme.lean b/Mathlib/AlgebraicGeometry/ProjectiveSpectrum/Scheme.lean
index 0df625c2b1eb3b..b56912df297135 100644
--- a/Mathlib/AlgebraicGeometry/ProjectiveSpectrum/Scheme.lean
+++ b/Mathlib/AlgebraicGeometry/ProjectiveSpectrum/Scheme.lean
@@ -383,10 +383,10 @@ variable (hm : 0 < m) (q : Spec.T A⁰_ f)
include hm
theorem carrier.zero_mem : (0 : A) ∈ carrier f_deg q := fun i => by
- convert! Submodule.zero_mem q.1 using 1
+ convert Submodule.zero_mem q.1
rw [HomogeneousLocalization.ext_iff_val, HomogeneousLocalization.val_mk,
HomogeneousLocalization.val_zero]; simp_rw [map_zero, zero_pow hm.ne']
- convert! Localization.mk_zero (S := Submonoid.powers f) _ using 1
+ exact Localization.mk_zero (S := Submonoid.powers f) _
theorem carrier.smul_mem (c x : A) (hx : x ∈ carrier f_deg q) : c • x ∈ carrier f_deg q := by
revert c
@@ -442,7 +442,7 @@ theorem carrier.denom_notMem : f ∉ carrier.asIdeal f_deg hm q := fun rid =>
q.isPrime.ne_top <|
(Ideal.eq_top_iff_one _).mpr
(by
- convert! rid m
+ convert rid m
rw [HomogeneousLocalization.ext_iff_val, HomogeneousLocalization.val_one,
HomogeneousLocalization.val_mk]
dsimp
@@ -460,7 +460,7 @@ theorem carrier.asIdeal.prime : (carrier.asIdeal f_deg hm q).IsPrime :=
(carrier.asIdeal.ne_top f_deg hm q) fun {x y} ⟨nx, hnx⟩ ⟨ny, hny⟩ hxy =>
show (∀ _, _ ∈ _) ∨ ∀ _, _ ∈ _ by
rw [← and_forall_ne nx, and_iff_left, ← and_forall_ne ny, and_iff_left]
- · apply q.2.mem_or_mem; convert! hxy (nx + ny) using 1
+ · apply q.2.mem_or_mem; convert! hxy (nx + ny)
dsimp
simp_rw [decompose_of_mem_same 𝒜 hnx, decompose_of_mem_same 𝒜 hny,
decompose_of_mem_same 𝒜 (SetLike.GradedMonoid.toGradedMul.mul_mem hnx hny),
@@ -469,7 +469,7 @@ theorem carrier.asIdeal.prime : (carrier.asIdeal f_deg hm q).IsPrime :=
HomogeneousLocalization.val_mul, Localization.mk_mul]
simp only [Submonoid.mk_mul_mk, mk_eq_monoidOf_mk']
all_goals
- intro n hn; convert! q.1.zero_mem using 1
+ intro n hn; convert q.1.zero_mem
rw [HomogeneousLocalization.ext_iff_val, HomogeneousLocalization.val_mk,
HomogeneousLocalization.val_zero]; simp_rw [proj_apply]
convert! mk_zero (S := Submonoid.powers f) _
@@ -742,7 +742,7 @@ lemma isLocalization_atPrime (f) (x : pbo f) {m} (f_deg : f ∈ 𝒜 m) (hm : 0
⟨f ^ i, SetLike.pow_mem_graded _ f_deg⟩, ⟨_, rfl⟩⟩,
(mk_mem_toSpec_base_apply _ _ _).not.mpr <| x.1.1.toIdeal.primeCompl.pow_mem hb' m⟩⟩,
val_injective _ ?_⟩
- · convert! SetLike.mul_mem_graded a.2 (SetLike.pow_mem_graded (m - 1) hb) using 2
+ · convert SetLike.mul_mem_graded a.2 (SetLike.pow_mem_graded (m - 1) hb)
rw [← succ_nsmul', tsub_add_cancel_of_le (by lia), mul_comm, smul_eq_mul]
· simp only [RingHom.algebraMap_toAlgebra, map_mk, GradedRingHom.id_apply, val_mul, val_mk,
mk_eq_mk', ← IsLocalization.mk'_mul, Submonoid.mk_mul_mk, IsLocalization.mk'_eq_iff_eq]
diff --git a/Mathlib/Analysis/Analytic/Basic.lean b/Mathlib/Analysis/Analytic/Basic.lean
index 56f14154b5d5fd..0c93cffbd50709 100644
--- a/Mathlib/Analysis/Analytic/Basic.lean
+++ b/Mathlib/Analysis/Analytic/Basic.lean
@@ -157,7 +157,7 @@ theorem HasFPowerSeriesOnBall.comp_sub (hf : HasFPowerSeriesOnBall f p x r) (y :
{ r_le := hf.r_le
r_pos := hf.r_pos
hasSum := fun {z} hz => by
- convert! hf.hasSum hz using 2
+ convert hf.hasSum hz
abel }
theorem HasFPowerSeriesWithinOnBall.comp_sub (hf : HasFPowerSeriesWithinOnBall f p s x r) (y : E) :
@@ -169,7 +169,7 @@ theorem HasFPowerSeriesWithinOnBall.comp_sub (hf : HasFPowerSeriesWithinOnBall f
simp only [add_singleton, image_add_right, mem_insert_iff, add_eq_left, mem_preimage] at hz1 ⊢
abel_nf at hz1
assumption
- convert! hf.hasSum this hz2 using 2
+ convert hf.hasSum this hz2
abel
theorem HasFPowerSeriesAt.comp_sub (hf : HasFPowerSeriesAt f p x) (y : E) :
@@ -192,7 +192,7 @@ theorem AnalyticOnNhd.comp_sub (hf : AnalyticOnNhd 𝕜 f s) (y : E) :
intro x hx
simp only [add_singleton, image_add_right, mem_preimage] at hx
rw [show x = (x - y) + y by abel]
- apply (hf (x - y) (by convert! hx using 1; abel)).comp_sub
+ apply (hf (x - y) (by convert hx; abel)).comp_sub
theorem AnalyticWithinAt.comp_sub (hf : AnalyticWithinAt 𝕜 f s x) (y : E) :
AnalyticWithinAt 𝕜 (fun z ↦ f (z - y)) (s + {y}) (x + y) := by
@@ -204,7 +204,7 @@ theorem AnalyticOn.comp_sub (hf : AnalyticOn 𝕜 f s) (y : E) :
intro x hx
simp only [add_singleton, image_add_right, mem_preimage] at hx
rw [show x = (x - y) + y by abel]
- apply (hf (x - y) (by convert! hx using 1; abel)).comp_sub
+ apply (hf (x - y) (by convert hx; abel)).comp_sub
theorem HasFPowerSeriesWithinOnBall.hasSum_sub (hf : HasFPowerSeriesWithinOnBall f p s x r) {y : E}
(hy : y ∈ (insert x s) ∩ Metric.eball x r) :
@@ -243,7 +243,7 @@ lemma HasFPowerSeriesWithinOnBall.congr {f g : E → F} {p : FormalMultilinearSe
HasFPowerSeriesWithinOnBall g p s x r := by
refine ⟨h.r_le, h.r_pos, ?_⟩
intro y hy h'y
- convert! h.hasSum hy h'y using 1
+ convert h.hasSum hy h'y
simp only [mem_insert_iff, add_eq_left] at hy
rcases hy with rfl | hy
· simpa using h''
@@ -258,7 +258,7 @@ lemma HasFPowerSeriesWithinOnBall.congr' {f g : E → F} {p : FormalMultilinearS
(h' : EqOn g f (insert x s ∩ Metric.eball x r)) :
HasFPowerSeriesWithinOnBall g p s x r := by
refine ⟨h.r_le, h.r_pos, fun {y} hy h'y ↦ ?_⟩
- convert! h.hasSum hy h'y using 1
+ convert h.hasSum hy h'y
exact h' ⟨hy, by simpa [edist_eq_enorm_sub] using h'y⟩
lemma HasFPowerSeriesWithinAt.congr {f g : E → F} {p : FormalMultilinearSeries 𝕜 E F} {s : Set E}
@@ -279,7 +279,7 @@ theorem HasFPowerSeriesOnBall.congr (hf : HasFPowerSeriesOnBall f p x r)
{ r_le := hf.r_le
r_pos := hf.r_pos
hasSum := fun {y} hy => by
- convert! hf.hasSum hy using 1
+ convert hf.hasSum hy
apply hg.symm
simpa [edist_eq_enorm_sub] using hy }
@@ -798,9 +798,9 @@ theorem HasFPowerSeriesWithinOnBall.isBigO_image_sub_image_sub_deriv_principal
exact Metric.eball_subset_eball hr.le hy'
set A : ℕ → F := fun n => (p n fun _ => y.1 - x) - p n fun _ => y.2 - x
have hA : HasSum (fun n => A (n + 2)) (f y.1 - f y.2 - p 1 fun _ => y.1 - y.2) := by
- convert!
+ convert
(hasSum_nat_add_iff' 2).2
- ((hf.hasSum_sub ⟨ys.1, hy.1⟩).sub (hf.hasSum_sub ⟨ys.2, hy.2⟩)) using 1
+ ((hf.hasSum_sub ⟨ys.1, hy.1⟩).sub (hf.hasSum_sub ⟨ys.2, hy.2⟩))
rw [Finset.sum_range_succ, Finset.sum_range_one, hf.coeff_zero, hf.coeff_zero, sub_self,
zero_add, ← Subsingleton.pi_single_eq (0 : Fin 1) (y.1 - x), Pi.single,
← Subsingleton.pi_single_eq (0 : Fin 1) (y.2 - x), Pi.single, ← (p 1).map_update_sub,
diff --git a/Mathlib/Analysis/Analytic/Binomial.lean b/Mathlib/Analysis/Analytic/Binomial.lean
index f155a1e5126456..e86e83ba680519 100644
--- a/Mathlib/Analysis/Analytic/Binomial.lean
+++ b/Mathlib/Analysis/Analytic/Binomial.lean
@@ -171,7 +171,7 @@ theorem one_div_one_sub_cpow_hasFPowerSeriesOnBall_zero (a : ℂ) :
theorem one_div_one_sub_pow_hasFPowerSeriesOnBall_zero (a : ℕ) :
HasFPowerSeriesOnBall (fun x ↦ 1 / (1 - x) ^ (a + 1))
(.ofScalars ℂ (𝕜 := ℂ) fun n ↦ ↑(Nat.choose (a + n) a)) 0 1 := by
- convert! one_div_one_sub_cpow_hasFPowerSeriesOnBall_zero (a + 1) using 3 with z n
+ convert one_div_one_sub_cpow_hasFPowerSeriesOnBall_zero (a + 1) with z n
· norm_cast
· rw [eq_comm, add_right_comm, add_sub_cancel_right, ← Nat.cast_add,
Ring.choose_natCast, Nat.choose_symm_add]
@@ -184,7 +184,7 @@ theorem one_div_sub_pow_hasFPowerSeriesOnBall_zero (a : ℕ) {z : ℂ} (hz : z
have := this.compContinuousLinearMap
have H : 1 / ‖(z⁻¹ • 1 : ℂ →L[ℂ] ℂ)‖ₑ = ‖z‖ₑ := by simp [enorm_smul, enorm_inv, hz]
simp only [one_div, FunLike.coe_smul, H, Function.comp_def] at this
- convert! (this.const_smul (c := (z ^ (a + 1))⁻¹)).congr ?_ using 2
+ convert (this.const_smul (c := (z ^ (a + 1))⁻¹)).congr ?_
· ext n
simp only [FormalMultilinearSeries.smul_apply, ContinuousMultilinearMap.smul_apply,
FormalMultilinearSeries.compContinuousLinearMap_apply]
@@ -213,9 +213,9 @@ theorem one_div_one_sub_sq_hasFPowerSeriesOnBall_zero :
theorem hasFPowerSeriesOnBall_ofScalars_mul_add_zero (a b : ℂ) :
HasFPowerSeriesOnBall (fun x ↦ (b - a) / (1 - x) + a / (1 - x) ^ 2)
(.ofScalars ℂ fun n ↦ a * n + b) 0 1 := by
- convert!
+ convert
(one_div_one_sub_hasFPowerSeriesOnBall_zero.const_smul (c := b - a)).add
- (one_div_one_sub_sq_hasFPowerSeriesOnBall_zero.const_smul (c := a)) using 2
+ (one_div_one_sub_sq_hasFPowerSeriesOnBall_zero.const_smul (c := a))
· simp [div_eq_mul_inv]
· ext; simp; ring
@@ -262,7 +262,7 @@ theorem one_div_one_sub_rpow_hasFPowerSeriesOnBall_zero (a : ℝ) :
(.ofScalars ℝ fun n ↦ Ring.choose (a + n - 1) n) 0 1 := by
have := (Complex.one_div_one_sub_cpow_hasFPowerSeriesOnBall_zero a).restrictScalars (𝕜 := ℝ)
rw [← Complex.ofRealCLM.map_zero] at this
- convert! (Complex.reCLM.comp_hasFPowerSeriesOnBall this.compContinuousLinearMap).congr ?_ using 1
+ convert (Complex.reCLM.comp_hasFPowerSeriesOnBall this.compContinuousLinearMap).congr ?_
· ext n
simp only [ContinuousLinearMap.compFormalMultilinearSeries_apply,
ContinuousLinearMap.compContinuousMultilinearMap_coe, Function.comp_apply,
@@ -281,7 +281,7 @@ theorem one_div_sub_pow_hasFPowerSeriesOnBall_zero (a : ℕ) {r : ℝ} (hr : r
have := (Complex.one_div_sub_pow_hasFPowerSeriesOnBall_zero a (z := r)
(by simpa)).restrictScalars (𝕜 := ℝ)
rw [← Complex.ofRealCLM.map_zero] at this
- convert! (Complex.reCLM.comp_hasFPowerSeriesOnBall this.compContinuousLinearMap) using 2
+ convert (Complex.reCLM.comp_hasFPowerSeriesOnBall this.compContinuousLinearMap)
· simp [-Complex.inv_re, ← Complex.ofReal_pow, ← Complex.ofReal_inv, ← Complex.ofReal_sub]
· ext n
simp only [ContinuousLinearMap.compFormalMultilinearSeries_apply,
diff --git a/Mathlib/Analysis/Analytic/OfScalars.lean b/Mathlib/Analysis/Analytic/OfScalars.lean
index 47d7b0d043060d..eb4d78c2e000e8 100644
--- a/Mathlib/Analysis/Analytic/OfScalars.lean
+++ b/Mathlib/Analysis/Analytic/OfScalars.lean
@@ -240,7 +240,7 @@ theorem ofScalars_radius_eq_of_tendsto [NormOneClass E] {r : NNReal} (hr : r ≠
suffices Tendsto (fun n ↦ ‖c n.succ‖ / ‖c n‖) atTop (𝓝 r⁻¹) by
convert! ofScalars_radius_eq_inv_of_tendsto E c (inv_ne_zero hr) this
simp
- convert! hc.inv₀ (NNReal.coe_ne_zero.mpr hr) using 1
+ convert hc.inv₀ (NNReal.coe_ne_zero.mpr hr)
simp
/-- The ratio test stating that if `‖c n.succ‖ / ‖c n‖` tends to zero, the radius is unbounded.
diff --git a/Mathlib/Analysis/Asymptotics/Lemmas.lean b/Mathlib/Analysis/Asymptotics/Lemmas.lean
index b9597d4931a31a..fed7b0cf783739 100644
--- a/Mathlib/Analysis/Asymptotics/Lemmas.lean
+++ b/Mathlib/Analysis/Asymptotics/Lemmas.lean
@@ -443,7 +443,7 @@ theorem IsLittleO.of_tendsto_div_atTop (h : Tendsto (fun x ↦ g x / f x) l atTo
theorem IsLittleO.of_tendsto_div_atBot (h : Tendsto (fun x ↦ g x / f x) l atBot) : f =o[l] g := by
refine IsLittleO.of_neg_left (IsLittleO.of_tendsto_div_atTop ?_)
rw [← tendsto_neg_atBot_iff]
- convert! h using 2
+ convert h
simp [div_neg_eq_neg_div]
end div_tendsto_infty
diff --git a/Mathlib/Analysis/CStarAlgebra/Spectrum.lean b/Mathlib/Analysis/CStarAlgebra/Spectrum.lean
index df886834d172f9..d821cfa99d5166 100644
--- a/Mathlib/Analysis/CStarAlgebra/Spectrum.lean
+++ b/Mathlib/Analysis/CStarAlgebra/Spectrum.lean
@@ -211,7 +211,7 @@ lemma IsSelfAdjoint.isConnected_spectrum_compl {a : A} (ha : IsSelfAdjoint a) :
suffices IsConnected (((σ ℂ a)ᶜ ∩ {z | 0 ≤ z.im}) ∪ (σ ℂ a)ᶜ ∩ {z | z.im ≤ 0}) by
rw [← Set.inter_union_distrib_left, ← Set.setOf_or] at this
rw [← Set.inter_univ (σ ℂ a)ᶜ]
- convert! this using 2
+ convert this
exact Eq.symm <| Set.eq_univ_of_forall (fun z ↦ le_total 0 z.im)
refine IsConnected.union ?nonempty ?upper ?lower
case nonempty =>
diff --git a/Mathlib/Analysis/Calculus/ContDiff/Convolution.lean b/Mathlib/Analysis/Calculus/ContDiff/Convolution.lean
index cb9f5ef728ebfc..36568b8076f591 100644
--- a/Mathlib/Analysis/Calculus/ContDiff/Convolution.lean
+++ b/Mathlib/Analysis/Calculus/ContDiff/Convolution.lean
@@ -115,7 +115,7 @@ variable [IsAddLeftInvariant μ] [SFinite μ]
theorem _root_.HasCompactSupport.hasDerivAt_convolution_right (hf : LocallyIntegrable f₀ μ)
(hcg : HasCompactSupport g₀) (hg : ContDiff 𝕜 1 g₀) (x₀ : 𝕜) :
HasDerivAt (f₀ ⋆[L, μ] g₀) ((f₀ ⋆[L, μ] deriv g₀) x₀) x₀ := by
- convert! (hcg.hasFDerivAt_convolution_right L hf hg x₀).hasDerivAt using 1
+ convert (hcg.hasFDerivAt_convolution_right L hf hg x₀).hasDerivAt
rw [convolution_precompR_apply L hf (hcg.fderiv 𝕜) (hg.continuous_fderiv one_ne_zero)]
rfl
diff --git a/Mathlib/Analysis/Calculus/Deriv/Inv.lean b/Mathlib/Analysis/Calculus/Deriv/Inv.lean
index d2ef70ed083d9a..5222e9c6990361 100644
--- a/Mathlib/Analysis/Calculus/Deriv/Inv.lean
+++ b/Mathlib/Analysis/Calculus/Deriv/Inv.lean
@@ -143,7 +143,7 @@ variable {𝕜' : Type*} [NontriviallyNormedField 𝕜'] [NormedAlgebra 𝕜
theorem HasDerivWithinAt.fun_div (hc : HasDerivWithinAt c c' s x) (hd : HasDerivWithinAt d d' s x)
(hx : d x ≠ 0) :
HasDerivWithinAt (fun y => c y / d y) ((c' * d x - c x * d') / d x ^ 2) s x := by
- convert! hc.fun_mul ((hasDerivAt_inv hx).comp_hasDerivWithinAt x hd) using 1
+ convert hc.fun_mul ((hasDerivAt_inv hx).comp_hasDerivWithinAt x hd)
· simp only [div_eq_mul_inv, (· ∘ ·)]
· simp [field]
ring
@@ -155,7 +155,7 @@ theorem HasDerivWithinAt.div (hc : HasDerivWithinAt c c' s x) (hd : HasDerivWith
theorem HasStrictDerivAt.fun_div (hc : HasStrictDerivAt c c' x) (hd : HasStrictDerivAt d d' x)
(hx : d x ≠ 0) : HasStrictDerivAt (fun y => c y / d y) ((c' * d x - c x * d') / d x ^ 2) x := by
- convert! hc.fun_mul ((hasStrictDerivAt_inv hx).comp x hd) using 1
+ convert hc.fun_mul ((hasStrictDerivAt_inv hx).comp x hd)
· simp only [div_eq_mul_inv, (· ∘ ·)]
· simp [field]
ring
diff --git a/Mathlib/Analysis/Calculus/Deriv/ZPow.lean b/Mathlib/Analysis/Calculus/Deriv/ZPow.lean
index 4f3d64e754953d..e474eb729abbb5 100644
--- a/Mathlib/Analysis/Calculus/Deriv/ZPow.lean
+++ b/Mathlib/Analysis/Calculus/Deriv/ZPow.lean
@@ -42,7 +42,7 @@ theorem hasStrictDerivAt_zpow (m : ℤ) (x : 𝕜) (h : x ≠ 0 ∨ 0 ≤ m) :
have : ∀ m : ℤ, 0 < m → HasStrictDerivAt (· ^ m) ((m : 𝕜) * x ^ (m - 1)) x := fun m hm ↦ by
lift m to ℕ using hm.le
simp only [zpow_natCast, Int.cast_natCast]
- convert! hasStrictDerivAt_pow m x using 2
+ convert hasStrictDerivAt_pow m x
rw [← Int.ofNat_one, ← Int.ofNat_sub, zpow_natCast]
norm_cast at hm
rcases lt_trichotomy m 0 with (hm | hm | hm)
diff --git a/Mathlib/Analysis/Calculus/FDeriv/Symmetric.lean b/Mathlib/Analysis/Calculus/FDeriv/Symmetric.lean
index 581c3531bff0ca..f6490c818ee8c8 100644
--- a/Mathlib/Analysis/Calculus/FDeriv/Symmetric.lean
+++ b/Mathlib/Analysis/Calculus/FDeriv/Symmetric.lean
@@ -273,7 +273,7 @@ theorem Convex.taylor_approx_two_segment {v w : E} (hv : x + v ∈ interior s)
· apply_rules [HasDerivAt.hasDerivWithinAt, HasDerivAt.smul_const, hasDerivAt_mul_const]
· suffices H : HasDerivWithinAt (fun u => ((u * h) ^ 2 / 2) • f'' w w)
((((2 : ℕ) : ℝ) * (t * h) ^ (2 - 1) * (1 * h) / 2) • f'' w w) (Icc 0 1) t by
- convert! H using 2
+ convert H
ring
apply_rules [HasDerivAt.hasDerivWithinAt, HasDerivAt.smul_const, hasDerivAt_id',
HasDerivAt.pow, HasDerivAt.mul_const]
diff --git a/Mathlib/Analysis/Calculus/LogDeriv.lean b/Mathlib/Analysis/Calculus/LogDeriv.lean
index 90db509ede78e6..c836382083c822 100644
--- a/Mathlib/Analysis/Calculus/LogDeriv.lean
+++ b/Mathlib/Analysis/Calculus/LogDeriv.lean
@@ -150,6 +150,6 @@ theorem AnalyticAt.tendsto_mul_logDeriv_simple_zero [CompleteSpace 𝕜]
(𝓝[≠] x) (𝓝 1) := by
have h_slope := hasDerivAt_iff_tendsto_slope.mp hf.differentiableAt.hasDerivAt
rw [← div_self hf']
- convert! hf.deriv.continuousAt.tendsto.mono_left nhdsWithin_le_nhds |>.div h_slope hf' using 2
+ convert hf.deriv.continuousAt.tendsto.mono_left nhdsWithin_le_nhds |>.div h_slope hf'
simp [logDeriv, slope, hfx]
field
diff --git a/Mathlib/Analysis/Calculus/Taylor.lean b/Mathlib/Analysis/Calculus/Taylor.lean
index 586b700edafde2..0a6d490a2fc651 100644
--- a/Mathlib/Analysis/Calculus/Taylor.lean
+++ b/Mathlib/Analysis/Calculus/Taylor.lean
@@ -152,7 +152,7 @@ theorem hasDerivWithinAt_taylor_coeff_within {f : ℝ → E} {x y : ℝ} {k :
((k ! : ℝ)⁻¹ * (x - y) ^ k) • iteratedDerivWithin (k + 1) f s y) t y := by
replace hf :
HasDerivWithinAt (iteratedDerivWithin (k + 1) f s) (iteratedDerivWithin (k + 2) f s y) t y := by
- convert! (hf.mono_of_mem_nhdsWithin hs).hasDerivWithinAt using 1
+ convert (hf.mono_of_mem_nhdsWithin hs).hasDerivWithinAt
rw [iteratedDerivWithin_succ]
exact (derivWithin_of_mem_nhdsWithin hs ht hf).symm
have : HasDerivWithinAt (fun t => ((k + 1 : ℝ) * k !)⁻¹ * (x - t) ^ (k + 1))
@@ -284,7 +284,7 @@ theorem Real.taylor_tendsto {f : ℝ → ℝ} {x₀ : ℝ} {n : ℕ} {s : Set
(hs : Convex ℝ s) (hx₀s : x₀ ∈ s) (hf : ContDiffOn ℝ n f s) :
Filter.Tendsto (fun x ↦ (f x - taylorWithinEval f n s x₀ x) / (x - x₀) ^ n)
(𝓝[s] x₀) (𝓝 0) := by
- convert! _root_.taylor_tendsto hs hx₀s hf using 2 with x
+ convert _root_.taylor_tendsto hs hx₀s hf with x
simp [div_eq_inv_mul]
diff --git a/Mathlib/Analysis/Complex/CoveringMap.lean b/Mathlib/Analysis/Complex/CoveringMap.lean
index 478849a45717a8..95872f8372379f 100644
--- a/Mathlib/Analysis/Complex/CoveringMap.lean
+++ b/Mathlib/Analysis/Complex/CoveringMap.lean
@@ -88,7 +88,7 @@ theorem isCoveringMapOn_zpow (n : ℤ) (hn : (n : 𝕜) ≠ 0) :
IsCoveringMapOn (fun x : 𝕜 ↦ x ^ n) {0}ᶜ := by
have (x : 𝕜) : x ^ n = 0 ↔ x = 0 := zpow_eq_zero_iff (by aesop)
refine .of_isCoveringMap_restrictPreimage _ (by simp) ?_ ?_
- · convert! isClosed_singleton (x := (0 : 𝕜)).isOpen_compl using 1
+ · convert isClosed_singleton (x := (0 : 𝕜)).isOpen_compl
ext; simp [this]
· convert! (isCoveringMap_zpow n hn).comp_homeomorph (.setCongr _) using 1
ext; simpa using! (this _).not
@@ -121,7 +121,7 @@ theorem isQuotientCoveringMap_zpow (n : ℤ) (hn : (n : 𝕜) ≠ 0)
obtain ⟨n, rfl | rfl⟩ := n.eq_nat_or_neg
· exact isQuotientCoveringMap_npow n (by aesop) (by simpa using surj)
rw [show (zpowGroupHom (α := 𝕜ˣ) (-n)).ker = (powMonoidHom n).ker by ext; simp]
- convert! (isQuotientCoveringMap_npow n (by aesop) _).homeomorph_comp (.inv 𝕜ˣ) using 1
+ convert (isQuotientCoveringMap_npow n (by aesop) _).homeomorph_comp (.inv 𝕜ˣ)
· ext; simp
convert! inv_involutive.surjective.comp surj; simp
diff --git a/Mathlib/Analysis/Complex/Exponential.lean b/Mathlib/Analysis/Complex/Exponential.lean
index 798b4ef9df2d96..5d95d2378a9389 100644
--- a/Mathlib/Analysis/Complex/Exponential.lean
+++ b/Mathlib/Analysis/Complex/Exponential.lean
@@ -477,7 +477,7 @@ lemma norm_exp_sub_sum_le_exp_norm_sub_sum (x : ℂ) (n : ℕ) :
exact Real.sum_le_exp_of_nonneg (norm_nonneg _) _
lemma norm_exp_le_exp_norm (x : ℂ) : ‖exp x‖ ≤ Real.exp ‖x‖ := by
- convert! norm_exp_sub_sum_le_exp_norm_sub_sum x 0 using 1 <;> simp
+ convert norm_exp_sub_sum_le_exp_norm_sub_sum x 0 <;> simp
lemma norm_exp_sub_sum_le_norm_mul_exp (x : ℂ) (n : ℕ) :
‖exp x - ∑ m ∈ range n, x ^ m / m.factorial‖ ≤ ‖x‖ ^ n * Real.exp ‖x‖ := by
@@ -515,7 +515,7 @@ open Complex Finset
nonrec theorem exp_bound {x : ℝ} (hx : |x| ≤ 1) {n : ℕ} (hn : 0 < n) :
|exp x - ∑ m ∈ range n, x ^ m / m.factorial| ≤ |x| ^ n * (n.succ / (n.factorial * n)) := by
have hxc : ‖(x : ℂ)‖ ≤ 1 := mod_cast hx
- convert! exp_bound hxc hn using 2 <;>
+ convert exp_bound hxc hn <;>
norm_cast
theorem exp_bound' {x : ℝ} (h1 : 0 ≤ x) (h2 : x ≤ 1) {n : ℕ} (hn : 0 < n) :
diff --git a/Mathlib/Analysis/Complex/HasPrimitives.lean b/Mathlib/Analysis/Complex/HasPrimitives.lean
index ce8389f6b12e97..fbb3957cd971b3 100644
--- a/Mathlib/Analysis/Complex/HasPrimitives.lean
+++ b/Mathlib/Analysis/Complex/HasPrimitives.lean
@@ -70,12 +70,12 @@ private lemma mem_closedBall_aux (z_in_ball : z ∈ closedBall c r) (y_in_I : y
private lemma mem_ball_of_map_re_aux {a₁ a₂ b : ℝ} (ha₁ : a₁ + b * I ∈ ball c r)
(ha₂ : a₂ + b * I ∈ ball c r) : (fun (x : ℝ) ↦ x + b * I) '' [[a₁, a₂]] ⊆ ball c r := by
- convert! Convex.rectangle_subset (convex_ball c r) ha₁ ha₂ ?_ ?_ using 1 <;>
+ convert Convex.rectangle_subset (convex_ball c r) ha₁ ha₂ ?_ ?_ <;>
simp [horizontalSegment_eq a₁ a₂ b, ha₁, ha₂, Rectangle]
private lemma mem_ball_of_map_im_aux₁ {a b₁ b₂ : ℝ} (hb₁ : a + b₁ * I ∈ ball c r)
(hb₂ : a + b₂ * I ∈ ball c r) : (fun (y : ℝ) ↦ a + y * I) '' [[b₁, b₂]] ⊆ ball c r := by
- convert! Convex.rectangle_subset (convex_ball c r) hb₁ hb₂ ?_ ?_ using 1 <;>
+ convert Convex.rectangle_subset (convex_ball c r) hb₁ hb₂ ?_ ?_ <;>
simp [verticalSegment_eq a b₁ b₂, hb₁, hb₂, Rectangle]
private lemma mem_ball_of_map_im_aux₂ {w : ℂ} (hw : w ∈ ball z (r - dist z c)) :
diff --git a/Mathlib/Analysis/Complex/Poisson.lean b/Mathlib/Analysis/Complex/Poisson.lean
index 4a0d2429ef660a..b154d58cc976bf 100644
--- a/Mathlib/Analysis/Complex/Poisson.lean
+++ b/Mathlib/Analysis/Complex/Poisson.lean
@@ -85,7 +85,7 @@ private lemma re_herglotzRieszKernel_le_aux (φ θ r R : ℝ) (h₁ : 0 < r) (h
have h_subst :
(R ^ 2 - r ^ 2) / (R ^ 2 + r ^ 2 - 2 * R * r * Real.cos (θ - φ)) ≤ (R + r) / (R - r) := by
rw [div_le_div_iff₀] <;> nlinarith [mul_pos h₁ (sub_pos.mpr h₂)]
- convert! h_subst using 1
+ convert h_subst
rw [← div_eq_mul_inv, poissonKernel_eq_re_herglotzRieszKernel_aux]
suffices (R * R * normSq (cexp (θ * I)) + r * r * normSq (cexp (φ * I)) -
2 * (R * Real.cos θ * (r * Real.cos φ) + R * Real.sin θ * (r * Real.sin φ))) =
diff --git a/Mathlib/Analysis/Complex/TaylorSeries.lean b/Mathlib/Analysis/Complex/TaylorSeries.lean
index ab6844609a75e1..139c1c86c83be1 100644
--- a/Mathlib/Analysis/Complex/TaylorSeries.lean
+++ b/Mathlib/Analysis/Complex/TaylorSeries.lean
@@ -54,7 +54,7 @@ lemma hasSum_taylorSeries_on_ball :
have H := (hf.mono <| Metric.closedBall_subset_ball hr').hasFPowerSeriesOnBall hr'₀
|>.hasSum_iteratedFDeriv hz'
simp only [add_sub_cancel] at H
- convert! H using 4 with n
+ convert H with n
simpa only [iteratedDeriv_eq_iteratedFDeriv, smul_eq_mul, mul_one, Finset.prod_const,
Finset.card_fin]
using ((iteratedFDeriv ℂ n f c).map_smul_univ (fun _ ↦ z - c) (fun _ ↦ 1)).symm
diff --git a/Mathlib/Analysis/Complex/ValueDistribution/FirstMainTheorem.lean b/Mathlib/Analysis/Complex/ValueDistribution/FirstMainTheorem.lean
index a08119f5e7bbc6..a4a7141a25b97c 100644
--- a/Mathlib/Analysis/Complex/ValueDistribution/FirstMainTheorem.lean
+++ b/Mathlib/Analysis/Complex/ValueDistribution/FirstMainTheorem.lean
@@ -147,7 +147,7 @@ theorem abs_characteristic_sub_characteristic_shift_le {r : ℝ} (h : Meromorphi
using (posLog_norm_add_le (f θ - a₀) a₀)
· simp only [abs_of_nonpos (le_of_not_ge h), neg_sub, tsub_le_iff_right,
add_comm (log⁺ ‖a₀‖ + log 2), ← add_assoc]
- convert! posLog_norm_add_le (-f θ) (a₀) using 2
+ convert! posLog_norm_add_le (-f θ) a₀ using 2
· rw [← norm_neg]
abel_nf
· simp
diff --git a/Mathlib/Analysis/ConstantSpeed.lean b/Mathlib/Analysis/ConstantSpeed.lean
index b01b287321c026..2af9ce0d7bf53b 100644
--- a/Mathlib/Analysis/ConstantSpeed.lean
+++ b/Mathlib/Analysis/ConstantSpeed.lean
@@ -200,7 +200,7 @@ monotonically maps `s` onto `t`, then `φ` is just a translation (on `s`).
theorem unique_unit_speed {φ : ℝ → ℝ} (φm : MonotoneOn φ s) (hfφ : HasUnitSpeedOn (f ∘ φ) s)
(hf : HasUnitSpeedOn f (φ '' s)) ⦃x : ℝ⦄ (xs : x ∈ s) : EqOn φ (fun y => y - x + φ x) s := by
dsimp only [HasUnitSpeedOn] at hf hfφ
- convert! HasConstantSpeedOnWith.ratio one_ne_zero φm hfφ hf xs using 3
+ convert HasConstantSpeedOnWith.ratio one_ne_zero φm hfφ hf xs
simp
/-- If both `f` and `f ∘ φ` have unit speed (on `Icc 0 t` and `Icc 0 s` respectively)
@@ -211,7 +211,7 @@ theorem unique_unit_speed_on_Icc_zero {s t : ℝ} (hs : 0 ≤ s) (ht : 0 ≤ t)
(hfφ : HasUnitSpeedOn (f ∘ φ) (Icc 0 s)) (hf : HasUnitSpeedOn f (Icc 0 t)) :
EqOn φ id (Icc 0 s) := by
rw [← φst] at hf
- convert! unique_unit_speed φm hfφ hf ⟨le_rfl, hs⟩ using 1
+ convert unique_unit_speed φm hfφ hf ⟨le_rfl, hs⟩
have : φ 0 = 0 := by
have hm : 0 ∈ φ '' Icc 0 s := by simp only [φst, ht, mem_Icc, le_refl, and_self]
obtain ⟨x, xs, hx⟩ := hm
diff --git a/Mathlib/Analysis/Convex/Basic.lean b/Mathlib/Analysis/Convex/Basic.lean
index 9c17d20218a071..14ef4e4268b6b4 100644
--- a/Mathlib/Analysis/Convex/Basic.lean
+++ b/Mathlib/Analysis/Convex/Basic.lean
@@ -643,7 +643,7 @@ lemma convex_of_nonneg_surjective_algebraMap [FaithfulSMul R A] {s : Set M}
intro u hu v hv a b ha hb hab
obtain ⟨c, hc1, hc2⟩ := halg ha
obtain ⟨d, hd1, hd2⟩ := halg hb
- convert! hs hu hv hc1 hd1 _ using 2
+ convert hs hu hv hc1 hd1 _
· rw [← hc2, algebraMap_smul]
· rw [← hd2, algebraMap_smul]
rw [← hc2, ← hd2, ← algebraMap.coe_add] at hab
diff --git a/Mathlib/Analysis/Convex/Deriv.lean b/Mathlib/Analysis/Convex/Deriv.lean
index bcfcdc3f7a0c35..0e4fae608c2e01 100644
--- a/Mathlib/Analysis/Convex/Deriv.lean
+++ b/Mathlib/Analysis/Convex/Deriv.lean
@@ -238,7 +238,7 @@ lemma convexOn_of_hasDerivWithinAt2_nonneg {D : Set ℝ} (hD : Convex ℝ D) {f
· rw [differentiableOn_congr this]
exact fun x hx ↦ (hf'' _ hx).differentiableWithinAt
· rintro x hx
- convert! hf''₀ _ hx using 1
+ convert hf''₀ _ hx
dsimp
rw [deriv_eqOn isOpen_interior (fun y hy ↦ ?_) hx]
exact (hf'' _ hy).congr this <| by rw [this hy]
@@ -254,7 +254,7 @@ lemma concaveOn_of_hasDerivWithinAt2_nonpos {D : Set ℝ} (hD : Convex ℝ D) {f
· rw [differentiableOn_congr this]
exact fun x hx ↦ (hf'' _ hx).differentiableWithinAt
· rintro x hx
- convert! hf''₀ _ hx using 1
+ convert hf''₀ _ hx
dsimp
rw [deriv_eqOn isOpen_interior (fun y hy ↦ ?_) hx]
exact (hf'' _ hy).congr this <| by rw [this hy]
diff --git a/Mathlib/Analysis/Convex/PathConnected.lean b/Mathlib/Analysis/Convex/PathConnected.lean
index 2daa2c26a7bec5..d42bac5955b2e9 100644
--- a/Mathlib/Analysis/Convex/PathConnected.lean
+++ b/Mathlib/Analysis/Convex/PathConnected.lean
@@ -109,7 +109,7 @@ protected theorem IsTopologicalAddGroup.pathConnectedSpace : PathConnectedSpace
is path connected in `p` then the complement of `q` is path connected in `E`. -/
theorem isPathConnected_compl_of_isPathConnected_compl_zero {p q : Submodule ℝ E}
(hpq : IsCompl p q) (hpc : IsPathConnected ({0}ᶜ : Set p)) : IsPathConnected (qᶜ : Set E) := by
- convert! (hpc.image continuous_subtype_val).add q.isPathConnected using 1
+ convert (hpc.image continuous_subtype_val).add q.isPathConnected
trans Submodule.prodEquivOfIsCompl p q hpq '' ({0}ᶜ ×ˢ univ)
· rw [prod_univ, LinearEquiv.image_eq_preimage_symm]
ext
diff --git a/Mathlib/Analysis/Convex/Segment.lean b/Mathlib/Analysis/Convex/Segment.lean
index 47dd1c0d348ad6..59bbbd65c0192f 100644
--- a/Mathlib/Analysis/Convex/Segment.lean
+++ b/Mathlib/Analysis/Convex/Segment.lean
@@ -218,12 +218,12 @@ theorem openSegment_eq_image' (x y : E) :
theorem segment_eq_image_lineMap (x y : E) : [x -[𝕜] y] =
AffineMap.lineMap x y '' Icc (0 : 𝕜) 1 := by
- convert! segment_eq_image 𝕜 x y using 2
+ convert segment_eq_image 𝕜 x y
exact AffineMap.lineMap_apply_module _ _ _
theorem openSegment_eq_image_lineMap (x y : E) :
openSegment 𝕜 x y = AffineMap.lineMap x y '' Ioo (0 : 𝕜) 1 := by
- convert! openSegment_eq_image 𝕜 x y using 2
+ convert openSegment_eq_image 𝕜 x y
exact AffineMap.lineMap_apply_module _ _ _
theorem lineMap_mem_openSegment (a b : E) {t : 𝕜} (ht : t ∈ Ioo 0 1) :
diff --git a/Mathlib/Analysis/Distribution/Sobolev.lean b/Mathlib/Analysis/Distribution/Sobolev.lean
index f92508dc030efc..3a49f003320e3a 100644
--- a/Mathlib/Analysis/Distribution/Sobolev.lean
+++ b/Mathlib/Analysis/Distribution/Sobolev.lean
@@ -252,7 +252,7 @@ theorem MemSobolev.fourier_memL1 {s : ℝ} (hs : Module.finrank ℝ E < 2 * s) {
norm_cast
simp_rw [ofReal_norm] at h
simp_rw [← enorm_pow]
- convert! h using 4
+ convert h
rw [← Real.rpow_mul_natCast (by positivity)]
simp
apply ((integrable_rpow_neg_one_add_norm_sq hs).congr _).lintegral_lt_top
diff --git a/Mathlib/Analysis/Distribution/TemperateGrowth.lean b/Mathlib/Analysis/Distribution/TemperateGrowth.lean
index 94f84510e344d4..0232517372254d 100644
--- a/Mathlib/Analysis/Distribution/TemperateGrowth.lean
+++ b/Mathlib/Analysis/Distribution/TemperateGrowth.lean
@@ -208,7 +208,7 @@ theorem HasTemperateGrowth.add (hf : f.HasTemperateGrowth) (hg : g.HasTemperateG
@[to_fun (attr := fun_prop)]
theorem HasTemperateGrowth.sub (hf : f.HasTemperateGrowth) (hg : g.HasTemperateGrowth) :
(f - g).HasTemperateGrowth := by
- convert! hf.add hg.neg using 1
+ convert hf.add hg.neg
grind
@[fun_prop]
diff --git a/Mathlib/Analysis/Fourier/AddCircle.lean b/Mathlib/Analysis/Fourier/AddCircle.lean
index 7ce45abcc6e465..36d822059852c1 100644
--- a/Mathlib/Analysis/Fourier/AddCircle.lean
+++ b/Mathlib/Analysis/Fourier/AddCircle.lean
@@ -192,7 +192,7 @@ theorem fourier_add_half_inv_index {n : ℤ} (hn : n ≠ 0) (hT : 0 < T) (x : Ad
Metric.unitSphere.coe_mul]
have : (@toCircle T (n • (T / 2 / n) : ℝ) : ℂ) = -1 := by
rw [zsmul_eq_mul, toCircle, Function.Periodic.lift_coe, Circle.coe_exp]
- convert! Complex.exp_pi_mul_I using 3
+ convert Complex.exp_pi_mul_I
field_simp
rw [this]; simp
@@ -461,7 +461,7 @@ theorem hasSum_sq_fourierCoeffOn
haveI := Fact.mk (by linarith : 0 < b - a)
rw [← add_sub_cancel a b] at hL2
have h := hL2.memLp_liftIoc.haarAddCircle
- convert! hasSum_sq_fourierCoeff h.toLp using 1
+ convert hasSum_sq_fourierCoeff h.toLp
· simp [fourierCoeff_congr_ae h.coeFn_toLp, fourierCoeff_liftIoc_eq]
· nth_rw 2 [← add_sub_cancel a b]
rw [← AddCircle.integral_liftIoc_eq_intervalIntegral, ← Function.comp_def (f := (‖·‖ ^ 2))]
diff --git a/Mathlib/Analysis/FunctionalSpaces/SobolevInequality.lean b/Mathlib/Analysis/FunctionalSpaces/SobolevInequality.lean
index 5412b5761d2357..e01a773a122aec 100644
--- a/Mathlib/Analysis/FunctionalSpaces/SobolevInequality.lean
+++ b/Mathlib/Analysis/FunctionalSpaces/SobolevInequality.lean
@@ -284,7 +284,7 @@ theorem lintegral_prod_lintegral_pow_le [Fintype ι] [∀ i, SigmaFinite (μ i)]
have h3 : (#ι - 1 : ℝ) * ((1 : ℝ) / (#ι - 1 : ℝ)) ≤ 1 := by field_simp; rfl
have h4 : p = 1 + 1 / (↑#ι - 1) := by simp [field]; rw [mul_comm, hp.sub_one_mul_conj]
rw [h4]
- convert! lintegral_mul_prod_lintegral_pow_le μ h2 h3 hf using 2
+ convert lintegral_mul_prod_lintegral_pow_le μ h2 h3 hf
field_simp
simp
diff --git a/Mathlib/Analysis/InnerProductSpace/Basic.lean b/Mathlib/Analysis/InnerProductSpace/Basic.lean
index e89df93fb1c72a..6171f8318e2fb8 100644
--- a/Mathlib/Analysis/InnerProductSpace/Basic.lean
+++ b/Mathlib/Analysis/InnerProductSpace/Basic.lean
@@ -818,7 +818,7 @@ theorem real_inner_div_norm_mul_norm_eq_neg_one_iff (x y : F) :
the equality case for Cauchy-Schwarz. -/
theorem inner_eq_one_iff_of_norm_eq_one {x y : E} (hx : ‖x‖ = 1) (hy : ‖y‖ = 1) :
⟪x, y⟫ = 1 ↔ x = y := by
- convert! inner_eq_norm_mul_iff (𝕜 := 𝕜) (E := E) using 2 <;> simp [hx, hy]
+ convert inner_eq_norm_mul_iff (𝕜 := 𝕜) (E := E) <;> simp [hx, hy]
/-- If the inner product of two unit vectors is `-1`, then the two vectors are negations of each
other. -/
diff --git a/Mathlib/Analysis/InnerProductSpace/LinearPMap.lean b/Mathlib/Analysis/InnerProductSpace/LinearPMap.lean
index 9cf3e7608ebd4c..da112f39df47a6 100644
--- a/Mathlib/Analysis/InnerProductSpace/LinearPMap.lean
+++ b/Mathlib/Analysis/InnerProductSpace/LinearPMap.lean
@@ -166,7 +166,7 @@ theorem mem_adjoint_domain_of_exists (y : F) (h : ∃ w : E, ∀ x : T.domain,
obtain ⟨w, hw⟩ := h
rw [T.mem_adjoint_domain_iff]
have : Continuous ((innerSL 𝕜 w).comp T.domain.subtypeL) := by fun_prop
- convert! this using 1
+ convert this
exact funext fun x => (hw x).symm
theorem adjoint_apply_of_not_dense (hT : ¬Dense (T.domain : Set E)) (y : T†.domain) : T† y = 0 := by
diff --git a/Mathlib/Analysis/InnerProductSpace/NormPow.lean b/Mathlib/Analysis/InnerProductSpace/NormPow.lean
index 31e04699fc7e43..ac271a6994117a 100644
--- a/Mathlib/Analysis/InnerProductSpace/NormPow.lean
+++ b/Mathlib/Analysis/InnerProductSpace/NormPow.lean
@@ -58,7 +58,7 @@ theorem differentiable_norm_rpow {p : ℝ} (hp : 1 < p) :
theorem hasDerivAt_norm_rpow (x : ℝ) {p : ℝ} (hp : 1 < p) :
HasDerivAt (fun x : ℝ ↦ ‖x‖ ^ p) (p * ‖x‖ ^ (p - 2) * x) x := by
- convert! hasFDerivAt_norm_rpow x hp |>.hasDerivAt using 1; simp
+ convert hasFDerivAt_norm_rpow x hp |>.hasDerivAt; simp
theorem hasDerivAt_abs_rpow (x : ℝ) {p : ℝ} (hp : 1 < p) :
HasDerivAt (fun x : ℝ ↦ |x| ^ p) (p * |x| ^ (p - 2) * x) x := by
diff --git a/Mathlib/Analysis/InnerProductSpace/OfNorm.lean b/Mathlib/Analysis/InnerProductSpace/OfNorm.lean
index 49ffd3cc9cd697..aca1a98baa557c 100644
--- a/Mathlib/Analysis/InnerProductSpace/OfNorm.lean
+++ b/Mathlib/Analysis/InnerProductSpace/OfNorm.lean
@@ -118,7 +118,7 @@ theorem inner_.norm_sq (x : E) : ‖x‖ ^ 2 = re (inner_ 𝕜 x x) := by
simp only [inner_, normSq_apply, ofNat_re, ofNat_im, map_sub, map_add,
ofReal_re, ofReal_im, mul_re, inv_re, mul_im, I_re, inv_im]
have h₁ : ‖x - x‖ = 0 := by simp
- have h₂ : ‖x + x‖ = 2 • ‖x‖ := by convert! norm_nsmul 𝕜 2 x using 2; module
+ have h₂ : ‖x + x‖ = 2 • ‖x‖ := by convert norm_nsmul 𝕜 2 x; module
rw [h₁, h₂]
ring
@@ -132,10 +132,10 @@ theorem inner_.conj_symm (x y : E) : conj (inner_ 𝕜 y x) = inner_ 𝕜 x y :=
have hI' := I_mul_I_of_nonzero hI
have I_smul (v : E) : ‖(I : 𝕜) • v‖ = ‖v‖ := by rw [norm_smul, norm_I_of_ne_zero hI, one_mul]
have h₁ : ‖(I : 𝕜) • y - x‖ = ‖(I : 𝕜) • x + y‖ := by
- convert! I_smul ((I : 𝕜) • x + y) using 2
+ convert I_smul ((I : 𝕜) • x + y)
linear_combination (norm := module) -hI' • x
have h₂ : ‖(I : 𝕜) • y + x‖ = ‖(I : 𝕜) • x - y‖ := by
- convert! (I_smul ((I : 𝕜) • y + x)).symm using 2
+ convert (I_smul ((I : 𝕜) • y + x)).symm
linear_combination (norm := module) -hI' • y
rw [h₁, h₂]
ring
diff --git a/Mathlib/Analysis/InnerProductSpace/Orthogonal.lean b/Mathlib/Analysis/InnerProductSpace/Orthogonal.lean
index 9025cd0391da01..82411d48abf41a 100644
--- a/Mathlib/Analysis/InnerProductSpace/Orthogonal.lean
+++ b/Mathlib/Analysis/InnerProductSpace/Orthogonal.lean
@@ -372,7 +372,7 @@ theorem IsOrtho.map_iff (f : E ≃ₗᵢ[𝕜] F) {U V : Submodule 𝕜 E} :
@[simp]
theorem IsOrtho.comap_iff (f : E ≃ₗᵢ[𝕜] F) {U V : Submodule 𝕜 F} :
U.comap (f : E →ₗ[𝕜] F) ⟂ V.comap (f : E →ₗ[𝕜] F) ↔ U ⟂ V := by
- convert! IsOrtho.map_iff f.symm using 2 <;>
+ convert IsOrtho.map_iff f.symm <;>
exact Submodule.comap_equiv_eq_map_symm (f : E ≃ₗ[𝕜] F) _
end Submodule
diff --git a/Mathlib/Analysis/InnerProductSpace/Projection/Basic.lean b/Mathlib/Analysis/InnerProductSpace/Projection/Basic.lean
index deaa7bed797ae5..95e52bd3f93f08 100644
--- a/Mathlib/Analysis/InnerProductSpace/Projection/Basic.lean
+++ b/Mathlib/Analysis/InnerProductSpace/Projection/Basic.lean
@@ -456,7 +456,7 @@ theorem IsOrtho.starProjection_comp_starProjection {U V : Submodule 𝕜 E}
theorem orthogonalProjectionOnto_comp_subtypeL_eq_zero_iff {U V : Submodule 𝕜 E}
[U.HasOrthogonalProjection] : U.orthogonalProjectionOnto ∘L V.subtypeL = 0 ↔ U ⟂ V := by
refine ⟨fun h u hu v hv ↦ ?_, Submodule.IsOrtho.orthogonalProjectionOnto_comp_subtypeL⟩
- convert! starProjection_inner_eq_zero v u hu using 2
+ convert starProjection_inner_eq_zero v u hu
have : U.orthogonalProjectionOnto v = 0 := DFunLike.congr_fun h (⟨_, hv⟩ : V)
rw [starProjection_apply, this, Submodule.coe_zero, sub_zero]
diff --git a/Mathlib/Analysis/InnerProductSpace/Projection/Reflection.lean b/Mathlib/Analysis/InnerProductSpace/Projection/Reflection.lean
index 28d00c7bd12d5f..84a98a95f9bd85 100644
--- a/Mathlib/Analysis/InnerProductSpace/Projection/Reflection.lean
+++ b/Mathlib/Analysis/InnerProductSpace/Projection/Reflection.lean
@@ -51,7 +51,7 @@ def reflection : E ≃ₗᵢ[𝕜] E :=
let w : K := K.orthogonalProjectionOnto x
let v := x - w
have : ⟪v, w⟫ = 0 := starProjection_inner_eq_zero x w w.2
- convert! norm_sub_eq_norm_add this using 2
+ convert norm_sub_eq_norm_add this
· dsimp [reflectionLinearEquiv, v, w]
abel
· simp only [v, add_sub_cancel] }
diff --git a/Mathlib/Analysis/InnerProductSpace/Projection/Submodule.lean b/Mathlib/Analysis/InnerProductSpace/Projection/Submodule.lean
index 8331a9fb57497c..f3ddc25cb6926e 100644
--- a/Mathlib/Analysis/InnerProductSpace/Projection/Submodule.lean
+++ b/Mathlib/Analysis/InnerProductSpace/Projection/Submodule.lean
@@ -47,7 +47,7 @@ theorem sup_orthogonal_inf_of_hasOrthogonalProjection {K₁ K₂ : Submodule
variable {K} in
/-- If `K` admits an orthogonal projection, then `K` and `Kᗮ` span the whole space. -/
theorem sup_orthogonal_of_hasOrthogonalProjection [K.HasOrthogonalProjection] : K ⊔ Kᗮ = ⊤ := by
- convert! Submodule.sup_orthogonal_inf_of_hasOrthogonalProjection (le_top : K ≤ ⊤) using 2
+ convert Submodule.sup_orthogonal_inf_of_hasOrthogonalProjection (le_top : K ≤ ⊤)
simp
/-- If `K` admits an orthogonal projection, then the orthogonal complement of its orthogonal
@@ -87,7 +87,7 @@ of all elements equal to zero. Then `Kᗮ = ⊥`, `Kᗮᗮ = ⊤`. -/
theorem orthogonal_orthogonal_eq_closure [CompleteSpace E] :
Kᗮᗮ = K.topologicalClosure := by
refine le_antisymm ?_ ?_
- · convert! Submodule.orthogonal_orthogonal_monotone K.le_topologicalClosure using 1
+ · convert Submodule.orthogonal_orthogonal_monotone K.le_topologicalClosure
rw [K.topologicalClosure.orthogonal_orthogonal]
· exact K.topologicalClosure_minimal K.le_orthogonal_orthogonal Kᗮ.isClosed_orthogonal
diff --git a/Mathlib/Analysis/MeanInequalities.lean b/Mathlib/Analysis/MeanInequalities.lean
index ac11e444efd9db..b6aba130f375d6 100644
--- a/Mathlib/Analysis/MeanInequalities.lean
+++ b/Mathlib/Analysis/MeanInequalities.lean
@@ -153,7 +153,7 @@ theorem geom_mean_le_arith_mean_weighted (w z : ι → ℝ) (hw : ∀ i ∈ s, 0
theorem geom_mean_le_arith_mean {ι : Type*} (s : Finset ι) (w : ι → ℝ) (z : ι → ℝ)
(hw : ∀ i ∈ s, 0 ≤ w i) (hw' : 0 < ∑ i ∈ s, w i) (hz : ∀ i ∈ s, 0 ≤ z i) :
(∏ i ∈ s, z i ^ w i) ^ (∑ i ∈ s, w i)⁻¹ ≤ (∑ i ∈ s, w i * z i) / (∑ i ∈ s, w i) := by
- convert! geom_mean_le_arith_mean_weighted s (fun i => (w i) / ∑ i ∈ s, w i) z ?_ ?_ hz using 2
+ convert geom_mean_le_arith_mean_weighted s (fun i => (w i) / ∑ i ∈ s, w i) z ?_ ?_ hz
· rw [← finsetProd_rpow _ _ (fun i hi => rpow_nonneg (hz _ hi) _) _]
refine Finset.prod_congr rfl (fun _ ih => ?_)
rw [div_eq_mul_inv, rpow_mul (hz _ ih)]
@@ -649,9 +649,9 @@ product of their `L^p` and `L^q` norms when `p`, `q`, and `r` form a `Real.Holde
theorem Lr_le_Lp_mul_Lq (f g : ι → ℝ≥0) {p q r : ℝ} (hpqr : p.HolderTriple q r) :
(∑ i ∈ s, (f i * g i) ^ r) ^ (1 / r) ≤
(∑ i ∈ s, f i ^ p) ^ (1 / p) * (∑ i ∈ s, g i ^ q) ^ (1 / q) := by
- convert!
+ convert
rpow_le_rpow_iff (inv_eq_one_div r ▸ inv_pos.mpr hpqr.pos' : 0 < 1 / r) |>.mpr <|
- Lr_rpow_le_Lp_mul_Lq s f g hpqr using 1
+ Lr_rpow_le_Lp_mul_Lq s f g hpqr
have hr := hpqr.pos'.ne'
simp only [← rpow_mul, mul_rpow]
field_simp
@@ -924,10 +924,9 @@ by (the `r`-power of) the product of their `L^p` and `L^q` norms, when `p`, `q`,
theorem Lr_rpow_le_Lp_mul_Lq_of_nonneg {ι : Type*} (s : Finset ι) {f g : ι → ℝ} {p q r : ℝ}
(hpqr : p.HolderTriple q r) (hf : ∀ i ∈ s, 0 ≤ f i) (hg : ∀ i ∈ s, 0 ≤ g i) :
∑ i ∈ s, (f i * g i) ^ r ≤ (∑ i ∈ s, f i ^ p) ^ (r / p) * (∑ i ∈ s, g i ^ q) ^ (r / q) := by
- convert! Lr_rpow_le_Lp_mul_Lq s f g hpqr using 3 with i hi
+ convert Lr_rpow_le_Lp_mul_Lq s f g hpqr with i hi
· rw [abs_of_nonneg (mul_nonneg (hf i hi) (hg i hi))]
all_goals
- congr! with i hi
exact Eq.symm (abs_of_nonneg (by grind))
/-- **Weighted Hölder inequality**. -/
@@ -1000,10 +999,10 @@ theorem Lr_le_Lp_mul_Lq_tsum_of_nonneg (hpqr : p.HolderTriple q r) (hf : ∀ i,
have hf' : 0 ≤ ∑' i, f i ^ p := tsum_nonneg fun i ↦ rpow_nonneg (hf i) p
have hg' : 0 ≤ ∑' i, g i ^ q := tsum_nonneg fun i ↦ rpow_nonneg (hg i) q
have hr := hpqr.pos'
- convert!
+ convert
rpow_le_rpow_iff (tsum_nonneg fun i ↦ by positivity [hf i, hg i]) (by positivity)
(inv_eq_one_div r ▸ inv_pos.mpr hr) |>.mpr <|
- Lr_rpow_le_Lp_mul_Lq_tsum_of_nonneg hpqr hf hg hf_sum hg_sum using 1
+ Lr_rpow_le_Lp_mul_Lq_tsum_of_nonneg hpqr hf hg hf_sum hg_sum
rw [mul_rpow (rpow_nonneg hf' _) (rpow_nonneg hg' _), ← Real.rpow_mul hg', ← Real.rpow_mul hf']
field_simp
diff --git a/Mathlib/Analysis/MeanInequalitiesPow.lean b/Mathlib/Analysis/MeanInequalitiesPow.lean
index 9fba152359c694..d157730b07294d 100644
--- a/Mathlib/Analysis/MeanInequalitiesPow.lean
+++ b/Mathlib/Analysis/MeanInequalitiesPow.lean
@@ -366,7 +366,7 @@ theorem rpow_add_le_mul_rpow_add_rpow'' (z₁ z₂ : ℝ≥0∞) {p : ℝ≥0∞
LpAddConst p * (z₁ ^ p.toReal⁻¹ + z₂ ^ p.toReal⁻¹) := by
by_cases p_zero : p = 0
· simp [p_zero, LpAddConst_zero]
- convert! rpow_add_le_mul_rpow_add_rpow' z₁ z₂ (p := p.toReal⁻¹) (by positivity) using 1
+ convert rpow_add_le_mul_rpow_add_rpow' z₁ z₂ (p := p.toReal⁻¹) (by positivity)
rw [← ENNReal.toReal_inv, ENNReal.ofReal_toReal (by simpa), inv_inv]
end ENNReal
diff --git a/Mathlib/Analysis/Meromorphic/Basic.lean b/Mathlib/Analysis/Meromorphic/Basic.lean
index 1d4d2a28f600dd..76db272de7420a 100644
--- a/Mathlib/Analysis/Meromorphic/Basic.lean
+++ b/Mathlib/Analysis/Meromorphic/Basic.lean
@@ -90,7 +90,7 @@ lemma smul {f : 𝕜 → 𝕜} {g : 𝕜 → E} (hf : MeromorphicAt f x) (hg : M
rcases hf with ⟨m, hf⟩
rcases hg with ⟨n, hg⟩
refine ⟨m + n, ?_⟩
- convert! hf.smul hg using 2 with z
+ convert hf.smul hg with z
simp
module
@@ -100,7 +100,7 @@ lemma mul {f g : 𝕜 → 𝕜'} (hf : MeromorphicAt f x) (hg : MeromorphicAt g
rcases hf with ⟨m, hf⟩
rcases hg with ⟨n, hg⟩
refine ⟨m + n, ?_⟩
- convert! hf.mul hg using 2 with z
+ convert hf.mul hg with z
simp
module
@@ -164,7 +164,7 @@ theorem finsum (hF : ∀ i, MeromorphicAt (F i) x) :
@[to_fun (attr := fun_prop)]
lemma neg {f : 𝕜 → E} (hf : MeromorphicAt f x) : MeromorphicAt (-f) x := by
- convert! (MeromorphicAt.const (-1 : 𝕜) x).smul hf using 1
+ convert (MeromorphicAt.const (-1 : 𝕜) x).smul hf
ext1 z
simp only [Pi.neg_apply, Pi.smul_apply', neg_smul, one_smul]
@@ -176,7 +176,7 @@ lemma neg_iff {f : 𝕜 → E} :
@[to_fun (attr := fun_prop)]
lemma sub {f g : 𝕜 → E} (hf : MeromorphicAt f x) (hg : MeromorphicAt g x) :
MeromorphicAt (f - g) x := by
- convert! hf.add hg.neg using 1
+ convert hf.add hg.neg
ext1 z
simp_rw [Pi.sub_apply, Pi.add_apply, Pi.neg_apply, sub_eq_add_neg]
diff --git a/Mathlib/Analysis/Normed/Algebra/Spectrum.lean b/Mathlib/Analysis/Normed/Algebra/Spectrum.lean
index dc1eb5d87b7a76..eb2a4e773f4a14 100644
--- a/Mathlib/Analysis/Normed/Algebra/Spectrum.lean
+++ b/Mathlib/Analysis/Normed/Algebra/Spectrum.lean
@@ -254,7 +254,7 @@ theorem spectralRadius_le_pow_nnnorm_pow_one_div (a : A) (n : ℕ) :
ENNReal.coe_mul] using coe_mono (Real.toNNReal_mono (norm_le_norm_mul_of_mem pow_mem))
-- take (n + 1)ᵗʰ roots and clean up the left-hand side
have hn : 0 < ((n + 1 : ℕ) : ℝ) := mod_cast Nat.succ_pos'
- convert! monotone_rpow_of_nonneg (one_div_pos.mpr hn).le nnnorm_pow_le using 1
+ convert monotone_rpow_of_nonneg (one_div_pos.mpr hn).le nnnorm_pow_le
all_goals dsimp
· rw [one_div, pow_rpow_inv_natCast]
positivity
diff --git a/Mathlib/Analysis/Normed/Group/Basic.lean b/Mathlib/Analysis/Normed/Group/Basic.lean
index e9a4d77951abf4..0409bfc27bfc7b 100644
--- a/Mathlib/Analysis/Normed/Group/Basic.lean
+++ b/Mathlib/Analysis/Normed/Group/Basic.lean
@@ -364,7 +364,7 @@ alias NormedAddCommGroup.nhds_zero_basis_norm_lt := NormedAddGroup.nhds_zero_bas
@[to_additive]
theorem NormedGroup.uniformity_basis_dist :
(𝓤 E).HasBasis (fun ε : ℝ => 0 < ε) fun ε => { p : E × E | ‖p.fst⁻¹ * p.snd‖ < ε } := by
- convert! Metric.uniformity_basis_dist (α := E) using 1
+ convert Metric.uniformity_basis_dist (α := E)
simp [dist_eq_norm_inv_mul]
open Finset
diff --git a/Mathlib/Analysis/SumOverResidueClass.lean b/Mathlib/Analysis/SumOverResidueClass.lean
index b29da2f5d98ff7..bdc7e0dec25275 100644
--- a/Mathlib/Analysis/SumOverResidueClass.lean
+++ b/Mathlib/Analysis/SumOverResidueClass.lean
@@ -44,7 +44,7 @@ lemma summable_indicator_mod_iff_summable {R : Type*} [AddCommGroup R] [Topologi
intro n hn
contrapose! hn
exact (Nat.range_mul_add m k).symm ▸ mem_of_indicator_ne_zero hn
- convert! (Function.Injective.summable_iff hg hg').symm using 3
+ convert (Function.Injective.summable_iff hg hg').symm
simp only [Function.comp_apply, mem_setOf_eq, Nat.cast_add, Nat.cast_mul, CharP.cast_eq_zero,
zero_mul, zero_add, le_add_iff_nonneg_left, zero_le, and_self, indicator_of_mem, g]
From 7ab1c47ce634c21b7321b7ee660f0645f75bca9b Mon Sep 17 00:00:00 2001
From: Laurance <60111599+LLaurance@users.noreply.github.com>
Date: Thu, 18 Jun 2026 08:17:39 +0000
Subject: [PATCH 0132/1300] chore(Probability): remove TODO on characteristic
functions (#40595)
mathlib4 now has Fourier transforms and the characteristic function is defined at [`MeasureTheory.charFun`](https://leanprover-community.github.io/mathlib4_docs/Mathlib/MeasureTheory/Measure/CharacteristicFunction/Basic.html#MeasureTheory.charFun).
---
Mathlib/Probability/Density.lean | 6 ------
1 file changed, 6 deletions(-)
diff --git a/Mathlib/Probability/Density.lean b/Mathlib/Probability/Density.lean
index d836da178c64e9..3b8db490b50342 100644
--- a/Mathlib/Probability/Density.lean
+++ b/Mathlib/Probability/Density.lean
@@ -43,12 +43,6 @@ random variables with this distribution.
* `MeasureTheory.pdf.IsUniform.integral_eq` : If `X` follows the uniform distribution with
its pdf having support `s`, then `X` has expectation `(λ s)⁻¹ * ∫ x in s, x dx` where `λ`
is the Lebesgue measure.
-
-## TODO
-
-Ultimately, we would also like to define characteristic functions to describe distributions as
-it exists for all random variables. However, to define this, we will need Fourier transforms
-which we currently do not have.
-/
@[expose] public section
From 7c0bf438871951abe1800ee614568a9bd8c5dd59 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=ABl=20Riou?=
<37772949+joelriou@users.noreply.github.com>
Date: Thu, 18 Jun 2026 10:07:08 +0000
Subject: [PATCH 0133/1300] feat(LinearAlgebra): generators of pi tensor
products (#26464)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
In this PR, we show that the `R`-module `⨂[R] i, M i` is finitely generated if the index type is finite and all `M i` are finitely generated. This follows from a more precise result about generators of `⨂[R] i, M i`.
Co-authored-by: Eric Wieser
---
Mathlib.lean | 5 +-
.../PiTensorProduct/ProjectiveSeminorm.lean | 2 +-
Mathlib/Data/Set/Card.lean | 9 ++
Mathlib/Data/SubtypeNeLift.lean | 45 ++++++
.../Basic.lean} | 0
.../PiTensorProduct/DFinsupp.lean | 2 +-
.../PiTensorProduct/DirectSum.lean | 2 +-
.../LinearAlgebra/PiTensorProduct/Finite.lean | 30 ++++
.../PiTensorProduct/Generators.lean | 153 ++++++++++++++++++
Mathlib/LinearAlgebra/TensorPower/Basic.lean | 2 +-
.../LinearAlgebra/TensorPower/Symmetric.lean | 2 +-
Mathlib/Logic/Equiv/Option.lean | 4 +
Mathlib/RingTheory/PiTensorProduct.lean | 2 +-
13 files changed, 251 insertions(+), 7 deletions(-)
create mode 100644 Mathlib/Data/SubtypeNeLift.lean
rename Mathlib/LinearAlgebra/{PiTensorProduct.lean => PiTensorProduct/Basic.lean} (100%)
create mode 100644 Mathlib/LinearAlgebra/PiTensorProduct/Finite.lean
create mode 100644 Mathlib/LinearAlgebra/PiTensorProduct/Generators.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index d03479050e91f0..3093c4df4064be 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -4387,6 +4387,7 @@ public import Mathlib.Data.String.Basic
public import Mathlib.Data.String.Defs
public import Mathlib.Data.String.Lemmas
public import Mathlib.Data.Subtype
+public import Mathlib.Data.SubtypeNeLift
public import Mathlib.Data.Sum.Basic
public import Mathlib.Data.Sum.Interval
public import Mathlib.Data.Sum.Lattice
@@ -5146,12 +5147,14 @@ public import Mathlib.LinearAlgebra.PID
public import Mathlib.LinearAlgebra.PerfectPairing.Basic
public import Mathlib.LinearAlgebra.PerfectPairing.Restrict
public import Mathlib.LinearAlgebra.Pi
-public import Mathlib.LinearAlgebra.PiTensorProduct
+public import Mathlib.LinearAlgebra.PiTensorProduct.Basic
public import Mathlib.LinearAlgebra.PiTensorProduct.Basis
public import Mathlib.LinearAlgebra.PiTensorProduct.DFinsupp
public import Mathlib.LinearAlgebra.PiTensorProduct.DirectSum
public import Mathlib.LinearAlgebra.PiTensorProduct.Dual
+public import Mathlib.LinearAlgebra.PiTensorProduct.Finite
public import Mathlib.LinearAlgebra.PiTensorProduct.Finsupp
+public import Mathlib.LinearAlgebra.PiTensorProduct.Generators
public import Mathlib.LinearAlgebra.Prod
public import Mathlib.LinearAlgebra.Projection
public import Mathlib.LinearAlgebra.Projectivization.Action
diff --git a/Mathlib/Analysis/Normed/Module/PiTensorProduct/ProjectiveSeminorm.lean b/Mathlib/Analysis/Normed/Module/PiTensorProduct/ProjectiveSeminorm.lean
index b2319ec8628f80..4a419478bd0338 100644
--- a/Mathlib/Analysis/Normed/Module/PiTensorProduct/ProjectiveSeminorm.lean
+++ b/Mathlib/Analysis/Normed/Module/PiTensorProduct/ProjectiveSeminorm.lean
@@ -6,7 +6,7 @@ Authors: Sophie Morel
module
public import Mathlib.Analysis.Normed.Module.Multilinear.Basic
-public import Mathlib.LinearAlgebra.PiTensorProduct
+public import Mathlib.LinearAlgebra.PiTensorProduct.Basic
/-!
# Projective seminorm on the tensor of a finite family of normed spaces.
diff --git a/Mathlib/Data/Set/Card.lean b/Mathlib/Data/Set/Card.lean
index 4ed3717591a453..5e66b76ade76d6 100644
--- a/Mathlib/Data/Set/Card.lean
+++ b/Mathlib/Data/Set/Card.lean
@@ -1162,10 +1162,19 @@ theorem ncard_add_ncard_compl (s : Set α) (hs : s.Finite := by toFinite_tac)
(hsc : sᶜ.Finite := by toFinite_tac) : s.ncard + sᶜ.ncard = Nat.card α := by
rw [← ncard_univ, ← ncard_union_eq (@disjoint_compl_right _ _ s) hs hsc, union_compl_self]
+theorem ncard_compl_add_ncard (s : Set α) (hs : s.Finite := by toFinite_tac)
+ (hsc : sᶜ.Finite := by toFinite_tac) : sᶜ.ncard + s.ncard = Nat.card α := by
+ rw [add_comm, ncard_add_ncard_compl s hs hsc]
+
theorem ncard_compl (s : Set α) (hs : s.Finite := by toFinite_tac)
(hsc : sᶜ.Finite := by toFinite_tac) : sᶜ.ncard = Nat.card α - s.ncard := by
rw [← ncard_add_ncard_compl s hs hsc, Nat.add_sub_cancel_left]
+theorem ncard_compl_of_ncard_eq_add [Finite α] (s : Set α) {n : ℕ}
+ (h : Nat.card α = n + s.ncard) :
+ sᶜ.ncard = n := by
+ rwa [← ncard_compl_add_ncard s, Nat.add_right_cancel_iff] at h
+
theorem eq_univ_iff_ncard [Finite α] (s : Set α) :
s = univ ↔ ncard s = Nat.card α := by
rw [← compl_empty_iff, ← ncard_eq_zero, ← ncard_add_ncard_compl s, left_eq_add]
diff --git a/Mathlib/Data/SubtypeNeLift.lean b/Mathlib/Data/SubtypeNeLift.lean
new file mode 100644
index 00000000000000..681cef12384c1b
--- /dev/null
+++ b/Mathlib/Data/SubtypeNeLift.lean
@@ -0,0 +1,45 @@
+/-
+Copyright (c) 2025 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.Logic.Equiv.Option
+
+/-!
+# Extending a function from the complement of a singleton
+
+In this file, we define `Function.subtypeNeLift` which allows to
+extend a (dependent) function defined on the complement of a singleton.
+
+-/
+
+@[expose] public section
+
+namespace Function
+
+variable {ι : Type*} [DecidableEq ι] {M : ι → Type*} (i₀ : ι)
+ (f : ∀ (j : { i // i ≠ i₀ }), M j) (x : M i₀)
+
+/-- Given `i₀ : ι` and `x : M i₀`, this is the (dependent) map `(i : ι) → M i`
+whose value at `i₀` is `x` and which extends a given map on the complement of `{i₀}`. -/
+def subtypeNeLift (i : ι) : M i :=
+ if h : i = i₀ then by rw [h]; exact x else f ⟨i, h⟩
+
+@[simp]
+lemma subtypeNeLift_self : subtypeNeLift i₀ f x i₀ = x := dif_pos rfl
+
+lemma subtypeNeLift_of_neq (i : ι) (h : i ≠ i₀) :
+ subtypeNeLift i₀ f x i = f ⟨i, h⟩ := dif_neg h
+
+@[simp]
+lemma subtypeNeLift_restriction (φ : ∀ i, M i) (i₀ : ι) :
+ subtypeNeLift i₀ (fun i ↦ φ i) (φ i₀) = φ := by
+ ext i
+ by_cases h : i = i₀
+ · subst h
+ simp
+ · rw [subtypeNeLift_of_neq _ _ _ _ h]
+
+end Function
diff --git a/Mathlib/LinearAlgebra/PiTensorProduct.lean b/Mathlib/LinearAlgebra/PiTensorProduct/Basic.lean
similarity index 100%
rename from Mathlib/LinearAlgebra/PiTensorProduct.lean
rename to Mathlib/LinearAlgebra/PiTensorProduct/Basic.lean
diff --git a/Mathlib/LinearAlgebra/PiTensorProduct/DFinsupp.lean b/Mathlib/LinearAlgebra/PiTensorProduct/DFinsupp.lean
index e568c71cdfac29..88c01be9919241 100644
--- a/Mathlib/LinearAlgebra/PiTensorProduct/DFinsupp.lean
+++ b/Mathlib/LinearAlgebra/PiTensorProduct/DFinsupp.lean
@@ -5,7 +5,7 @@ Authors: Sophie Morel, Eric Wieser
-/
module
-public import Mathlib.LinearAlgebra.PiTensorProduct
+public import Mathlib.LinearAlgebra.PiTensorProduct.Basic
public import Mathlib.LinearAlgebra.DFinsupp
public import Mathlib.LinearAlgebra.Multilinear.DFinsupp
diff --git a/Mathlib/LinearAlgebra/PiTensorProduct/DirectSum.lean b/Mathlib/LinearAlgebra/PiTensorProduct/DirectSum.lean
index 7741c57f651ac2..6b41366b14043c 100644
--- a/Mathlib/LinearAlgebra/PiTensorProduct/DirectSum.lean
+++ b/Mathlib/LinearAlgebra/PiTensorProduct/DirectSum.lean
@@ -5,7 +5,7 @@ Authors: Sophie Morel, Eric Wieser
-/
module
-public import Mathlib.LinearAlgebra.PiTensorProduct
+public import Mathlib.LinearAlgebra.PiTensorProduct.Basic
public import Mathlib.LinearAlgebra.PiTensorProduct.DFinsupp
public import Mathlib.Algebra.DirectSum.Module
diff --git a/Mathlib/LinearAlgebra/PiTensorProduct/Finite.lean b/Mathlib/LinearAlgebra/PiTensorProduct/Finite.lean
new file mode 100644
index 00000000000000..ede782165307c3
--- /dev/null
+++ b/Mathlib/LinearAlgebra/PiTensorProduct/Finite.lean
@@ -0,0 +1,30 @@
+/-
+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.RingTheory.Finiteness.Basic
+public import Mathlib.LinearAlgebra.PiTensorProduct.Generators
+
+/-!
+# A multiple tensor product of finitely generated modules is finitely generated
+
+-/
+
+public section
+
+open TensorProduct
+
+namespace PiTensorProduct
+
+instance finite {R : Type*} [CommRing R] {ι : Type*} [Finite ι]
+ {M : ι → Type*} [∀ i, AddCommGroup (M i)] [∀ i, Module R (M i)]
+ [∀ i, Module.Finite R (M i)] :
+ Module.Finite R (⨂[R] i, M i) := by
+ choose n γ hg using fun i => Module.Finite.exists_fin (R := R) (M := M i)
+ rw [Module.finite_def, ← submodule_span_eq_top hg]
+ exact Submodule.fg_span (Set.finite_range _)
+
+end PiTensorProduct
diff --git a/Mathlib/LinearAlgebra/PiTensorProduct/Generators.lean b/Mathlib/LinearAlgebra/PiTensorProduct/Generators.lean
new file mode 100644
index 00000000000000..fb60ceae5a3b65
--- /dev/null
+++ b/Mathlib/LinearAlgebra/PiTensorProduct/Generators.lean
@@ -0,0 +1,153 @@
+/-
+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.Data.SubtypeNeLift
+public import Mathlib.Data.Set.Card
+public import Mathlib.LinearAlgebra.PiTensorProduct.Basic
+public import Mathlib.LinearAlgebra.Quotient.Basic
+public import Mathlib.LinearAlgebra.TensorProduct.Map
+public import Mathlib.SetTheory.Cardinal.Finite
+
+/-!
+# Generators of multiple tensor products
+
+Given a finite family of `R`-modules `M i`, if we have, for each `i`,
+a family of generators of the module `M i`, then the tensor products
+of these elements generate `⨂[R] i, M i`.
+
+In `LinearAlgebra.PiTensorProduct.Finite`, we deduce that if the modules `M i`
+are finitely generated, then so is `⨂[R] i, M i`.
+
+-/
+
+@[expose] public section
+
+open TensorProduct
+
+namespace PiTensorProduct
+
+variable (R : Type*)
+
+section equivPiTensorComplSingletonTensor
+
+variable {ι : Type*} [DecidableEq ι] (M : ι → Type*)
+ [CommSemiring R] [∀ i, AddCommMonoid (M i)] [∀ i, Module R (M i)]
+
+/-- The linear equivalence between `⨂[R] i, M i` and the tensor product of
+the pi tensor product indexed by the complement of `{i₀}` and `M i₀`. -/
+noncomputable def equivPiTensorComplSingletonTensor (i₀ : ι) :
+ (⨂[R] i, M i) ≃ₗ[R] ((⨂[R] (i : ({i₀}ᶜ : Set ι)), M i) ⊗[R] M i₀) :=
+ (reindex R (s := M) (Equiv.subtypeNeSumPUnit.{0} i₀).symm).trans
+ ((tmulEquivDep R (fun i ↦ M (Equiv.subtypeNeSumPUnit i₀ i))).symm.trans
+ (LinearEquiv.lTensor _ (subsingletonEquiv Unit.unit)))
+
+variable (i₀ : ι)
+
+set_option backward.isDefEq.respectTransparency false in
+@[simp]
+lemma equivPiTensorComplSingletonTensor_tprod (i₀ : ι) (m : ∀ i, M i) :
+ equivPiTensorComplSingletonTensor R M i₀ (⨂ₜ[R] i, m i) =
+ (⨂ₜ[R] (j : ((Set.singleton i₀)ᶜ : Set ι)), m j) ⊗ₜ m i₀:= by
+ dsimp [equivPiTensorComplSingletonTensor]
+ have : (reindex R M (Equiv.subtypeNeSumPUnit.{0} i₀).symm) (⨂ₜ[R] (i : ι), m i) =
+ ⨂ₜ[R] j, m ((Equiv.subtypeNeSumPUnit.{0} i₀) j) := by
+ simp_rw [reindex_tprod (R := R) (s := M), Equiv.symm_symm]
+ rw [dsimp% this, dsimp% tmulEquivDep_symm_apply R
+ (fun i ↦ M ((Equiv.subtypeNeSumPUnit.{0} i₀) i))]
+ exact (LinearEquiv.lTensor_tmul _ _ _ _).trans (by congr; simp)
+
+@[simp]
+lemma equivPiTensorComplSingletonTensor_symm_tmul (i₀ : ι)
+ (m : ∀ (i : ((Set.singleton i₀)ᶜ : Set ι)), M i) (x : M i₀) :
+ (equivPiTensorComplSingletonTensor R M i₀).symm
+ ((⨂ₜ[R] (j : ((Set.singleton i₀)ᶜ : Set ι)), m j) ⊗ₜ x) =
+ (⨂ₜ[R] i, Function.subtypeNeLift i₀ m x i) := by
+ apply (equivPiTensorComplSingletonTensor R M i₀).injective
+ simp only [LinearEquiv.apply_symm_apply, equivPiTensorComplSingletonTensor_tprod,
+ Function.subtypeNeLift_self]
+ congr
+ ext ⟨i, hi⟩
+ rw [Function.subtypeNeLift_of_neq _ _ _ _ hi]
+ rfl
+
+end equivPiTensorComplSingletonTensor
+
+variable {R} {ι : Type*} [Finite ι] {M : ι → Type*} {N : Type*} {γ : ι → Type*}
+
+section AddCommMonoid
+
+variable [CommSemiring R] [∀ i, AddCommMonoid (M i)] [∀ i, Module R (M i)]
+ [AddCommMonoid N] [Module R N] {g : ⦃i : ι⦄ → (j : γ i) → M i}
+
+lemma ext_of_span_eq_top
+ (hg : ∀ i, Submodule.span R (Set.range (@g i)) = ⊤)
+ {φ φ' : (⨂[R] i, M i) →ₗ[R] N}
+ (h : ∀ (j : (i : ι) → γ i),
+ φ (tprod _ (fun i ↦ g (j i))) = φ' (tprod _ (fun i ↦ g (j i)))) :
+ φ = φ' := by
+ obtain ⟨n, hι⟩ : ∃ (n : ℕ), Nat.card ι = n := ⟨_, rfl⟩
+ induction n generalizing ι with
+ | zero =>
+ ext x
+ have : IsEmpty ι := (Nat.card_eq_zero.1 hι).resolve_right <| Finite.not_infinite ‹_›
+ obtain rfl : x = fun i ↦ @g i (isEmptyElim i) := Subsingleton.elim _ _
+ apply h
+ | succ n hn =>
+ classical
+ have : Nonempty ι := ((Nat.card_pos_iff (α := ι)).1 (by omega)).1
+ have i₀ : ι := Classical.arbitrary _
+ let e := (equivPiTensorComplSingletonTensor R M i₀).trans (TensorProduct.comm _ _ _)
+ obtain ⟨ψ, rfl⟩ : ∃ ψ, φ = LinearMap.comp ψ e.toLinearMap :=
+ ⟨φ.comp e.symm.toLinearMap, by ext; simp⟩
+ obtain ⟨ψ', rfl⟩ : ∃ ψ', φ' = LinearMap.comp ψ' e.toLinearMap :=
+ ⟨φ'.comp e.symm.toLinearMap, by ext; simp⟩
+ dsimp [e] at h
+ congr 1
+ apply (TensorProduct.lift.equiv _ _ _ _).symm.injective
+ rw [Submodule.linearMap_eq_iff_of_span_eq_top _ _ (hg i₀)]
+ rintro ⟨_, ⟨g₀, rfl⟩⟩
+ apply hn (g := fun i (j : γ i.1) ↦ by exact g j)
+ · intro
+ exact hg _
+ · intro j
+ have : (g g₀ ⊗ₜ[R] (tprod R) fun i ↦ g (j i)) =
+ TensorProduct.comm R _ _ ((equivPiTensorComplSingletonTensor R M i₀)
+ (⨂ₜ[R] (i : ι), g (Function.subtypeNeLift i₀ j g₀ i))) := by
+ simp only [equivPiTensorComplSingletonTensor_tprod, Function.subtypeNeLift_self]
+ congr
+ ext ⟨x, hx⟩
+ congr
+ rw [Function.subtypeNeLift_of_neq _ _ _ _ (by assumption)]
+ rfl
+ simpa only [lift.equiv_symm_apply, this] using h (Function.subtypeNeLift i₀ j g₀)
+ · exact Set.ncard_compl_of_ncard_eq_add _ (by simpa)
+
+lemma _root_.MultilinearMap.ext_of_span_eq_top
+ (hg : ∀ i, Submodule.span R (Set.range (@g i)) = ⊤)
+ {φ φ' : MultilinearMap R M N}
+ (h : ∀ (j : (i : ι) → γ i), φ (fun i ↦ g (j i)) = φ' (fun i ↦ g (j i))) :
+ φ = φ' := by
+ suffices lift φ = lift φ' by
+ ext m
+ simpa using DFunLike.congr_fun this (tprod _ m)
+ exact PiTensorProduct.ext_of_span_eq_top hg (fun j ↦ by simpa using h j)
+
+end AddCommMonoid
+
+variable [CommRing R] [∀ i, AddCommGroup (M i)] [∀ i, Module R (M i)]
+ [AddCommMonoid N] [Module R N] {g : ⦃i : ι⦄ → (j : γ i) → M i}
+
+lemma submodule_span_eq_top
+ (hg : ∀ i, Submodule.span R (Set.range (@g i)) = ⊤) :
+ Submodule.span R (Set.range (fun j : ((i : ι) → γ i) ↦
+ ⨂ₜ[R] (i : ι), g (j i))) = ⊤ := by
+ rw [← (Submodule.span R _).ker_mkQ, LinearMap.ker_eq_top]
+ refine ext_of_span_eq_top hg (fun j ↦ ?_)
+ simp only [Submodule.mkQ_apply, LinearMap.zero_apply, Submodule.Quotient.mk_eq_zero]
+ exact Submodule.subset_span ⟨j, rfl⟩
+
+end PiTensorProduct
diff --git a/Mathlib/LinearAlgebra/TensorPower/Basic.lean b/Mathlib/LinearAlgebra/TensorPower/Basic.lean
index 99590b87b2047a..8c920d358a1af7 100644
--- a/Mathlib/LinearAlgebra/TensorPower/Basic.lean
+++ b/Mathlib/LinearAlgebra/TensorPower/Basic.lean
@@ -5,7 +5,7 @@ Authors: Eric Wieser
-/
module
-public import Mathlib.LinearAlgebra.PiTensorProduct
+public import Mathlib.LinearAlgebra.PiTensorProduct.Basic
public import Mathlib.Logic.Equiv.Fin.Basic
public import Mathlib.Algebra.DirectSum.Algebra
diff --git a/Mathlib/LinearAlgebra/TensorPower/Symmetric.lean b/Mathlib/LinearAlgebra/TensorPower/Symmetric.lean
index 3549e86257a213..7eed6268cd9194 100644
--- a/Mathlib/LinearAlgebra/TensorPower/Symmetric.lean
+++ b/Mathlib/LinearAlgebra/TensorPower/Symmetric.lean
@@ -5,7 +5,7 @@ Authors: Kenny Lau
-/
module
-public import Mathlib.LinearAlgebra.PiTensorProduct
+public import Mathlib.LinearAlgebra.PiTensorProduct.Basic
public import Mathlib.Tactic.SuppressCompilation
/-!
diff --git a/Mathlib/Logic/Equiv/Option.lean b/Mathlib/Logic/Equiv/Option.lean
index 895f3324f211fb..6419012cdb2fb3 100644
--- a/Mathlib/Logic/Equiv/Option.lean
+++ b/Mathlib/Logic/Equiv/Option.lean
@@ -289,4 +289,8 @@ def optionIsSomeEquiv (α) : { x : Option α // x.isSome } ≃ α where
left_inv _ := Subtype.ext <| Option.some_get _
right_inv _ := Option.get_some _ _
+/-- The bijection `{ i // i ≠ i₀ } ⊕ PUnit ≃ α` for any `i₀ : α`. -/
+abbrev subtypeNeSumPUnit (i₀ : α) : { i // i ≠ i₀ } ⊕ PUnit.{u + 1} ≃ α :=
+ (Equiv.optionEquivSumPUnit.{u} _).symm.trans (Equiv.optionSubtypeNe i₀)
+
end Equiv
diff --git a/Mathlib/RingTheory/PiTensorProduct.lean b/Mathlib/RingTheory/PiTensorProduct.lean
index 2543913444a0a1..df1663a1d5ae85 100644
--- a/Mathlib/RingTheory/PiTensorProduct.lean
+++ b/Mathlib/RingTheory/PiTensorProduct.lean
@@ -5,7 +5,7 @@ Authors: Jujian Zhang
-/
module
-public import Mathlib.LinearAlgebra.PiTensorProduct
+public import Mathlib.LinearAlgebra.PiTensorProduct.Basic
public import Mathlib.Algebra.Algebra.Bilinear
public import Mathlib.Algebra.Algebra.Equiv
public import Mathlib.Data.Finset.NoncommProd
From 17367c7945865a81a93b76faf79de9721f062545 Mon Sep 17 00:00:00 2001
From: David Gross
Date: Thu, 18 Jun 2026 10:58:11 +0000
Subject: [PATCH 0134/1300] refactor(PiTensorProduct/{InjectiveNorm,
ProjectiveNorm}): deprecate `injectiveSeminorm` (#35569)
This PR:
* Deprecates `PiTensorProduct.injectiveSeminorm` and supporting lemmas.
* Moves the theory of `liftEquiv` from InjectiveSeminorm.lean to ProjectiveSeminorm.lean.
No changes are introduced beyond adding deprecation notices, adapting docstrings, and moving material between files.
The PR leaves InjectiveSeminorm.lean almost empty. A new implementation of `injectiveSeminorm`, one which reflects the common mathematical definition, is to be done.
This is the third in a series of three PRs with the goal to [deprecate `PiTensorProuduct.injectiveSeminorm`](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/injectiveSeminorm/with/568798633).
---
.../PiTensorProduct/InjectiveSeminorm.lean | 258 ++----------------
.../PiTensorProduct/ProjectiveSeminorm.lean | 210 +++++++++++++-
2 files changed, 227 insertions(+), 241 deletions(-)
diff --git a/Mathlib/Analysis/Normed/Module/PiTensorProduct/InjectiveSeminorm.lean b/Mathlib/Analysis/Normed/Module/PiTensorProduct/InjectiveSeminorm.lean
index 50460c1ef4893c..4a18420fc5ec71 100644
--- a/Mathlib/Analysis/Normed/Module/PiTensorProduct/InjectiveSeminorm.lean
+++ b/Mathlib/Analysis/Normed/Module/PiTensorProduct/InjectiveSeminorm.lean
@@ -11,79 +11,36 @@ import Mathlib.LinearAlgebra.Isomorphisms
/-!
# Injective seminorm on the tensor of a finite family of normed spaces.
-Let `𝕜` be a nontrivially normed field and `E` be a family of normed `𝕜`-vector spaces `Eᵢ`,
-indexed by a finite type `ι`. We define a seminorm on `⨂[𝕜] i, Eᵢ`, which we call the
-"injective seminorm". It is chosen to satisfy the following property: for every
-normed `𝕜`-vector space `F`, the linear equivalence
-`MultilinearMap 𝕜 E F ≃ₗ[𝕜] (⨂[𝕜] i, Eᵢ) →ₗ[𝕜] F`
-expressing the universal property of the tensor product induces an isometric linear equivalence
-`ContinuousMultilinearMap 𝕜 E F ≃ₗᵢ[𝕜] (⨂[𝕜] i, Eᵢ) →L[𝕜] F`.
+The purpose of this file is to develop the theory of the injective tensor norm.
-The idea is the following: Every normed `𝕜`-vector space `F` defines a linear map
-from `⨂[𝕜] i, Eᵢ` to `ContinuousMultilinearMap 𝕜 E F →ₗ[𝕜] F`, which sends `x` to the map
-`f ↦ f.lift x`. Thanks to `PiTensorProduct.norm_eval_le_projectiveSeminorm`, this map lands in
-`ContinuousMultilinearMap 𝕜 E F →L[𝕜] F`. As this last space has a natural operator (semi)norm,
-we get an induced seminorm on `⨂[𝕜] i, Eᵢ`, which, by
-`PiTensorProduct.norm_eval_le_projectiveSeminorm`, is bounded above by the projective seminorm
-`PiTensorProduct.projectiveSeminorm`. We then take the `sup` of these seminorms as `F` varies;
-as this family of seminorms is bounded, its `sup` has good properties.
+A first formalization turned out not to capture the common mathematical definition and is
+now deprecated. See
-In fact, we cannot take the `sup` over all normed spaces `F` because of set-theoretical issues,
-so we only take spaces `F` in the same universe as `⨂[𝕜] i, Eᵢ`. We prove in
-`norm_eval_le_injectiveSeminorm` that this gives the same result, because every multilinear map
-from `E = Πᵢ Eᵢ` to `F` factors though a normed vector space in the same universe as
-`⨂[𝕜] i, Eᵢ`.
-
-We then prove the universal property and the functoriality of `⨂[𝕜] i, Eᵢ` as a normed vector
-space.
+https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/injectiveSeminorm/with/568798633
## Main definitions
* `PiTensorProduct.toDualContinuousMultilinearMap`: The `𝕜`-linear map from
`⨂[𝕜] i, Eᵢ` to `ContinuousMultilinearMap 𝕜 E F →L[𝕜] F` sending `x` to the map
`f ↦ f x`.
-* `PiTensorProduct.injectiveSeminorm`: The injective seminorm on `⨂[𝕜] i, Eᵢ`.
-* `PiTensorProduct.liftEquiv`: The bijection between `ContinuousMultilinearMap 𝕜 E F`
- and `(⨂[𝕜] i, Eᵢ) →L[𝕜] F`, as a continuous linear equivalence.
-* `PiTensorProduct.liftIsometry`: The bijection between `ContinuousMultilinearMap 𝕜 E F`
- and `(⨂[𝕜] i, Eᵢ) →L[𝕜] F`, as an isometric linear equivalence.
-* `PiTensorProduct.tprodL`: The canonical continuous multilinear map from `E = Πᵢ Eᵢ`
- to `⨂[𝕜] i, Eᵢ`.
-* `PiTensorProduct.mapL`: The continuous linear map from `⨂[𝕜] i, Eᵢ` to `⨂[𝕜] i, E'ᵢ`
- induced by a family of continuous linear maps `Eᵢ →L[𝕜] E'ᵢ`.
-* `PiTensorProduct.mapLMultilinear`: The continuous multilinear map from
- `Πᵢ (Eᵢ →L[𝕜] E'ᵢ)` to `(⨂[𝕜] i, Eᵢ) →L[𝕜] (⨂[𝕜] i, E'ᵢ)` sending a family
- `f` to `PiTensorProduct.mapL f`.
-
-## Main results
-
-* `PiTensorProduct.norm_eval_le_injectiveSeminorm`: The main property of the injective seminorm
- on `⨂[𝕜] i, Eᵢ`: for every `x` in `⨂[𝕜] i, Eᵢ` and every continuous multilinear map `f` from
- `E = Πᵢ Eᵢ` to a normed space `F`, we have `‖f.lift x‖ ≤ ‖f‖ * injectiveSeminorm x `.
-* `PiTensorProduct.mapL_opNorm`: If `f` is a family of continuous linear maps
- `fᵢ : Eᵢ →L[𝕜] Fᵢ`, then `‖PiTensorProduct.mapL f‖ ≤ ∏ i, ‖fᵢ‖`.
-* `PiTensorProduct.mapLMultilinear_opNorm` : If `F` is a normed vector space, then
- `‖mapLMultilinear 𝕜 E F‖ ≤ 1`.
## TODO
-* If all `Eᵢ` are separated and satisfy `SeparatingDual`, then the seminorm on
- `⨂[𝕜] i, Eᵢ` is a norm. This uses the construction of a basis of the `PiTensorProduct`, hence
- depends on PR https://github.com/leanprover-community/mathlib4/pull/11156.
- It should probably go in a separate file.
-
-* Adapt the remaining functoriality constructions/properties from `PiTensorProduct`.
+* Reimplement `injectiveSeminorm`.
-/
@[expose] public section
-universe uι u𝕜 uE uF
+-- These explicit universe variables are required by `injectiveSeminorm`, which
+-- has been marked as deprecated on 2026-06-10. TBD: delete the variables after the
+-- deprecated material has been removed or re-implemented.
+universe uι u𝕜 uE
variable {ι : Type uι} [Fintype ι]
variable {𝕜 : Type u𝕜} [NontriviallyNormedField 𝕜]
variable {E : ι → Type uE} [∀ i, SeminormedAddCommGroup (E i)] [∀ i, NormedSpace 𝕜 (E i)]
-variable {F : Type uF} [SeminormedAddCommGroup F] [NormedSpace 𝕜 F]
+variable {F : Type*} [SeminormedAddCommGroup F] [NormedSpace 𝕜 F]
open scoped TensorProduct
@@ -116,11 +73,15 @@ theorem toDualContinuousMultilinearMap_le_projectiveSeminorm (x : ⨂[𝕜] i, E
normed vector spaces `F`. In fact, we only take in the same universe as `⨂[𝕜] i, Eᵢ`, and then
prove in `PiTensorProduct.norm_eval_le_injectiveSeminorm` that this gives the same result.
-/
+@[deprecated
+ "`injectiveSeminorm` is deprecated in favor of the extensionally equal `projectiveSeminorm`"
+ (since := "2026-06-10")]
noncomputable irreducible_def injectiveSeminorm : Seminorm 𝕜 (⨂[𝕜] i, E i) :=
sSup {p | ∃ (G : Type (max uι u𝕜 uE)) (_ : SeminormedAddCommGroup G)
(_ : NormedSpace 𝕜 G), p = Seminorm.comp (normSeminorm 𝕜 (ContinuousMultilinearMap 𝕜 E G →L[𝕜] G))
(toDualContinuousMultilinearMap G (𝕜 := 𝕜) (E := E))}
+@[deprecated "no replacement" (since := "2026-06-10")]
lemma dualSeminorms_bounded : BddAbove {p | ∃ (G : Type (max uι u𝕜 uE))
(_ : SeminormedAddCommGroup G) (_ : NormedSpace 𝕜 G),
p = Seminorm.comp (normSeminorm 𝕜 (ContinuousMultilinearMap 𝕜 E G →L[𝕜] G))
@@ -130,6 +91,9 @@ lemma dualSeminorms_bounded : BddAbove {p | ∃ (G : Type (max uι u𝕜 uE))
intro p G _ _ hp x
simpa [hp] using toDualContinuousMultilinearMap_le_projectiveSeminorm _
+@[deprecated
+ "`injectiveSeminorm` is deprecated in favor of the extensionally equal `projectiveSeminorm`"
+ (since := "2026-06-10")]
theorem injectiveSeminorm_apply (x : ⨂[𝕜] i, E i) :
injectiveSeminorm x = ⨆ p : {p | ∃ (G : Type (max uι u𝕜 uE))
(_ : SeminormedAddCommGroup G) (_ : NormedSpace 𝕜 G), p = Seminorm.comp (normSeminorm 𝕜
@@ -138,6 +102,10 @@ theorem injectiveSeminorm_apply (x : ⨂[𝕜] i, E i) :
simpa only [injectiveSeminorm, Set.coe_setOf, Set.mem_setOf_eq]
using Seminorm.sSup_apply dualSeminorms_bounded
+attribute [-instance] instSeminormedAddCommGroup in
+@[deprecated
+ "`injectiveSeminorm` is deprecated in favor of the extensionally equal `projectiveSeminorm`"
+ (since := "2026-06-10")]
theorem norm_eval_le_injectiveSeminorm (f : ContinuousMultilinearMap 𝕜 E F) (x : ⨂[𝕜] i, E i) :
‖lift f.toMultilinearMap x‖ ≤ ‖f‖ * injectiveSeminorm x := by
/- If `F` were in `Type (max uι u𝕜 uE)` (which is the type of `⨂[𝕜] i, E i`), then the
@@ -191,6 +159,9 @@ theorem norm_eval_le_injectiveSeminorm (f : ContinuousMultilinearMap 𝕜 E F) (
rw [mul_comm]
exact ContinuousLinearMap.le_opNorm _ _
+@[deprecated
+ "`injectiveSeminorm` is deprecated in favor of the extensionally equal `projectiveSeminorm`"
+ (since := "2026-06-10")]
theorem injectiveSeminorm_le_projectiveSeminorm :
injectiveSeminorm (𝕜 := 𝕜) (E := E) ≤ projectiveSeminorm := by
rw [injectiveSeminorm]
@@ -208,188 +179,13 @@ theorem injectiveSeminorm_le_projectiveSeminorm :
rw [h]; intro x; simp only [Seminorm.comp_apply, coe_normSeminorm]
exact toDualContinuousMultilinearMap_le_projectiveSeminorm _
+@[deprecated
+ "`injectiveSeminorm` is deprecated in favor of the extensionally equal `projectiveSeminorm`"
+ (since := "2026-06-10")]
theorem injectiveSeminorm_tprod_le (m : Π (i : ι), E i) :
injectiveSeminorm (⨂ₜ[𝕜] i, m i) ≤ ∏ i, ‖m i‖ :=
le_trans (injectiveSeminorm_le_projectiveSeminorm _) (projectiveSeminorm_tprod_le m)
--- Use `projectiveSeminorm` to turn the `PiTensorProduct` into a seminormed space.
--- The definition `injectiveSeminorm` is subject to deprecation in a follow-up PR. See:
--- https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/injectiveSeminorm/with/568798633
-noncomputable instance : SeminormedAddCommGroup (⨂[𝕜] i, E i) :=
- fast_instance% AddGroupSeminorm.toSeminormedAddCommGroup projectiveSeminorm.toAddGroupSeminorm
-
-noncomputable instance : NormedSpace 𝕜 (⨂[𝕜] i, E i) := ⟨projectiveSeminorm_smul_le⟩
-
-variable (𝕜 E F)
-
-/-- The linear equivalence between `ContinuousMultilinearMap 𝕜 E F` and `(⨂[𝕜] i, Eᵢ) →L[𝕜] F`
-induced by `PiTensorProduct.lift`, for every normed space `F`.
--/
-@[simps]
-noncomputable def liftEquiv : ContinuousMultilinearMap 𝕜 E F ≃ₗ[𝕜] (⨂[𝕜] i, E i) →L[𝕜] F where
- toFun f := LinearMap.mkContinuous (lift f.toMultilinearMap) ‖f‖ fun x ↦
- norm_eval_le_projectiveSeminorm f x
- map_add' f g := by ext; simp
- map_smul' a f := by ext; simp
- invFun l := MultilinearMap.mkContinuous (lift.symm l.toLinearMap) ‖l‖ fun x ↦
- ContinuousLinearMap.le_opNorm_of_le _ (projectiveSeminorm_tprod_le x)
- left_inv f := by ext; simp
- right_inv l := by
- rw [← ContinuousLinearMap.coe_inj]
- ext; simp
-
-/-- For a normed space `F`, we have constructed in `PiTensorProduct.liftEquiv` the canonical
-linear equivalence between `ContinuousMultilinearMap 𝕜 E F` and `(⨂[𝕜] i, Eᵢ) →L[𝕜] F`
-(induced by `PiTensorProduct.lift`). Here we give the upgrade of this equivalence to
-an isometric linear equivalence; in particular, it is a continuous linear equivalence. -/
-noncomputable def liftIsometry : ContinuousMultilinearMap 𝕜 E F ≃ₗᵢ[𝕜] (⨂[𝕜] i, E i) →L[𝕜] F :=
- LinearIsometryEquiv.ofBounds (liftEquiv 𝕜 E F)
- (fun f ↦ LinearMap.mkContinuous_norm_le _ (norm_nonneg f) (norm_eval_le_projectiveSeminorm f))
- (fun f ↦ by
- rw [liftEquiv_symm_apply]
- exact MultilinearMap.mkContinuous_norm_le _ (norm_nonneg f) _)
-
-variable {𝕜 E F}
-
--- API missing for `LinearIsometryEquiv.ofBounds`?
-@[simp]
-theorem liftIsometry_apply_apply (f : ContinuousMultilinearMap 𝕜 E F) (x : ⨂[𝕜] i, E i) :
- liftIsometry 𝕜 E F f x = lift f.toMultilinearMap x := by
- simp [LinearIsometryEquiv.ofBounds, liftIsometry]
-
-variable (𝕜) in
-/-- The canonical continuous multilinear map from `E = Πᵢ Eᵢ` to `⨂[𝕜] i, Eᵢ`. -/
-@[simps! toFun]
-noncomputable def tprodL : ContinuousMultilinearMap 𝕜 E (⨂[𝕜] i, E i) :=
- (liftIsometry 𝕜 E _).symm (ContinuousLinearMap.id 𝕜 _)
-
-@[simp]
-theorem tprodL_coe : (tprodL 𝕜).toMultilinearMap = tprod 𝕜 (s := E) := by
- ext; simp
-
-@[simp]
-theorem liftIsometry_symm_apply (l : (⨂[𝕜] i, E i) →L[𝕜] F) :
- (liftIsometry 𝕜 E F).symm l = l.compContinuousMultilinearMap (tprodL 𝕜) := by
- rfl
-
-@[simp]
-theorem liftIsometry_tprodL :
- liftIsometry 𝕜 E _ (tprodL 𝕜) = ContinuousLinearMap.id 𝕜 (⨂[𝕜] i, E i) := by
- ext; simp
-
end seminorm
-section map
-
-variable {E' E'' : ι → Type*}
-variable [∀ i, SeminormedAddCommGroup (E' i)] [∀ i, NormedSpace 𝕜 (E' i)]
-variable [∀ i, SeminormedAddCommGroup (E'' i)] [∀ i, NormedSpace 𝕜 (E'' i)]
-variable (g : Π i, E' i →L[𝕜] E'' i) (f : Π i, E i →L[𝕜] E' i)
-
-/-- Let `Eᵢ` and `E'ᵢ` be two families of normed `𝕜`-vector spaces.
-Let `f` be a family of continuous `𝕜`-linear maps between `Eᵢ` and `E'ᵢ`, i.e.
-`f : Πᵢ Eᵢ →L[𝕜] E'ᵢ`, then there is an induced continuous linear map
-`⨂ᵢ Eᵢ → ⨂ᵢ E'ᵢ` by `⨂ aᵢ ↦ ⨂ fᵢ aᵢ`. -/
-noncomputable def mapL : (⨂[𝕜] i, E i) →L[𝕜] ⨂[𝕜] i, E' i :=
- liftIsometry 𝕜 E _ <| (tprodL 𝕜).compContinuousLinearMap f
-
-@[simp]
-theorem mapL_coe : (mapL f).toLinearMap = map (fun i ↦ (f i).toLinearMap) := by
- ext; simp [mapL]
-
-@[simp]
-theorem mapL_apply (x : ⨂[𝕜] i, E i) : mapL f x = map (fun i ↦ (f i).toLinearMap) x := by
- rfl
-
-/-- Given submodules `pᵢ ⊆ Eᵢ`, this is the natural map: `⨂[𝕜] i, pᵢ → ⨂[𝕜] i, Eᵢ`.
-This is the continuous version of `PiTensorProduct.mapIncl`. -/
-@[simp]
-noncomputable def mapLIncl (p : Π i, Submodule 𝕜 (E i)) : (⨂[𝕜] i, p i) →L[𝕜] ⨂[𝕜] i, E i :=
- mapL fun (i : ι) ↦ (p i).subtypeL
-
-theorem mapL_comp : mapL (fun (i : ι) ↦ g i ∘L f i) = mapL g ∘L mapL f := by
- apply ContinuousLinearMap.coe_injective
- ext; simp
-
-theorem liftIsometry_comp_mapL (h : ContinuousMultilinearMap 𝕜 E' F) :
- liftIsometry 𝕜 E' F h ∘L mapL f = liftIsometry 𝕜 E F (h.compContinuousLinearMap f) := by
- apply ContinuousLinearMap.coe_injective
- ext; simp
-
-@[simp]
-theorem mapL_id : mapL (fun i ↦ ContinuousLinearMap.id 𝕜 (E i)) = ContinuousLinearMap.id _ _ := by
- apply ContinuousLinearMap.coe_injective
- ext; simp
-
-@[simp]
-theorem mapL_one : mapL (fun (i : ι) ↦ (1 : E i →L[𝕜] E i)) = 1 :=
- mapL_id
-
-theorem mapL_mul (f₁ f₂ : Π i, E i →L[𝕜] E i) :
- mapL (fun i ↦ f₁ i * f₂ i) = mapL f₁ * mapL f₂ :=
- mapL_comp f₁ f₂
-
-/-- Upgrading `PiTensorProduct.mapL` to a `MonoidHom` when `E = E'`. -/
-@[simps]
-noncomputable def mapLMonoidHom : (Π i, E i →L[𝕜] E i) →* ((⨂[𝕜] i, E i) →L[𝕜] ⨂[𝕜] i, E i) where
- toFun := mapL
- map_one' := mapL_one
- map_mul' := mapL_mul
-
-@[simp]
-protected theorem mapL_pow (f : Π i, E i →L[𝕜] E i) (n : ℕ) :
- mapL (f ^ n) = mapL f ^ n := MonoidHom.map_pow mapLMonoidHom f n
-
--- We redeclare `ι` here, and later dependent arguments,
--- to avoid the `[Fintype ι]` assumption present throughout the rest of the file.
-open Function in
-private theorem mapL_add_smul_aux {ι : Type uι}
- {E : ι → Type uE} [(i : ι) → SeminormedAddCommGroup (E i)] [(i : ι) → NormedSpace 𝕜 (E i)]
- {E' : ι → Type u_1} [(i : ι) → SeminormedAddCommGroup (E' i)] [(i : ι) → NormedSpace 𝕜 (E' i)]
- (f : (i : ι) → E i →L[𝕜] E' i) [DecidableEq ι] (i : ι) (u : E i →L[𝕜] E' i) :
- (fun j ↦ (update f i u j).toLinearMap) =
- update (fun j ↦ (f j).toLinearMap) i u.toLinearMap := by
- grind
-
-open Function in
-protected theorem mapL_add [DecidableEq ι] (i : ι) (u v : E i →L[𝕜] E' i) :
- mapL (update f i (u + v)) = mapL (update f i u) + mapL (update f i v) := by
- ext
- simp [mapL_add_smul_aux, PiTensorProduct.map_update_add]
-
-open Function in
-protected theorem mapL_smul [DecidableEq ι] (i : ι) (c : 𝕜) (u : E i →L[𝕜] E' i) :
- mapL (update f i (c • u)) = c • mapL (update f i u) := by
- ext
- simp [mapL_add_smul_aux, PiTensorProduct.map_update_smul]
-
-theorem mapL_opNorm : ‖mapL f‖ ≤ ∏ i, ‖f i‖ := by
- refine (ContinuousLinearMap.opNorm_le_iff (by positivity)).mpr fun x ↦ ?_
- apply le_trans (norm_eval_le_projectiveSeminorm ..) (mul_le_mul_of_nonneg_right _ (norm_nonneg x))
- refine (ContinuousMultilinearMap.opNorm_le_iff (by positivity)).mpr fun m ↦ ?_
- apply le_trans (projectiveSeminorm_tprod_le fun i ↦ f i (m i))
- rw [← Finset.prod_mul_distrib]
- gcongr
- exact ContinuousLinearMap.le_opNorm _ _
-
-variable (𝕜 E E')
-
-/-- The tensor of a family of linear maps from `Eᵢ` to `E'ᵢ`, as a continuous multilinear map of
-the family. -/
-@[simps! toFun_apply]
-noncomputable def mapLMultilinear : ContinuousMultilinearMap 𝕜 (fun (i : ι) ↦ E i →L[𝕜] E' i)
- ((⨂[𝕜] i, E i) →L[𝕜] ⨂[𝕜] i, E' i) :=
- MultilinearMap.mkContinuous
- { toFun := mapL
- map_update_smul' := fun _ _ _ _ ↦ PiTensorProduct.mapL_smul _ _ _ _
- map_update_add' := fun _ _ _ _ ↦ PiTensorProduct.mapL_add _ _ _ _ }
- 1 (fun f ↦ by rw [one_mul]; exact mapL_opNorm f)
-
-variable {𝕜 E E'}
-
-theorem mapLMultilinear_opNorm : ‖mapLMultilinear 𝕜 E E'‖ ≤ 1 :=
- MultilinearMap.mkContinuous_norm_le _ zero_le_one _
-
-end map
-
end PiTensorProduct
diff --git a/Mathlib/Analysis/Normed/Module/PiTensorProduct/ProjectiveSeminorm.lean b/Mathlib/Analysis/Normed/Module/PiTensorProduct/ProjectiveSeminorm.lean
index 4a419478bd0338..432bc808ff1f78 100644
--- a/Mathlib/Analysis/Normed/Module/PiTensorProduct/ProjectiveSeminorm.lean
+++ b/Mathlib/Analysis/Normed/Module/PiTensorProduct/ProjectiveSeminorm.lean
@@ -1,7 +1,7 @@
/-
Copyright (c) 2024 Sophie Morel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
-Authors: Sophie Morel
+Authors: Sophie Morel, David Gross, Davood Haji Taghi Tehrani
-/
module
@@ -23,27 +23,41 @@ for every `m` in `Π i, Eᵢ` is bounded above by the projective seminorm.
## Main definitions
* `PiTensorProduct.projectiveSeminorm`: The projective seminorm on `⨂[𝕜] i, Eᵢ`.
+* `PiTensorProduct.liftEquiv`: The bijection between `ContinuousMultilinearMap 𝕜 E F`
+ and `(⨂[𝕜] i, Eᵢ) →L[𝕜] F`, as a continuous linear equivalence.
+* `PiTensorProduct.liftIsometry`: The bijection between `ContinuousMultilinearMap 𝕜 E F`
+ and `(⨂[𝕜] i, Eᵢ) →L[𝕜] F`, as an isometric linear equivalence.
+* `PiTensorProduct.tprodL`: The canonical continuous multilinear map from `E = Πᵢ Eᵢ`
+ to `⨂[𝕜] i, Eᵢ`.
+* `PiTensorProduct.mapL`: The continuous linear map from `⨂[𝕜] i, Eᵢ` to `⨂[𝕜] i, E'ᵢ`
+ induced by a family of continuous linear maps `Eᵢ →L[𝕜] E'ᵢ`.
+* `PiTensorProduct.mapLMultilinear`: The continuous multilinear map from
+ `Πᵢ (Eᵢ →L[𝕜] E'ᵢ)` to `(⨂[𝕜] i, Eᵢ) →L[𝕜] (⨂[𝕜] i, E'ᵢ)` sending a family
+ `f` to `PiTensorProduct.mapL f`.
## Main results
* `PiTensorProduct.norm_eval_le_projectiveSeminorm`: If `f` is a continuous multilinear map on
`E = Π i, Eᵢ` and `x` is in `⨂[𝕜] i, Eᵢ`, then `‖f.lift x‖ ≤ projectiveSeminorm x * ‖f‖`.
+* `PiTensorProduct.mapL_opNorm`: If `f` is a family of continuous linear maps
+ `fᵢ : Eᵢ →L[𝕜] Fᵢ`, then `‖PiTensorProduct.mapL f‖ ≤ ∏ i, ‖fᵢ‖`.
+* `PiTensorProduct.opNorm_mapLMultilinear_le` : If `F` is a normed vecteor space, then
+ `‖mapLMultilinear 𝕜 E F‖ ≤ 1`.
## TODO
* If the base field is `ℝ` or `ℂ` (or more generally if the injection of `Eᵢ` into its bidual is
an isometry for every `i`), then we have `projectiveSeminorm ⨂ₜ[𝕜] i, mᵢ = Π i, ‖mᵢ‖`.
-
-* The functoriality.
+* If all `Eᵢ` are separated and satisfy `SeparatingDual`, then the seminorm on
+ `⨂[𝕜] i, Eᵢ` is a norm.
+* Adapt the remaining functoriality constructions/properties from `PiTensorProduct`.
-/
@[expose] public section
-universe uι u𝕜 uE uF
-
-variable {ι : Type uι} [Fintype ι]
-variable {𝕜 : Type u𝕜}
-variable {E : ι → Type uE} [∀ i, SeminormedAddCommGroup (E i)]
+variable {ι : Type*} [Fintype ι]
+variable {𝕜 : Type*}
+variable {E : ι → Type*} [∀ i, SeminormedAddCommGroup (E i)]
open scoped TensorProduct
@@ -86,7 +100,7 @@ noncomputable instance : Norm (⨂[𝕜] i, E i) :=
theorem norm_def (x : ⨂[𝕜] i, E i) :
‖x‖ = iInf (fun (p : lifts x) ↦ projectiveSeminormAux p.val) := rfl
-@[deprecated (since := "2026-03-13")] alias projectiveSeminormFun := norm
+@[deprecated (since := "2026-06-10")] alias projectiveSeminormFun := norm
theorem projectiveSeminorm_zero : ‖(0 : ⨂[𝕜] i, E i)‖ = 0 :=
le_antisymm (ciInf_le (bddBelow_projectiveSemiNormAux _) ⟨0, lifts_zero⟩)
@@ -108,7 +122,12 @@ of `∑ j, Π i, ‖mⱼ i‖`. -/
noncomputable def projectiveSeminorm : Seminorm 𝕜 (⨂[𝕜] i, E i) := .ofSMulLE
_ projectiveSeminorm_zero projectiveSeminorm_add_le projectiveSeminorm_smul_le
-@[deprecated norm_def (since := "2026-03-06")]
+noncomputable instance : SeminormedAddCommGroup (⨂[𝕜] i, E i) :=
+ fast_instance% AddGroupSeminorm.toSeminormedAddCommGroup projectiveSeminorm.toAddGroupSeminorm
+
+noncomputable instance : NormedSpace 𝕜 (⨂[𝕜] i, E i) := ⟨projectiveSeminorm_smul_le⟩
+
+@[deprecated norm_def (since := "2026-06-10")]
theorem projectiveSeminorm_apply (x : ⨂[𝕜] i, E i) :
projectiveSeminorm x = iInf (fun (p : lifts x) ↦ projectiveSeminormAux p.1) := rfl
@@ -136,6 +155,177 @@ theorem norm_eval_le_projectiveSeminorm {G : Type*} [SeminormedAddCommGroup G]
simpa [norm_smul, ← mul_assoc, mul_comm ‖f‖ _] using
fun a m _ ↦ mul_le_mul_of_nonneg_left (f.le_opNorm _) (norm_nonneg _)
+variable {F : Type*} [SeminormedAddCommGroup F] [NormedSpace 𝕜 F]
+
+variable (𝕜 E F)
+
+/-- The linear equivalence between `ContinuousMultilinearMap 𝕜 E F` and `(⨂[𝕜] i, Eᵢ) →L[𝕜] F`
+induced by `PiTensorProduct.lift`, for every normed space `F`.
+-/
+@[simps]
+noncomputable def liftEquiv : ContinuousMultilinearMap 𝕜 E F ≃ₗ[𝕜] (⨂[𝕜] i, E i) →L[𝕜] F where
+ toFun f := LinearMap.mkContinuous (lift f.toMultilinearMap) ‖f‖ fun x ↦
+ norm_eval_le_projectiveSeminorm f x
+ map_add' f g := by ext; simp
+ map_smul' a f := by ext; simp
+ invFun l := MultilinearMap.mkContinuous (lift.symm l.toLinearMap) ‖l‖ fun x ↦
+ ContinuousLinearMap.le_opNorm_of_le _ (projectiveSeminorm_tprod_le x)
+ left_inv f := by ext; simp
+ right_inv l := by
+ rw [← ContinuousLinearMap.coe_inj]
+ ext; simp
+
+/-- For a normed space `F`, we have constructed in `PiTensorProduct.liftEquiv` the canonical
+linear equivalence between `ContinuousMultilinearMap 𝕜 E F` and `(⨂[𝕜] i, Eᵢ) →L[𝕜] F`
+(induced by `PiTensorProduct.lift`). Here we give the upgrade of this equivalence to
+an isometric linear equivalence; in particular, it is a continuous linear equivalence. -/
+noncomputable def liftIsometry : ContinuousMultilinearMap 𝕜 E F ≃ₗᵢ[𝕜] (⨂[𝕜] i, E i) →L[𝕜] F :=
+ LinearIsometryEquiv.ofBounds (liftEquiv 𝕜 E F)
+ (fun f ↦ LinearMap.mkContinuous_norm_le _ (norm_nonneg f) (norm_eval_le_projectiveSeminorm f))
+ (fun f ↦ by
+ rw [liftEquiv_symm_apply]
+ exact MultilinearMap.mkContinuous_norm_le _ (norm_nonneg f) _)
+
+variable {𝕜 E F}
+
+@[simp]
+theorem liftIsometry_apply_apply (f : ContinuousMultilinearMap 𝕜 E F) (x : ⨂[𝕜] i, E i) :
+ liftIsometry 𝕜 E F f x = lift f.toMultilinearMap x := by
+ simp [LinearIsometryEquiv.ofBounds, liftIsometry]
+
+variable (𝕜) in
+/-- The canonical continuous multilinear map from `E = Πᵢ Eᵢ` to `⨂[𝕜] i, Eᵢ`. -/
+@[simps! toFun]
+noncomputable def tprodL : ContinuousMultilinearMap 𝕜 E (⨂[𝕜] i, E i) :=
+ (liftIsometry 𝕜 E _).symm (ContinuousLinearMap.id 𝕜 _)
+
+@[simp]
+theorem tprodL_coe : (tprodL 𝕜).toMultilinearMap = tprod 𝕜 (s := E) := by
+ ext; simp
+
+@[simp]
+theorem liftIsometry_symm_apply (l : (⨂[𝕜] i, E i) →L[𝕜] F) :
+ (liftIsometry 𝕜 E F).symm l = l.compContinuousMultilinearMap (tprodL 𝕜) := by
+ rfl
+
+@[simp]
+theorem liftIsometry_tprodL :
+ liftIsometry 𝕜 E _ (tprodL 𝕜) = ContinuousLinearMap.id 𝕜 (⨂[𝕜] i, E i) := by
+ ext; simp
+
+section map
+
+variable {E' E'' : ι → Type*}
+variable [∀ i, SeminormedAddCommGroup (E' i)] [∀ i, NormedSpace 𝕜 (E' i)]
+variable [∀ i, SeminormedAddCommGroup (E'' i)] [∀ i, NormedSpace 𝕜 (E'' i)]
+variable (g : Π i, E' i →L[𝕜] E'' i) (f : Π i, E i →L[𝕜] E' i)
+
+/-- Let `Eᵢ` and `E'ᵢ` be two families of normed `𝕜`-vector spaces.
+Let `f` be a family of continuous `𝕜`-linear maps between `Eᵢ` and `E'ᵢ`, i.e.
+`f : Πᵢ Eᵢ →L[𝕜] E'ᵢ`, then there is an induced continuous linear map
+`⨂ᵢ Eᵢ → ⨂ᵢ E'ᵢ` by `⨂ aᵢ ↦ ⨂ fᵢ aᵢ`. -/
+noncomputable def mapL : (⨂[𝕜] i, E i) →L[𝕜] ⨂[𝕜] i, E' i :=
+ liftIsometry 𝕜 E _ <| (tprodL 𝕜).compContinuousLinearMap f
+
+@[simp]
+theorem mapL_coe : (mapL f).toLinearMap = map (fun i ↦ (f i).toLinearMap) := by
+ ext; simp [mapL]
+
+@[simp]
+theorem mapL_apply (x : ⨂[𝕜] i, E i) : mapL f x = map (fun i ↦ (f i).toLinearMap) x := by
+ rfl
+
+/-- Given submodules `pᵢ ⊆ Eᵢ`, this is the natural map: `⨂[𝕜] i, pᵢ → ⨂[𝕜] i, Eᵢ`.
+This is the continuous version of `PiTensorProduct.mapIncl`. -/
+@[simp]
+noncomputable def mapLIncl (p : Π i, Submodule 𝕜 (E i)) : (⨂[𝕜] i, p i) →L[𝕜] ⨂[𝕜] i, E i :=
+ mapL fun (i : ι) ↦ (p i).subtypeL
+
+theorem mapL_comp : mapL (fun (i : ι) ↦ g i ∘L f i) = mapL g ∘L mapL f := by
+ apply ContinuousLinearMap.coe_injective
+ ext; simp
+
+theorem liftIsometry_comp_mapL (h : ContinuousMultilinearMap 𝕜 E' F) :
+ liftIsometry 𝕜 E' F h ∘L mapL f = liftIsometry 𝕜 E F (h.compContinuousLinearMap f) := by
+ apply ContinuousLinearMap.coe_injective
+ ext; simp
+
+@[simp]
+theorem mapL_id : mapL (fun i ↦ ContinuousLinearMap.id 𝕜 (E i)) = ContinuousLinearMap.id _ _ := by
+ apply ContinuousLinearMap.coe_injective
+ ext; simp
+
+@[simp]
+theorem mapL_one : mapL (fun (i : ι) ↦ (1 : E i →L[𝕜] E i)) = 1 :=
+ mapL_id
+
+theorem mapL_mul (f₁ f₂ : Π i, E i →L[𝕜] E i) :
+ mapL (fun i ↦ f₁ i * f₂ i) = mapL f₁ * mapL f₂ :=
+ mapL_comp f₁ f₂
+
+/-- Upgrading `PiTensorProduct.mapL` to a `MonoidHom` when `E = E'`. -/
+@[simps]
+noncomputable def mapLMonoidHom : (Π i, E i →L[𝕜] E i) →* ((⨂[𝕜] i, E i) →L[𝕜] ⨂[𝕜] i, E i) where
+ toFun := mapL
+ map_one' := mapL_one
+ map_mul' := mapL_mul
+
+@[simp]
+protected theorem mapL_pow (f : Π i, E i →L[𝕜] E i) (n : ℕ) :
+ mapL (f ^ n) = mapL f ^ n := MonoidHom.map_pow mapLMonoidHom f n
+
+-- We redeclare `ι` here, and later dependent arguments,
+-- to avoid the `[Fintype ι]` assumption present throughout the rest of the file.
+open Function in
+private theorem mapL_add_smul_aux {ι : Type*}
+ {E : ι → Type*} [∀ i, SeminormedAddCommGroup (E i)] [∀ i, NormedSpace 𝕜 (E i)]
+ {E' : ι → Type*} [∀ i, SeminormedAddCommGroup (E' i)] [∀ i, NormedSpace 𝕜 (E' i)]
+ (f : (i : ι) → E i →L[𝕜] E' i) [DecidableEq ι] (i : ι) (u : E i →L[𝕜] E' i) :
+ (fun j ↦ (update f i u j).toLinearMap) =
+ update (fun j ↦ (f j).toLinearMap) i u.toLinearMap := by
+ grind
+
+open Function in
+protected theorem mapL_add [DecidableEq ι] (i : ι) (u v : E i →L[𝕜] E' i) :
+ mapL (update f i (u + v)) = mapL (update f i u) + mapL (update f i v) := by
+ ext
+ simp [mapL_add_smul_aux, PiTensorProduct.map_update_add]
+
+open Function in
+protected theorem mapL_smul [DecidableEq ι] (i : ι) (c : 𝕜) (u : E i →L[𝕜] E' i) :
+ mapL (update f i (c • u)) = c • mapL (update f i u) := by
+ ext
+ simp [mapL_add_smul_aux, PiTensorProduct.map_update_smul]
+
+theorem opNorm_mapL : ‖mapL f‖ ≤ ∏ i, ‖f i‖ := by
+ refine (ContinuousLinearMap.opNorm_le_iff (by positivity)).mpr fun x ↦ ?_
+ apply le_trans (norm_eval_le_projectiveSeminorm ..) (mul_le_mul_of_nonneg_right _ (norm_nonneg x))
+ refine (ContinuousMultilinearMap.opNorm_le_iff (by positivity)).mpr fun m ↦ ?_
+ apply le_trans (projectiveSeminorm_tprod_le fun i ↦ f i (m i))
+ rw [← Finset.prod_mul_distrib]
+ gcongr
+ exact ContinuousLinearMap.le_opNorm _ _
+
+variable (𝕜 E E')
+
+/-- The tensor of a family of linear maps from `Eᵢ` to `E'ᵢ`, as a continuous multilinear map of
+the family. -/
+@[simps! toFun_apply]
+noncomputable def mapLMultilinear : ContinuousMultilinearMap 𝕜 (fun (i : ι) ↦ E i →L[𝕜] E' i)
+ ((⨂[𝕜] i, E i) →L[𝕜] ⨂[𝕜] i, E' i) :=
+ MultilinearMap.mkContinuous
+ { toFun := mapL
+ map_update_smul' := fun _ _ _ _ ↦ PiTensorProduct.mapL_smul _ _ _ _
+ map_update_add' := fun _ _ _ _ ↦ PiTensorProduct.mapL_add _ _ _ _ }
+ 1 (fun f ↦ by rw [one_mul]; exact opNorm_mapL f)
+
+variable {𝕜 E E'}
+
+theorem opNorm_mapLMultilinear_le : ‖mapLMultilinear 𝕜 E E'‖ ≤ 1 :=
+ MultilinearMap.mkContinuous_norm_le _ zero_le_one _
+
+end map
+
end NontriviallyNormedField
end PiTensorProduct
From cb934192a12300b675308b0469207f53b3f8e4c8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=ABl=20Riou?=
<37772949+joelriou@users.noreply.github.com>
Date: Thu, 18 Jun 2026 10:58:13 +0000
Subject: [PATCH 0135/1300] =?UTF-8?q?feat(CategoryTheory):=20the=20=CE=BA-?=
=?UTF-8?q?accessible=20category=20of=20=CE=BA-directed=20posets=20(#39655?=
=?UTF-8?q?)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Given a regular cardinal `κ : Cardinal.{u}`, we show that the category `CardinalFilteredPoset κ` of `κ`-directed partially ordered types (with order embeddings as morphisms) is a `κ`-accessible category.
---
.../Presentable/CardinalDirectedPoset.lean | 191 +++++++++++++++++-
1 file changed, 189 insertions(+), 2 deletions(-)
diff --git a/Mathlib/CategoryTheory/Presentable/CardinalDirectedPoset.lean b/Mathlib/CategoryTheory/Presentable/CardinalDirectedPoset.lean
index 287c35be55071b..8217cf1f7859a6 100644
--- a/Mathlib/CategoryTheory/Presentable/CardinalDirectedPoset.lean
+++ b/Mathlib/CategoryTheory/Presentable/CardinalDirectedPoset.lean
@@ -14,8 +14,13 @@ public import Mathlib.Order.Category.PartOrdEmb
Given a regular cardinal `κ : Cardinal.{u}`, we define the
category `CardinalFilteredPoset κ` of `κ`-directed partially ordered
-types (with order embeddings as morphisms). We shall show that it is
-a `κ`-accessible category (TODO @joelriou).
+types (with order embeddings as morphisms), and we show that it is
+a `κ`-accessible category.
+
+If `κ ≤ κ'` where `κ'` is also a regular cardinal, we characterize
+the `κ'`-presentable objects of `CardinalFilteredPoset κ` as
+the objects `J` such that the underlying type `J.obj` has
+cardinality `< κ'`.
## References
* [Adámek, J. and Rosický, J., *Locally presentable and accessible categories*][Adamek_Rosicky_1994]
@@ -105,6 +110,13 @@ abbrev of (J : PartOrdEmb.{u}) [IsCardinalFiltered J κ] : CardinalFilteredPoset
obj := J
property := inferInstance
+lemma Hom.injective {J₁ J₂ : CardinalFilteredPoset κ} (f : J₁ ⟶ J₂) :
+ Function.Injective f := f.hom.injective
+
+lemma Hom.le_iff_le {J₁ J₂ : CardinalFilteredPoset κ} (f : J₁ ⟶ J₂) (x₁ x₂ : J₁.obj) :
+ f x₁ ≤ f x₂ ↔ x₁ ≤ x₂ :=
+ f.hom.hom.le_iff_le
+
instance (J : CardinalFilteredPoset κ) : IsCardinalFiltered J.obj κ := J.property
instance (J : CardinalFilteredPoset κ) : IsFiltered J.obj :=
@@ -170,6 +182,77 @@ noncomputable def isColimitCoconeOfPredicateSet
end
+variable (κ) in
+/-- The property of posets in `CardinalFilteredPoset κ` that are
+of cardinality `< κ` and have terminal object. -/
+def hasCardinalLTWithTerminal : ObjectProperty (CardinalFilteredPoset κ) :=
+ fun J ↦ HasCardinalLT J.obj κ ∧ HasTerminal J.obj
+
+instance : ObjectProperty.EssentiallySmall.{u} (hasCardinalLTWithTerminal κ) where
+ exists_small_le' := by
+ obtain ⟨X, hX⟩ : ∃ (X : Type u), Cardinal.mk X = κ := ⟨κ.ord.ToType, by simp⟩
+ let α : Type u := Σ (S : Set X) (_ : PartialOrder S),
+ ULift.{u} (PLift (IsCardinalFiltered S κ))
+ let (a : α) : PartialOrder a.1 := a.2.1
+ let ι (a : α) : CardinalFilteredPoset κ :=
+ { obj := .of a.1
+ property := a.2.2.down.down }
+ refine ⟨.ofObj ι, inferInstance, fun J ⟨hJ, _⟩ ↦ ?_⟩
+ obtain ⟨f⟩ : Cardinal.mk J.obj ≤ Cardinal.mk X := by
+ simpa [hX] using ((hasCardinalLT_iff_cardinal_mk_lt _ _).1 hJ).le
+ let e := Equiv.ofInjective _ f.injective
+ letI : PartialOrder (Set.range f) := PartialOrder.lift _ e.symm.injective
+ let e' : Set.range f ≃o J.obj := { toEquiv := e.symm, map_rel_iff' := by rfl }
+ exact ⟨_, ⟨⟨Set.range f, inferInstance,
+ ⟨⟨IsCardinalFiltered.of_equivalence κ e'.symm.equivalence⟩⟩⟩⟩,
+ ⟨CardinalFilteredPoset.ι.preimageIso (PartOrdEmb.Iso.mk (by exact e'.symm))⟩⟩
+
+set_option backward.defeqAttrib.useBackward true in
+set_option backward.isDefEq.respectTransparency false in
+lemma isCardinalPresentable_of_hasCardinalLT_of_le (J : CardinalFilteredPoset κ)
+ {κ' : Cardinal.{u}} [Fact κ'.IsRegular] (hJ : HasCardinalLT J.obj κ') (h : κ ≤ κ') :
+ IsCardinalPresentable J κ' where
+ preservesColimitOfShape A _ _ := ⟨fun {F} ↦ ⟨fun {c} hc ↦ ⟨by
+ · have := isFiltered_of_isCardinalFiltered A κ'
+ have := IsCardinalFiltered.of_le A h
+ replace hc := isColimitOfPreserves (forget _) hc
+ refine Types.FilteredColimit.isColimitOf' _ _ (fun f ↦ ?_) (fun j f g h ↦ ?_)
+ · dsimp at f
+ choose j g hg using fun (x : J.obj) ↦ Types.jointly_surjective_of_isColimit hc (f x)
+ let m := IsCardinalFiltered.max j hJ
+ let φ (x : J.obj) : (F.obj m).obj := F.map (IsCardinalFiltered.toMax j hJ x) (g x)
+ have hφ (x : J.obj) : f x = c.ι.app _ (φ x) := by
+ dsimp [φ]
+ rw [← hg, ← ConcreteCategory.comp_apply, c.w]
+ rfl
+ refine ⟨m,
+ ObjectProperty.homMk (PartOrdEmb.ofHom
+ { toFun := φ
+ inj' x y h := Hom.injective f (by simpa [hφ])
+ map_rel_iff' {x y} := ?_ }), ?_⟩
+ · simp [← Hom.le_iff_le f, hφ]
+ · dsimp
+ ext x
+ trans c.ι.app (j x) (g x)
+ · exact (hg x).symm
+ · exact (ConcreteCategory.congr_hom (c.w (IsCardinalFiltered.toMax j hJ x)).symm (g x))
+ · choose k a hk using fun (x : J.obj) ↦
+ (Types.FilteredColimit.isColimit_eq_iff' hc _ _).1 (ConcreteCategory.congr_hom h x)
+ dsimp at f g h k a hk ⊢
+ obtain ⟨l, b, c, hl⟩ : ∃ (l : A) (c : j ⟶ l) (b : ∀ x, k x ⟶ l),
+ ∀ x, a x ≫ b x = c := by
+ let φ (x : J.obj) : j ⟶ IsCardinalFiltered.max k hJ :=
+ a x ≫ IsCardinalFiltered.toMax k hJ x
+ exact ⟨IsCardinalFiltered.coeq φ hJ,
+ IsCardinalFiltered.toCoeq φ hJ,
+ fun x ↦ IsCardinalFiltered.toMax k hJ x ≫ IsCardinalFiltered.coeqHom φ hJ,
+ fun x ↦ by simpa [φ] using IsCardinalFiltered.coeq_condition φ hJ x⟩
+ refine ⟨l, b, ?_⟩
+ ext x
+ simpa only [← hl x, Functor.map_comp, ObjectProperty.FullSubcategory.comp_hom,
+ PartOrdEmb.hom_comp, RelEmbedding.coe_trans, Function.comp_apply]
+ using! congr_arg _ (hk x)⟩⟩⟩
+
section
variable (J : CardinalFilteredPoset κ)
@@ -240,8 +323,112 @@ noncomputable def isColimitCoconeWithTop : IsColimit (coconeWithTop J κ') :=
| some a => exact ⟨_, propSetWithTop_pair _ a, by aesop⟩
| none => exact ⟨_, propSetWithTop_pair _ (Classical.arbitrary _), by aesop⟩)
+variable {κ'} in
+protected lemma isCardinalPresentable_iff (h : κ ≤ κ') :
+ IsCardinalPresentable J κ' ↔ HasCardinalLT J.obj κ' := by
+ refine ⟨fun _ ↦ ?_, fun hJ ↦ isCardinalPresentable_of_hasCardinalLT_of_le _ hJ h⟩
+ obtain ⟨X, f, hf⟩ :=
+ IsCardinalPresentable.exists_hom_of_isColimit κ' (isColimitCoconeWithTop J κ')
+ (ObjectProperty.homMk (PartOrdEmb.ofHom WithTop.coeOrderHom))
+ replace hf : OrderEmbedding.subtype X.1 ∘ f = WithTop.coeOrderHom := by
+ ext x
+ exact ConcreteCategory.congr_hom hf x
+ refine X.2.1.of_injective f (Function.Injective.of_comp
+ (f := OrderEmbedding.subtype X.1) ?_)
+ dsimp at hf ⊢
+ rw [hf]
+ exact WithTop.coe_injective
+
end
+protected lemma isCardinalPresentable_iff' (J : CardinalFilteredPoset κ) :
+ IsCardinalPresentable J κ ↔ HasCardinalLT J.obj κ :=
+ CardinalFilteredPoset.isCardinalPresentable_iff _ (le_refl _)
+
+section
+
+variable (J : CardinalFilteredPoset κ)
+
+/-- Given `J : CardinalFilteredPoset κ`, this is the predicate
+on `Set J.obj` that is satisfied by subsets that are of
+cardinality `< κ` and have a terminal object. -/
+def PropSet (S : Set J.obj) : Prop :=
+ HasCardinalLT S κ ∧ HasTerminal S
+
+instance (S : Subtype J.PropSet) : HasTerminal S := S.prop.2
+
+instance (S : Subtype J.PropSet) : IsCardinalFiltered S κ :=
+ isCardinalFiltered_of_hasTerminal _ _
+
+variable {J} in
+lemma propSet_singleton (j : J.obj) : J.PropSet {j} :=
+ ⟨hasCardinalLT_of_finite _ _ (Cardinal.IsRegular.aleph0_le Fact.out), by
+ let : OrderTop ({j} : Set J.obj) := { top := ⟨j, rfl⟩, le_top := by simp }
+ exact isTerminalTop.hasTerminal⟩
+
+instance : IsCardinalFiltered (Subtype J.PropSet) κ :=
+ isCardinalFiltered_preorder _ _ (fun K α hK ↦ by
+ rw [← hasCardinalLT_iff_cardinal_mk_lt] at hK
+ let t (k : K) : (α k).val := ⊤_ _
+ let m := IsCardinalFiltered.max (fun k ↦ (t k).val) hK
+ let S : Set J.obj := (⋃ (k : K), α k) ∪ {m}
+ let : OrderTop S :=
+ { top := ⟨m, by simp [S]⟩
+ le_top := by
+ rintro ⟨s, hs⟩
+ simp only [Set.union_singleton, Set.mem_insert_iff, Set.mem_iUnion, S] at hs
+ obtain rfl | ⟨k, hs⟩ := hs
+ · simp
+ · simp only [Subtype.mk_le_mk]
+ exact leOfHom ((by exact terminal.from (C := (α k).val) ⟨_, hs⟩) ≫
+ IsCardinalFiltered.toMax _ hK k) }
+ refine ⟨⟨S, ?_, isTerminalTop.hasTerminal⟩, fun k ↦ ?_⟩
+ · have hκ : Cardinal.aleph0 ≤ κ := Cardinal.IsRegular.aleph0_le Fact.out
+ exact hasCardinalLT_union hκ (hasCardinalLT_iUnion _ hK (fun k ↦ (α k).2.1))
+ (hasCardinalLT_of_finite _ _ hκ)
+ · simp only [← Subtype.coe_le_coe, Set.le_eq_subset]
+ exact subset_trans (Set.subset_iUnion_of_subset k (subset_refl _)) Set.subset_union_left )
+
+instance : IsFiltered (Subtype J.PropSet) := isFiltered_of_isCardinalFiltered _ κ
+
+instance : IsDirectedOrder (Subtype J.PropSet) :=
+ IsFiltered.isDirectedOrder _
+
+instance : Nonempty (Subtype J.PropSet) :=
+ IsFiltered.nonempty
+
+/-- For any object `J : CardinalFilteredPoset κ`, this is a colimit
+cocone exhibiting `J` as the colimit of its subsets
+that are of cardinality `< κ` and have a terminal object. -/
+abbrev cocone : Cocone (functorOfPredicateSet J.PropSet) :=
+ coconeOfPredicateSet J.PropSet
+
+/-- Any object `J : CardinalFilteredPoset κ` is a colimit
+of its subsets that are of cardinality `< κ` and have a terminal object. -/
+noncomputable def isColimitCocone (J : CardinalFilteredPoset κ) :
+ IsColimit (cocone J) :=
+ isColimitCoconeOfPredicateSet _ (fun a ↦ ⟨_, propSet_singleton a, by simp⟩)
+
+end
+
+variable (κ) in
+lemma isCardinalFilteredGenerator_hasCardinalLTWithTerminal :
+ (hasCardinalLTWithTerminal κ).IsCardinalFilteredGenerator κ where
+ le_isCardinalPresentable := by
+ rintro J ⟨_, _⟩
+ rwa [isCardinalPresentable_iff, J.isCardinalPresentable_iff']
+ exists_colimitsOfShape J :=
+ ⟨_, inferInstance, inferInstance, ⟨{
+ diag := _
+ ι := _
+ isColimit := isColimitCocone J
+ prop_diag_obj j := j.prop }⟩⟩
+
+instance : IsCardinalAccessibleCategory (CardinalFilteredPoset κ) κ where
+ exists_generator :=
+ ⟨hasCardinalLTWithTerminal κ, inferInstance,
+ isCardinalFilteredGenerator_hasCardinalLTWithTerminal κ⟩
+
end CardinalFilteredPoset
end CategoryTheory
From ec6e08b4dfb414460f0ee7b4ce9678e7e04fb584 Mon Sep 17 00:00:00 2001
From: Xavier Roblot <46200072+xroblot@users.noreply.github.com>
Date: Thu, 18 Jun 2026 10:58:15 +0000
Subject: [PATCH 0136/1300] feat(FieldTheory/IntermediateField): add
eq_iff_finrank_eq_of_le(') (#40300)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Adds the iff-strengthenings of `IntermediateField.eq_of_le_of_finrank_eq` and `eq_of_le_of_finrank_eq'`:
- `eq_iff_finrank_eq_of_le`: for `F ≤ E` with `[E : K]` finite, `F = E ↔ finrank K F = finrank K E`.
- `eq_iff_finrank_eq_of_le'`: for `F ≤ E` with `[L : F]` finite, `F = E ↔ finrank F L = finrank E L`.
:robot: This PR was extracted from the [SKW project](https://github.com/xroblot/SKW) by Claude.
---
Mathlib/FieldTheory/IntermediateField/Algebraic.lean | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/Mathlib/FieldTheory/IntermediateField/Algebraic.lean b/Mathlib/FieldTheory/IntermediateField/Algebraic.lean
index a6f80e8bd9464c..0898d2e4d45ed6 100644
--- a/Mathlib/FieldTheory/IntermediateField/Algebraic.lean
+++ b/Mathlib/FieldTheory/IntermediateField/Algebraic.lean
@@ -75,6 +75,12 @@ theorem eq_of_le_of_finrank_eq [FiniteDimensional K E] (h_le : F ≤ E)
(h_finrank : finrank K F = finrank K E) : F = E :=
eq_of_le_of_finrank_le h_le h_finrank.ge
+/-- If `F ≤ E` are two intermediate fields of `L / K` such that `[E : K]` is finite,
+then `F = E` iff `[F : K] = [E : K]`. -/
+theorem eq_iff_finrank_eq_of_le [FiniteDimensional K E] (h_le : F ≤ E) :
+ F = E ↔ finrank K F = finrank K E :=
+ ⟨fun h ↦ by rw [h], eq_of_le_of_finrank_eq h_le⟩
+
-- If `F ≤ E` are two intermediate fields of a finite extension `L / K` such that
-- `[L : F] ≤ [L : E]`, then `F = E`. Marked as private since it's a direct corollary of
-- `eq_of_le_of_finrank_le'` (the `FiniteDimensional K L` implies `FiniteDimensional F L`
@@ -101,6 +107,12 @@ theorem eq_of_le_of_finrank_eq' [FiniteDimensional F L] (h_le : F ≤ E)
(h_finrank : finrank F L = finrank E L) : F = E :=
eq_of_le_of_finrank_le' h_le h_finrank.le
+/-- If `F ≤ E` are two intermediate fields of `L / K` such that `[L : F]` is finite,
+then `F = E` iff `[L : F] = [L : E]`. -/
+theorem eq_iff_finrank_eq_of_le' [FiniteDimensional F L] (h_le : F ≤ E) :
+ F = E ↔ finrank F L = finrank E L :=
+ ⟨fun h ↦ by rw [h], eq_of_le_of_finrank_eq' h_le⟩
+
lemma finrank_lt_of_gt [FiniteDimensional F L] (H : F < E) :
Module.finrank E L < Module.finrank F L := by
letI := (IntermediateField.inclusion H.le).toAlgebra
From 028b72cdeb34a8dcf80f29ba01136ce9dc86ec15 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=ABl=20Riou?=
<37772949+joelriou@users.noreply.github.com>
Date: Thu, 18 Jun 2026 11:17:00 +0000
Subject: [PATCH 0137/1300] =?UTF-8?q?feat(AlgebraicGeometry):=20points=20o?=
=?UTF-8?q?f=20the=20small=20=C3=A9tale=20site=20(#35136)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The main definition in this PR is `Scheme.pointSmallEtale`. Given a morphism `Spec (.of Ω) ⟶ S` where `Ω` is
a separably closed field, we define the corresponding point of the small étale site of `S`. We show that these points form a conservative family.
(This PR also removes the definition `Scheme.geometricFiber` which was not correct.)
Co-authored-by: Christian Merten
---
Mathlib.lean | 1 +
Mathlib/AlgebraicGeometry/Fiber.lean | 5 +
.../AlgebraicGeometry/Morphisms/Etale.lean | 50 +++++-
Mathlib/AlgebraicGeometry/Sites/Etale.lean | 31 ++++
.../AlgebraicGeometry/Sites/EtalePoint.lean | 164 ++++++++++++++++++
Mathlib/CategoryTheory/Elements.lean | 21 +++
Mathlib/CategoryTheory/EssentialImage.lean | 12 +-
.../CategoryTheory/Limits/FinallySmall.lean | 11 ++
.../Limits/MorphismProperty.lean | 5 +-
.../MorphismProperty/Comma.lean | 2 +-
.../MorphismProperty/CommaSites.lean | 2 +
.../CategoryTheory/ObjectProperty/Small.lean | 5 +
12 files changed, 302 insertions(+), 7 deletions(-)
create mode 100644 Mathlib/AlgebraicGeometry/Sites/EtalePoint.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 3093c4df4064be..73d920207c69ad 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -1458,6 +1458,7 @@ public import Mathlib.AlgebraicGeometry.Sites.BigZariski
public import Mathlib.AlgebraicGeometry.Sites.ConstantSheaf
public import Mathlib.AlgebraicGeometry.Sites.ElladicCohomology
public import Mathlib.AlgebraicGeometry.Sites.Etale
+public import Mathlib.AlgebraicGeometry.Sites.EtalePoint
public import Mathlib.AlgebraicGeometry.Sites.Fpqc
public import Mathlib.AlgebraicGeometry.Sites.MorphismProperty
public import Mathlib.AlgebraicGeometry.Sites.Pretopology
diff --git a/Mathlib/AlgebraicGeometry/Fiber.lean b/Mathlib/AlgebraicGeometry/Fiber.lean
index bb0a2929f09ea1..186ddc1e9f5700 100644
--- a/Mathlib/AlgebraicGeometry/Fiber.lean
+++ b/Mathlib/AlgebraicGeometry/Fiber.lean
@@ -47,6 +47,11 @@ def Scheme.Hom.fiberToSpecResidueField (f : X ⟶ Y) (y : Y) :
f.fiber y ⟶ Spec (Y.residueField y) :=
pullback.snd _ _
+@[reassoc]
+lemma Scheme.Hom.fiber_fac (f : X ⟶ Y) (y : Y) :
+ f.fiberι y ≫ f = f.fiberToSpecResidueField y ≫ Y.fromSpecResidueField y :=
+ pullback.condition
+
/-- The fiber of `f` at `y` is naturally a `κ(y)`-scheme. -/
@[reducible] def Scheme.Hom.fiberOverSpecResidueField
(f : X ⟶ Y) (y : Y) : (f.fiber y).Over (Spec (Y.residueField y)) where
diff --git a/Mathlib/AlgebraicGeometry/Morphisms/Etale.lean b/Mathlib/AlgebraicGeometry/Morphisms/Etale.lean
index 52bc757775e5a9..0b4f6f16974b8b 100644
--- a/Mathlib/AlgebraicGeometry/Morphisms/Etale.lean
+++ b/Mathlib/AlgebraicGeometry/Morphisms/Etale.lean
@@ -149,21 +149,65 @@ namespace Scheme
/-- The category `Etale X` is the category of schemes étale over `X`. -/
protected def Etale (X : Scheme.{u}) : Type _ := MorphismProperty.Over @Etale ⊤ X
-deriving Category, HasPullbacks
+deriving Category, HasPullbacks, HasFiniteLimits
variable (X : Scheme.{u})
-instance (Y : X.Etale) : Etale Y.hom := Y.prop
+set_option backward.defeqAttrib.useBackward true in
+instance (Y : X.Etale) : dsimp% Etale Y.hom := Y.prop
+
+instance {X : Scheme.{u}} {Z Y : X.Etale} (f : Z ⟶ Y) : Etale f.left := by
+ have : Etale (f.left ≫ Y.hom) := by rw [CategoryTheory.Over.w]; infer_instance
+ exact Etale.of_comp f.left Y.hom
/-- The forgetful functor from schemes étale over `X` to schemes over `X`. -/
def Etale.forget : X.Etale ⥤ Over X :=
MorphismProperty.Over.forget @Etale ⊤ X
-deriving Functor.Full, Functor.Faithful
/-- The forgetful functor from schemes étale over `X` to schemes over `X` is fully faithful. -/
def Etale.forgetFullyFaithful : (Etale.forget X).FullyFaithful :=
MorphismProperty.Comma.forgetFullyFaithful _ _ _
+-- Note: using `deriving Functor.Full/Faithful` in the declaration of `Etale.forget`
+-- would "succeed", but it seems it would fail to create the next two instances
+instance : (Etale.forget X).Full :=
+ (Etale.forgetFullyFaithful X).full
+
+instance : (Etale.forget X).Faithful :=
+ (Etale.forgetFullyFaithful X).faithful
+
+variable {X} in
+/-- Constructor for objects in the étale site of a scheme `X`: it takes
+an étale morphism `f : Y ⟶ X` as an input. -/
+abbrev Etale.mk {Y : Scheme.{u}} (f : Y ⟶ X) [Etale f] : X.Etale :=
+ MorphismProperty.Over.mk _ f inferInstance
+
+variable {X} in
+@[simp]
+lemma Etale.forget_mk {Y : Scheme.{u}} (f : Y ⟶ X) [Etale f] :
+ (Etale.forget X).obj (.mk f) = Over.mk f := rfl
+
+@[simp]
+lemma Etale.forget_obj_left (Y : X.Etale) :
+ ((Etale.forget X).obj Y).left = Y.left := rfl
+
+@[simp]
+lemma Etale.forget_obj_hom (Y : X.Etale) :
+ ((Etale.forget X).obj Y).hom = Y.hom := rfl
+
+instance (Y : X.Etale) : Etale (Y.left ↘ X) := Y.prop
+
+/-- Induction principle for the objects of the small étale site of a scheme. -/
+@[elab_as_elim, cases_eliminator, induction_eliminator]
+def Etale.rec {motive : X.Etale → Sort*}
+ (mk : ∀ (Y : Scheme.{u}) (f : Y ⟶ X) (_ : Etale f), motive (Etale.mk f))
+ (T : X.Etale) :
+ motive T :=
+ mk _ _ T.prop
+
+instance : PreservesFiniteLimits (Etale.forget X) :=
+ inferInstanceAs (PreservesFiniteLimits (MorphismProperty.Over.forget _ ⊤ X))
+
end Scheme
end AlgebraicGeometry
diff --git a/Mathlib/AlgebraicGeometry/Sites/Etale.lean b/Mathlib/AlgebraicGeometry/Sites/Etale.lean
index b54e73a748fcdd..6bcfcdeea6eb76 100644
--- a/Mathlib/AlgebraicGeometry/Sites/Etale.lean
+++ b/Mathlib/AlgebraicGeometry/Sites/Etale.lean
@@ -54,6 +54,37 @@ def smallEtaleTopology (X : Scheme.{u}) : GrothendieckTopology X.Etale :=
def smallEtalePretopology (X : Scheme.{u}) : Pretopology X.Etale :=
X.smallPretopology (Q := @Etale) (P := @Etale)
+set_option backward.defeqAttrib.useBackward true in
+set_option backward.isDefEq.respectTransparency false in
+lemma ofArrows_mem_smallEtaleTopology_iff
+ {X : Scheme.{u}} {W : X.Etale} {ι : Type*}
+ {Z : ι → X.Etale} (f : ∀ i, Z i ⟶ W) :
+ Sieve.ofArrows _ f ∈ smallEtaleTopology _ _ ↔
+ ⋃ i, Set.range (f i).left = .univ := by
+ refine ⟨fun hf ↦ ?_, fun hf ↦ (mem_smallGrothendieckTopology _ _).2 ?_⟩
+ · obtain ⟨U, _, _, hU⟩ := (mem_smallGrothendieckTopology _ _).1 hf
+ ext y
+ simp only [Set.mem_iUnion, Set.mem_range, Set.mem_univ, iff_true]
+ obtain ⟨i, ⟨u, rfl⟩⟩ := ((ofArrows_mem_precoverage_iff _).1 U.mem₀).1 y
+ obtain ⟨_, b, _, ⟨j⟩, fac⟩ := hU _ _ ⟨i⟩
+ replace fac : b.left ≫ (f j).left = U.f i :=
+ (Etale.forget _ ⋙ CategoryTheory.Over.forget _).congr_map fac
+ exact ⟨j, b.left u, by simp [← fac]⟩
+ · have (w : W.left) : ∃ (i : ι), w ∈ Set.range (f i).left := by
+ have := Set.mem_univ w
+ simpa [← hf]
+ choose i z hz using this
+ let V : Cover (precoverage @Etale) W.left :=
+ Cover.mkOfCovers W.left (fun w ↦ (Z (i w)).left)
+ (fun w ↦ (f (i w)).left) (fun w ↦ ⟨_, _, hz w⟩) inferInstance
+ letI : Cover.Over X V :=
+ { over w := ⟨(Z (i w)).hom⟩
+ isOver_map w := by cat_disch }
+ have (w : W.left) : Etale (V.X w ↘ X) := (Z (i w)).prop
+ refine ⟨V, inferInstance, inferInstance, ?_⟩
+ rintro _ _ ⟨w⟩
+ refine ⟨_, 𝟙 _, _, ⟨i w⟩, by cat_disch⟩
+
instance {S : Scheme.{u}} (𝒰 : S.Cover (precoverage @Etale)) (i : 𝒰.I₀) : Etale (𝒰.f i) :=
𝒰.map_prop i
diff --git a/Mathlib/AlgebraicGeometry/Sites/EtalePoint.lean b/Mathlib/AlgebraicGeometry/Sites/EtalePoint.lean
new file mode 100644
index 00000000000000..46192dcbf7394a
--- /dev/null
+++ b/Mathlib/AlgebraicGeometry/Sites/EtalePoint.lean
@@ -0,0 +1,164 @@
+/-
+Copyright (c) 2026 Joël Riou. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Christian Merten, Joël Riou
+-/
+module
+
+public import Mathlib.AlgebraicGeometry.Fiber
+public import Mathlib.AlgebraicGeometry.Sites.AffineEtale
+public import Mathlib.CategoryTheory.Functor.TypeValuedFlat
+public import Mathlib.CategoryTheory.Limits.Elements
+public import Mathlib.CategoryTheory.Sites.Point.Conservative
+
+public import Mathlib.FieldTheory.SeparableClosure
+
+/-!
+
+# Points of the étale site
+
+In this file, we show that a morphism `Spec (.of Ω) ⟶ S` where `Ω` is
+a separably closed field defines a point on the small étale site of `S`.
+We show that these points form a conservative family.
+
+-/
+
+@[expose] public section
+
+universe u
+
+open CategoryTheory Opposite
+
+namespace AlgebraicGeometry.Scheme
+
+variable {S : Scheme.{u}} {Ω : Type u} [Field Ω] [IsSepClosed Ω]
+ (s : Spec (.of Ω) ⟶ S)
+
+lemma exists_fac_of_etale_of_isSepClosed {X S : Scheme.{u}} (f : X ⟶ S) [Etale f]
+ {Ω : Type u} [Field Ω] [IsSepClosed Ω] (s : Spec (.of Ω) ⟶ S)
+ (x : X) (hx : f x = s default) :
+ ∃ (l : Spec (.of Ω) ⟶ X), l ≫ f = s ∧ l default = x := by
+ obtain ⟨⟨s, a⟩, rfl⟩ := (SpecToEquivOfField Ω S).symm.surjective s
+ obtain rfl : f x = s := by simp [hx, SpecToEquivOfField]
+ let m := (f.residueFieldMap x).hom
+ dsimp at m
+ algebraize [m, a.hom]
+ let b : X.residueField x →ₐ[S.residueField (f x)] Ω :=
+ IsSepClosed.lift
+ have : f.residueFieldMap x ≫ CommRingCat.ofHom b.toRingHom = a := by
+ ext1; exact b.comp_algebraMap
+ refine ⟨Spec.map (CommRingCat.ofHom b.toRingHom) ≫ X.fromSpecResidueField x, ?_, ?_⟩
+ · simp [SpecToEquivOfField, ← this]
+ rfl
+ · dsimp
+ apply fromSpecResidueField_apply
+
+instance : IsCofiltered (Etale.forget S ⋙ coyoneda.obj (op (Over.mk s))).Elements :=
+ Functor.isCofiltered_elements _
+
+set_option backward.defeqAttrib.useBackward true in
+/-- A morphism `s : Spec (.of Ω) ⟶ S` where `Ω` is a separably closed field
+defines a point for the small étale site of `S`. -/
+@[simps]
+noncomputable def pointSmallEtale : (smallEtaleTopology S).Point where
+ fiber := Etale.forget S ⋙ coyoneda.obj (op (Over.mk s))
+ initiallySmall :=
+ initiallySmall_of_essentiallySmall_weakly_initial_objectProperty
+ (Functor.Elements.precomp (AffineEtale.Spec S)
+ (Etale.forget S ⋙ coyoneda.obj (op (Over.mk s)))).essImage (by
+ rintro ⟨X, x⟩
+ cases X with | _ Y f
+ obtain ⟨y, hy, rfl⟩ := Over.homMk_surjective x
+ dsimp at y hy
+ obtain ⟨R, j, _, y', rfl⟩ : ∃ (R : CommRingCat) (j : Spec (.of R) ⟶ Y)
+ (_ : IsOpenImmersion j) (y' : _ ⟶ _), y' ≫ j = y := by
+ obtain ⟨R, j, _, hj, _⟩ := exists_affine_mem_range_and_range_subset
+ (x := y.base default) (U := ⊤) (by simp)
+ refine ⟨R, j, inferInstance, _, IsOpenImmersion.lift_fac j y ?_⟩
+ rintro _ ⟨a, rfl⟩
+ rwa [Subsingleton.elim a default]
+ exact ⟨_,
+ ⟨Functor.elementsMk _ (AffineEtale.mk (j ≫ f)) (Over.homMk y'), ⟨Iso.refl _⟩⟩,
+ ⟨⟨MorphismProperty.Over.homMk j rfl (by simp), by cat_disch⟩⟩⟩)
+ jointly_surjective {X} R hR φ := by
+ cases X with | _ X f
+ obtain ⟨φ : Spec (.of Ω) ⟶ X, rfl : φ ≫ f = s, rfl⟩ := Over.homMk_surjective φ
+ obtain ⟨𝒰, h, _, le⟩ := (mem_smallGrothendieckTopology _ _).1 hR
+ obtain ⟨i, y, hy⟩ := 𝒰.exists_eq (φ default)
+ obtain ⟨l, hl₁, hl₂⟩ := exists_fac_of_etale_of_isSepClosed (𝒰.f i) φ _ hy
+ have : 𝒰.f i ≫ f = 𝒰.X i ↘ S := HomIsOver.comp_over (f := 𝒰.f i) (S := S)
+ exact ⟨(𝒰.X i).asOverProp S inferInstance,
+ MorphismProperty.Over.homMk (𝒰.f i), le _ _ ⟨i⟩, Over.homMk l, by cat_disch⟩
+
+variable {s₀ : S} (hs₀ : s default = s₀)
+
+/-- Given a morphism `s : Spec (.of Ω) ⟶ S` with image `s₀ : S` where `Ω` is a
+separably closed field, this is the canonical map
+`(pointSmallEtale s).fiber.obj X ⟶ X.hom ⁻¹' {s₀}` for `X : S.Etale`. -/
+@[simps]
+noncomputable def pointSmallEtaleFiberObjToPreimage {X : S.Etale}
+ (t : (pointSmallEtale s).fiber.obj X) :
+ X.hom ⁻¹' {s₀} :=
+ ⟨t.left (default : Spec (.of Ω)), by
+ have := (Over.w t).symm
+ cat_disch⟩
+
+set_option backward.isDefEq.respectTransparency false in
+instance {Y X : Scheme.{u}} (f : Y ⟶ X) [Etale f] (x : X) :
+ Etale (f.fiberToSpecResidueField x) := by
+ dsimp [Hom.fiberToSpecResidueField]
+ infer_instance
+
+set_option backward.isDefEq.respectTransparency false in
+lemma pointSmallEtaleFiberObjToPreimage_surjective (X : S.Etale) :
+ Function.Surjective (pointSmallEtaleFiberObjToPreimage s hs₀ (X := X)) := by
+ intro y
+ obtain ⟨y, rfl⟩ := (X.hom.fiberHomeo s₀).surjective y
+ obtain ⟨⟨t, a⟩, rfl⟩ := (Scheme.SpecToEquivOfField Ω _).symm.surjective s
+ obtain rfl : t = s₀ := by simp [SpecToEquivOfField, ← hs₀]
+ obtain ⟨l, hl, rfl⟩ := exists_fac_of_etale_of_isSepClosed
+ (X.hom.fiberToSpecResidueField _) (Spec.map a) y (by subsingleton)
+ refine ⟨Over.homMk (l ≫ X.hom.fiberι t) ?_, rfl⟩
+ simp [X.hom.fiber_fac, reassoc_of% hl]
+ rfl
+
+set_option backward.isDefEq.respectTransparency false in
+lemma isConservative_pointSmallEtale
+ {ι : Type*} {S : Scheme.{u}}
+ {Ω : ι → Type u} [∀ i, Field (Ω i)] [∀ i, IsSepClosed (Ω i)]
+ (s : ∀ i, Spec (.of (Ω i)) ⟶ S)
+ (hs : ⋃ i, Set.range (s i) = .univ) :
+ (ObjectProperty.ofObj (fun i ↦ pointSmallEtale (s i))).IsConservativeFamilyOfPoints :=
+ .mk' (fun X R hR ↦ by
+ obtain ⟨α, T, f, rfl⟩ := R.exists_eq_ofArrows
+ rw [ofArrows_mem_smallEtaleTopology_iff]
+ ext x
+ simp only [Set.mem_iUnion, Set.mem_range, Set.mem_univ, iff_true]
+ obtain ⟨i, hi⟩ : ∃ i, s i default = X.hom x := by
+ have := Set.mem_univ (X.hom x)
+ simp only [← hs, Functor.id_obj, Set.mem_iUnion, Set.mem_range] at this
+ obtain ⟨i, y, hy⟩ := this
+ obtain rfl := Subsingleton.elim y default
+ exact ⟨i, hy⟩
+ obtain ⟨x', hx'⟩ := pointSmallEtaleFiberObjToPreimage_surjective (s i) hi X ⟨x, by simp⟩
+ rw [Subtype.ext_iff] at hx'
+ simp only [Functor.id_obj, pointSmallEtaleFiberObjToPreimage_coe, Etale.forget_obj_left] at hx'
+ subst hx'
+ obtain ⟨W, g, ⟨Z, p, _, ⟨a⟩, rfl⟩, y, rfl⟩ := hR ⟨_, ⟨i⟩⟩ x'
+ exact ⟨a, (pointSmallEtaleFiberObjToPreimage (s i) hi (y ≫ p.hom)).1, rfl⟩)
+
+lemma isConservativeFamilyOfPoints_pointSmallEtale' (S : Scheme.{u}) :
+ (ObjectProperty.ofObj (fun (s : S) ↦ pointSmallEtale
+ ((SpecToEquivOfField (SeparableClosure (S.residueField s)) _).2
+ ⟨s, CommRingCat.ofHom
+ (algebraMap (S.residueField s) _)⟩))).IsConservativeFamilyOfPoints :=
+ isConservative_pointSmallEtale _ (by
+ ext s
+ simp only [Equiv.invFun_as_coe, Set.mem_iUnion, Set.mem_range, Set.mem_univ, iff_true]
+ exact ⟨s, default, by simp [SpecToEquivOfField]⟩)
+
+instance : GrothendieckTopology.HasEnoughPoints.{u} (smallEtaleTopology S) where
+ exists_objectProperty :=
+ ⟨_, inferInstance, isConservativeFamilyOfPoints_pointSmallEtale' S⟩
+
+end AlgebraicGeometry.Scheme
diff --git a/Mathlib/CategoryTheory/Elements.lean b/Mathlib/CategoryTheory/Elements.lean
index d8f926c0b5c135..2548dd3b2a4c39 100644
--- a/Mathlib/CategoryTheory/Elements.lean
+++ b/Mathlib/CategoryTheory/Elements.lean
@@ -7,6 +7,7 @@ module
public import Mathlib.CategoryTheory.Comma.StructuredArrow.Basic
public import Mathlib.CategoryTheory.EssentiallySmall
+public import Mathlib.CategoryTheory.ObjectProperty.Small
/-!
# The category of elements
@@ -354,6 +355,26 @@ def Elements.initial (A : C) : (yoneda.obj A).Elements :=
def Elements.isInitial (A : C) : Limits.IsInitial (Elements.initial A) :=
isInitialOfRepresentableBy (.yoneda A)
+/-- The functor `(F ⋙ G).Elements ⥤ G.Elements`. -/
+@[simps]
+def Elements.precomp {D : Type*} [Category D] (F : C ⥤ D) (G : D ⥤ Type w) :
+ (F ⋙ G).Elements ⥤ G.Elements where
+ obj x := G.elementsMk (F.obj x.fst) x.snd
+ map f := ⟨F.map f.1, f.2⟩
+
+instance Elements.essentiallySmall {C : Type u} [Category.{v} C]
+ (F : C ⥤ Type w) [EssentiallySmall.{w} C] :
+ EssentiallySmall.{w} F.Elements := by
+ rw [essentiallySmall_iff_objectPropertyEssentiallySmall_top]
+ obtain ⟨P, _, hP⟩ := ObjectProperty.EssentiallySmall.exists_small_le' (⊤ : ObjectProperty C)
+ refine ⟨fun x ↦ P x.1, ?_, fun y _ ↦ ?_⟩
+ · exact small_of_surjective.{w} (α := Σ (Z : Subtype P), F.obj Z.1)
+ (f := fun x ↦ ⟨F.elementsMk _ x.2, x.1.2⟩)
+ (fun ⟨x, hx⟩ ↦ ⟨⟨⟨x.1, hx⟩, x.2⟩, rfl⟩)
+ · obtain ⟨Z, hZ, ⟨e⟩⟩ := hP y.fst (by simp)
+ exact ⟨F.elementsMk Z (F.map e.hom y.snd), hZ,
+ ⟨CategoryOfElements.isoMk _ _ e rfl⟩⟩
+
end Functor
end CategoryTheory
diff --git a/Mathlib/CategoryTheory/EssentialImage.lean b/Mathlib/CategoryTheory/EssentialImage.lean
index 2a5f68abd05fcd..03c782c1703628 100644
--- a/Mathlib/CategoryTheory/EssentialImage.lean
+++ b/Mathlib/CategoryTheory/EssentialImage.lean
@@ -5,10 +5,9 @@ Authors: Bhavik Mehta
-/
module
-public import Mathlib.CategoryTheory.NatIso
public import Mathlib.CategoryTheory.ObjectProperty.ClosedUnderIsomorphisms
public import Mathlib.CategoryTheory.ObjectProperty.FullSubcategory
-public import Mathlib.Data.Set.Operations
+public import Mathlib.Order.BooleanAlgebra.Defs
/-!
# Essential image of a functor
@@ -223,4 +222,13 @@ lemma faithful_of_comp_essSurj (F : D ⥤ E) (L : C ⥤ D) [EssSurj L]
end Functor
+lemma ObjectProperty.map_top (F : C ⥤ D) :
+ (⊤ : ObjectProperty C).map F = F.essImage := by
+ ext Y
+ refine ⟨?_, ?_⟩
+ · rintro ⟨X, _, ⟨e⟩⟩
+ exact ⟨X, ⟨e⟩⟩
+ · rintro ⟨X, ⟨e⟩⟩
+ exact ⟨X, by simp, ⟨e⟩⟩
+
end CategoryTheory
diff --git a/Mathlib/CategoryTheory/Limits/FinallySmall.lean b/Mathlib/CategoryTheory/Limits/FinallySmall.lean
index cc3b7f4bcf28d8..e551669d855b26 100644
--- a/Mathlib/CategoryTheory/Limits/FinallySmall.lean
+++ b/Mathlib/CategoryTheory/Limits/FinallySmall.lean
@@ -212,6 +212,17 @@ theorem initiallySmall_of_small_weakly_initial_set [IsCofilteredOrEmpty J] (s :
obtain ⟨j, hj₁, hj₂⟩ := hs i
exact ⟨⟨j, hj₁⟩, hj₂⟩
+variable {J} in
+theorem initiallySmall_of_essentiallySmall_weakly_initial_objectProperty
+ [IsCofilteredOrEmpty J] (P : ObjectProperty J) [ObjectProperty.EssentiallySmall.{v} P]
+ (hP : ∀ i, ∃ j, P j ∧ Nonempty (j ⟶ i)) : InitiallySmall.{v} J := by
+ obtain ⟨Q, H, hQ⟩ := ObjectProperty.EssentiallySmall.exists_small_le'.{v} P
+ have : Small.{v} (show Set _ from Q) := by assumption
+ refine initiallySmall_of_small_weakly_initial_set Q (fun i ↦ ?_)
+ obtain ⟨j, hj, ⟨f⟩⟩ := hP i
+ obtain ⟨k, hk, ⟨e⟩⟩ := hQ _ hj
+ exact ⟨k, hk, ⟨e.inv ≫ f⟩⟩
+
theorem initiallySmall_iff_exists_small_weakly_initial_set [IsCofilteredOrEmpty J] :
InitiallySmall.{v} J ↔ ∃ (s : Set J) (_ : Small.{v} s), ∀ i, ∃ j ∈ s, Nonempty (j ⟶ i) := by
refine ⟨fun _ => InitiallySmall.exists_small_weakly_initial_set _, fun h => ?_⟩
diff --git a/Mathlib/CategoryTheory/Limits/MorphismProperty.lean b/Mathlib/CategoryTheory/Limits/MorphismProperty.lean
index 2f52b596250381..469c6f3a5e85bd 100644
--- a/Mathlib/CategoryTheory/Limits/MorphismProperty.lean
+++ b/Mathlib/CategoryTheory/Limits/MorphismProperty.lean
@@ -296,9 +296,12 @@ instance (priority := 900) hasPullbacks [HasPullbacks T] [P.IsStableUnderComposi
[P.IsStableUnderBaseChange] [P.HasOfPostcompProperty P] : HasPullbacks (P.Over ⊤ X) :=
CostructuredArrow.hasPullbacks _ _
-variable [HasPullbacks T] [P.IsStableUnderComposition] [P.ContainsIdentities]
+variable [HasPullbacks T] [P.IsMultiplicative]
[P.IsStableUnderBaseChange] [P.HasOfPostcompProperty P]
+instance hasFiniteLimits : HasFiniteLimits (P.Over ⊤ X) :=
+ hasFiniteLimits_of_hasTerminal_and_pullbacks
+
noncomputable instance : CreatesFiniteLimits (Over.forget P ⊤ X) :=
createsFiniteLimitsOfCreatesTerminalAndPullbacks _
diff --git a/Mathlib/CategoryTheory/MorphismProperty/Comma.lean b/Mathlib/CategoryTheory/MorphismProperty/Comma.lean
index f5c14845afd010..8a0d834d4922e1 100644
--- a/Mathlib/CategoryTheory/MorphismProperty/Comma.lean
+++ b/Mathlib/CategoryTheory/MorphismProperty/Comma.lean
@@ -790,7 +790,7 @@ lemma CostructuredArrow.Hom.ext {A B : P.CostructuredArrow Q F X} {f g : A ⟶ B
ext <;> simp [h]
variable {P Q F X} in
-/-- Construct an morphism in `P.CostructuredArrow Q F X` by giving the isomorphism
+/-- Construct an isomorphism in `P.CostructuredArrow Q F X` by giving the isomorphism
on the underlying objects of `C`. -/
@[simps]
def CostructuredArrow.isoMk {A B : P.CostructuredArrow Q F X} (f : A.left ≅ B.left) (hf : Q f.hom)
diff --git a/Mathlib/CategoryTheory/MorphismProperty/CommaSites.lean b/Mathlib/CategoryTheory/MorphismProperty/CommaSites.lean
index aa594de4c9e6b3..6a5196bfc86997 100644
--- a/Mathlib/CategoryTheory/MorphismProperty/CommaSites.lean
+++ b/Mathlib/CategoryTheory/MorphismProperty/CommaSites.lean
@@ -93,6 +93,8 @@ lemma coverPreserving_comap_forget (H : K ≤ P.precoverage) :
variable [HasFiniteWidePullbacks C] [P.HasOfPostcompProperty P] [P.IsStableUnderBaseChange]
[P.ContainsIdentities]
+attribute [local instance] hasFiniteLimits_of_hasTerminal_and_pullbacks
+ preservesFiniteLimits_of_preservesTerminal_and_pullbacks in
lemma isContinuous_comap_forget (H : K ≤ P.precoverage) :
(Over.forget P ⊤ S).IsContinuous
(Precoverage.comap (Over.forget P ⊤ S ⋙ CategoryTheory.Over.forget S) K).toGrothendieck
diff --git a/Mathlib/CategoryTheory/ObjectProperty/Small.lean b/Mathlib/CategoryTheory/ObjectProperty/Small.lean
index 5404a7f6c78704..b78a4eb6bc8e70 100644
--- a/Mathlib/CategoryTheory/ObjectProperty/Small.lean
+++ b/Mathlib/CategoryTheory/ObjectProperty/Small.lean
@@ -218,6 +218,11 @@ instance (P : ObjectProperty C) [ObjectProperty.EssentiallySmall.{w} P]
obtain ⟨Q, _, h₁, h₂⟩ := EssentiallySmall.exists_small_le P
exact ⟨Q.strictMap F, inferInstance, (map_monotone h₂ F).trans (by simp)⟩
+instance [EssentiallySmall.{w} C] (F : C ⥤ D) :
+ ObjectProperty.EssentiallySmall.{w} F.essImage := by
+ rw [← ObjectProperty.map_top]
+ infer_instance
+
instance (P : ObjectProperty C) [LocallySmall.{w} C]
[ObjectProperty.EssentiallySmall.{w} P] : EssentiallySmall.{w} P.FullSubcategory := by
obtain ⟨Q, _, h₁, h₂⟩ := EssentiallySmall.exists_small_le P
From 08af479bddcb3abff52bf57d88b2be1db9c0bcb0 Mon Sep 17 00:00:00 2001
From: Yongle Hu
Date: Thu, 18 Jun 2026 11:44:43 +0000
Subject: [PATCH 0138/1300] feat(RingTheory): let `B` be a faithfully flat
`A`-algebra, then `A` is a local ring if `B` is (#39611)
Let `B` be a faithfully flat `A`-algebra, then `A` is a local ring if `B` is.
---
Mathlib/RingTheory/Flat/FaithfullyFlat/Algebra.lean | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/Mathlib/RingTheory/Flat/FaithfullyFlat/Algebra.lean b/Mathlib/RingTheory/Flat/FaithfullyFlat/Algebra.lean
index b043b91f8e57c3..d304770bc068e5 100644
--- a/Mathlib/RingTheory/Flat/FaithfullyFlat/Algebra.lean
+++ b/Mathlib/RingTheory/Flat/FaithfullyFlat/Algebra.lean
@@ -154,3 +154,16 @@ lemma PrimeSpectrum.comap_surjective_of_faithfullyFlat :
@[deprecated (since := "2025-12-10")]
alias PrimeSpectrum.specComap_surjective_of_faithfullyFlat :=
PrimeSpectrum.comap_surjective_of_faithfullyFlat
+
+section IsLocalRing
+
+variable (A B)
+
+instance Module.FaithfullyFlat.isLocalHom : IsLocalHom (algebraMap A B) :=
+ IsLocalHom.of_comap_surjective (algebraMap A B) PrimeSpectrum.comap_surjective_of_faithfullyFlat
+
+/-- Let `B` be a faithfully flat `A`-algebra, then `A` is a local ring if `B` is. -/
+theorem Module.FaithfullyFlat.isLocalRing [IsLocalRing B] : IsLocalRing A :=
+ (algebraMap A B).domain_isLocalRing
+
+end IsLocalRing
From 63b065c2a061e7286444690d8c427f79cd6d5b6d Mon Sep 17 00:00:00 2001
From: Thomas Browning <13339017+tb65536@users.noreply.github.com>
Date: Thu, 18 Jun 2026 11:44:45 +0000
Subject: [PATCH 0139/1300] feat(NumberTheory/RamificationInertia/Galois):
ramification inertia refactor (#40126)
This is a first pass at refactoring `NumberTheory/RamificationInertia/Galois` to use the new definitions `ramificationIdx'` and `inertiaDeg'`. I tried to keep changes to downstream files minimal to keep this PR on the smaller side. More will be done in future PRs.
Co-authored-by: tb65536
---
.../NumberTheory/NumberField/ClassNumber.lean | 19 +-
.../NumberField/Cyclotomic/Galois.lean | 5 +-
.../NumberField/Cyclotomic/Ideal.lean | 55 +++---
.../NumberField/ExistsRamified.lean | 8 +-
.../RamificationInertia/Galois.lean | 179 ++++++++----------
.../RamificationInertia/HilbertTheory.lean | 27 +--
Mathlib/RingTheory/Ideal/GoingUp.lean | 6 +-
Mathlib/RingTheory/Ideal/Norm/RelNorm.lean | 8 +-
8 files changed, 149 insertions(+), 158 deletions(-)
diff --git a/Mathlib/NumberTheory/NumberField/ClassNumber.lean b/Mathlib/NumberTheory/NumberField/ClassNumber.lean
index b95c4f81116f64..488c642d93a9bf 100644
--- a/Mathlib/NumberTheory/NumberField/ClassNumber.lean
+++ b/Mathlib/NumberTheory/NumberField/ClassNumber.lean
@@ -143,7 +143,7 @@ The way this theorem should be used is to first compute `⌊(M K)⌋₊` and the
to deal with the finite number of primes `p` in the interval. -/
theorem isPrincipalIdealRing_of_isPrincipal_of_pow_le_of_mem_primesOver_of_mem_Icc
(h : ∀ p ∈ Finset.Icc 1 ⌊(M K)⌋₊, p.Prime → ∀ (P : Ideal (𝓞 K)),
- P ∈ primesOver (span {(p : ℤ)}) (𝓞 K) → p ^ ((span ({↑p} : Set ℤ)).inertiaDeg P) ≤ ⌊(M K)⌋₊ →
+ P ∈ primesOver (span {(p : ℤ)}) (𝓞 K) → p ^ P.inertiaDeg' ℤ ≤ ⌊(M K)⌋₊ →
Submodule.IsPrincipal P) : IsPrincipalIdealRing (𝓞 K) := by
refine isPrincipalIdealRing_of_isPrincipal_of_norm_le_of_isPrime <|
fun ⟨P, HP⟩ hP hPN ↦ ?_
@@ -157,16 +157,19 @@ theorem isPrincipalIdealRing_of_isPrincipal_of_pow_le_of_mem_primesOver_of_mem_I
simpa [h, span_singleton_neg p, ← submodule_span_eq, ← hp] using over_under P
have hspan : span {↑p.natAbs} = span {p} := by
rcases abs_choice p with h | h <;> simp [h]
- have hple : p.natAbs ^ (span {(p.natAbs : ℤ)}).inertiaDeg P ≤ ⌊(M K)⌋₊ := by
+ have hple : p.natAbs ^ P.inertiaDeg' ℤ ≤ ⌊(M K)⌋₊ := by
refine le_floor ?_
- simpa only [hspan, ← cast_pow, ← absNorm_eq_pow_inertiaDeg P (hpprime (hP.under _))] using hPN
+ have : P.IsMaximal := hP.isMaximal (by simpa using HP.2)
+ have : (span {p}).IsMaximal := (hpprime (.under ℤ P)).isMaximal_span_singleton
+ simpa only [hspan, ← cast_pow, absNorm_eq_pow_inertiaDeg P (hpprime (hP.under _)),
+ inertiaDeg_eq_inertiaDeg'] using hPN
have hpabsprime := Int.prime_iff_natAbs_prime.mp (hpprime (hP.under _))
refine h _ ?_ hpabsprime _ ⟨hP, ?_⟩ hple
- · suffices 0 < (span {(p.natAbs : ℤ)}).inertiaDeg P by
+ · suffices 0 < P.inertiaDeg' ℤ by
exact Finset.mem_Icc.mpr ⟨hpabsprime.one_le, le_trans (le_pow this) hple⟩
have := (isPrime_of_prime (prime_span_singleton_iff.mpr <|
hpprime (hP.under _))).isMaximal <| by simp [((hpprime (hP.under _))).ne_zero]
- exact hspan ▸ inertiaDeg_pos ..
+ exact inertiaDeg'_pos ..
· exact hspan ▸ hlies
/-- Let `K` be a number field such that `K/ℚ` is Galois and let `M K` be the Minkowski bound of `K`.
@@ -181,7 +184,7 @@ to deal with the finite number of primes `p` in the interval. -/
theorem isPrincipalIdealRing_of_isPrincipal_of_lt_or_isPrincipal_of_mem_primesOver_of_mem_Icc
[IsGalois ℚ K] (h : ∀ p ∈ Finset.Icc 1 ⌊(M K)⌋₊, p.Prime →
∃ P ∈ primesOver (span {(p : ℤ)}) (𝓞 K),
- ⌊(M K)⌋₊ < p ^ ((span ({↑p} : Set ℤ)).inertiaDeg P) ∨
+ ⌊(M K)⌋₊ < p ^ P.inertiaDeg' ℤ ∨
Submodule.IsPrincipal P) :
IsPrincipalIdealRing (𝓞 K) := by
refine isPrincipalIdealRing_of_isPrincipal_of_pow_le_of_mem_primesOver_of_mem_Icc
@@ -189,9 +192,9 @@ theorem isPrincipalIdealRing_of_isPrincipal_of_lt_or_isPrincipal_of_mem_primesOv
obtain ⟨Q, ⟨hQ1, hQ2⟩, H⟩ := h p hpmem hp
have := (isPrime_of_prime (prime_span_singleton_iff.mpr (prime_iff_prime_int.mp hp))).isMaximal
(by simp [hp.ne_zero])
- by_cases h : ⌊(M K)⌋₊ < p ^ ((span ({↑p} : Set ℤ)).inertiaDeg P)
+ by_cases h : ⌊(M K)⌋₊ < p ^ P.inertiaDeg' ℤ
· linarith
- rw [inertiaDeg_eq_of_isGaloisGroup _ Q P (K ≃ₐ[ℚ] K)] at H
+ rw [inertiaDeg_eq_of_isGaloisGroup (span {↑p}) Q P (K ≃ₐ[ℚ] K)] at H
obtain ⟨σ, rfl⟩ := exists_smul_eq_of_isGaloisGroup (span ({↑p} : Set ℤ)) Q P (K ≃ₐ[ℚ] K)
exact (H.resolve_left h).map_ringHom (MulSemiringAction.toRingHom (K ≃ₐ[ℚ] K) (𝓞 K) σ)
diff --git a/Mathlib/NumberTheory/NumberField/Cyclotomic/Galois.lean b/Mathlib/NumberTheory/NumberField/Cyclotomic/Galois.lean
index 61777a3e550dca..a83cce9bee0610 100644
--- a/Mathlib/NumberTheory/NumberField/Cyclotomic/Galois.lean
+++ b/Mathlib/NumberTheory/NumberField/Cyclotomic/Galois.lean
@@ -146,10 +146,9 @@ theorem galEquivZMod_stabilizer :
rw [Fintype.card_eq_nat_card, Fintype.card_eq_nat_card, SetLike.coe_sort_coe, Nat.card_zpowers,
MulEquiv.mapSubgroup_apply, Subgroup.coe_map]
change _ ≤ Nat.card ((galEquivZMod n K).toEquiv '' _)
- rw [Nat.card_image_equiv, SetLike.coe_sort_coe, Ideal.card_stabilizer_eq (span {(p : ℤ)})
- (by simp [hp.out.ne_zero]), inertiaDegIn_eq_of_not_dvd p K hn,
+ rw [Nat.card_image_equiv, SetLike.coe_sort_coe, Ideal.card_stabilizer_eq (span {(p : ℤ)}),
ramificationIdxIn_eq_of_not_dvd p K hn, one_mul, ← orderOf_injective _ Units.coeHom_injective,
- Units.coeHom_apply, ZMod.coe_unitOfCoprime]
+ Units.coeHom_apply, ZMod.coe_unitOfCoprime, inertiaDegIn_eq_of_not_dvd p K hn]
end stabilizer
diff --git a/Mathlib/NumberTheory/NumberField/Cyclotomic/Ideal.lean b/Mathlib/NumberTheory/NumberField/Cyclotomic/Ideal.lean
index 6a23ec72c2d291..e8061a821c8f55 100644
--- a/Mathlib/NumberTheory/NumberField/Cyclotomic/Ideal.lean
+++ b/Mathlib/NumberTheory/NumberField/Cyclotomic/Ideal.lean
@@ -89,8 +89,9 @@ instance liesOver_span_zeta_sub_one : (span {hζ.toInteger - 1}).LiesOver 𝒑 :
rw [span_singleton_le_iff_mem, mem_comap, algebraMap_int_eq, map_natCast]
exact p_mem_span_zeta_sub_one p k hζ
-theorem inertiaDeg_span_zeta_sub_one : inertiaDeg 𝒑 (span {hζ.toInteger - 1}) = 1 := by
- have := liesOver_span_zeta_sub_one p k hζ
+theorem inertiaDeg_span_zeta_sub_one : inertiaDeg' (span {hζ.toInteger - 1}) ℤ = 1 := by
+ have : IsMaximal (span {hζ.toInteger - 1}) := .of_liesOver_isMaximal _ 𝒑
+ rw [← inertiaDeg_eq_inertiaDeg' 𝒑]
rw [← Nat.pow_right_inj hp.out.one_lt, pow_one, ← absNorm_eq_pow_inertiaDeg' _ hp.out,
absNorm_span_zeta_sub_one]
@@ -114,12 +115,14 @@ theorem map_eq_span_zeta_sub_one_pow :
← Nat.card_eq_fintype_card, IsGalois.card_aut_eq_finrank]
theorem ramificationIdx_span_zeta_sub_one :
- ramificationIdx 𝒑 (span {hζ.toInteger - 1}) = p ^ k * (p - 1) := by
+ ramificationIdx' (span {hζ.toInteger - 1}) ℤ = p ^ k * (p - 1) := by
have h := isPrime_span_zeta_sub_one p k hζ
- rw [← Nat.totient_prime_pow_succ hp.out, ← finrank _ K,
+ have hp0 : 𝒑 ≠ ⊥ := by simpa using hp.out.ne_zero
+ rw [← ramificationIdx_eq_ramificationIdx' 𝒑 _ hp0,
+ ← Nat.totient_prime_pow_succ hp.out, ← finrank _ K,
IsDedekindDomain.ramificationIdx_eq_multiplicity _ h, map_eq_span_zeta_sub_one_pow p k hζ,
multiplicity_pow_self (span_zeta_sub_one_ne_bot p k hζ) (isUnit_iff.not.mpr h.ne_top)]
- exact map_ne_bot_of_ne_bot <| by simpa using hp.out.ne_zero
+ exact map_ne_bot_of_ne_bot hp0
variable (K)
@@ -127,8 +130,7 @@ include hK in
theorem ncard_primesOver_of_prime_pow :
(primesOver 𝒑 (𝓞 K)).ncard = 1 := by
have : IsGalois ℚ K := isGalois {p ^ (k + 1)} ℚ K
- have : 𝒑 ≠ ⊥ := by simpa using hp.out.ne_zero
- have h_main := ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn this (𝓞 K) Gal(K/ℚ)
+ have h_main := ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn 𝒑 (𝓞 K) Gal(K/ℚ)
have hζ := hK.zeta_spec
have := liesOver_span_zeta_sub_one p k hζ
rwa [ramificationIdxIn_eq_ramificationIdx 𝒑 (span {hζ.toInteger - 1}) Gal(K/ℚ),
@@ -148,12 +150,12 @@ theorem eq_span_zeta_sub_one_of_liesOver (P : Ideal (𝓞 K)) [hP₁ : P.IsPrime
include hK in
theorem inertiaDeg_eq_of_prime_pow (P : Ideal (𝓞 K)) [hP₁ : P.IsPrime] [hP₂ : P.LiesOver 𝒑] :
- inertiaDeg 𝒑 P = 1 := by
+ inertiaDeg' P ℤ = 1 := by
rw [eq_span_zeta_sub_one_of_liesOver p k K hK.zeta_spec P, inertiaDeg_span_zeta_sub_one]
include hK in
theorem ramificationIdx_eq_of_prime_pow (P : Ideal (𝓞 K)) [hP₁ : P.IsPrime] [hP₂ : P.LiesOver 𝒑] :
- ramificationIdx 𝒑 P = p ^ k * (p - 1) := by
+ ramificationIdx' P ℤ = p ^ k * (p - 1) := by
rw [eq_span_zeta_sub_one_of_liesOver p k K hK.zeta_spec P, ramificationIdx_span_zeta_sub_one]
include hK in
@@ -180,12 +182,12 @@ instance isPrime_span_zeta_sub_one' : IsPrime (span {hζ.toInteger - 1}) := by
rw [← pow_one p] at hK hζ
exact isPrime_span_zeta_sub_one p 0 hζ
-theorem inertiaDeg_span_zeta_sub_one' : inertiaDeg 𝒑 (span {hζ.toInteger - 1}) = 1 := by
+theorem inertiaDeg_span_zeta_sub_one' : inertiaDeg' (span {hζ.toInteger - 1}) ℤ = 1 := by
rw [← pow_one p] at hK hζ
exact inertiaDeg_span_zeta_sub_one p 0 hζ
theorem ramificationIdx_span_zeta_sub_one' :
- ramificationIdx 𝒑 (span {hζ.toInteger - 1}) = p - 1 := by
+ ramificationIdx' (span {hζ.toInteger - 1}) ℤ = p - 1 := by
rw [← pow_one p] at hK hζ
rw [ramificationIdx_span_zeta_sub_one p 0 hζ, pow_zero, one_mul]
@@ -204,12 +206,12 @@ theorem eq_span_zeta_sub_one_of_liesOver' (P : Ideal (𝓞 K)) [hP₁ : P.IsPrim
include hK in
theorem inertiaDeg_eq_of_prime (P : Ideal (𝓞 K)) [hP₁ : P.IsPrime] [hP₂ : P.LiesOver 𝒑] :
- inertiaDeg 𝒑 P = 1 := by
+ inertiaDeg' P ℤ = 1 := by
rw [eq_span_zeta_sub_one_of_liesOver' p K hK.zeta_spec P, inertiaDeg_span_zeta_sub_one']
include hK in
theorem ramificationIdx_eq_of_prime (P : Ideal (𝓞 K)) [hP₁ : P.IsPrime] [hP₂ : P.LiesOver 𝒑] :
- ramificationIdx 𝒑 P = p - 1 := by
+ ramificationIdx' P ℤ = p - 1 := by
rw [eq_span_zeta_sub_one_of_liesOver' p K hK.zeta_spec P, ramificationIdx_span_zeta_sub_one']
include hK in
@@ -236,7 +238,7 @@ open NumberField.Ideal Polynomial
variable {m} [NeZero m] [hK : IsCyclotomicExtension {m} ℚ K]
theorem inertiaDeg_eq_of_not_dvd (hm : ¬ p ∣ m) :
- inertiaDeg 𝒑 P = orderOf (p : ZMod m) := by
+ inertiaDeg' P ℤ = orderOf (p : ZMod m) := by
replace hm : p.Coprime m := hp.out.coprime_iff_not_dvd.mpr hm
let ζ := (zeta_spec m ℚ K).toInteger
have h₁ : ¬ p ∣ exponent ζ := by
@@ -247,6 +249,8 @@ theorem inertiaDeg_eq_of_not_dvd (hm : ¬ p ∣ m) :
simp only [Subtype.coe_eta, Equiv.symm_apply_apply] at h₃
rw [Multiset.mem_toFinset, Polynomial.mem_normalizedFactors_iff
(map_monic_ne_zero (minpoly.monic ζ.isIntegral))] at h₂
+ have : P.IsMaximal := .of_liesOver_isMaximal P 𝒑
+ rw [← inertiaDeg_eq_inertiaDeg' 𝒑]
rw [h₃, natDegree_of_dvd_cyclotomic_of_irreducible (by simp) hm (f := 1) _ h₂.1]
· simpa using (orderOf_injective _ Units.coeHom_injective (ZMod.unitOfCoprime p hm)).symm
· refine dvd_trans h₂.2.2 ?_
@@ -258,7 +262,7 @@ theorem inertiaDeg_eq_of_not_dvd (hm : ¬ p ∣ m) :
alias inertiaDeg_of_not_dvd := inertiaDeg_eq_of_not_dvd
theorem ramificationIdx_eq_of_not_dvd (hm : ¬ p ∣ m) :
- ramificationIdx 𝒑 P = 1 := by
+ ramificationIdx' P ℤ = 1 := by
let ζ := (zeta_spec m ℚ K).toInteger
have h₁ : ¬ p ∣ exponent ζ := by
rw [exponent_eq_one_iff.mpr <| adjoin_singleton_eq_top (zeta_spec m ℚ K)]
@@ -268,7 +272,7 @@ theorem ramificationIdx_eq_of_not_dvd (hm : ¬ p ∣ m) :
simp only [Subtype.coe_eta, Equiv.symm_apply_apply] at h₃
rw [Multiset.mem_toFinset, Polynomial.mem_normalizedFactors_iff
(map_monic_ne_zero (minpoly.monic ζ.isIntegral))] at h₂
- rw [h₃]
+ rw [← ramificationIdx_eq_ramificationIdx' 𝒑 P (by simpa using hp.out.ne_zero), h₃]
refine multiplicity_eq_of_emultiplicity_eq_some (le_antisymm ?_ ?_)
· apply emultiplicity_le_one_of_separable
· exact isUnit_iff_degree_eq_zero.not.mpr (Irreducible.degree_pos h₂.1).ne'
@@ -309,7 +313,6 @@ private theorem inertiaDegIn_ramificationIdxIn_aux (hn : n = p ^ (k + 1) * m) (h
have : IsAbelianGalois ℚ K := IsCyclotomicExtension.isAbelianGalois {n} ℚ K
have : NeZero m := ⟨fun h ↦ by simp [h] at hm⟩
have : NeZero n := ⟨hn ▸ NeZero.ne (p ^ (k + 1) * m)⟩
- have hp' : 𝒑 ≠ ⊥ := by simpa using hp.out.ne_zero
let ζ := zeta n ℚ K
have hζ := zeta_spec n ℚ K
-- We construct `ℚ⟮ζₘ⟯ ⊆ ℚ⟮ζₙ⟯`
@@ -332,7 +335,7 @@ private theorem inertiaDegIn_ramificationIdxIn_aux (hn : n = p ^ (k + 1) * m) (h
Pₘ.inertiaDegIn (𝓞 K) * (Pₘ.primesOver (𝓞 K)).ncard = 1 by
replace this := Nat.eq_one_of_mul_eq_one_right this
rw [← inertiaDegIn_mul_inertiaDegIn 𝒑 Pₘ Gal(Fₘ/ℚ) _ Gal(K/ℚ) Gal(K/Fₘ),
- ← ramificationIdxIn_mul_ramificationIdxIn' Pₚ Gal(Fₚ/ℚ) _ Gal(K/ℚ) Gal(K/Fₚ),
+ ← ramificationIdxIn_mul_ramificationIdxIn Pₚ Gal(Fₚ/ℚ) _ Gal(K/ℚ) Gal(K/Fₚ),
Nat.eq_one_of_mul_eq_one_left this, Nat.eq_one_of_mul_eq_one_right this, mul_one, mul_one,
inertiaDegIn_eq_of_not_dvd p _ hm, ramificationIdxIn_eq_of_prime_pow p k Fₚ]
exact ⟨rfl, rfl⟩
@@ -341,18 +344,18 @@ private theorem inertiaDegIn_ramificationIdxIn_aux (hn : n = p ^ (k + 1) * m) (h
exact Nat.Coprime.pow_left (k + 1) (by rwa [hp.out.coprime_iff_not_dvd])
rwa [← IsGalois.card_aut_eq_finrank, ← IsGalois.card_aut_eq_finrank,
← IsGalois.card_aut_eq_finrank,
- ← ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn hp' (𝓞 Fₘ) Gal(Fₘ/ℚ),
- ← ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn hp' (𝓞 Fₚ) Gal(Fₚ/ℚ),
- ← ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn hp' (𝓞 K) Gal(K/ℚ),
- ← ncard_primesOver_mul_ncard_primesOver Pₘ Gal(Fₘ/ℚ) (𝓞 K) Gal(K/ℚ) Gal(K/Fₘ) hp',
+ ← ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn 𝒑 (𝓞 Fₘ) Gal(Fₘ/ℚ),
+ ← ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn 𝒑 (𝓞 Fₚ) Gal(Fₚ/ℚ),
+ ← ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn 𝒑 (𝓞 K) Gal(K/ℚ),
+ ← ncard_primesOver_mul_ncard_primesOver Pₘ Gal(Fₘ/ℚ) (𝓞 K) Gal(K/ℚ) Gal(K/Fₘ),
ramificationIdxIn_eq_of_not_dvd p Fₘ hm, inertiaDegIn_eq_of_prime_pow p k Fₚ,
ncard_primesOver_of_prime_pow p k Fₚ, one_mul, one_mul, mul_one, mul_assoc, mul_assoc,
mul_right_inj' (IsDedekindDomain.primesOver_ncard_ne_zero 𝒑 _), ← mul_assoc,
← mul_rotate (𝒑.inertiaDegIn (𝓞 K)),
← inertiaDegIn_mul_inertiaDegIn 𝒑 Pₘ Gal(Fₘ/ℚ) (𝓞 K) Gal(K/ℚ) Gal(K/Fₘ), mul_assoc, mul_assoc,
mul_right_inj' (inertiaDegIn_ne_zero Gal(Fₘ/ℚ)), ← mul_rotate',
- ← ramificationIdxIn_mul_ramificationIdxIn' (p := 𝒑) Pₚ Gal(Fₚ/ℚ) (𝓞 K) Gal(K/ℚ) Gal(K/Fₚ),
- eq_comm, mul_assoc, mul_eq_left₀ (ramificationIdxIn_ne_zero Gal(Fₚ/ℚ) hp'), ← mul_assoc]
+ ← ramificationIdxIn_mul_ramificationIdxIn (p := 𝒑) Pₚ Gal(Fₚ/ℚ) (𝓞 K) Gal(K/ℚ) Gal(K/Fₚ),
+ eq_comm, mul_assoc, mul_eq_left₀ (ramificationIdxIn_ne_zero Gal(Fₚ/ℚ)), ← mul_assoc]
at h_main
/--
@@ -372,12 +375,12 @@ theorem ramificationIdxIn_eq (hn : n = p ^ (k + 1) * m) (hm : ¬ p ∣ m) :
(inertiaDegIn_ramificationIdxIn_aux n K hn hm).2
theorem inertiaDeg_eq (hn : n = p ^ (k + 1) * m) (hm : ¬ p ∣ m) :
- inertiaDeg 𝒑 P = orderOf (p : ZMod m) := by
+ inertiaDeg' P ℤ = orderOf (p : ZMod m) := by
have : IsGalois ℚ K := isGalois {n} ℚ K
rw [← inertiaDegIn_eq_inertiaDeg 𝒑 P Gal(K/ℚ), inertiaDegIn_eq n K hn hm]
theorem ramificationIdx_eq (hn : n = p ^ (k + 1) * m) (hm : ¬ p ∣ m) :
- ramificationIdx 𝒑 P = p ^ k * (p - 1) := by
+ ramificationIdx' P ℤ = p ^ k * (p - 1) := by
have : IsGalois ℚ K := isGalois {n} ℚ K
rw [← ramificationIdxIn_eq_ramificationIdx 𝒑 P Gal(K/ℚ), ramificationIdxIn_eq n K hn hm]
diff --git a/Mathlib/NumberTheory/NumberField/ExistsRamified.lean b/Mathlib/NumberTheory/NumberField/ExistsRamified.lean
index 1856156665d1d5..f1c8f469117601 100644
--- a/Mathlib/NumberTheory/NumberField/ExistsRamified.lean
+++ b/Mathlib/NumberTheory/NumberField/ExistsRamified.lean
@@ -8,6 +8,7 @@ module
public import Mathlib.NumberTheory.NumberField.Discriminant.Basic
public import Mathlib.NumberTheory.NumberField.Discriminant.Different
public import Mathlib.NumberTheory.RamificationInertia.Galois
+public import Mathlib.RingTheory.Ideal.Quotient.HasFiniteQuotients
public import Mathlib.RingTheory.Unramified.Dedekind
/-!
@@ -83,7 +84,6 @@ lemma NumberField.exists_not_isUnramifiedAt_int_of_isGalois [IsGalois ℚ K]
(map_dvd (algebraMap _ _) p.associated_natAbs.symm.dvd) (by simpa using hQ)
have : .span {p} = Ideal.under ℤ Q :=
((Ideal.liesOver_span_iff Ideal.IsPrime.ne_top' this).mpr hQ).1
- rwa [Algebra.isUnramifiedAt_iff_of_isDedekindDomain (by aesop),
- ← Ideal.ramificationIdxIn_eq_ramificationIdx _ _ Gal(K/ℚ), ← this, ← hp,
- Ideal.ramificationIdxIn_eq_ramificationIdx _ P Gal(K/ℚ),
- ← Algebra.isUnramifiedAt_iff_of_isDedekindDomain (Ideal.IsMaximal.ne_bot_of_isIntegral_int _)]
+ rwa [← Ideal.ramificationIdx'_eq_one_iff,
+ ← Ideal.ramificationIdxIn_eq_ramificationIdx (Q.under ℤ) _ Gal(K/ℚ), ← this, ← hp,
+ Ideal.ramificationIdxIn_eq_ramificationIdx _ P Gal(K/ℚ), Ideal.ramificationIdx'_eq_one_iff]
diff --git a/Mathlib/NumberTheory/RamificationInertia/Galois.lean b/Mathlib/NumberTheory/RamificationInertia/Galois.lean
index cfeb2838861a4d..b0f32f53494438 100644
--- a/Mathlib/NumberTheory/RamificationInertia/Galois.lean
+++ b/Mathlib/NumberTheory/RamificationInertia/Galois.lean
@@ -5,8 +5,7 @@ Authors: Yongle Hu, Jiedong Jiang
-/
module
-public import Mathlib.FieldTheory.Galois.IsGaloisGroup
-public import Mathlib.NumberTheory.RamificationInertia.Basic
+public import Mathlib.RingTheory.RamificationInertia.Basic
/-!
# Ramification theory in Galois extensions of Dedekind domains
@@ -58,7 +57,7 @@ open scoped Classical in
maximal ideal `p` of `A` are the same, which we define as `Ideal.ramificationIdxIn`. -/
noncomputable def ramificationIdxIn {A : Type*} [CommRing A] (p : Ideal A)
(B : Type*) [CommRing B] [Algebra A B] : ℕ :=
- if h : ∃ P : Ideal B, P.IsPrime ∧ P.LiesOver p then p.ramificationIdx h.choose
+ if h : ∃ P : Ideal B, P.IsPrime ∧ P.LiesOver p then h.choose.ramificationIdx' A
else 0
open scoped Classical in
@@ -67,7 +66,7 @@ open scoped Classical in
maximal ideal `p` of `A` are the same, which we define as `Ideal.inertiaDegIn`. -/
noncomputable def inertiaDegIn {A : Type*} [CommRing A] (p : Ideal A)
(B : Type*) [CommRing B] [Algebra A B] : ℕ :=
- if h : ∃ P : Ideal B, P.IsPrime ∧ P.LiesOver p then p.inertiaDeg h.choose else 0
+ if h : ∃ P : Ideal B, P.IsPrime ∧ P.LiesOver p then h.choose.inertiaDeg' A else 0
section MulAction
@@ -135,92 +134,78 @@ instance isPretransitive_of_isGaloisGroup : MulAction.IsPretransitive G (primesO
rcases exists_smul_eq_of_isGaloisGroup p P Q G with ⟨σ, hs⟩
exact ⟨σ, Subtype.val_inj.mp hs⟩
-include G in
+include p G in
/-- All the `Ideal.ramificationIdx` over a fixed maximal ideal are the same. -/
theorem ramificationIdx_eq_of_isGaloisGroup :
- ramificationIdx p P = ramificationIdx p Q := by
+ P.ramificationIdx' A = Q.ramificationIdx' A := by
rcases exists_smul_eq_of_isGaloisGroup p P Q G with ⟨σ, rfl⟩
- exact (ramificationIdx_map_eq p P (MulSemiringAction.toAlgEquiv A B σ)).symm
+ rw [ramificationIdx'_smul]
-include G in
+include p G in
/-- All the `Ideal.inertiaDeg` over a fixed maximal ideal are the same. -/
theorem inertiaDeg_eq_of_isGaloisGroup :
- inertiaDeg p P = inertiaDeg p Q := by
+ P.inertiaDeg' A = Q.inertiaDeg' A := by
rcases exists_smul_eq_of_isGaloisGroup p P Q G with ⟨σ, rfl⟩
- exact (inertiaDeg_map_eq p P (MulSemiringAction.toAlgEquiv A B σ)).symm
+ rw [inertiaDeg'_smul]
-include G in
+include p G in
/-- The `ramificationIdxIn` is equal to any ramification index over the same ideal. -/
theorem ramificationIdxIn_eq_ramificationIdx :
- ramificationIdxIn p B = ramificationIdx p P := by
+ ramificationIdxIn p B = P.ramificationIdx' A := by
have h : ∃ P : Ideal B, P.IsPrime ∧ P.LiesOver p := ⟨P, hPp, hp⟩
obtain ⟨_, _⟩ := h.choose_spec
rw [ramificationIdxIn, dif_pos h]
exact ramificationIdx_eq_of_isGaloisGroup p h.choose P G
include G in
-theorem ramificationIdxIn_ne_zero [IsDedekindDomain B] {p : Ideal A} [p.IsPrime] (hp : p ≠ ⊥)
- [IsDomain A] [IsTorsionFree A B] : p.ramificationIdxIn B ≠ 0 := by
- have : Algebra.IsIntegral A B := IsGaloisGroup.isInvariant.isIntegral A B G
+theorem ramificationIdxIn_ne_zero [Module.Finite A B] [FaithfulSMul A B] {p : Ideal A} [p.IsPrime] :
+ p.ramificationIdxIn B ≠ 0 := by
obtain ⟨P⟩ := (inferInstance : Nonempty (primesOver p B))
rw [ramificationIdxIn_eq_ramificationIdx p P G]
- exact IsDedekindDomain.ramificationIdx_ne_zero_of_liesOver P.1 hp
+ exact (P.1.ramificationIdx'_pos A).ne'
include G in
/-- The `inertiaDegIn` is equal to any ramification index over the same ideal. -/
theorem inertiaDegIn_eq_inertiaDeg :
- inertiaDegIn p B = inertiaDeg p P := by
+ inertiaDegIn p B = P.inertiaDeg' A := by
have h : ∃ P : Ideal B, P.IsPrime ∧ P.LiesOver p := ⟨P, hPp, hp⟩
obtain ⟨_, _⟩ := h.choose_spec
rw [inertiaDegIn, dif_pos h]
exact inertiaDeg_eq_of_isGaloisGroup p h.choose P G
include G in
-theorem inertiaDegIn_ne_zero {p : Ideal A} [p.IsMaximal] [IsDomain A] [IsTorsionFree A B]
- [Module.Finite A B] [Nontrivial B] :
+theorem inertiaDegIn_ne_zero [Module.Finite A B] [FaithfulSMul A B] {p : Ideal A} [p.IsPrime] :
inertiaDegIn p B ≠ 0 := by
obtain ⟨P⟩ := (inferInstance : Nonempty (primesOver p B))
rw [inertiaDegIn_eq_inertiaDeg p P G]
- exact inertiaDeg_ne_zero _ _
+ exact (P.1.inertiaDeg'_pos A).ne'
section tower
-variable (C : Type*) [CommRing C] [IsDomain C] [Algebra A C] [Algebra B C] [Module.Finite B C]
- [IsDomain B] [IsTorsionFree B C] [IsScalarTower A B C]
+variable (C : Type*) [CommRing C] [Algebra A C] [Algebra B C]
+ [Nonempty (P.primesOver C)] [IsScalarTower A B C]
(GAC : Type*) [Group GAC] [Finite GAC] [MulSemiringAction GAC C] [IsGaloisGroup GAC A C]
(GBC : Type*) [Group GBC] [Finite GBC] [MulSemiringAction GBC C] [IsGaloisGroup GBC B C]
include G GAC GBC in
-theorem inertiaDegIn_mul_inertiaDegIn [p.IsMaximal] [P.IsMaximal] :
+theorem inertiaDegIn_mul_inertiaDegIn :
p.inertiaDegIn B * P.inertiaDegIn C = p.inertiaDegIn C := by
- obtain ⟨⟨Q, _, _⟩⟩ := P.nonempty_primesOver (S := C)
+ obtain ⟨⟨Q, _, _⟩⟩ := (inferInstance : Nonempty (primesOver P C))
have : Q.LiesOver p := LiesOver.trans Q P p
rw [inertiaDegIn_eq_inertiaDeg p P G, inertiaDegIn_eq_inertiaDeg p Q GAC,
- inertiaDegIn_eq_inertiaDeg P Q GBC, inertiaDeg_algebra_tower p P Q]
-
-set_option linter.overlappingInstances false
+ inertiaDegIn_eq_inertiaDeg P Q GBC, ← inertiaDeg'_tower P Q]
variable {p} in
include G GAC GBC in
-theorem ramificationIdxIn_mul_ramificationIdxIn [IsDedekindDomain B] [IsDedekindDomain C]
- (hp : map (algebraMap A C) p ≠ ⊥) (hP : map (algebraMap B C) P ≠ ⊥) :
+theorem ramificationIdxIn_mul_ramificationIdxIn [Flat B C] :
p.ramificationIdxIn B * P.ramificationIdxIn C = p.ramificationIdxIn C := by
- obtain ⟨⟨Q, _, hQ⟩⟩ := P.nonempty_primesOver (S := C)
+ obtain ⟨⟨Q, _, hQ⟩⟩ := (inferInstance : Nonempty (primesOver P C))
have : Q.LiesOver p := LiesOver.trans Q P p
rw [ramificationIdxIn_eq_ramificationIdx p P G, ramificationIdxIn_eq_ramificationIdx p Q GAC,
- ramificationIdxIn_eq_ramificationIdx P Q GBC, ramificationIdx_algebra_tower hP hp]
- exact over_def Q P ▸ map_comap_le
+ ramificationIdxIn_eq_ramificationIdx P Q GBC, ← ramificationIdx'_tower P Q]
-variable {p} in
-omit hp in
-include G GAC GBC in
-theorem ramificationIdxIn_mul_ramificationIdxIn' [IsDomain A] [IsTorsionFree A B]
- [IsDedekindDomain B] [IsDedekindDomain C] [P.LiesOver p] :
- p.ramificationIdxIn B * P.ramificationIdxIn C = p.ramificationIdxIn C := by
- obtain ⟨⟨Q, _, hQ⟩⟩ := P.nonempty_primesOver (S := C)
- have : Q.LiesOver p := LiesOver.trans Q P p
- rw [ramificationIdxIn_eq_ramificationIdx p P G, ramificationIdxIn_eq_ramificationIdx p Q GAC,
- ramificationIdxIn_eq_ramificationIdx P Q GBC, ramificationIdx_algebra_tower' p P Q]
+@[deprecated (since := "2026-06-18")] alias ramificationIdxIn_mul_ramificationIdxIn' :=
+ ramificationIdxIn_mul_ramificationIdxIn
end tower
@@ -228,59 +213,52 @@ end RamificationInertia
section fundamental_identity
-variable {A : Type*} [CommRing A] [IsDedekindDomain A] {p : Ideal A} (hpb : p ≠ ⊥) [p.IsMaximal]
- (B : Type*) [CommRing B] [IsDedekindDomain B] [Algebra A B] [Module.Finite A B]
- [IsTorsionFree A B]
+variable {A : Type*} [CommRing A] [IsDomain A] (p : Ideal A) [p.IsPrime]
+ (B : Type*) [CommRing B] [IsDomain B] [Algebra A B] [Module.Finite A B] [Flat A B]
(G : Type*) [Group G] [Finite G] [MulSemiringAction G B] [IsGaloisGroup G A B]
-include hpb in
/-- The form of the **fundamental identity** in the case of Galois extension. -/
theorem ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn :
(primesOver p B).ncard * (ramificationIdxIn p B * inertiaDegIn p B) = Nat.card G := by
- let K := FractionRing A
- let L := FractionRing B
- let := IsFractionRing.mulSemiringAction G A B (FractionRing A) (FractionRing B)
- rw [← smul_eq_mul, ← IsDedekindDomain.coe_primesOverFinset hpb B, Set.ncard_coe_finset,
- ← Finset.sum_const]
- rw [(IsGaloisGroup.toFractionRing G A B).card_eq_finrank, ← sum_ramification_inertia B K L hpb]
+ have : Fintype (primesOver p B) := (QuasiFinite.finite_primesOver p).fintype
+ rw [← smul_eq_mul, ← Set.fintypeCard_eq_ncard, ← Finset.card_univ, ← Finset.sum_const,
+ ← sum_ramification_inertia_eq_card p B]
apply Finset.sum_congr rfl
intro P hp
- rw [← Finset.mem_coe, IsDedekindDomain.coe_primesOverFinset hpb B] at hp
- obtain ⟨_, _⟩ := hp
rw [ramificationIdxIn_eq_ramificationIdx p P G, inertiaDegIn_eq_inertiaDeg p P G]
end fundamental_identity
section tower
-variable {A B : Type*} [CommRing A] [IsDedekindDomain A] [CommRing B] [IsDedekindDomain B]
- [Algebra A B] [IsTorsionFree A B] {p : Ideal A} (P : Ideal B) [p.IsMaximal]
- [P.IsMaximal] [P.LiesOver p] (G : Type*) [Group G] [Finite G] [MulSemiringAction G B]
- [IsGaloisGroup G A B] (C : Type*) [CommRing C] [IsDedekindDomain C] [Algebra A C] [Algebra B C]
- [Module.Finite A B] [Module.Finite A C] [Module.Finite B C] [IsTorsionFree A C]
- [IsTorsionFree B C] [IsScalarTower A B C]
+variable {A B : Type*} [CommRing A] [IsDomain A] [CommRing B] [IsDomain B]
+ [Algebra A B] [Flat A B] {p : Ideal A} (P : Ideal B) [p.IsPrime]
+ [P.IsPrime] [P.LiesOver p] (G : Type*) [Group G] [Finite G] [MulSemiringAction G B]
+ [IsGaloisGroup G A B] (C : Type*) [CommRing C] [IsDomain C] [Algebra A C]
+ [Algebra B C] [Module.Finite A B] [Module.Finite A C] [Module.Finite B C] [Flat A C]
+ [Flat B C] [IsScalarTower A B C]
(GAC : Type*) [Group GAC] [Finite GAC] [MulSemiringAction GAC C] [IsGaloisGroup GAC A C]
(GBC : Type*) [Group GBC] [Finite GBC] [MulSemiringAction GBC C] [IsGaloisGroup GBC B C]
+-- todo: use transitivity to prove this under much weaker assumptions
include G GAC GBC in
-theorem ncard_primesOver_mul_ncard_primesOver (hp : p ≠ ⊥) :
+theorem ncard_primesOver_mul_ncard_primesOver :
(p.primesOver B).ncard * (P.primesOver C).ncard = (p.primesOver C).ncard := by
- have hP : P ≠ ⊥ := ne_bot_of_liesOver_of_ne_bot hp P
let := IsFractionRing.mulSemiringAction G A B (FractionRing A) (FractionRing B)
let := IsFractionRing.mulSemiringAction GAC A C (FractionRing A) (FractionRing C)
let := IsFractionRing.mulSemiringAction GBC B C (FractionRing B) (FractionRing C)
have : p.ramificationIdxIn C * p.inertiaDegIn C ≠ 0 :=
- mul_ne_zero (ramificationIdxIn_ne_zero GAC hp) (inertiaDegIn_ne_zero GAC)
- rw [← Nat.mul_left_inj this, ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn hp C GAC]
+ mul_ne_zero (ramificationIdxIn_ne_zero GAC) (inertiaDegIn_ne_zero GAC)
+ rw [← Nat.mul_left_inj this, ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn p C GAC]
calc
_ = ((p.primesOver B).ncard * (p.ramificationIdxIn B * p.inertiaDegIn B)) *
((P.primesOver C).ncard * (P.ramificationIdxIn C * P.inertiaDegIn C)) := by
rw [← inertiaDegIn_mul_inertiaDegIn p P G C GAC GBC,
- ← ramificationIdxIn_mul_ramificationIdxIn' P G C GAC GBC]
+ ← ramificationIdxIn_mul_ramificationIdxIn P G C GAC GBC]
ring
_ = Nat.card GAC := by
- rw [ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn hp B G,
- ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn hP C GBC,
+ rw [ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn p B G,
+ ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn P C GBC,
(IsGaloisGroup.toFractionRing G A B).card_eq_finrank,
(IsGaloisGroup.toFractionRing GAC A C).card_eq_finrank,
(IsGaloisGroup.toFractionRing GBC B C).card_eq_finrank, Module.finrank_mul_finrank]
@@ -297,52 +275,57 @@ open scoped Pointwise
open Algebra
attribute [local instance] Ideal.Quotient.field in
-theorem card_stabilizer_eq_card_inertia_mul_finrank (p : Ideal R) [p.IsMaximal]
- (P : Ideal S) [P.LiesOver p] [P.IsMaximal] [Algebra.IsSeparable (R ⧸ p) (S ⧸ P)] :
- Nat.card (MulAction.stabilizer G P) = Nat.card (inertia G P) *
- Module.finrank (R ⧸ p) (S ⧸ P) := by
- have : IsGalois (R ⧸ p) (S ⧸ P) := { __ := Ideal.Quotient.normal (A := R) G p P }
- have := Ideal.Quotient.finite_of_isInvariant G p P
+theorem card_stabilizer_eq_card_inertia_mul_finrank (p : Ideal R) [p.IsPrime]
+ (P : Ideal S) [P.LiesOver p] [P.IsPrime] [PerfectField p.ResidueField] :
+ Nat.card (MulAction.stabilizer G P) = Nat.card (inertia G P) * P.inertiaDeg' R := by
+ let := Localization.AtPrime.algebraOfLiesOver p P
+ let : Algebra (R ⧸ p) p.ResidueField := inferInstance
+ let : Algebra (S ⧸ P) P.ResidueField := inferInstance
+ have heq : (algebraMap (S ⧸ P) P.ResidueField).comp (algebraMap (R ⧸ p) (S ⧸ P)) =
+ (algebraMap p.ResidueField P.ResidueField).comp (algebraMap (R ⧸ p) p.ResidueField) := by
+ ext
+ simp [← IsScalarTower.algebraMap_apply]
+ let := ((algebraMap (S ⧸ P) P.ResidueField).comp (algebraMap (R ⧸ p) (S ⧸ P))).toAlgebra
+ have : IsScalarTower (R ⧸ p) (S ⧸ P) P.ResidueField := .of_algebraMap_eq' rfl
+ have : IsScalarTower (R ⧸ p) p.ResidueField P.ResidueField := .of_algebraMap_eq' heq
+ have : IsGalois p.ResidueField P.ResidueField :=
+ { __ := Ideal.IsFractionRing.normal G p P p.ResidueField P.ResidueField }
+ have : Module.Finite p.ResidueField P.ResidueField :=
+ Ideal.IsFractionRing.finite_of_isInvariant G p P p.ResidueField P.ResidueField
have : Subgroup.index _ = _ := Nat.card_congr
- (Quotient.stabilizerQuotientInertiaEquiv G p P).toEquiv
- rw [← IsGalois.card_aut_eq_finrank, ← this,
+ (IsFractionRing.stabilizerQuotientInertiaEquiv G p P p.ResidueField P.ResidueField).toEquiv
+ rw [inertiaDeg'_eq p P, ← IsGalois.card_aut_eq_finrank p.ResidueField P.ResidueField, ← this,
← ((inertia G P).subgroupOf (MulAction.stabilizer G P)).card_mul_index,
Nat.card_congr (Subgroup.subgroupOfEquivOfLe (inertia_le_stabilizer (M := G) P)).toEquiv,
AddSubgroup.subgroupOf_inertia]
-lemma ncard_primesOver_mul_card_inertia_mul_finrank (p : Ideal R) [p.IsMaximal]
- (P : Ideal S) [P.LiesOver p] [P.IsMaximal] [Algebra.IsSeparable (R ⧸ p) (S ⧸ P)] :
- (p.primesOver S).ncard * Nat.card (P.inertia G) *
- Module.finrank (R ⧸ p) (S ⧸ P) = Nat.card G := by
- rw [mul_assoc, ← card_stabilizer_eq_card_inertia_mul_finrank,
+lemma ncard_primesOver_mul_card_inertia_mul_finrank (p : Ideal R) [p.IsPrime]
+ (P : Ideal S) [P.LiesOver p] [P.IsPrime] [PerfectField p.ResidueField] :
+ (p.primesOver S).ncard * Nat.card (P.inertia G) * P.inertiaDeg' R = Nat.card G := by
+ rw [mul_assoc, ← card_stabilizer_eq_card_inertia_mul_finrank p P,
← IsInvariant.orbit_eq_primesOver R S G p P]
simpa using Nat.card_congr (MulAction.orbitProdStabilizerEquivGroup G P)
/-- The cardinality of the inertia group is equal to the ramification index. -/
-lemma card_inertia_eq_ramificationIdxIn
- [IsDedekindDomain R] [IsDedekindDomain S] [Module.Finite R S]
- [IsTorsionFree R S]
- (p : Ideal R) (hp : p ≠ ⊥)
- (P : Ideal S) [P.LiesOver p] [P.IsMaximal] [Algebra.IsSeparable (R ⧸ p) (S ⧸ P)] :
+lemma card_inertia_eq_ramificationIdxIn [IsDomain R] [IsDomain S] [Module.Finite R S] [Flat R S]
+ (p : Ideal R) (P : Ideal S) [P.LiesOver p] [p.IsPrime] [P.IsPrime]
+ [PerfectField p.ResidueField] :
Nat.card (P.inertia G) = Ideal.ramificationIdxIn p S := by
- have := (show p.IsPrime from P.over_def p ▸ inferInstance).isMaximal hp
have H := ncard_primesOver_mul_card_inertia_mul_finrank (G := G) p P
- refine mul_right_injective₀ (IsDedekindDomain.primesOver_ncard_ne_zero p S) ?_
- refine mul_left_injective₀ (b := Module.finrank (R ⧸ p) (S ⧸ P)) ?_ ?_
- · intro e; simp [e, eq_comm, Nat.card_eq_zero, ‹Finite G›.not_infinite] at H
- dsimp only
- rw [H, mul_assoc, ← inertiaDeg_algebraMap, ← inertiaDegIn_eq_inertiaDeg p P G,
- ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn hp S G]
+ rw [← inertiaDegIn_eq_inertiaDeg p P G] at H
+ have h1 : (p.primesOver S).ncard ≠ 0 := by grind [Nat.card_pos]
+ have h2 : p.inertiaDegIn S ≠ 0 := by grind [Nat.card_pos]
+ rwa [← ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn p S G,
+ mul_assoc, mul_right_inj' h1, mul_left_inj' h2] at H
/-- The cardinality of the decomposition group is equal to the ramification index times the
inertia degree. -/
-lemma card_stabilizer_eq [IsDedekindDomain R] [IsDedekindDomain S] [Module.Finite R S]
- [IsTorsionFree R S] (p : Ideal R) (hp : p ≠ ⊥) (P : Ideal S) [P.LiesOver p] [P.IsMaximal]
- [Algebra.IsSeparable (R ⧸ p) (S ⧸ P)] :
+lemma card_stabilizer_eq [IsDomain R] [IsDomain S] [Module.Finite R S] [Flat R S]
+ (p : Ideal R) (P : Ideal S) [P.LiesOver p] [p.IsPrime] [P.IsPrime]
+ [PerfectField p.ResidueField] :
Nat.card (MulAction.stabilizer G P) = p.ramificationIdxIn S * p.inertiaDegIn S := by
- have := (show p.IsPrime from P.over_def p ▸ inferInstance).isMaximal hp
- rw [card_stabilizer_eq_card_inertia_mul_finrank p P, card_inertia_eq_ramificationIdxIn p hp,
- inertiaDegIn_eq_inertiaDeg p P G, inertiaDeg_algebraMap]
+ rw [card_stabilizer_eq_card_inertia_mul_finrank p P, card_inertia_eq_ramificationIdxIn p,
+ inertiaDegIn_eq_inertiaDeg p P G]
end inertia
diff --git a/Mathlib/NumberTheory/RamificationInertia/HilbertTheory.lean b/Mathlib/NumberTheory/RamificationInertia/HilbertTheory.lean
index ab55582a4b8ab5..d5378c4a94dcc3 100644
--- a/Mathlib/NumberTheory/RamificationInertia/HilbertTheory.lean
+++ b/Mathlib/NumberTheory/RamificationInertia/HilbertTheory.lean
@@ -181,7 +181,7 @@ theorem IsDecompositionField.rank_left (hp : p ≠ ⊥) :
Module.finrank D L = p.ramificationIdxIn B * p.inertiaDegIn B := by
have : p.IsMaximal := over_def P p ▸ Ideal.IsMaximal.under A P
have : Finite (A ⧸ p) := Ring.HasFiniteQuotients.finiteQuotient hp
- rw [← IsGaloisGroup.card_eq_finrank (stabilizer Gal(L/K) P) D L, card_stabilizer_eq p hp]
+ rw [← IsGaloisGroup.card_eq_finrank (stabilizer Gal(L/K) P) D L, card_stabilizer_eq p]
/--
The degree `[D : K]` of the decomposition field `D` over `K` equals the number of prime ideals
@@ -195,7 +195,7 @@ theorem IsDecompositionField.rank_right [IsGalois K L] [Algebra K D] [IsScalarTo
refine mul_left_injective₀ (b := Module.finrank D L) Module.finrank_pos.ne' ?_
dsimp only
rw [Module.finrank_mul_finrank, rank_left A K L P D hp,
- ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn hp B Gal(L/K),
+ ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn p B Gal(L/K),
IsGaloisGroup.card_eq_finrank Gal(L/K) K L]
variable (E : Type*) [Field E] [Algebra E L] [IsInertiaField K L P E]
@@ -207,8 +207,7 @@ theorem IsInertiaField.rank_left (hp : p ≠ ⊥) :
Module.finrank E L = p.ramificationIdxIn B := by
have : p.IsMaximal := over_def P p ▸ Ideal.IsMaximal.under A P
have : Finite (A ⧸ p) := Ring.HasFiniteQuotients.finiteQuotient hp
- rw [← IsGaloisGroup.card_eq_finrank (inertia Gal(L/K) P) E L,
- card_inertia_eq_ramificationIdxIn p hp]
+ rw [← IsGaloisGroup.card_eq_finrank (inertia Gal(L/K) P) E L, card_inertia_eq_ramificationIdxIn p]
/--
The degree `[E : K]` of the inertia field `E` over `K` equals the product of the number of
@@ -221,7 +220,7 @@ theorem IsInertiaField.rank_right [IsGalois K L] [Algebra K E] [IsScalarTower K
refine mul_left_injective₀ (b := Module.finrank E L) Module.finrank_pos.ne' ?_
dsimp only
rw [Module.finrank_mul_finrank, rank_left A K L P E hp, mul_assoc, mul_comm (p.inertiaDegIn B),
- ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn hp B Gal(L/K),
+ ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn p B Gal(L/K),
IsGaloisGroup.card_eq_finrank Gal(L/K) K L]
/--
@@ -291,15 +290,18 @@ private lemma ramificationIdxIn_eq_and_inertiaDegIn_eq (hp : p ≠ ⊥) :
ramificationIdxIn 𝓟D B = p.ramificationIdxIn B ∧ inertiaDegIn 𝓟D B = p.inertiaDegIn B := by
obtain ⟨_, _, _, _, _, h𝓟⟩ := instances A K L P D 𝓞D 𝓟D hp
refine eq_and_eq_of_pos_of_le_of_mul_le_mul ?_ ?_ ?_ ?_ ?_
- · exact Nat.pos_of_ne_zero <| ramificationIdxIn_ne_zero (stabilizer Gal(L/K) P) h𝓟
+ · exact Nat.pos_of_ne_zero <| ramificationIdxIn_ne_zero (stabilizer Gal(L/K) P)
· exact Nat.pos_of_ne_zero <| inertiaDegIn_ne_zero (stabilizer Gal(L/K) P)
· rw [ramificationIdxIn_eq_ramificationIdx p P Gal(L/K),
ramificationIdxIn_eq_ramificationIdx _ P (stabilizer Gal(L/K) P)]
+ rw [← ramificationIdx_eq_ramificationIdx' p _ hp,
+ ← ramificationIdx_eq_ramificationIdx' 𝓟D _ h𝓟]
exact IsDedekindDomain.ramificationIdx_le_ramificationIdx _ _ _ hp
· rw [inertiaDegIn_eq_inertiaDeg p P Gal(L/K),
inertiaDegIn_eq_inertiaDeg _ P (stabilizer Gal(L/K) P)]
+ rw [← inertiaDeg_eq_inertiaDeg' p, ← inertiaDeg_eq_inertiaDeg' 𝓟D]
exact inertiaDeg_le_inertiaDeg p 𝓟D P
- · have := ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn h𝓟 B (stabilizer Gal(L/K) P)
+ · have := ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn 𝓟D B (stabilizer Gal(L/K) P)
rw [primesOver_eq_singleton K L P D 𝓞D, Set.ncard_singleton, one_mul] at this
rw [this, IsGaloisGroup.card_eq_finrank (stabilizer Gal(L/K) P) D L,
IsDecompositionField.rank_left A K L P D hp]
@@ -328,13 +330,12 @@ Let `D` be the decomposition field of `P` in `L/K`. Let `𝓟D` be a prime ideal
then `𝓟D` is unramified over `K`.
-/
theorem ramificationIdx_eq (hp : p ≠ ⊥) :
- ramificationIdx p 𝓟D = 1 := by
+ 𝓟D.ramificationIdx' A = 1 := by
obtain ⟨_, _, _, _, _, h𝓟⟩ := instances A K L P D 𝓞D 𝓟D hp
- have := ramificationIdx_algebra_tower (map_ne_bot_of_ne_bot h𝓟) (map_ne_bot_of_ne_bot hp)
- (map_le_iff_le_comap.mpr ((liesOver_iff P 𝓟D).mp inferInstance).le)
+ have := ramificationIdx'_tower (R := A) 𝓟D P
rwa [← ramificationIdxIn_eq_ramificationIdx 𝓟D P (stabilizer Gal(L/K) P),
ramificationIdxIn_eq A K L P D 𝓞D 𝓟D hp, ramificationIdxIn_eq_ramificationIdx p P Gal(L/K),
- right_eq_mul₀ <| IsDedekindDomain.ramificationIdx_ne_zero_of_liesOver P hp] at this
+ right_eq_mul₀ <| (ramificationIdx'_pos P A).ne'] at this
include K L D P in
/--
@@ -342,9 +343,9 @@ Let `D` be the decomposition field of `P` in `L/K`. Let `𝓟D` be a prime ideal
then the inertia degree of `𝓟D` over `K` is equal to `1`.
-/
theorem inertiaDeg_eq (hp : p ≠ ⊥) :
- inertiaDeg p 𝓟D = 1 := by
+ 𝓟D.inertiaDeg' A = 1 := by
obtain ⟨_, _, _, _, _, _⟩ := instances A K L P D 𝓞D 𝓟D hp
- have := inertiaDeg_algebra_tower p 𝓟D P
+ have := inertiaDeg'_tower (R := A) 𝓟D P
rwa [← inertiaDegIn_eq_inertiaDeg p P Gal(L/K), ← inertiaDegIn_eq A K L P D 𝓞D 𝓟D hp,
← inertiaDegIn_eq_inertiaDeg 𝓟D P (stabilizer Gal(L/K) P),
right_eq_mul₀ <| inertiaDegIn_ne_zero (stabilizer Gal(L/K) P)] at this
diff --git a/Mathlib/RingTheory/Ideal/GoingUp.lean b/Mathlib/RingTheory/Ideal/GoingUp.lean
index 17e2381d93e229..3c9b3441e4d7fe 100644
--- a/Mathlib/RingTheory/Ideal/GoingUp.lean
+++ b/Mathlib/RingTheory/Ideal/GoingUp.lean
@@ -333,10 +333,10 @@ theorem exists_ideal_over_prime_of_isIntegral [Algebra.IsIntegral R S] (P : Idea
obtain ⟨Q, hQ, hQ', hQ''⟩ := exists_ideal_over_prime_of_isIntegral_of_isPrime P P' hP''
exact ⟨Q, hP.trans hQ, hQ', hQ''⟩
-instance nonempty_primesOver [IsDomain R] [Nontrivial S] [Algebra.IsIntegral R S]
- [Module.IsTorsionFree R S] (P : Ideal R) [P.IsPrime] :
+instance nonempty_primesOver [Algebra.IsIntegral R S] [FaithfulSMul R S] (P : Ideal R) [P.IsPrime] :
Nonempty (primesOver P S) := by
- obtain ⟨Q, _, hQ₁, hQ₂⟩ := exists_ideal_over_prime_of_isIntegral P (⊥ : Ideal S) (by simp)
+ obtain ⟨Q, _, hQ₁, hQ₂⟩ := exists_ideal_over_prime_of_isIntegral P (⊥ : Ideal S)
+ (by simp [← RingHom.ker_eq_comap_bot])
exact ⟨Q, ⟨hQ₁, (liesOver_iff _ _).mpr hQ₂.symm⟩⟩
/-- `comap (algebraMap R S)` is a surjection from the max spec of `S` to max spec of `R`.
diff --git a/Mathlib/RingTheory/Ideal/Norm/RelNorm.lean b/Mathlib/RingTheory/Ideal/Norm/RelNorm.lean
index 74b68815f1914c..86337c0e1acc2c 100644
--- a/Mathlib/RingTheory/Ideal/Norm/RelNorm.lean
+++ b/Mathlib/RingTheory/Ideal/Norm/RelNorm.lean
@@ -399,6 +399,7 @@ be Galois.
-/
theorem relNorm_eq_pow_of_isPrime_isGalois [p.IsMaximal] [P.IsPrime]
[IsGalois (FractionRing R) (FractionRing S)] : relNorm R P = p ^ p.inertiaDeg P := by
+ have : P.IsMaximal := IsMaximal.of_liesOver_isMaximal P p
let G := Gal(FractionRing S/FractionRing R)
let := IsIntegralClosure.MulSemiringAction R (FractionRing R) (FractionRing S) S
have := IsGaloisGroup.of_isFractionRing G R S (FractionRing R) (FractionRing S)
@@ -417,6 +418,7 @@ theorem relNorm_eq_pow_of_isPrime_isGalois [p.IsMaximal] [P.IsPrime]
rw [Set.mem_toFinset] at hQ
have : Q.IsPrime := hQ.1
have : Q.LiesOver p := hQ.2
+ rw [ramificationIdx_eq_ramificationIdx' p Q hp]
rw [← ramificationIdxIn_eq_ramificationIdx p Q G]
obtain ⟨σ, rfl⟩ := Ideal.exists_smul_eq_of_isGaloisGroup p P Q G
rw [relNorm_smul, hs, ← pow_mul, mul_comm]
@@ -424,12 +426,12 @@ theorem relNorm_eq_pow_of_isPrime_isGalois [p.IsMaximal] [P.IsPrime]
map_algebraMap_eq_finsetProd_pow hp).symm.trans <| relNorm_algebraMap S p
simp +contextual only [map_prod, map_pow, h₀, Finset.prod_const, ← pow_mul] at h
rwa [← IsGaloisGroup.card_eq_finrank G (FractionRing R) (FractionRing S),
- ← Ideal.ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn hp S G, mul_comm,
+ ← Ideal.ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn p S G, mul_comm,
← Set.ncard_eq_toFinset_card',
((IsLeftCancelMulZero.mul_left_cancel_of_ne_zero hp).pow_injective _).eq_iff,
mul_right_inj' (IsDedekindDomain.primesOver_ncard_ne_zero p S),
- mul_right_inj' (ramificationIdxIn_ne_zero G hp),
- inertiaDegIn_eq_inertiaDeg p P G] at h
+ mul_right_inj' (ramificationIdxIn_ne_zero G),
+ inertiaDegIn_eq_inertiaDeg p P G, ← inertiaDeg_eq_inertiaDeg' p P] at h
rw [one_eq_top]
exact IsMaximal.ne_top inferInstance
From c5517cc67b022ecda5b14c46d2953a39361155f9 Mon Sep 17 00:00:00 2001
From: Jovan Gerbscheid <56355248+JovanGerb@users.noreply.github.com>
Date: Thu, 18 Jun 2026 11:44:48 +0000
Subject: [PATCH 0140/1300] chore: replace some `convert!` with `convert`
(#40731)
Replace some uses of `convert!` by `convert`.
In particular, the field `AffineSubspace.smul_vsub_vadd_mem` didn't have the right type, so I've fixed this in this PR.
Note: many such replacements should be done automatically with a direct replacement. This should be done automatically in another PR.
---
Mathlib/Data/Fintype/List.lean | 12 +++-----
Mathlib/GroupTheory/Coset/Basic.lean | 4 +--
.../AffineSpace/AffineSubspace/Basic.lean | 4 +--
.../AffineSpace/AffineSubspace/Defs.lean | 29 ++++++++++---------
.../UniqueFactorizationDomain/Basic.lean | 5 +---
.../NormalizedFactors.lean | 3 +-
6 files changed, 25 insertions(+), 32 deletions(-)
diff --git a/Mathlib/Data/Fintype/List.lean b/Mathlib/Data/Fintype/List.lean
index d81ea4001438e3..536953e26212ba 100644
--- a/Mathlib/Data/Fintype/List.lean
+++ b/Mathlib/Data/Fintype/List.lean
@@ -66,18 +66,14 @@ instance fintypeNodupList [Fintype α] : Fintype { l : List α // l.Nodup } := b
constructor
· simp only [Finset.coe_toList]
rfl
- · convert! Finset.nodup_toList (Finset.univ.powerset : Finset (Finset α))
- ext l
- unfold Nodup
- refine Pairwise.iff ?_
- intro m n
+ · -- Unfold `List.Nodup` in the type of the proof term to make it match with the goal.
+ convert dsimp% [List.Nodup] Finset.nodup_toList (Finset.univ.powerset : Finset (Finset α))
+ with m n
simp only [_root_.Disjoint]
rw [← m.coe_toList, ← n.coe_toList, Multiset.lists_coe, Multiset.lists_coe]
have := Multiset.coe_disjoint m.toList.permutations n.toList.permutations
rw [_root_.Disjoint] at this
- rw [this]
- simp only [ne_eq]
- rw [List.disjoint_iff_ne]
+ rw [this, List.disjoint_iff_ne]
constructor
· intro h
by_contra hc
diff --git a/Mathlib/GroupTheory/Coset/Basic.lean b/Mathlib/GroupTheory/Coset/Basic.lean
index fdf30d965ac8ec..896ebb56a467bd 100644
--- a/Mathlib/GroupTheory/Coset/Basic.lean
+++ b/Mathlib/GroupTheory/Coset/Basic.lean
@@ -285,8 +285,8 @@ theorem strictMono_comap_prod_image :
refine fun t₁ t₂ h ↦ ⟨⟨Subgroup.comap_mono h.1, Set.image_mono h.1⟩,
mt (fun ⟨le1, le2⟩ a ha ↦ ?_) h.2⟩
obtain ⟨a', h', eq⟩ := le2 ⟨_, ha, rfl⟩
- convert! ← t₁.mul_mem h' (@le1 ⟨_, QuotientGroup.eq.1 eq⟩ <| t₂.mul_mem (t₂.inv_mem <| h.1 h') ha)
- apply mul_inv_cancel_left
+ convert t₁.mul_mem h' (@le1 ⟨_, QuotientGroup.eq.1 eq⟩ <| t₂.mul_mem (t₂.inv_mem <| h.1 h') ha)
+ simp
variable {s} {a b : α}
diff --git a/Mathlib/LinearAlgebra/AffineSpace/AffineSubspace/Basic.lean b/Mathlib/LinearAlgebra/AffineSpace/AffineSubspace/Basic.lean
index b4f59a92546e88..5874d92b08e4bd 100644
--- a/Mathlib/LinearAlgebra/AffineSpace/AffineSubspace/Basic.lean
+++ b/Mathlib/LinearAlgebra/AffineSpace/AffineSubspace/Basic.lean
@@ -530,7 +530,7 @@ namespace AffineSubspace
/-- The image of an affine subspace under an affine map as an affine subspace. -/
def map (s : AffineSubspace k P₁) : AffineSubspace k P₂ where
carrier := f '' s
- smul_vsub_vadd_mem := by
+ smul_vsub_vadd_mem' := by
rintro t - - - ⟨p₁, h₁, rfl⟩ ⟨p₂, h₂, rfl⟩ ⟨p₃, h₃, rfl⟩
use t • (p₁ -ᵥ p₂) +ᵥ p₃
suffices t • (p₁ -ᵥ p₂) +ᵥ p₃ ∈ s by
@@ -723,7 +723,7 @@ namespace AffineSubspace
/-- The preimage of an affine subspace under an affine map as an affine subspace. -/
def comap (f : P₁ →ᵃ[k] P₂) (s : AffineSubspace k P₂) : AffineSubspace k P₁ where
carrier := f ⁻¹' s
- smul_vsub_vadd_mem t p₁ p₂ p₃ (hp₁ : f p₁ ∈ s) (hp₂ : f p₂ ∈ s) (hp₃ : f p₃ ∈ s) :=
+ smul_vsub_vadd_mem' t p₁ p₂ p₃ (hp₁ : f p₁ ∈ s) (hp₂ : f p₂ ∈ s) (hp₃ : f p₃ ∈ s) :=
show f _ ∈ s by
rw [AffineMap.map_vadd, map_smul, AffineMap.linearMap_vsub]
apply s.smul_vsub_vadd_mem _ hp₁ hp₂ hp₃
diff --git a/Mathlib/LinearAlgebra/AffineSpace/AffineSubspace/Defs.lean b/Mathlib/LinearAlgebra/AffineSpace/AffineSubspace/Defs.lean
index fdb3d07ecd305c..485c4d8a1dc3fc 100644
--- a/Mathlib/LinearAlgebra/AffineSpace/AffineSubspace/Defs.lean
+++ b/Mathlib/LinearAlgebra/AffineSpace/AffineSubspace/Defs.lean
@@ -150,14 +150,12 @@ structure AffineSubspace (k : Type*) {V : Type*} (P : Type*) [Ring k] [AddCommGr
[Module k V] [AffineSpace V P] where
/-- The affine subspace seen as a subset. -/
carrier : Set P
- smul_vsub_vadd_mem :
- ∀ (c : k) {p₁ p₂ p₃ : P},
- p₁ ∈ carrier → p₂ ∈ carrier → p₃ ∈ carrier → c • (p₁ -ᵥ p₂ : V) +ᵥ p₃ ∈ carrier
+ protected smul_vsub_vadd_mem' (c : k) {p₁ p₂ p₃ : P} :
+ p₁ ∈ carrier → p₂ ∈ carrier → p₃ ∈ carrier → c • (p₁ -ᵥ p₂ : V) +ᵥ p₃ ∈ carrier
namespace AffineSubspace
-variable (k : Type*) {V : Type*} (P : Type*) [Ring k] [AddCommGroup V] [Module k V]
- [AffineSpace V P]
+variable {k V P : Type*} [Ring k] [AddCommGroup V] [Module k V] [AffineSpace V P]
instance : SetLike (AffineSubspace k P) P where
coe := carrier
@@ -167,12 +165,14 @@ instance : PartialOrder (AffineSubspace k P) := .ofSetLike (AffineSubspace k P)
@[simp] lemma carrier_eq_coe (s : AffineSubspace k P) : s.carrier = s := rfl
+lemma smul_vsub_vadd_mem (s : AffineSubspace k P) (c : k) {p₁ p₂ p₃ : P} :
+ p₁ ∈ s → p₂ ∈ s → p₃ ∈ s → c • (p₁ -ᵥ p₂ : V) +ᵥ p₃ ∈ s :=
+ s.smul_vsub_vadd_mem' c
+
/-- A point is in an affine subspace coerced to a set if and only if it is in that affine
subspace. -/
theorem mem_coe (p : P) (s : AffineSubspace k P) : p ∈ (s : Set P) ↔ p ∈ s := by simp
-variable {k P}
-
/-- Two affine subspaces are equal if they have the same points. -/
theorem coe_injective : Function.Injective ((↑) : AffineSubspace k P → Set P) :=
SetLike.coe_injective
@@ -193,7 +193,7 @@ variable {k V : Type*} [Ring k] [AddCommGroup V] [Module k V]
/-- Reinterprets `p : Submodule k V` as an `AffineSubspace k V`. -/
@[coe] def toAffineSubspace (p : Submodule k V) : AffineSubspace k V where
carrier := p
- smul_vsub_vadd_mem _ _ _ _ h₁ h₂ h₃ := p.add_mem (p.smul_mem _ (p.sub_mem h₁ h₂)) h₃
+ smul_vsub_vadd_mem' _ _ _ _ h₁ h₂ h₃ := p.add_mem (p.smul_mem _ (p.sub_mem h₁ h₂)) h₃
instance : Coe (Submodule k V) (AffineSubspace k V) := ⟨toAffineSubspace⟩
@@ -252,7 +252,8 @@ def directionOfNonempty {s : AffineSubspace k P} (h : (s : Set P).Nonempty) : Su
rintro _ _ ⟨p₁, hp₁, p₂, hp₂, rfl⟩ ⟨p₃, hp₃, p₄, hp₄, rfl⟩
rw [← vadd_vsub_assoc]
refine vsub_mem_vsub ?_ hp₄
- convert! s.smul_vsub_vadd_mem 1 hp₁ hp₂ hp₃
+ rw [mem_coe]
+ convert s.smul_vsub_vadd_mem 1 hp₁ hp₂ hp₃
rw [one_smul]
smul_mem' := by
rintro c _ ⟨p₁, hp₁, p₂, hp₂, rfl⟩
@@ -287,7 +288,7 @@ theorem vadd_mem_of_mem_direction {s : AffineSubspace k P} {v : V} (hv : v ∈ s
rw [mem_direction_iff_eq_vsub ⟨p, hp⟩] at hv
rcases hv with ⟨p₁, hp₁, p₂, hp₂, hv⟩
rw [hv]
- convert! s.smul_vsub_vadd_mem 1 hp₁ hp₂ hp
+ convert s.smul_vsub_vadd_mem 1 hp₁ hp₂ hp
rw [one_smul]
/-- Subtracting two points in the subspace produces a vector in the direction. -/
@@ -386,7 +387,7 @@ theorem eq_iff_direction_eq_of_mem {s₁ s₂ : AffineSubspace k P} {p : P} (h
/-- Construct an affine subspace from a point and a direction. -/
def mk' (p : P) (direction : Submodule k V) : AffineSubspace k P where
carrier := { q | q -ᵥ p ∈ direction }
- smul_vsub_vadd_mem c p₁ p₂ p₃ hp₁ hp₂ hp₃ := by
+ smul_vsub_vadd_mem' c p₁ p₂ p₃ hp₁ hp₂ hp₃ := by
simpa [vadd_vsub_assoc] using
direction.add_mem (direction.smul_mem c (direction.sub_mem hp₁ hp₂)) hp₃
@@ -463,7 +464,7 @@ variable (k : Type*) {V : Type*} {P : Type*} [Ring k] [AddCommGroup V] [Module k
(Actually defined here in terms of spans in modules.) -/
def affineSpan (s : Set P) : AffineSubspace k P where
carrier := spanPoints k s
- smul_vsub_vadd_mem c _ _ _ hp₁ hp₂ hp₃ :=
+ smul_vsub_vadd_mem' c _ _ _ hp₁ hp₂ hp₃ :=
vadd_mem_spanPoints_of_mem_spanPoints_of_mem_vectorSpan k hp₃
((vectorSpan k s).smul_mem c
(vsub_mem_vectorSpan_of_mem_spanPoints_of_mem_spanPoints k hp₁ hp₂))
@@ -542,11 +543,11 @@ instance : CompleteLattice (AffineSubspace k P) where
inf_le_right := fun _ _ => Set.inter_subset_right
top :=
{ carrier := Set.univ
- smul_vsub_vadd_mem := fun _ _ _ _ _ _ _ => Set.mem_univ _ }
+ smul_vsub_vadd_mem' _ _ _ _ _ _ _ := Set.mem_univ _ }
le_top := fun _ _ _ => Set.mem_univ _
bot :=
{ carrier := ∅
- smul_vsub_vadd_mem := fun _ _ _ _ => False.elim }
+ smul_vsub_vadd_mem' _ _ _ _ := False.elim }
bot_le := fun _ _ => False.elim
sSup := fun s => affineSpan k (⋃ s' ∈ s, (s' : Set P))
sInf := fun s =>
diff --git a/Mathlib/RingTheory/UniqueFactorizationDomain/Basic.lean b/Mathlib/RingTheory/UniqueFactorizationDomain/Basic.lean
index a1ad2b43565bc1..7b93b28eee7e7d 100644
--- a/Mathlib/RingTheory/UniqueFactorizationDomain/Basic.lean
+++ b/Mathlib/RingTheory/UniqueFactorizationDomain/Basic.lean
@@ -57,10 +57,7 @@ end WfDvdMonoid
theorem WfDvdMonoid.of_wellFoundedLT_associates [CommMonoidWithZero α] [IsCancelMulZero α]
(h : WellFoundedLT (Associates α)) : WfDvdMonoid α :=
WfDvdMonoid.of_wfDvdMonoid_associates
- ⟨by
- convert! h.wf
- ext
- exact Associates.dvdNotUnit_iff_lt⟩
+ ⟨by convert h.wf; exact Associates.dvdNotUnit_iff_lt⟩
theorem WfDvdMonoid.iff_wellFounded_associates [CommMonoidWithZero α] [IsCancelMulZero α] :
WfDvdMonoid α ↔ WellFoundedLT (Associates α) :=
diff --git a/Mathlib/RingTheory/UniqueFactorizationDomain/NormalizedFactors.lean b/Mathlib/RingTheory/UniqueFactorizationDomain/NormalizedFactors.lean
index d02f959aebbcb2..3db98f8f11a8b3 100644
--- a/Mathlib/RingTheory/UniqueFactorizationDomain/NormalizedFactors.lean
+++ b/Mathlib/RingTheory/UniqueFactorizationDomain/NormalizedFactors.lean
@@ -41,8 +41,7 @@ if `M` has a trivial group of units. -/
theorem factors_eq_normalizedFactors {M : Type*} [CommMonoidWithZero M]
[UniqueFactorizationMonoid M] [Subsingleton Mˣ] (x : M) : factors x = normalizedFactors x := by
unfold normalizedFactors
- convert! (Multiset.map_id (factors x)).symm
- ext p
+ convert (Multiset.map_id (factors x)).symm with p
exact normalize_eq p
theorem prod_normalizedFactors {a : α} (ane0 : a ≠ 0) :
From cf36f3ffdb98eee75df18f733538eb51cbdfea3e Mon Sep 17 00:00:00 2001
From: Yan Yablonovskiy <186670707+YanYablonovskiy@users.noreply.github.com>
Date: Thu, 18 Jun 2026 11:44:50 +0000
Subject: [PATCH 0141/1300] feat(StrongMeasurability): `fun_prop` for
`integral_kernel` (#40751)
See [this](https://github.com/YaelDillies/gibbs-measure/pull/26#discussion_r3434489840) discussion.
Co-authored-by: YanYablonovskiy
---
Mathlib/Probability/Kernel/MeasurableIntegral.lean | 1 +
1 file changed, 1 insertion(+)
diff --git a/Mathlib/Probability/Kernel/MeasurableIntegral.lean b/Mathlib/Probability/Kernel/MeasurableIntegral.lean
index 8e8545a71131ec..0adc5b18d2add3 100644
--- a/Mathlib/Probability/Kernel/MeasurableIntegral.lean
+++ b/Mathlib/Probability/Kernel/MeasurableIntegral.lean
@@ -51,6 +51,7 @@ namespace MeasureTheory
variable [NormedSpace ℝ E]
omit [IsSFiniteKernel κ] in
+@[fun_prop]
theorem StronglyMeasurable.integral_kernel ⦃f : β → E⦄
(hf : StronglyMeasurable f) : StronglyMeasurable fun x ↦ ∫ y, f y ∂κ x := by
classical
From 80379404120370388cda81cb32434da332cfceb1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=ABl=20Riou?=
<37772949+joelriou@users.noreply.github.com>
Date: Thu, 18 Jun 2026 12:22:21 +0000
Subject: [PATCH 0142/1300] refactor(Topology): redefine Delta-generated spaces
(#38431)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Delta-generated spaces are made particular cases of `X`-generated spaces, where `X` is the family of spaces `Fin n → ℝ`.
---
Mathlib/Topology/Category/DeltaGenerated.lean | 94 +++-----
.../Compactness/DeltaGeneratedSpace.lean | 224 +++++++-----------
Mathlib/Topology/Convenient/Category.lean | 26 +-
Mathlib/Topology/Convenient/GeneratedBy.lean | 11 +
4 files changed, 144 insertions(+), 211 deletions(-)
diff --git a/Mathlib/Topology/Category/DeltaGenerated.lean b/Mathlib/Topology/Category/DeltaGenerated.lean
index 836bf8399aa1de..77fb4f8368300f 100644
--- a/Mathlib/Topology/Category/DeltaGenerated.lean
+++ b/Mathlib/Topology/Category/DeltaGenerated.lean
@@ -1,25 +1,30 @@
/-
Copyright (c) 2024 Ben Eltschig. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
-Authors: Ben Eltschig
+Authors: Ben Eltschig, Joël Riou
-/
module
public import Mathlib.CategoryTheory.Monad.Limits
public import Mathlib.Topology.Category.TopCat.Limits.Basic
public import Mathlib.Topology.Compactness.DeltaGeneratedSpace
+public import Mathlib.Topology.Convenient.Category
/-!
# Delta-generated topological spaces
-The category of delta-generated spaces.
+This file defines the category `DeltaGenerated` of delta-generated spaces.
+This is a particular case of the construction in the file
+`Mathlib/Topology/Convenient/Category.Lean`: this is the category of
+`X`-generated spaces where `X` is the family of spaces `Fin n → ℝ`
+for all `n : ℕ`.
-See https://ncatlab.org/nlab/show/Delta-generated+topological+space.
+## TODO
+* `DeltaGenerated` is Cartesian closed (@joelriou).
-Adapted from `Mathlib/Topology/Category/CompactlyGenerated.lean`.
+## References
+* https://ncatlab.org/nlab/show/Delta-generated+topological+space
-## TODO
-* `DeltaGenerated` is Cartesian closed.
-/
@[expose] public section
@@ -28,77 +33,32 @@ universe u
open CategoryTheory
-/-- The type of delta-generated topological spaces. -/
-structure DeltaGenerated where
- /-- the underlying topological space -/
- toTop : TopCat.{u}
- /-- The underlying topological space is delta-generated. -/
- deltaGenerated : DeltaGeneratedSpace toTop := by infer_instance
-
-namespace DeltaGenerated
+/-- The category of delta-generated topological spaces. -/
+abbrev DeltaGenerated := GeneratedByTopCat.{u} (fun n ↦ Fin n → ℝ)
-instance : CoeSort DeltaGenerated Type* :=
- ⟨fun X ↦ X.toTop⟩
-
-attribute [instance] deltaGenerated
-
-instance : LargeCategory.{u} DeltaGenerated.{u} :=
- inferInstanceAs <| Category (InducedCategory _ toTop)
+/-- The faithful (but not full) functor taking each topological space to its delta-generated
+ coreflection. -/
+abbrev TopCat.toDeltaGenerated : TopCat.{u} ⥤ DeltaGenerated.{u} :=
+ TopCat.toGeneratedByTopCat
-instance : ConcreteCategory.{u} DeltaGenerated.{u} (C(·, ·)) :=
- inferInstanceAs <| ConcreteCategory (InducedCategory _ toTop) _
+namespace DeltaGenerated
/-- Constructor for objects of the category `DeltaGenerated` -/
-abbrev of (X : Type u) [TopologicalSpace X] [DeltaGeneratedSpace X] : DeltaGenerated.{u} where
- toTop := TopCat.of X
- deltaGenerated := ‹_›
+abbrev of (X : Type u) [TopologicalSpace X] [DeltaGeneratedSpace X] : DeltaGenerated.{u} :=
+ GeneratedByTopCat.of X
/-- The forgetful functor `DeltaGenerated ⥤ TopCat` -/
-@[simps!]
-def deltaGeneratedToTop : DeltaGenerated.{u} ⥤ TopCat.{u} :=
- inducedFunctor _
+abbrev deltaGeneratedToTop : DeltaGenerated.{u} ⥤ TopCat.{u} :=
+ GeneratedByTopCat.toTopCat
/-- `deltaGeneratedToTop` is fully faithful. -/
-def fullyFaithfulDeltaGeneratedToTop : deltaGeneratedToTop.{u}.FullyFaithful :=
- fullyFaithfulInducedFunctor _
-
-instance : deltaGeneratedToTop.{u}.Full := fullyFaithfulDeltaGeneratedToTop.full
-
-instance : deltaGeneratedToTop.{u}.Faithful := fullyFaithfulDeltaGeneratedToTop.faithful
-
-/-- The faithful (but not full) functor taking each topological space to its delta-generated
- coreflection. -/
-@[simps!]
-def topToDeltaGenerated : TopCat.{u} ⥤ DeltaGenerated.{u} where
- obj X := of (DeltaGeneratedSpace.of X)
- map {_ Y} f := ConcreteCategory.ofHom ⟨f, (continuous_to_deltaGenerated (Y := Y)).mpr <|
- continuous_le_dom deltaGenerated_le f.hom.continuous⟩
+abbrev fullyFaithfulDeltaGeneratedToTop : deltaGeneratedToTop.{u}.FullyFaithful :=
+ GeneratedByTopCat.fullyFaithfulToTopCat _
-instance : topToDeltaGenerated.{u}.Faithful :=
- ⟨fun h ↦ by ext x; exact CategoryTheory.congr_fun h x⟩
+@[deprecated (since := "2026-04-23")] alias topToDeltaGenerated := TopCat.toDeltaGenerated
/-- The adjunction between the forgetful functor `DeltaGenerated ⥤ TopCat` and its coreflector. -/
-def coreflectorAdjunction : deltaGeneratedToTop ⊣ topToDeltaGenerated :=
- Adjunction.mkOfUnitCounit {
- unit := {
- app X := ConcreteCategory.ofHom
- ⟨id, continuous_iff_coinduced_le.mpr (eq_deltaGenerated (X := X)).le⟩ }
- counit := {
- app X := ConcreteCategory.ofHom
- ⟨DeltaGeneratedSpace.counit, DeltaGeneratedSpace.continuous_counit⟩ } }
-
-/-- The category of delta-generated spaces is coreflective in the category of topological spaces. -/
-instance deltaGeneratedToTop.coreflective : Coreflective deltaGeneratedToTop where
- R := topToDeltaGenerated
- adj := coreflectorAdjunction
-
-noncomputable instance deltaGeneratedToTop.createsColimits : CreatesColimits deltaGeneratedToTop :=
- comonadicCreatesColimits deltaGeneratedToTop
-
-instance hasLimits : Limits.HasLimits DeltaGenerated :=
- hasLimits_of_coreflective deltaGeneratedToTop
-
-instance hasColimits : Limits.HasColimits DeltaGenerated :=
- hasColimits_of_hasColimits_createsColimits deltaGeneratedToTop
+abbrev coreflectorAdjunction : deltaGeneratedToTop ⊣ TopCat.toDeltaGenerated :=
+ GeneratedByTopCat.adj
end DeltaGenerated
diff --git a/Mathlib/Topology/Compactness/DeltaGeneratedSpace.lean b/Mathlib/Topology/Compactness/DeltaGeneratedSpace.lean
index 808a0556b7a8ce..d06465c472ce81 100644
--- a/Mathlib/Topology/Compactness/DeltaGeneratedSpace.lean
+++ b/Mathlib/Topology/Compactness/DeltaGeneratedSpace.lean
@@ -1,10 +1,11 @@
/-
Copyright (c) 2024 Ben Eltschig. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
-Authors: Ben Eltschig
+Authors: Ben Eltschig, Joël Riou
-/
module
+public import Mathlib.Topology.Convenient.GeneratedBy
public import Mathlib.Analysis.LocallyConvex.WithSeminorms
/-!
@@ -17,7 +18,11 @@ locally path-connected, sequential and in particular compactly generated.
See https://ncatlab.org/nlab/show/Delta-generated+topological+space.
-Adapted from `Mathlib/Topology/Compactness/CompactlyGeneratedSpace.lean`.
+The notions defined in this file (see also the file
+`Mathlib/Topology/Category/DeltaGenerated.lean` for the category `DeltaGenerated`)
+are a particular case of the notion of `X`-generated topological spaces where
+`X` is a family of topological spaces (see the file
+`Mathlib/Topology/Convenient/GeneratedBy.lean`.)
## TODO
* All locally path-connected first-countable spaces are delta-generated - in particular, all normed
@@ -28,158 +33,95 @@ Adapted from `Mathlib/Topology/Compactness/CompactlyGeneratedSpace.lean`.
@[expose] public section
-variable {X Y : Type*} [tX : TopologicalSpace X] [tY : TopologicalSpace Y]
-
open TopologicalSpace Topology
+/-- A topological space is Delta-generated if its topology is generated
+by the continuous maps from topological spaces of the form `Fin n → ℝ`. -/
+abbrev DeltaGeneratedSpace (Y : Type*) [TopologicalSpace Y] : Prop :=
+ IsGeneratedBy (fun n ↦ Fin n → ℝ) Y
+
+namespace DeltaGeneratedSpace
+
+variable {X Y : Type*} [TopologicalSpace X] [TopologicalSpace Y]
+
+/-- Type synonym to be equipped with the delta-generated topology. -/
+abbrev of : Type _ := WithGeneratedByTopology (fun n ↦ Fin n → ℝ) Y
+
+/-- Delta-generated spaces are locally path-connected. -/
+instance [DeltaGeneratedSpace X] :
+ LocPathConnectedSpace X := by
+ rw [← IsGeneratedBy.generatedBy_eq (X := fun n ↦ Fin n → ℝ) (Y := X),
+ generatedBy_eq_coinduced]
+ exact LocPathConnectedSpace.coinduced _
+
+/-- Delta-generated spaces are sequential. -/
+instance [DeltaGeneratedSpace X] : SequentialSpace X := by
+ rw [← IsGeneratedBy.generatedBy_eq (X := fun n ↦ Fin n → ℝ) (Y := X)]
+ exact SequentialSpace.iSup (fun n ↦ SequentialSpace.iSup
+ (fun f ↦ SequentialSpace.coinduced _))
+
+end DeltaGeneratedSpace
+
/-- The topology coinduced by all maps from ℝⁿ into a space. -/
-@[implicit_reducible]
+@[implicit_reducible, deprecated "Use TopologicalSpace.generatedBy" (since := "2026-04-23")]
def TopologicalSpace.deltaGenerated (X : Type*) [TopologicalSpace X] : TopologicalSpace X :=
⨆ f : (n : ℕ) × C(((Fin n) → ℝ), X), coinduced f.2 inferInstance
-/-- The delta-generated topology is also coinduced by a single map out of a sigma type. -/
-lemma deltaGenerated_eq_coinduced : deltaGenerated X = coinduced
- (fun x : (f : (n : ℕ) × C(Fin n → ℝ, X)) × (Fin f.1 → ℝ) ↦ x.1.2 x.2) inferInstance := by
- rw [deltaGenerated, instTopologicalSpaceSigma, coinduced_iSup]; rfl
-
-/-- The delta-generated topology is at least as fine as the original one. -/
-lemma deltaGenerated_le : deltaGenerated X ≤ tX :=
- iSup_le_iff.mpr fun f ↦ f.2.continuous.coinduced_le
-
-/-- A set is open in `deltaGenerated X` iff all its preimages under continuous functions ℝⁿ → X are
- open. -/
-lemma isOpen_deltaGenerated_iff {u : Set X} :
- IsOpen[deltaGenerated X] u ↔ ∀ n (p : C(Fin n → ℝ, X)), IsOpen (p ⁻¹' u) := by
- simp_rw +instances [deltaGenerated, isOpen_iSup_iff, isOpen_coinduced, Sigma.forall]
-
-/-- A map from ℝⁿ to X is continuous iff it is continuous regarding the
- delta-generated topology on X. Outside of this file, use the more general
- `continuous_to_deltaGenerated` instead. -/
-private lemma continuous_euclidean_to_deltaGenerated {n : ℕ} {f : (Fin n → ℝ) → X} :
- Continuous[_, deltaGenerated X] f ↔ Continuous f := by
- simp_rw [continuous_iff_coinduced_le]
- refine ⟨fun h ↦ h.trans deltaGenerated_le, fun h ↦ ?_⟩
- simp_rw [deltaGenerated]
- exact le_iSup_of_le (i := ⟨n, f, continuous_iff_coinduced_le.mpr h⟩) le_rfl
-
-/-- `deltaGenerated` is idempotent as a function `TopologicalSpace X → TopologicalSpace X`. -/
-lemma deltaGenerated_deltaGenerated_eq :
- @deltaGenerated X (deltaGenerated X) = deltaGenerated X := by
- ext u; simp_rw [isOpen_deltaGenerated_iff]; refine forall_congr' fun n ↦ ?_
- -- somewhat awkward because `ContinuousMap` doesn't play well with multiple topologies.
- refine ⟨fun h p ↦ h <| @ContinuousMap.mk _ _ _ (_) p ?_, fun h p ↦ h ⟨p, ?_⟩⟩
- · exact continuous_euclidean_to_deltaGenerated.mpr p.2
- · exact continuous_euclidean_to_deltaGenerated.mp <| @ContinuousMap.continuous_toFun _ _ _ (_) p
-
-/-- A space is delta-generated if its topology is equal to the delta-generated topology, i.e.
- coinduced by all continuous maps ℝⁿ → X. Since the delta-generated topology is always finer
- than the original one, it suffices to show that it is also coarser. -/
-class DeltaGeneratedSpace (X : Type*) [t : TopologicalSpace X] : Prop where
- le_deltaGenerated : t ≤ deltaGenerated X
-
-lemma eq_deltaGenerated [DeltaGeneratedSpace X] : tX = deltaGenerated X :=
- eq_of_le_of_ge DeltaGeneratedSpace.le_deltaGenerated deltaGenerated_le
-
-/-- A subset of a delta-generated space is open iff its preimage is open for every
- continuous map from ℝⁿ to X. -/
-lemma DeltaGeneratedSpace.isOpen_iff [DeltaGeneratedSpace X] {u : Set X} :
- IsOpen u ↔ ∀ (n : ℕ) (p : ContinuousMap ((Fin n) → ℝ) X), IsOpen (p ⁻¹' u) := by
- nth_rewrite 1 [eq_deltaGenerated (X := X)]; exact isOpen_deltaGenerated_iff
-
-/-- A map out of a delta-generated space is continuous iff it preserves continuity of maps
- from ℝⁿ into X. -/
-lemma DeltaGeneratedSpace.continuous_iff [DeltaGeneratedSpace X] {f : X → Y} :
- Continuous f ↔ ∀ (n : ℕ) (p : C(((Fin n) → ℝ), X)), Continuous (f ∘ p) := by
- simp_rw [continuous_iff_coinduced_le]
- nth_rewrite 1 [eq_deltaGenerated (X := X), deltaGenerated]
- simp [coinduced_compose, Sigma.forall]
-
-/-- A map out of a delta-generated space is continuous iff it is continuous with respect
- to the delta-generated topology on the codomain. -/
-lemma continuous_to_deltaGenerated [DeltaGeneratedSpace X] {f : X → Y} :
- Continuous[_, deltaGenerated Y] f ↔ Continuous f := by
- simp_rw [DeltaGeneratedSpace.continuous_iff, continuous_euclidean_to_deltaGenerated]
-
-/-- The delta-generated topology on `X` does in fact turn `X` into a delta-generated space. -/
-lemma deltaGeneratedSpace_deltaGenerated {X : Type*} {t : TopologicalSpace X} :
- @DeltaGeneratedSpace X (@deltaGenerated X t) := by
- let _ := @deltaGenerated X t; constructor; rw [@deltaGenerated_deltaGenerated_eq X t]
-
-lemma deltaGenerated_mono {X : Type*} {t₁ t₂ : TopologicalSpace X} (h : t₁ ≤ t₂) :
- @deltaGenerated X t₁ ≤ @deltaGenerated X t₂ := by
- rw [← continuous_id_iff_le, @continuous_to_deltaGenerated _ _
- (@deltaGenerated X t₁) t₂ deltaGeneratedSpace_deltaGenerated id]
- exact continuous_id_iff_le.2 <| (@deltaGenerated_le X t₁).trans h
+@[deprecated (since := "2026-04-23")]
+alias deltaGenerated_eq_coinduced := generatedBy_eq_coinduced
-namespace DeltaGeneratedSpace
+@[deprecated (since := "2026-04-23")] alias deltaGenerated_le := generatedBy_le
-/-- Type synonym to be equipped with the delta-generated topology. -/
-def of (X : Type*) := X
+@[deprecated (since := "2026-04-23")]
+alias isOpen_deltaGenerated_iff := WithGeneratedByTopology.isOpen_iff
-instance : TopologicalSpace (of X) := deltaGenerated X
+@[deprecated (since := "2026-04-23")]
+alias deltaGenerated_deltaGenerated_eq := generatedBy_generatedBy
-instance : DeltaGeneratedSpace (of X) :=
- deltaGeneratedSpace_deltaGenerated
+@[deprecated (since := "2026-04-23")]
+alias eq_deltaGenerated := IsGeneratedBy.generatedBy_eq
-/-- The natural map from `DeltaGeneratedSpace.of X` to `X`. -/
-def counit : (of X) → X := id
+@[deprecated (since := "2026-04-23")]
+alias DeltaGeneratedSpace.isOpen_iff := IsGeneratedBy.isOpen_iff
-lemma continuous_counit : Continuous (counit : _ → X) := by
- rw [continuous_iff_coinduced_le]; exact deltaGenerated_le
+@[deprecated (since := "2026-04-23")]
+alias DeltaGeneratedSpace.continuous_iff := IsGeneratedBy.continuous_iff
-/-- Delta-generated spaces are locally path-connected. -/
-instance [DeltaGeneratedSpace X] : LocPathConnectedSpace X := by
- rw [eq_deltaGenerated (X := X), deltaGenerated_eq_coinduced]
- exact LocPathConnectedSpace.coinduced _
+@[deprecated (since := "2026-04-23")]
+alias continuous_to_deltaGenerated := WithGeneratedByTopology.continuous_equiv
-/-- Delta-generated spaces are sequential. -/
-instance [DeltaGeneratedSpace X] : SequentialSpace X := by
- rw [eq_deltaGenerated (X := X)]
- exact SequentialSpace.iSup fun p ↦ SequentialSpace.coinduced p.2
+@[deprecated (since := "2026-04-23")]
+alias deltaGeneratedSpace_deltaGenerated := IsGeneratedBy.instWithGeneratedByTopology
-end DeltaGeneratedSpace
+@[deprecated (since := "2026-04-23")]
+alias deltaGenerated_mono := generatedBy_mono
+
+@[deprecated (since := "2026-04-23")]
+alias DeltaGeneratedSpace.counit := WithGeneratedByTopology.equiv
+
+@[deprecated (since := "2026-04-23")]
+alias DeltaGeneratedSpace.continuous_counit := WithGeneratedByTopology.continuous_equiv
+
+@[deprecated (since := "2026-04-23")]
+alias DeltaGeneratedSpace.coinduced := IsGeneratedBy.coinduced
+
+@[deprecated (since := "2026-04-23")]
+alias DeltaGeneratedSpace.iSup := IsGeneratedBy.iSup
+
+@[deprecated (since := "2026-04-23")]
+alias DeltaGeneratedSpace.sup := IsGeneratedBy.sup
+
+@[deprecated (since := "2026-04-23")]
+alias Topology.IsQuotientMap.deltaGeneratedSpace := Topology.IsQuotientMap.isGeneratedBy
+
+@[deprecated (since := "2026-04-23")]
+alias Quot.deltaGeneratedSpace := Quot.isGeneratedBy
+
+@[deprecated (since := "2026-04-23")]
+alias Quotient.deltaGeneratedSpace := Quotient.isGeneratedBy
+
+@[deprecated (since := "2026-04-23")]
+alias Sum.deltaGeneratedSpace := Sum.isGeneratedBy
-omit tY in
-/-- Any topology coinduced by a delta-generated topology is delta-generated. -/
-lemma DeltaGeneratedSpace.coinduced [DeltaGeneratedSpace X] (f : X → Y) :
- @DeltaGeneratedSpace Y (tX.coinduced f) :=
- let _ := tX.coinduced f
- ⟨(continuous_to_deltaGenerated.2 continuous_coinduced_rng).coinduced_le⟩
-
-/-- Suprema of delta-generated topologies are delta-generated. -/
-protected lemma DeltaGeneratedSpace.iSup {X : Type*} {ι : Sort*} {t : ι → TopologicalSpace X}
- (h : ∀ i, @DeltaGeneratedSpace X (t i)) : @DeltaGeneratedSpace X (⨆ i, t i) :=
- let _ := ⨆ i, t i
- ⟨iSup_le_iff.2 fun i ↦ (h i).le_deltaGenerated.trans <| deltaGenerated_mono <| le_iSup t i⟩
-
-/-- Suprema of delta-generated topologies are delta-generated. -/
-protected lemma DeltaGeneratedSpace.sup {X : Type*} {t₁ t₂ : TopologicalSpace X}
- (h₁ : @DeltaGeneratedSpace X t₁) (h₂ : @DeltaGeneratedSpace X t₂) :
- @DeltaGeneratedSpace X (t₁ ⊔ t₂) := by
- rw [sup_eq_iSup]
- exact .iSup <| Bool.forall_bool.2 ⟨h₂, h₁⟩
-
-/-- Quotients of delta-generated spaces are delta-generated. -/
-lemma Topology.IsQuotientMap.deltaGeneratedSpace [DeltaGeneratedSpace X]
- {f : X → Y} (h : IsQuotientMap f) : DeltaGeneratedSpace Y :=
- h.isCoinducing.eq_coinduced ▸ DeltaGeneratedSpace.coinduced f
-
-/-- Quotients of delta-generated spaces are delta-generated. -/
-instance Quot.deltaGeneratedSpace [DeltaGeneratedSpace X] {r : X → X → Prop} :
- DeltaGeneratedSpace (Quot r) :=
- isQuotientMap_quot_mk.deltaGeneratedSpace
-
-/-- Quotients of delta-generated spaces are delta-generated. -/
-instance Quotient.deltaGeneratedSpace [DeltaGeneratedSpace X] {s : Setoid X} :
- DeltaGeneratedSpace (Quotient s) :=
- isQuotientMap_quotient_mk'.deltaGeneratedSpace
-
-/-- Disjoint unions of delta-generated spaces are delta-generated. -/
-instance Sum.deltaGeneratedSpace [DeltaGeneratedSpace X] [DeltaGeneratedSpace Y] :
- DeltaGeneratedSpace (X ⊕ Y) :=
- DeltaGeneratedSpace.sup (.coinduced Sum.inl) (.coinduced Sum.inr)
-
-/-- Disjoint unions of delta-generated spaces are delta-generated. -/
-instance Sigma.deltaGeneratedSpace {ι : Type*} {X : ι → Type*} [∀ i, TopologicalSpace (X i)]
- [∀ i, DeltaGeneratedSpace (X i)] : DeltaGeneratedSpace (Σ i, X i) :=
- .iSup fun _ ↦ .coinduced _
+@[deprecated (since := "2026-04-23")]
+alias Sigma.deltaGeneratedSpace := Sigma.isGeneratedBy
diff --git a/Mathlib/Topology/Convenient/Category.lean b/Mathlib/Topology/Convenient/Category.lean
index e071b214b5b54f..fdde8314b14324 100644
--- a/Mathlib/Topology/Convenient/Category.lean
+++ b/Mathlib/Topology/Convenient/Category.lean
@@ -5,8 +5,8 @@ Authors: Joël Riou
-/
module
-public import Mathlib.CategoryTheory.Adjunction.FullyFaithful
-public import Mathlib.Topology.Category.TopCat.Basic
+public import Mathlib.CategoryTheory.Monad.Limits
+public import Mathlib.Topology.Category.TopCat.Limits.Basic
public import Mathlib.Topology.Convenient.ContinuousMapGeneratedBy
/-!
@@ -37,7 +37,7 @@ structure on `GeneratedByTopCat X` under suitable assumptions (TODO @joelriou).
universe v t u
-open CategoryTheory Topology
+open CategoryTheory Topology Limits
variable {ι : Type t} (X : ι → Type u) [∀ i, TopologicalSpace (X i)]
@@ -203,6 +203,9 @@ instance : (toTopCat.{v} X).IsLeftAdjoint := adj.isLeftAdjoint
instance : (TopCat.toContinuousGeneratedByCat.{v} X).IsRightAdjoint := adj.isRightAdjoint
+instance : (TopCat.toContinuousGeneratedByCat.{v} X).Faithful where
+ map_injective h := by ext x; exact ConcreteCategory.congr_hom h x
+
instance : IsIso (adj.{v} (X := X)).unit := inferInstanceAs (IsIso adjUnitIso.hom)
/-- The functor `GeneratedByTopCat X ⥤ ContinuousGeneratedByCat X` which is
@@ -307,4 +310,21 @@ instance (Z : TopCat.{v}) :
inferInstanceAs (IsIso ((TopCat.toContinuousGeneratedByCat X).map
(ContinuousGeneratedByCat.adj.counit.app Z)))
+instance : (TopCat.toGeneratedByTopCat.{v} (X := X)).Faithful where
+ map_injective h := by ext x; exact ConcreteCategory.congr_hom h x
+
+/-- The category of `X`-generated spaces is coreflective in the category of topological spaces. -/
+instance : Coreflective (toTopCat.{v} (X := X)) where
+ R := TopCat.toGeneratedByTopCat
+ adj := adj
+
+noncomputable instance : CreatesColimits (toTopCat.{v} (X := X)) :=
+ comonadicCreatesColimits _
+
+instance : HasLimits (GeneratedByTopCat X) :=
+ hasLimits_of_coreflective toTopCat
+
+instance : HasColimits (GeneratedByTopCat X) :=
+ hasColimits_of_hasColimits_createsColimits toTopCat
+
end GeneratedByTopCat
diff --git a/Mathlib/Topology/Convenient/GeneratedBy.lean b/Mathlib/Topology/Convenient/GeneratedBy.lean
index 3dd6e77999c945..b7523666776849 100644
--- a/Mathlib/Topology/Convenient/GeneratedBy.lean
+++ b/Mathlib/Topology/Convenient/GeneratedBy.lean
@@ -51,6 +51,13 @@ by all continuous maps `X i → Y`. -/
def generatedBy : TopologicalSpace Y :=
⨆ (i : ι) (f : C(X i, Y)), coinduced f inferInstance
+/-- The `X`-generated topology is also coinduced by a single map out of a sigma type. -/
+lemma generatedBy_eq_coinduced :
+ generatedBy X (Y := Y) =
+ coinduced (fun (x : (f : (i : ι) × C(X i, Y)) × X f.1) ↦ x.1.2 x.2) inferInstance := by
+ rw [generatedBy, instTopologicalSpaceSigma, coinduced_iSup, iSup_sigma]
+ rfl
+
end TopologicalSpace
variable {X}
@@ -265,3 +272,7 @@ instance Sum.isGeneratedBy [IsGeneratedBy X Y] [IsGeneratedBy X Z] :
instance Sigma.isGeneratedBy {κ : Type*} {Y : κ → Type*} [∀ k, TopologicalSpace (Y k)]
[∀ k, IsGeneratedBy X (Y k)] : IsGeneratedBy X (Σ k, Y k) :=
.iSup fun _ ↦ .coinduced _
+
+lemma TopologicalSpace.generatedBy_generatedBy (Y : Type*) [TopologicalSpace Y] :
+ generatedBy X (tY := generatedBy X) = generatedBy X (Y := Y):=
+ IsGeneratedBy.generatedBy_eq (X := X) (Y := WithGeneratedByTopology X Y)
From 5c73b0cd146b6b4bf4d7f5fe1ed7e721d19d8cde Mon Sep 17 00:00:00 2001
From: Michael Rothgang <10105016+grunweg@users.noreply.github.com>
Date: Thu, 18 Jun 2026 12:22:24 +0000
Subject: [PATCH 0143/1300] feat: `Sum.inl` and `Sum.inr` are smooth embeddings
(#40507)
This provides an alternative proof of their smoothness. A future PR will use this to golf the existing proofs.
Inspired by in-person discussions with Christian Merten and Edward van de Meent.
---
Mathlib/Geometry/Manifold/Immersion.lean | 29 +++++++++++++++++++
.../Geometry/Manifold/SmoothEmbedding.lean | 12 ++++++++
2 files changed, 41 insertions(+)
diff --git a/Mathlib/Geometry/Manifold/Immersion.lean b/Mathlib/Geometry/Manifold/Immersion.lean
index 7ab117455ac9af..852f775a9a4acd 100644
--- a/Mathlib/Geometry/Manifold/Immersion.lean
+++ b/Mathlib/Geometry/Manifold/Immersion.lean
@@ -54,6 +54,8 @@ This shortens the overall argument, as the definition of submersions has the sam
* `IsImmersion.id`: the identity map is an immersion
* `IsImmersion.of_opens`: the inclusion of an open subset `s → M` of a smooth manifold
is a smooth immersion
+* `IsImmersionOfComplement.sumInl` and `IsImmersionOfComplement.sumInr`: given `C^n` manifolds
+ `M` and `N`, `Sum.inl : M → M ⊕ N` and `Sum.inr : N → M ⊕ N` are `C^n` immersions
* `IsImmersionAt.contMDiffAt`: if f is an immersion at `x`, it is `C^n` at `x`.
* `IsImmersion.contMDiff`: if f is a `C^n` immersion, it is automatically `C^n`
in the sense of `ContMDiff`.
@@ -615,6 +617,7 @@ In other words, `f` is an immersion at each `x ∈ M`.
This definition has a fixed parameter `F`, which is a choice of complement of `E` in `E'`:
being an immersion at `x` includes a choice of linear isomorphism between `E × F` and `E'`.
-/
+@[expose]
def IsImmersionOfComplement (f : M → N) : Prop := ∀ x, IsImmersionAtOfComplement F I J n f x
variable (I J n) in
@@ -696,6 +699,32 @@ lemma of_opens [IsManifold I n M] (s : TopologicalSpace.Opens M) :
IsImmersionOfComplement PUnit I I n (Subtype.val : s → M) :=
fun y ↦ IsImmersionAtOfComplement.of_opens s y
+/-- Given `C^n` manifolds `M` and `N` over the same model `I`,
+`Sum.inl : M → M ⊕ N` is a `C^n` immersion with complement `Unit` -/
+lemma sumInl {M' : Type*} [TopologicalSpace M'] [ChartedSpace H M'] [IsManifold I n M]
+ [IsManifold I n M'] : IsImmersionOfComplement Unit I I n (@Sum.inl M M') := by
+ intro x
+ apply IsImmersionAtOfComplement.mk_of_continuousAt (equiv := (.prodUnique 𝕜 E _))
+ (by fun_prop) _ _ (mem_chart_source H x) (mem_chart_source H (Sum.inl x))
+ (IsManifold.chart_mem_maximalAtlas x) (IsManifold.chart_mem_maximalAtlas (Sum.inl x))
+ intro y hy
+ have : I ((chartAt H x) ((chartAt H x).symm (I.symm y))) = y := by
+ rw [(chartAt H x).right_inv (by simp_all), I.right_inv (by simp_all)]
+ simpa
+
+/-- Given `C^n` manifolds `M` and `N` over the same model `I`,
+`Sum.inr : N → M ⊕ N` is a `C^n` immersion with complement `Unit` -/
+lemma sumInr {M' : Type*} [TopologicalSpace M'] [ChartedSpace H M'] [IsManifold I n M]
+ [IsManifold I n M'] : IsImmersionOfComplement Unit I I n (@Sum.inr M M') := by
+ intro x
+ apply IsImmersionAtOfComplement.mk_of_continuousAt (equiv := (.prodUnique 𝕜 E _))
+ (by fun_prop) _ _ (mem_chart_source H x) (mem_chart_source H (Sum.inr x))
+ (IsManifold.chart_mem_maximalAtlas x) (IsManifold.chart_mem_maximalAtlas (Sum.inr x))
+ intro y hy
+ have : I ((chartAt H x) ((chartAt H x).symm (I.symm y))) = y := by
+ rw [(chartAt H x).right_inv (by simp_all), I.right_inv (by simp_all)]
+ simpa
+
@[deprecated (since := "2025-12-16")] alias ofOpen := of_opens
/-- A `C^n` immersion is `C^n`. -/
diff --git a/Mathlib/Geometry/Manifold/SmoothEmbedding.lean b/Mathlib/Geometry/Manifold/SmoothEmbedding.lean
index da93f47ed144a8..14656f059daa50 100644
--- a/Mathlib/Geometry/Manifold/SmoothEmbedding.lean
+++ b/Mathlib/Geometry/Manifold/SmoothEmbedding.lean
@@ -22,6 +22,8 @@ This will be useful to define embedded submanifolds.
* `IsSmoothEmbedding.id`: the identity map is a smooth embedding
* `IsSmoothEmbedding.of_opens`: the inclusion of an open subset `s → M` of a smooth manifold
is a smooth embedding
+* `IsSmoothEmbedding.sumInl` and `IsSmoothEmbedding.sumInr`: given `C^n` manifolds `M` and `N`,
+ `Sum.inl : M → M ⊕ N` and `Sum.inr : N → M ⊕ N` are `C^n` embeddings
* `IsSmoothEmbedding.contMDiff`: if `f` is a `C^n` embedding, it is automatically `C^n`
in the sense of `ContMDiff`.
@@ -92,6 +94,16 @@ lemma of_opens [IsManifold I n M] (s : TopologicalSpace.Opens M) :
rw [isSmoothEmbedding_iff]
exact ⟨IsImmersion.of_opens s, IsEmbedding.subtypeVal⟩
+/-- Given `C^n` manifolds `M` and `N`, `Sum.inl : M → M ⊕ N` is a `C^n` embedding. -/
+lemma sumInl {M' : Type*} [TopologicalSpace M'] [ChartedSpace H M']
+ [IsManifold I n M] [IsManifold I n M'] : IsSmoothEmbedding I I n (@Sum.inl M M') :=
+ ⟨IsImmersionOfComplement.sumInl.isImmersion, Topology.IsEmbedding.inl⟩
+
+/-- Given `C^n` manifolds `M` and `N`, `Sum.inr : N → M ⊕ N` is a `C^n` embedding. -/
+lemma sumInr {M' : Type*} [TopologicalSpace M'] [ChartedSpace H M']
+ [IsManifold I n M] [IsManifold I n M'] : IsSmoothEmbedding I I n (@Sum.inr M M') :=
+ ⟨IsImmersionOfComplement.sumInr.isImmersion, Topology.IsEmbedding.inr⟩
+
/-- A smooth embedding is automatically smooth. -/
lemma contMDiff (hf : IsSmoothEmbedding I J n f) :
ContMDiff I J n f :=
From 4176265817544b3530eca07e02a704b0316f2ab8 Mon Sep 17 00:00:00 2001
From: "mathlib-splicebot[bot]"
<261196803+mathlib-splicebot[bot]@users.noreply.github.com>
Date: Thu, 18 Jun 2026 12:22:27 +0000
Subject: [PATCH 0144/1300] chore(Algebra/Module/ZLattice/Summable): automated
extraction from #39646 (#40752)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Author: Yaël Dillies
Co-authored-by: YaelDillies <14090593+YaelDillies@users.noreply.github.com>
---
Mathlib/Algebra/Module/ZLattice/Summable.lean | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/Mathlib/Algebra/Module/ZLattice/Summable.lean b/Mathlib/Algebra/Module/ZLattice/Summable.lean
index 3e39537c3564fd..243715f9a2b4f2 100644
--- a/Mathlib/Algebra/Module/ZLattice/Summable.lean
+++ b/Mathlib/Algebra/Module/ZLattice/Summable.lean
@@ -147,9 +147,7 @@ lemma sum_piFinset_Icc_rpow_le {ι : Type*} [Fintype ι] [DecidableEq ι]
rw [← Real.rpow_natCast, ← Real.rpow_add (by positivity), Nat.cast_sub hd]
norm_cast
_ ≤ 2 * d * 3 ^ (d - 1) * ε ^ r * ∑ k ∈ range (n + 1), (k : ℝ) ^ (d - 1 + r) := by
- gcongr
- rw [Finset.sum_range_succ', le_add_iff_nonneg_right]
- positivity
+ grw [Finset.sum_range_succ', Nat.cast_zero, ← Real.rpow_nonneg le_rfl, add_zero]
_ ≤ 2 * d * 3 ^ (d - 1) * ε ^ r * ∑' k : ℕ, (k : ℝ) ^ (d - 1 + r) := by
gcongr
refine Summable.sum_le_tsum _ (fun _ _ ↦ by positivity) (Real.summable_nat_rpow.mpr ?_)
From 6b7272bed994cfedecc3ced84af38dc922cab594 Mon Sep 17 00:00:00 2001
From: Jovan Gerbscheid <56355248+JovanGerb@users.noreply.github.com>
Date: Thu, 18 Jun 2026 12:56:57 +0000
Subject: [PATCH 0145/1300] =?UTF-8?q?perf:=20add=20a=20shortcut=20instance?=
=?UTF-8?q?=20for=20`=E2=84=A4=E1=B5=90=E2=81=B0`=20(#40750)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
See https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/abbrev.20causes.20unfolding/with/604081047
---
Mathlib/Algebra/Order/GroupWithZero/Canonical.lean | 3 +++
1 file changed, 3 insertions(+)
diff --git a/Mathlib/Algebra/Order/GroupWithZero/Canonical.lean b/Mathlib/Algebra/Order/GroupWithZero/Canonical.lean
index c7ac6009323965..94095610485d23 100644
--- a/Mathlib/Algebra/Order/GroupWithZero/Canonical.lean
+++ b/Mathlib/Algebra/Order/GroupWithZero/Canonical.lean
@@ -520,6 +520,9 @@ instance instLinearOrderedCommMonoidWithZero [CommMonoid α] [LinearOrder α]
instance instLinearOrderedCommGroupWithZero [CommGroup α] [LinearOrder α] [IsOrderedMonoid α] :
LinearOrderedCommGroupWithZero (WithZero α) where
+-- Add a shortcut instance for the common case, to speed up unification.
+instance : LinearOrderedCommGroupWithZero ℤᵐ⁰ := inferInstance
+
/-! ### Exponential and logarithm -/
variable {G : Type*} [Preorder G] {a b : G}
From c980ea08a8b62ae08897917a968d99906e8be719 Mon Sep 17 00:00:00 2001
From: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com>
Date: Thu, 18 Jun 2026 13:40:23 +0000
Subject: [PATCH 0146/1300] feat(Combinatorics/SimpleGraph/Sum): `edgeSet`
equivalence (#36099)
---
Mathlib/Combinatorics/SimpleGraph/Sum.lean | 23 ++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/Mathlib/Combinatorics/SimpleGraph/Sum.lean b/Mathlib/Combinatorics/SimpleGraph/Sum.lean
index d31abcd13e4d58..349388d30ddf97 100644
--- a/Mathlib/Combinatorics/SimpleGraph/Sum.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/Sum.lean
@@ -45,6 +45,12 @@ protected def sum (G : SimpleGraph V) (H : SimpleGraph W) : SimpleGraph (V ⊕ W
@[inherit_doc] infixl:60 " ⊕g " => SimpleGraph.sum
+theorem sum_adj_inl : (G ⊕g H).Adj (.inl v) (.inl v') ↔ G.Adj v v' := by
+ simp
+
+theorem sum_adj_inr : (G ⊕g H).Adj (.inr w) (.inr w') ↔ H.Adj w w' := by
+ simp
+
/-- The disjoint sum is commutative up to isomorphism. `Iso.sumComm` as a graph isomorphism. -/
@[simps!]
def Iso.sumComm : G ⊕g H ≃g H ⊕g G := ⟨Equiv.sumComm V W, by
@@ -126,6 +132,23 @@ lemma Iso.sumAssoc_comp_sumCongr (f : G ≃g G') (g : H ≃g H') (h : I ≃g I')
comp sumAssoc (sumCongr (sumCongr f g) h) = comp (sumCongr f (sumCongr g h)) sumAssoc := by
ext ((v | w) | u) <;> simp
+/-- The edges of the disjoint sum of `G` and `H` are in bijection with
+the disjoint sum of the edges of `G` and the edges of `H` -/
+def edgeSetSumEquiv : (G ⊕g H).edgeSet ≃ G.edgeSet ⊕ H.edgeSet where
+ toFun :=
+ fun ⟨e, he⟩ ↦ e.fromRelNdrec (sym := symm _) he (fun
+ | Sum.inl u, Sum.inl v, h => .inl ⟨s(u, v), h⟩
+ | Sum.inr u, Sum.inr v, h => .inr ⟨s(u, v), h⟩
+ | Sum.inl u, Sum.inr v, h => by contradiction
+ | Sum.inr u, Sum.inl v, h => by contradiction
+ ) (by grind)
+ invFun
+ | Sum.inl ⟨e, he⟩ =>
+ e.fromRelNdrec (sym := G.symm) he (fun u v h ↦ ⟨s(.inl u, .inl v), h⟩) <| by simp
+ | Sum.inr ⟨e, he⟩ =>
+ e.fromRelNdrec (sym := H.symm) he (fun u v h ↦ ⟨s(.inr u, .inr v), h⟩) <| by simp
+ left_inv := by rintro ⟨⟨u | u, v | v⟩, h⟩ <;> first | contradiction | rfl
+ right_inv := by rintro (⟨⟨u, v⟩, h⟩ | ⟨⟨u, v⟩, h⟩) <;> rfl
lemma not_adj_sum_inl_inr (v w) : ¬(G ⊕g H).Adj (.inl v) (.inr w) := by simp
From 1f813eaf7a039c9f8d49d6dd727cdecdef970f25 Mon Sep 17 00:00:00 2001
From: "Yi.Yuan"
Date: Thu, 18 Jun 2026 13:40:25 +0000
Subject: [PATCH 0147/1300] refactor(Analysis): golf
`Mathlib/Analysis/Normed/Operator/ContinuousLinearMap` (#39896)
- refactors `Normed/Operator/ContinuousLinearMap` by reusing `mkContinuous` in bounded linear continuity proofs and shortening `homothety_inverse`
Extracted from #37968
[](https://gitpod.io/from-referrer/)
Co-authored-by: Monica Omar <23701951+themathqueen@users.noreply.github.com>
---
.../Normed/Operator/ContinuousLinearMap.lean | 12 ++----------
1 file changed, 2 insertions(+), 10 deletions(-)
diff --git a/Mathlib/Analysis/Normed/Operator/ContinuousLinearMap.lean b/Mathlib/Analysis/Normed/Operator/ContinuousLinearMap.lean
index 5eaecf831a2ed2..7cbae9947bc753 100644
--- a/Mathlib/Analysis/Normed/Operator/ContinuousLinearMap.lean
+++ b/Mathlib/Analysis/Normed/Operator/ContinuousLinearMap.lean
@@ -75,11 +75,7 @@ theorem continuous_of_linear_of_boundₛₗ {f : E → F} (h_add : ∀ x y, f (x
theorem continuous_of_linear_of_bound {f : E → G} (h_add : ∀ x y, f (x + y) = f x + f y)
(h_smul : ∀ (c : 𝕜) (x), f (c • x) = c • f x) {C : ℝ} (h_bound : ∀ x, ‖f x‖ ≤ C * ‖x‖) :
Continuous f :=
- let φ : E →ₗ[𝕜] G :=
- { toFun := f
- map_add' := h_add
- map_smul' := h_smul }
- AddMonoidHomClass.continuous_of_bound φ C h_bound
+ continuous_of_linear_of_boundₛₗ (σ := RingHom.id 𝕜) h_add h_smul h_bound
@[simp, norm_cast]
theorem LinearMap.mkContinuous_coe (C : ℝ) (h : ∀ x, ‖f x‖ ≤ C * ‖x‖) :
@@ -186,11 +182,7 @@ variable {σ₂₁ : 𝕜₂ →+* 𝕜} [RingHomInvPair σ σ₂₁] [RingHomIn
theorem ContinuousLinearEquiv.homothety_inverse (a : ℝ) (ha : 0 < a) (f : E ≃ₛₗ[σ] F) :
(∀ x : E, ‖f x‖ = a * ‖x‖) → ∀ y : F, ‖f.symm y‖ = a⁻¹ * ‖y‖ := by
intro hf y
- calc
- ‖f.symm y‖ = a⁻¹ * (a * ‖f.symm y‖) := by
- rw [← mul_assoc, inv_mul_cancel₀ (ne_of_lt ha).symm, one_mul]
- _ = a⁻¹ * ‖f (f.symm y)‖ := by rw [hf]
- _ = a⁻¹ * ‖y‖ := by simp
+ simpa [eq_inv_mul_iff_mul_eq₀ (ne_of_gt ha)] using (hf (f.symm y)).symm
/-- A linear equivalence which is a homothety is a continuous linear equivalence. -/
noncomputable def ContinuousLinearEquiv.ofHomothety (f : E ≃ₛₗ[σ] F) (a : ℝ) (ha : 0 < a)
From b446f18f2b42f7089c28a174015062fd92476367 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Javier=20G=C3=B3mez=20Zaragoza?=
<51706873+javgomzar@users.noreply.github.com>
Date: Thu, 18 Jun 2026 13:40:28 +0000
Subject: [PATCH 0148/1300] chore: typo in submersion docstring (#40756)
Typo in the definition of C^n submersions.
Co-authored-by: Javier Gomez Zaragoza
---
Mathlib/Geometry/Manifold/Submersion.lean | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/Mathlib/Geometry/Manifold/Submersion.lean b/Mathlib/Geometry/Manifold/Submersion.lean
index 46a6531defd097..af48f831ada3a6 100644
--- a/Mathlib/Geometry/Manifold/Submersion.lean
+++ b/Mathlib/Geometry/Manifold/Submersion.lean
@@ -330,8 +330,8 @@ lemma _root_.isOpen_isSubmersionAtOfComplement :
exact IsOpen.liftSourceTargetPropertyAt
set_option backward.isDefEq.respectTransparency false in
-/-- If `f: M → N` and `g: M' × N'` are submersions at `x` and `x'`, respectively,
-then `f × g: M × N → M' × N'` is a submersion at `(x, x')`. -/
+/-- If `f: M → N` and `g: M' → N'` are submersions at `x` and `x'`, respectively,
+then `f × g: M × M' → N × N'` is a submersion at `(x, x')`. -/
theorem prodMap {f : M → N} {g : M' → N'} {x' : M'}
[IsManifold I n M] [IsManifold I' n M'] [IsManifold J n N] [IsManifold J' n N']
(hf : IsSubmersionAtOfComplement F I J n f x)
@@ -483,8 +483,8 @@ lemma _root_.isOpen_isSubmersionAt :
fun y hy ↦ hy.isSubmersionAt,
isOpen_isSubmersionAtOfComplement, by simp [hx.isSubmersionAtOfComplement_complement]⟩
-/-- If `f: M → N` and `g: M' × N'` are submersions at `x` and `x'`, respectively,
-then `f × g: M × N → M' × N'` is a submersion at `(x, x')`. -/
+/-- If `f: M → N` and `g: M' → N'` are submersions at `x` and `x'`, respectively,
+then `f × g: M × M' → N × N'` is a submersion at `(x, x')`. -/
theorem prodMap {f : M → N} {g : M' → N'} {x' : M'}
[IsManifold I n M] [IsManifold I' n M'] [IsManifold J n N] [IsManifold J' n N']
(hf : IsSubmersionAt I J n f x) (hg : IsSubmersionAt I' J' n g x') :
@@ -539,8 +539,8 @@ lemma congr_F (e : F ≃L[𝕜] F') :
IsSubmersionOfComplement F I J n f ↔ IsSubmersionOfComplement F' I J n f :=
⟨fun h ↦ trans_F (e := e) h, fun h ↦ trans_F (e := e.symm) h⟩
-/-- If `f: M → N` and `g: M' × N'` are submersions at `x` and `x'` (w.r.t. `F` and `F'`),
-respectively, then `f × g: M × N → M' × N'` is a submersion at `(x, x')` w.r.t. `F × F'`. -/
+/-- If `f: M → N` and `g: M' → N'` are submersions at `x` and `x'` (w.r.t. `F` and `F'`),
+respectively, then `f × g: M × M' → N × N'` is a submersion at `(x, x')` w.r.t. `F × F'`. -/
theorem prodMap {f : M → N} {g : M' → N'}
[IsManifold I n M] [IsManifold I' n M'] [IsManifold J n N] [IsManifold J' n N']
(h : IsSubmersionOfComplement F I J n f) (h' : IsSubmersionOfComplement F' I' J' n g) :
@@ -602,8 +602,8 @@ lemma isSubmersionAt (h : IsSubmersion I J n f) (x : M) : IsSubmersionAt I J n f
use h.complement, by infer_instance, by infer_instance
exact h.isSubmersionOfComplement_complement x
-/-- If `f: M → N` and `g: M' × N'` are submersions at `x` and `x'`, respectively,
-then `f × g: M × N → M' × N'` is a submersion at `(x, x')`. -/
+/-- If `f: M → N` and `g: M' → N'` are submersions at `x` and `x'`, respectively,
+then `f × g: M × M' → N × N'` is a submersion at `(x, x')`. -/
theorem prodMap {f : M → N} {g : M' → N'}
[IsManifold I n M] [IsManifold I' n M'] [IsManifold J n N] [IsManifold J' n N']
(hf : IsSubmersion I J n f) (hg : IsSubmersion I' J' n g) :
From 86c0c038136c86682e63a575e4326d521963aa2c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Javier=20G=C3=B3mez=20Zaragoza?=
<51706873+javgomzar@users.noreply.github.com>
Date: Thu, 18 Jun 2026 14:48:01 +0000
Subject: [PATCH 0149/1300] feat(FinitelyPresentedGroup): comap of finitely
generated normal subgroup (#40726)
@tb65536 suggested this addition for the FinitelyPresentedGroup project in [#40200](https://github.com/leanprover-community/mathlib4/pull/40200).
[#38114](https://github.com/leanprover-community/mathlib4/pull/38114) will also be simplified using this theorem.
Co-authored-by: Javier Gomez Zaragoza
---
Mathlib/Algebra/Group/Subgroup/Basic.lean | 13 ++++++++++
.../GroupTheory/FinitelyPresentedGroup.lean | 25 +++++++++++++++++++
2 files changed, 38 insertions(+)
diff --git a/Mathlib/Algebra/Group/Subgroup/Basic.lean b/Mathlib/Algebra/Group/Subgroup/Basic.lean
index 0123582c1c283b..8d12154ce3c9d3 100644
--- a/Mathlib/Algebra/Group/Subgroup/Basic.lean
+++ b/Mathlib/Algebra/Group/Subgroup/Basic.lean
@@ -528,6 +528,13 @@ theorem conj_mem_conjugatesOfSet {x c : G} :
rcases mem_conjugatesOfSet_iff.1 H with ⟨a, h₁, h₂⟩
exact mem_conjugatesOfSet_iff.2 ⟨a, h₁, h₂.trans (isConj_iff.2 ⟨c, rfl⟩)⟩
+/-- The set of conjugates of the union of two sets is the union of the conjugates -/
+@[to_additive /-- The set of additive conjugates of the union of two sets is the union
+of the additive conjugates. -/]
+theorem conjugatesOfSet_union {G : Type*} [Group G] (s t : Set G) :
+ conjugatesOfSet (s ∪ t) = conjugatesOfSet s ∪ conjugatesOfSet t := by
+ simp_rw [conjugatesOfSet, Set.biUnion_union]
+
end Group
namespace Subgroup
@@ -620,6 +627,12 @@ theorem normalClosure_closure_eq_normalClosure {s : Set G} :
lemma normalClosure_empty : normalClosure (∅ : Set G) = (⊥ : Subgroup G) := by
rw [← normalClosure_closure_eq_normalClosure, closure_empty, normalClosure_eq_self]
+/-- The normal closure of the union of sets is the join of the normal closures of each set. -/
+@[to_additive]
+theorem normalClosure_union {G : Type*} [Group G] (s t : Set G) :
+ normalClosure (s ∪ t) = normalClosure s ⊔ normalClosure t := by
+ simp_rw [normalClosure, Group.conjugatesOfSet_union, closure_union]
+
/-- The normal core of a subgroup `H` is the largest normal subgroup of `G` contained in `H`,
as shown by `Subgroup.normalCore_eq_iSup`. -/
@[to_additive /-- The normal core of an additive subgroup `H` is the largest normal additive
diff --git a/Mathlib/GroupTheory/FinitelyPresentedGroup.lean b/Mathlib/GroupTheory/FinitelyPresentedGroup.lean
index 244c014130652c..81cbeaebab1630 100644
--- a/Mathlib/GroupTheory/FinitelyPresentedGroup.lean
+++ b/Mathlib/GroupTheory/FinitelyPresentedGroup.lean
@@ -51,6 +51,23 @@ protected theorem map {N : Subgroup G} (hN : N.IsNormalClosureFG)
refine ⟨f '' S, hSfinite.image _, ?_⟩
rw [← hSclosure, Subgroup.map_normalClosure _ _ hf]
+open Function Set Subgroup in
+/-- The preimage of a finitely generated normal subgroup by a surjective homomorphism with
+a finitely generated kernel is finitely generated. -/
+@[to_additive /-- The preimage of a finitely generated normal subgroup by a surjective additive
+homomorphism with a finitely generated kernel is finitely generated. -/]
+protected theorem comap {N : Subgroup H} (hN : N.IsNormalClosureFG)
+ {f : G →* H} (hf : Surjective f) (hf' : f.ker.IsNormalClosureFG) :
+ (N.comap f).IsNormalClosureFG := by
+ obtain ⟨S, hS_fin, hS⟩ := hN
+ obtain ⟨T, hT_fin, hT⟩ := hf'
+ have : ∃ S', S'.Finite ∧ f '' S' = S :=
+ ⟨surjInv hf '' S, hS_fin.image _, by rw [← image_comp, comp_surjInv, image_id]⟩
+ clear hS_fin
+ obtain ⟨S, hS_fin, rfl⟩ := this
+ refine ⟨S ∪ T, hS_fin.union hT_fin, ?_⟩
+ rw [← hS, ← map_normalClosure S f hf, comap_map_eq, ← hT, normalClosure_union]
+
/-- The trivial group is the normal closure of a finite set of relations. -/
@[to_additive /-- The trivial additive group is the normal closure of a finite set of relations. -/]
protected theorem bot : (⊥ : Subgroup G).IsNormalClosureFG :=
@@ -81,6 +98,14 @@ theorem equiv (iso : G ≃* H) (h : IsFinitelyPresented G) : IsFinitelyPresented
refine ⟨n, (iso : G →* H).comp φ, iso.surjective.comp hφsurj, ?_⟩
rwa [φ.ker_mulEquiv_comp iso]
+theorem of_surjective [hG : IsFinitelyPresented G] (f : G →* H)
+ (hf_surj : Function.Surjective f) (hf_ker : f.ker.IsNormalClosureFG) :
+ IsFinitelyPresented H := by
+ obtain ⟨n, φ, hφ_surj, hφ_ker⟩ := hG.out
+ refine ⟨n, f.comp φ, hf_surj.comp hφ_surj, ?_⟩
+ rw [← MonoidHom.comap_ker]
+ exact hf_ker.comap hφ_surj hφ_ker
+
/-- A free group with a finite number of generators is finitely presented. -/
@[to_additive /-- A free additive group with a finite number of generators is finitely presented. -/
]
From fbf0644640719705c7dbfbf3fa9df21844858fd6 Mon Sep 17 00:00:00 2001
From: Kim Morrison <477956+kim-em@users.noreply.github.com>
Date: Thu, 18 Jun 2026 15:54:37 +0000
Subject: [PATCH 0150/1300] ci: self-heal nightly-testing-green after a history
rewrite (#40586)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This PR makes the `nightly-testing-green` update in `nightly_detect_failure.yml` recover from a history rewrite instead of freezing. The branch is advanced with a plain fast-forward push, which is rejected once `nightly-testing` history diverges from its tip (for example a toolchain reset to an rc). #38847 dropped the original `git push --force` and deferred recovery to a daily force-push job that does not exist, so the branch stuck on `v4.31.0-rc1` for weeks until it was force-pushed by hand.
Fast-forward when possible, and otherwise force-push to recover, but only when the tested commit is strictly newer than the current tip by committer date, so an out-of-order CI completion can never roll the branch backwards. `nightly-testing-daily` keeps its fast-forward-or-skip behavior, and the stale comment referencing the nonexistent force-push job is corrected.
🤖 Prepared with Claude Code
---
.github/workflows/nightly_detect_failure.yml | 38 +++++++++++++-------
1 file changed, 26 insertions(+), 12 deletions(-)
diff --git a/.github/workflows/nightly_detect_failure.yml b/.github/workflows/nightly_detect_failure.yml
index caad2ec4c0811d..8a2010877288a3 100644
--- a/.github/workflows/nightly_detect_failure.yml
+++ b/.github/workflows/nightly_detect_failure.yml
@@ -133,11 +133,27 @@ jobs:
- name: Update the nightly-testing-green branch
continue-on-error: true
run: |
- # The `nightly-testing-green` branch keeps a record of the successfully built commits on `nightly-testing`.
- # This is allowed to fail, because it might happen that an earlier commit finishes CI later,
- # we don't want to force push and revert the history.
- # In case `nightly-testing` does have its history rewritten, we do a force push once a day.
- git push origin HEAD:nightly-testing-green
+ # `nightly-testing-green` records the latest successfully built `nightly-testing` commit;
+ # live.lean-lang.org and the prebuilt CI tools read from it. Normally a fast-forward, but
+ # if `nightly-testing`'s history is rewritten (e.g. a toolchain reset to an rc) the branch
+ # diverges and a plain push is rejected forever, freezing it. Recover by force-pushing, but
+ # only when the tested commit is strictly newer than the current tip (by committer date), so
+ # an out-of-order CI completion (older commit finishing after a newer one) can't roll back.
+ if git push origin HEAD:nightly-testing-green; then
+ exit 0
+ fi
+ git fetch origin nightly-testing-green
+ tip="$(git rev-parse FETCH_HEAD)"
+ head_ct="$(git show -s --format=%ct HEAD)"
+ tip_ct="$(git show -s --format=%ct "$tip")"
+ if git merge-base --is-ancestor HEAD "$tip"; then
+ echo "nightly-testing-green is ahead of the tested commit; leaving it alone."
+ elif (( head_ct > tip_ct )); then
+ echo "nightly-testing-green diverged; force-pushing to the newer tested commit."
+ git push --force-with-lease=refs/heads/nightly-testing-green:"$tip" origin HEAD:nightly-testing-green
+ else
+ echo "nightly-testing-green is newer than the tested commit; leaving it alone."
+ fi
- name: Create a nightly-testing-YYYY-MM-DD tag
id: tag
run: |
@@ -162,14 +178,12 @@ jobs:
exit 1
fi
fi
- # Fast-forward `nightly-testing-daily` and `nightly-testing-green` to the tested SHA.
- # Since we now pin to the SHA whose CI succeeded (which may be older than the current
- # tip of `nightly-testing` if a CI run for an older commit finishes after a newer one),
- # we must NOT force-push here, or we'd roll the tracking branches backwards.
- # If the push isn't a fast-forward, leave the branch alone; the daily force-push
- # job handles the rare case where `nightly-testing` history has been rewritten.
+ # Fast-forward `nightly-testing-daily` to the tested SHA. We pin to the SHA whose CI
+ # succeeded, which may be older than the current `nightly-testing` tip if an older
+ # commit's CI finishes after a newer one's, so don't force-push here or we'd roll the
+ # branch backwards. (`nightly-testing-green` is advanced, with recovery from a history
+ # rewrite, by the `Update the nightly-testing-green branch` step above.)
git push origin HEAD:nightly-testing-daily || echo "Skipping nightly-testing-daily update: not a fast-forward."
- git push origin HEAD:nightly-testing-green || echo "Skipping nightly-testing-green update: not a fast-forward."
hash="$(git rev-parse "nightly-testing-${version}")"
curl -X POST "https://speed.lean-lang.org/mathlib4/api/queue/commit/e7b27246-a3e6-496a-b552-ff4b45c7236e/$hash" -u "admin:${{ secrets.SPEED }}"
fi
From 9808d392b10a1a0d349a63b635ef5cbe13c5d2a4 Mon Sep 17 00:00:00 2001
From: Nailin Guan <150537269+Thmoas-Guan@users.noreply.github.com>
Date: Thu, 18 Jun 2026 16:07:25 +0000
Subject: [PATCH 0151/1300] feat(Algebra/Homology): add
`Exact.isZero_of_both_isZero` (#40758)
Add `CategoryTheory.ShortComplex.Exact.isZero_of_both_isZero`, a variant of `ShortComplex.Exact.isZero_of_both_zeros` would be useful in long exact sequence.
---
Mathlib/Algebra/Homology/ShortComplex/Exact.lean | 6 ++++++
Mathlib/CategoryTheory/ObjectProperty/Extensions.lean | 2 +-
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/Mathlib/Algebra/Homology/ShortComplex/Exact.lean b/Mathlib/Algebra/Homology/ShortComplex/Exact.lean
index f02dbe63a3ce37..fee0ba6b919258 100644
--- a/Mathlib/Algebra/Homology/ShortComplex/Exact.lean
+++ b/Mathlib/Algebra/Homology/ShortComplex/Exact.lean
@@ -261,6 +261,12 @@ lemma Exact.isZero_of_both_zeros (ex : S.Exact) (hf : S.f = 0) (hg : S.g = 0) :
IsZero S.X₂ :=
(ShortComplex.HomologyData.ofZeros S hf hg).exact_iff.1 ex
+/-- In an exact short complex, if the two outer objects are zero objects, then so is the
+middle object. -/
+lemma Exact.isZero_of_both_isZero (ex : S.Exact) (hX₁ : IsZero S.X₁) (hX₃ : IsZero S.X₃) :
+ IsZero S.X₂ :=
+ ex.isZero_of_both_zeros (hX₁.eq_zero_of_src _) (hX₃.eq_zero_of_tgt _)
+
end
section Preadditive
diff --git a/Mathlib/CategoryTheory/ObjectProperty/Extensions.lean b/Mathlib/CategoryTheory/ObjectProperty/Extensions.lean
index 7bdf56218c1788..a006f41f609248 100644
--- a/Mathlib/CategoryTheory/ObjectProperty/Extensions.lean
+++ b/Mathlib/CategoryTheory/ObjectProperty/Extensions.lean
@@ -52,7 +52,7 @@ instance : (⊤ : ObjectProperty C).IsClosedUnderExtensions where
instance : IsClosedUnderExtensions (IsZero (C := C)) where
prop_X₂_of_shortExact hS h₁ h₃ :=
- hS.exact.isZero_of_both_zeros (h₁.eq_of_src _ _) (h₃.eq_of_tgt _ _)
+ hS.exact.isZero_of_both_isZero h₁ h₃
instance [P.IsClosedUnderExtensions] (F : D ⥤ C)
[HasZeroMorphisms D] [F.PreservesZeroMorphisms]
From c4b0d8e362fe47e8b2356c9e34b121990cfa21d2 Mon Sep 17 00:00:00 2001
From: Yakov Pechersky <5342086+pechersky@users.noreply.github.com>
Date: Thu, 18 Jun 2026 16:25:49 +0000
Subject: [PATCH 0152/1300] feat(Algebra/Group/WithOne):
`lift_symm_injective_of_injective` (#40691)
`WithOne.lift.symm` preserves injectivity (it is functorial)
---
Mathlib/Algebra/Group/WithOne/Basic.lean | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/Mathlib/Algebra/Group/WithOne/Basic.lean b/Mathlib/Algebra/Group/WithOne/Basic.lean
index c3ce8cae26ec97..165315cf6d0ddc 100644
--- a/Mathlib/Algebra/Group/WithOne/Basic.lean
+++ b/Mathlib/Algebra/Group/WithOne/Basic.lean
@@ -76,6 +76,11 @@ theorem lift_unique (f : WithOne α →* β) : f = lift (f.toMulHom.comp coeMulH
@[to_additive (attr := simp)]
theorem lift_symm_apply (f : WithOne α →* β) (x : α) : lift.symm f x = f x := rfl
+@[to_additive]
+lemma lift_symm_injective_of_injective {f : WithOne α →* β} (hf : Function.Injective f) :
+ Function.Injective (lift.symm f) :=
+ fun _ _ ↦ by simp [hf.eq_iff]
+
end lift
section Map
From 9aac62798eba680875a3baaead1b3d96db7a1c86 Mon Sep 17 00:00:00 2001
From: Marcelo Lynch
Date: Thu, 18 Jun 2026 17:15:56 +0000
Subject: [PATCH 0153/1300] fix(cache): correct the marker probe on Windows,
add Windows test (#40767)
On Windows, `cache query` and the "HEAD not built by CI" note always report no cache, even though the commit is cached and `cache get` downloads it. `probeContainerForSHA` discards curl's output to `/dev/null`, which isn't a valid path on Windows, so curl exits non-zero and the probe reads every marker as absent.
The fix is to use a platform null device (`NUL` on Windows) in the probe.
Also adds a `windows-latest` leg to the cache-test workflow to catch such regressions.
Follow-up to #40035
---
.github/workflows/cache_test.yml | 49 +++++++++++++++++++++++---------
Cache/IO.lean | 4 +++
Cache/Query.lean | 4 ++-
Cache/Test.lean | 10 +++----
4 files changed, 47 insertions(+), 20 deletions(-)
diff --git a/.github/workflows/cache_test.yml b/.github/workflows/cache_test.yml
index ecf1b87a9cf864..3348e82783dbce 100644
--- a/.github/workflows/cache_test.yml
+++ b/.github/workflows/cache_test.yml
@@ -1,8 +1,9 @@
-# Runs the cache tool's unit tests (`lake exe cache-test`) on PRs that touch
-# the cache code. The suite is deliberately network-free (see the module
-# docstring in `Cache/Test.lean`), so the job only needs the toolchain and the
-# package dependencies' sources — not a Mathlib build — and finishes in a few
-# minutes on a hosted runner.
+# Runs the cache tool's unit tests (`lake exe cache-test`) on Linux and Windows
+# for PRs that touch the cache code. The suite is deliberately network-free (see
+# the module docstring in `Cache/Test.lean`), so the job only needs the toolchain
+# and the package dependencies' sources — not a Mathlib build — and finishes in a
+# few minutes on a hosted runner. The Windows job guards the tool's cross-platform
+# behavior, such as the null device the marker probe and tests discard output to.
name: cache tests
on:
@@ -13,6 +14,8 @@ on:
# a bump can break the tool's compilation even with `Cache/` untouched.
- 'lakefile.lean'
- 'lean-toolchain'
+ # Re-run when the workflow itself changes.
+ - '.github/workflows/cache_test.yml'
concurrency:
group: cache-test-${{ github.ref }}
@@ -24,20 +27,38 @@ permissions:
jobs:
cache-test:
if: github.repository == 'leanprover-community/mathlib4'
- runs-on: ubuntu-latest
+ strategy:
+ # Report both platforms even if one fails, so a Windows-only break is
+ # never hidden by a Linux failure (or vice versa).
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, windows-latest]
+ runs-on: ${{ matrix.os }}
+ # `leanprover/lean-action` installs elan from a bash step and adds it to the
+ # path in that shell's form, so the `lake` steps below must also run under
+ # bash (available on Windows runners as Git Bash) to find it.
+ defaults:
+ run:
+ shell: bash
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- - name: install elan
- run: |
- set -o pipefail
- curl -o elan-init.sh -sSfL https://elan.lean-lang.org/elan-init.sh
- chmod +x elan-init.sh
- ./elan-init.sh -y --default-toolchain none
- echo "$HOME/.elan/bin" >> "${GITHUB_PATH}"
+ # Install elan and the toolchain cross-platform. Build/test/lint, the
+ # Mathlib cache, and the GitHub cache are all disabled, so this is a
+ # toolchain-only setup; the `lake` steps below then build and run just the
+ # `cache-test` target. (The GitHub cache cannot be written from fork PRs
+ # and buys little for this small build, so it stays off.)
+ - uses: leanprover/lean-action@38fbc41a8c28c4cbaec22d7f7de508ec2e7c0dd9 # v1.5.0
+ with:
+ auto-config: "false"
+ build: "false"
+ test: "false"
+ lint: "false"
+ use-mathlib-cache: "false"
+ use-github-cache: "false"
- name: build cache-test
run: lake build cache-test
- name: run cache tests
- run: .lake/build/bin/cache-test
+ run: lake exe cache-test
diff --git a/Cache/IO.lean b/Cache/IO.lean
index f5e6eac9485adf..c83b5752d383e2 100644
--- a/Cache/IO.lean
+++ b/Cache/IO.lean
@@ -82,6 +82,10 @@ def CURLBIN :=
def EXE := if System.Platform.isWindows then ".exe" else ""
+/-- The platform's null device, for discarding a command's output: `NUL` on
+Windows, `/dev/null` elsewhere. -/
+def nullDevice : String := if System.Platform.isWindows then "NUL" else "/dev/null"
+
def LAKEPACKAGESDIR : FilePath :=
".lake" / "packages"
diff --git a/Cache/Query.lean b/Cache/Query.lean
index 147f1d16723b4e..8dd967501722be 100644
--- a/Cache/Query.lean
+++ b/Cache/Query.lean
@@ -86,9 +86,11 @@ billed as a Read op.
def probeContainerForSHA (container : Container) (repo sha : String) :
IO Bool := do
let url := markerURL container repo sha
+ -- Discard the response body to the platform null device (`NUL` on Windows),
+ -- so curl reports a write error only on a genuine failure, not on every probe.
let out ← IO.Process.output
{cmd := (← IO.getCurl),
- args := #["-s", "-o", "/dev/null", "-w", "%{http_code}", "-I", url],
+ args := #["-s", "-o", IO.nullDevice, "-w", "%{http_code}", "-I", url],
cwd := "."}
if out.exitCode != 0 then
-- Network error; assume no cache at this SHA
diff --git a/Cache/Test.lean b/Cache/Test.lean
index 15bf2c1b701839..b620ffeefc079d 100644
--- a/Cache/Test.lean
+++ b/Cache/Test.lean
@@ -74,14 +74,14 @@ def assertEq (name expected actual : String) : IO Unit := do
IO.eprintln s!" FAIL: {name}\n expected: {expected}\n actual: {actual}"
failures.modify (· + 1)
-/-- Run `action` with both stdout and stderr redirected to /dev/null. Restores
-both on completion, including on exception. Apply this to every production code
-call in tests so diagnostic prints never mix with test output, regardless of
-whether the production code currently produces any. -/
+/-- Run `action` with both stdout and stderr redirected to the platform null
+device. Restores both on completion, including on exception. Apply this to every
+production code call in tests so diagnostic prints never mix with test output,
+regardless of whether the production code currently produces any. -/
private def withSuppressedOutput (action : IO α) : IO α := do
let savedOut ← IO.getStdout
let savedErr ← IO.getStderr
- let sink ← IO.FS.Handle.mk "/dev/null" IO.FS.Mode.append
+ let sink ← IO.FS.Handle.mk Cache.IO.nullDevice IO.FS.Mode.append
let sinkStream := IO.FS.Stream.ofHandle sink
-- `IO.setStdout`/`IO.setStderr` return the previous stream; we already saved it,
-- so discard the return value here.
From a912e82324c89ceb9b7f5741a2b481273264762b Mon Sep 17 00:00:00 2001
From: Marcelo Lynch
Date: Thu, 18 Jun 2026 17:32:24 +0000
Subject: [PATCH 0154/1300] fix(cache): lowercase fork repo in cache blob paths
(#40766)
Azure Blob Storage paths are case-sensitive, but GitHub owner/repo names are not. CI uploads a fork's cache under its "canonical" (according to GitHub) repo name, while `cache get`/`query` derive the repo from a local git remote URL (often lowercased).
Lowercase the repo path segment in `mkFileURL` and `markerURL` so uploads and downloads always meet at one key.
Follow-up to #40035.
---
Cache/Infra.lean | 11 +++++++++++
Cache/Marker.lean | 6 ++++--
Cache/Requests.lean | 4 ++++
Cache/Test.lean | 10 ++++++++++
4 files changed, 29 insertions(+), 2 deletions(-)
diff --git a/Cache/Infra.lean b/Cache/Infra.lean
index 3b133b2d1e3bd7..4bf54177c86d0f 100644
--- a/Cache/Infra.lean
+++ b/Cache/Infra.lean
@@ -24,6 +24,17 @@ def MATHLIBREPO := "leanprover-community/mathlib4"
/-- The full name of the Mathlib nightly-testing GitHub repository. -/
def NIGHTLY_TESTING_REPO := "leanprover-community/mathlib4-nightly-testing"
+/--
+Canonical form of a GitHub `owner/repo` name for use as a cache blob path
+segment.
+
+GitHub treats owner and repository names case-insensitively, while Azure Blob
+Storage paths are case-sensitive. Lowercasing yields one shared key whatever
+capitalization a remote URL or the GitHub Actions context supplies, so a fork's
+uploads and downloads always meet at the same path.
+-/
+def normalizeRepo (repo : String) : String := repo.toLower
+
/--
Trust-classified Azure storage containers for the Mathlib cache.
diff --git a/Cache/Marker.lean b/Cache/Marker.lean
index 56a3e9d43def93..69f830eb0633b3 100644
--- a/Cache/Marker.lean
+++ b/Cache/Marker.lean
@@ -20,7 +20,9 @@ namespace Cache.Requests
open System (FilePath)
/--
-URL for the per-SHA marker blob: `{container}/m/{repo}/{sha}`.
+URL for the per-SHA marker blob: `{container}/m/{repo}/{sha}`, where `repo` is
+lowercased via `normalizeRepo` so the path is case-insensitive in the GitHub
+owner/repo name.
The marker is uploaded by `put-staged` as the last step when an upload is
SHA-scoped (`MATHLIB_CACHE_REPO_SCOPE` set). Its presence at this URL
@@ -29,7 +31,7 @@ indicates that the full `.ltar` upload completed for this commit, and lets
a blob-listing call.
-/
def markerURL (container : Container) (repo sha : String) : String :=
- s!"{container.azureURL}/m/{repo}/{sha}"
+ s!"{container.azureURL}/m/{normalizeRepo repo}/{sha}"
/--
Upload a tiny marker blob to `/m/{repo}/{sha}` in the given container. The
diff --git a/Cache/Requests.lean b/Cache/Requests.lean
index 2b766e6fd7ba47..ddd52c5000ef8d 100644
--- a/Cache/Requests.lean
+++ b/Cache/Requests.lean
@@ -348,9 +348,13 @@ container (see `Container.flatPath`), not the repo: the same hash under
`container` is `none` for the user-supplied `MATHLIB_CACHE_GET_URL` /
`MATHLIB_CACHE_PUT_URL` URLs, where no container policy applies; the path then
follows the repo directly — flat for `MATHLIBREPO`, prefixed otherwise.
+
+`repo` is lowercased via `normalizeRepo` so the repo-namespaced path is
+case-insensitive in the GitHub owner/repo name.
-/
def mkFileURL (container : Option Container) (repo containerURL fileName : String)
(repoScope : Option String := none) : String :=
+ let repo := normalizeRepo repo
let flat := match container with
| some c => c.flatPath repo
| none => repo == MATHLIBREPO
diff --git a/Cache/Test.lean b/Cache/Test.lean
index b620ffeefc079d..93cb93991ec46a 100644
--- a/Cache/Test.lean
+++ b/Cache/Test.lean
@@ -279,6 +279,11 @@ def test_mkFileURL : IO Unit := do
assertEq "scope is ignored on a flat legacy path"
"https://lakecache.blob.core.windows.net/mathlib4/f/abc.ltar"
(mkFileURL (some .legacy) MATHLIBREPO Container.legacy.azureURL "abc.ltar" (some "abc123def"))
+ -- The repo segment is lowercased, so a mixed-case GitHub owner resolves to the
+ -- same path whether it reaches the cache from CI or a local remote URL.
+ assertEq "fork repo is lowercased in the path"
+ "https://lakecache.blob.core.windows.net/mathlib4-forks/f/alice/mathlib4/abc.ltar"
+ (mkFileURL (some .forks) "Alice/Mathlib4" Container.forks.azureURL "abc.ltar")
end MkFileURL
@@ -476,6 +481,11 @@ def test_markerURL : IO Unit := do
assertEq "marker URL respects the container base"
"https://lakecache.blob.core.windows.net/mathlib4/m/someorg/mathlib4/sha9999"
(markerURL .legacy "someorg/mathlib4" "sha9999")
+ -- The repo segment is lowercased, so an upload and a probe for the same fork
+ -- meet at one path regardless of how the owner name was capitalized.
+ assertEq "marker repo is lowercased in the path"
+ "https://lakecache.blob.core.windows.net/mathlib4-forks/m/alice/mathlib4/abc123"
+ (markerURL .forks "Alice/Mathlib4" "abc123")
end Marker
From bd5ddedec83813f0dab190d95173d44bcdee5258 Mon Sep 17 00:00:00 2001
From: Marcelo Lynch
Date: Thu, 18 Jun 2026 17:53:10 +0000
Subject: [PATCH 0155/1300] fix(cache): cross-repo PRs from the nightly-testing
fork (#40770)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
A PR opened from the `mathlib4-nightly-testing` fork into mathlib4 (e.g. #40732, a `bump/*` toolchain bump) is a cross-repo PR, built by `build_fork.yml` with fork-trust credentials — so its cache can only live in the `forks` container. Two things broke:
- **Upload (CI):** the trust dispatch keyed the container on the head repo, so it targeted `nightly-testing` and Azure RBAC returned 403. Classify any build whose repo differs from `$GITHUB_REPOSITORY` (the credential context) as a fork → `forks`.
- **Read (tool):** `defaultContainersForRepo` for the nightly-testing repo was `[nightly-testing, legacy]`, omitting `forks` — so neither the contributor's local `lake exe cache get` nor CI could find the upload. Add `forks` to the chain. It's no wider a trust grant than `nightly-testing` itself: `forks` reads are HEAD-scoped and namespaced under `/f/{repo}/…`, so only artifacts built for the exact checked-out commit by a push-access writer are served. `pr-toolchain-tests` stays excluded (unscoped, arbitrary-toolchain).
Native master/nightly/pr-toolchain and ordinary fork PRs are unchanged.
Follow-up to #40035.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---
.github/actions/cache-trust-dispatch/action.yml | 13 +++++++++++++
Cache/Infra.lean | 9 +++------
Cache/Test.lean | 4 ++--
3 files changed, 18 insertions(+), 8 deletions(-)
diff --git a/.github/actions/cache-trust-dispatch/action.yml b/.github/actions/cache-trust-dispatch/action.yml
index 7e3811a969d2d9..3cb8f2f785ced0 100644
--- a/.github/actions/cache-trust-dispatch/action.yml
+++ b/.github/actions/cache-trust-dispatch/action.yml
@@ -66,6 +66,18 @@ runs:
# be 403'd by Azure regardless. The dispatch exists so the workflow
# does the right thing in the honest case; defence in depth is RBAC.
+ # Privileged containers (master, nightly-testing, pr-toolchain-tests) are
+ # writable only by a repo's own native CI, whose OIDC token is RBAC-scoped
+ # to match. A cross-repo pull request is built by build_fork.yml with
+ # fork-trust credentials whatever repo the PR head lives on (e.g. a `bump/*`
+ # branch on the mathlib4-nightly-testing fork), so it can only write
+ # `forks`. `GITHUB_REPOSITORY` is the repo the workflow runs as (the base
+ # repo for `pull_request_target`), i.e. the one whose credentials this job
+ # holds; when it differs from `$REPO`, the build is a fork and targets
+ # `forks` regardless of the head repo's own trust class.
+ if [ "$REPO" != "$GITHUB_REPOSITORY" ]; then
+ PRIMARY="forks"
+ else
case "$REPO" in
"leanprover-community/mathlib4")
case "$BRANCH" in
@@ -121,6 +133,7 @@ runs:
PRIMARY="forks"
;;
esac
+ fi
# Per-commit cache namespace, only for fork-trust uploads. Closes the
# within-fork temporal replay attack: each commit's CI run gets its
diff --git a/Cache/Infra.lean b/Cache/Infra.lean
index 4bf54177c86d0f..47b88f739ea583 100644
--- a/Cache/Infra.lean
+++ b/Cache/Infra.lean
@@ -150,12 +150,9 @@ def defaultContainersForRepo (repo : String) : List Container :=
if repo == MATHLIBREPO then
[.master, .legacy]
else if repo == NIGHTLY_TESTING_REPO then
- -- Trusted-nightly consumers (`nightly-testing`, `nightly-testing-green`,
- -- `bump/*`) read only `nightly-testing` + `legacy`; `pr-toolchain-tests` is
- -- excluded so low-trust toolchain-PR uploads can't reach them. Toolchain-PR
- -- branches opt into reading their own uploads with `--cache-from=...` (or,
- -- in CI, via the `MATHLIB_CACHE_FROM` env var).
- [.nightlyTesting, .legacy]
+ -- `forks` is needed for PRs opened from this repo into mathlib4: their CI
+ -- uploads land in `forks`. `pr-toolchain-tests` is excluded.
+ [.nightlyTesting, .forks, .legacy]
else
-- Forks and everything else: `master` for shared upstream deps, the fork's
-- own container for PR-specific files, then `legacy`.
diff --git a/Cache/Test.lean b/Cache/Test.lean
index 93cb93991ec46a..e9aa2298bdefd9 100644
--- a/Cache/Test.lean
+++ b/Cache/Test.lean
@@ -200,8 +200,8 @@ def test_defaultContainersForRepo : IO Unit := do
IO.println "defaultContainersForRepo:"
assert "canonical repo → [master, legacy]"
(defaultContainersForRepo MATHLIBREPO == [.master, .legacy])
- assert "nightly-testing repo → [nightly-testing, legacy], no pr-toolchain-tests"
- (defaultContainersForRepo NIGHTLY_TESTING_REPO == [.nightlyTesting, .legacy])
+ assert "nightly-testing repo → [nightly-testing, forks, legacy], no pr-toolchain-tests"
+ (defaultContainersForRepo NIGHTLY_TESTING_REPO == [.nightlyTesting, .forks, .legacy])
assert "fork repo → [master, forks, legacy]"
(defaultContainersForRepo "alice/mathlib4" == [.master, .forks, .legacy])
assert "unknown repo falls back to the fork chain"
From 113f7cc9a477501aba4decac2a6f60103a660308 Mon Sep 17 00:00:00 2001
From: Thomas Browning <13339017+tb65536@users.noreply.github.com>
Date: Thu, 18 Jun 2026 19:42:16 +0000
Subject: [PATCH 0156/1300] refactor(FieldTheory/*): swap imports of
`IsSepClosed` and `SeparableClosure` (#40777)
This PR swaps the imports order between `IsSepClosed.lean` and `SeparableClosure.lean`. This allows basic facts like adjoining separable elements gives a separable extension to be used to golf a proof in `Galois/Basic.lean`.
These basic facts basically require knowing the existence of the separable closure, but right now `SeparableClosure.lean` imports `IsSepClosed.lean` which imports `Galois/Basic.lean`.
This PR is a slight modification of #40339.
Co-authored-by: tb65536
---
Mathlib/FieldTheory/Galois/Basic.lean | 38 +++-------------
Mathlib/FieldTheory/Galois/GaloisClosure.lean | 3 +-
Mathlib/FieldTheory/IsSepClosed.lean | 41 +++++++++++++++++
.../FieldTheory/PurelyInseparable/Basic.lean | 2 +-
Mathlib/FieldTheory/Separable.lean | 7 +++
Mathlib/FieldTheory/SeparableClosure.lean | 45 +++++--------------
Mathlib/FieldTheory/SeparableDegree.lean | 7 +++
7 files changed, 74 insertions(+), 69 deletions(-)
diff --git a/Mathlib/FieldTheory/Galois/Basic.lean b/Mathlib/FieldTheory/Galois/Basic.lean
index 878605b075d283..9a8f0a6a0aed22 100644
--- a/Mathlib/FieldTheory/Galois/Basic.lean
+++ b/Mathlib/FieldTheory/Galois/Basic.lean
@@ -8,6 +8,7 @@ module
public import Mathlib.FieldTheory.Fixed
public import Mathlib.FieldTheory.Normal.Closure
public import Mathlib.FieldTheory.PrimitiveElement
+public import Mathlib.FieldTheory.SeparableClosure
public import Mathlib.GroupTheory.GroupAction.FixingSubgroup
/-!
@@ -502,6 +503,8 @@ theorem of_card_aut_eq_finrank [FiniteDimensional F E]
variable {F} {E}
variable {p : F[X]}
+@[deprecated "No replacement; this was an auxiliary lemma used to prove \
+`Algebra.isSeparable_of_separable_splitting_field`." (since := "2026-06-12")]
theorem of_separable_splitting_field_aux [hFE : FiniteDimensional F E] [sp : p.IsSplittingField F E]
(hp : p.Separable) (K : Type*) [Field K] [Algebra F K] [Algebra K E] [IsScalarTower F K E]
{x : E} (hx : x ∈ p.aroots E) :
@@ -532,37 +535,10 @@ theorem of_separable_splitting_field_aux [hFE : FiniteDimensional F E] [sp : p.I
· apply sp.splits.of_dvd (Polynomial.map_ne_zero h1)
rwa [← f.comp_algebraMap, ← p.map_map, RingHom.algebraMap_toAlgebra, Polynomial.map_dvd_map']
-theorem of_separable_splitting_field [sp : p.IsSplittingField F E] (hp : p.Separable) :
- IsGalois F E := by
- haveI hFE : FiniteDimensional F E := Polynomial.IsSplittingField.finiteDimensional E p
- letI := Classical.decEq E
- let s := p.rootSet E
- have adjoin_root : IntermediateField.adjoin F s = ⊤ := by
- apply IntermediateField.toSubalgebra_injective
- rw [IntermediateField.top_toSubalgebra, ← top_le_iff, ← sp.adjoin_rootSet]
- apply IntermediateField.algebra_adjoin_le_adjoin
- let P : IntermediateField F E → Prop := fun K => Nat.card (K →ₐ[F] E) = finrank F K
- suffices P (IntermediateField.adjoin F s) by
- rw [adjoin_root] at this
- apply of_card_aut_eq_finrank
- rw [← Eq.trans this (LinearEquiv.finrank_eq IntermediateField.topEquiv.toLinearEquiv)]
- exact Nat.card_congr ((algEquivEquivAlgHom F E).toEquiv.trans
- (IntermediateField.topEquiv.symm.arrowCongr AlgEquiv.refl))
- apply IntermediateField.induction_on_adjoin_finset _ P
- · have key := IntermediateField.card_algHom_adjoin_integral F (K := E)
- (show IsIntegral F (0 : E) from isIntegral_zero)
- rw [IsSeparable, minpoly.zero, Polynomial.natDegree_X] at key
- specialize key Polynomial.separable_X (Polynomial.Splits.X.map (algebraMap F E))
- rw [← @Subalgebra.finrank_bot F E _ _ _, ← IntermediateField.bot_toSubalgebra] at key
- refine Eq.trans ?_ key
- apply Nat.card_congr
- rw [IntermediateField.adjoin_zero]
- intro K x hx hK
- simp only [P] at *
- rw [of_separable_splitting_field_aux hp K (Multiset.mem_toFinset.mp hx), hK, finrank_mul_finrank]
- symm
- refine LinearEquiv.finrank_eq ?_
- rfl
+theorem of_separable_splitting_field [p.IsSplittingField F E] (hp : p.Separable) :
+ IsGalois F E :=
+ { to_isSeparable := Algebra.isSeparable_of_separable_splitting_field F E hp,
+ to_normal := Normal.of_isSplittingField p }
/-- Equivalent characterizations of a Galois extension of finite degree. -/
theorem tfae [FiniteDimensional F E] : List.TFAE [
diff --git a/Mathlib/FieldTheory/Galois/GaloisClosure.lean b/Mathlib/FieldTheory/Galois/GaloisClosure.lean
index b08c80a7d8ed8a..dfc8954599d3c3 100644
--- a/Mathlib/FieldTheory/Galois/GaloisClosure.lean
+++ b/Mathlib/FieldTheory/Galois/GaloisClosure.lean
@@ -5,8 +5,7 @@ Authors: Nailin Guan, Yuyang Zhao
-/
module
-public import Mathlib.FieldTheory.Normal.Closure
-public import Mathlib.FieldTheory.SeparableClosure
+public import Mathlib.FieldTheory.Galois.Basic
/-!
diff --git a/Mathlib/FieldTheory/IsSepClosed.lean b/Mathlib/FieldTheory/IsSepClosed.lean
index 823bf62ad7f452..fd98ad49f7d954 100644
--- a/Mathlib/FieldTheory/IsSepClosed.lean
+++ b/Mathlib/FieldTheory/IsSepClosed.lean
@@ -6,6 +6,7 @@ Authors: Jz Pan
module
public import Mathlib.FieldTheory.Galois.Basic
+public import Mathlib.FieldTheory.SeparableClosure
/-!
# Separably Closed Field
@@ -311,3 +312,43 @@ noncomputable def equiv : L ≃ₐ[K] M :=
(IsSepClosed.lift : L →ₐ[K] M) (IsSepClosed.lift : M →ₐ[K] L)).1
end IsSepClosure
+
+section separableClosure
+
+variable (F E : Type*) [Field F] [Field E] [Algebra F E]
+
+/-- If `E` is normal over `F`, then the separable closure of `F` in `E` is Galois (i.e.
+normal and separable) over `F`. -/
+@[stacks 0EXK]
+instance separableClosure.isGalois [Normal F E] : IsGalois F (separableClosure F E) where
+ to_isSeparable := separableClosure.isSeparable F E
+ to_normal := by
+ rw [← separableClosure.normalClosure_eq_self]
+ exact normalClosure.normal F _ E
+
+/-- If `E / F` is a field extension and `E` is separably closed, then the separable closure
+of `F` in `E` is equal to `F` if and only if `F` is separably closed. -/
+theorem IsSepClosed.separableClosure_eq_bot_iff [IsSepClosed E] :
+ separableClosure F E = ⊥ ↔ IsSepClosed F := by
+ refine ⟨fun h ↦ IsSepClosed.of_exists_root _ fun p _ hirr hsep ↦ ?_,
+ fun _ ↦ IntermediateField.eq_bot_of_isSepClosed_of_isSeparable _⟩
+ obtain ⟨x, hx⟩ := IsSepClosed.exists_aeval_eq_zero E p (degree_pos_of_irreducible hirr).ne' hsep
+ obtain ⟨x, rfl⟩ := h ▸ mem_separableClosure_iff.2 (hsep.of_dvd <| minpoly.dvd _ x hx)
+ exact ⟨x, by simpa [Algebra.ofId_apply] using hx⟩
+
+/-- If `E` is separably closed, then the separable closure of `F` in `E` is an absolute
+separable closure of `F`. -/
+instance separableClosure.isSepClosure [IsSepClosed E] : IsSepClosure F (separableClosure F E) :=
+ ⟨(IsSepClosed.separableClosure_eq_bot_iff _ E).mp (separableClosure.separableClosure_eq_bot F E),
+ isSeparable F E⟩
+
+/-- The absolute separable closure is defined to be the relative separable closure inside the
+algebraic closure. It is indeed a separable closure (`IsSepClosure`) by
+`separableClosure.isSepClosure`, and it is Galois (`IsGalois`) by `separableClosure.isGalois`
+or `IsSepClosure.isGalois`, and every separable extension embeds into it (`IsSepClosed.lift`). -/
+abbrev SeparableClosure : Type _ := separableClosure F (AlgebraicClosure F)
+
+instance SeparableClosure.isSepClosed : IsSepClosed (SeparableClosure F) :=
+ (inferInstance : IsSepClosure F (SeparableClosure F)).sep_closed
+
+end separableClosure
diff --git a/Mathlib/FieldTheory/PurelyInseparable/Basic.lean b/Mathlib/FieldTheory/PurelyInseparable/Basic.lean
index 6baa4cd00f365a..e737a97e815e7d 100644
--- a/Mathlib/FieldTheory/PurelyInseparable/Basic.lean
+++ b/Mathlib/FieldTheory/PurelyInseparable/Basic.lean
@@ -6,7 +6,7 @@ Authors: Jz Pan
module
public import Mathlib.Algebra.CharP.IntermediateField
-public import Mathlib.FieldTheory.SeparableClosure
+public import Mathlib.FieldTheory.IsSepClosed
/-!
diff --git a/Mathlib/FieldTheory/Separable.lean b/Mathlib/FieldTheory/Separable.lean
index 3d7f1ab88bce21..6fe3e6458fa074 100644
--- a/Mathlib/FieldTheory/Separable.lean
+++ b/Mathlib/FieldTheory/Separable.lean
@@ -755,6 +755,13 @@ lemma Algebra.IsSeparable.of_equiv_equiv [Algebra.IsSeparable A₁ B₁] : Algeb
⟨fun x ↦ (e₂.apply_symm_apply x) ▸ _root_.IsSeparable.of_equiv_equiv e₁ e₂ he
(Algebra.IsSeparable.isSeparable _ _)⟩
+lemma Algebra.IsSeparable.iff_of_equiv_equiv :
+ Algebra.IsSeparable A₁ B₁ ↔ Algebra.IsSeparable A₂ B₂ :=
+ ⟨fun _ ↦ Algebra.IsSeparable.of_equiv_equiv e₁ e₂ he,
+ fun _ ↦ Algebra.IsSeparable.of_equiv_equiv e₁.symm e₂.symm (by
+ ext x
+ simpa [RingEquiv.eq_symm_apply] using (RingHom.ext_iff.mp he (e₁.symm x)).symm)⟩
+
end AlgEquiv
section CardAlgHom
diff --git a/Mathlib/FieldTheory/SeparableClosure.lean b/Mathlib/FieldTheory/SeparableClosure.lean
index b6aafbad0637f8..cad4b2aad6bfbb 100644
--- a/Mathlib/FieldTheory/SeparableClosure.lean
+++ b/Mathlib/FieldTheory/SeparableClosure.lean
@@ -6,7 +6,6 @@ Authors: Jz Pan
module
public import Mathlib.FieldTheory.SeparableDegree
-public import Mathlib.FieldTheory.IsSepClosed
public import Mathlib.RingTheory.AlgebraicIndependent.AlgebraicClosure
/-!
@@ -64,6 +63,8 @@ separable degree, degree, separable closure
@[expose] public section
+assert_not_exists IsGalois
+
open Module Polynomial IntermediateField Field
noncomputable section
@@ -180,46 +181,20 @@ theorem separableClosure.normalClosure_eq_self :
(AlgEquiv.Algebra.isSeparable (AlgEquiv.ofInjectiveField i))
le_separableClosure F E _) (le_normalClosure _)
-/-- If `E` is normal over `F`, then the separable closure of `F` in `E` is Galois (i.e.
-normal and separable) over `F`. -/
-@[stacks 0EXK]
-instance separableClosure.isGalois [Normal F E] : IsGalois F (separableClosure F E) where
- to_isSeparable := separableClosure.isSeparable F E
- to_normal := by
- rw [← separableClosure.normalClosure_eq_self]
- exact normalClosure.normal F _ E
-
-/-- If `E / F` is a field extension and `E` is separably closed, then the separable closure
-of `F` in `E` is equal to `F` if and only if `F` is separably closed. -/
-theorem IsSepClosed.separableClosure_eq_bot_iff [IsSepClosed E] :
- separableClosure F E = ⊥ ↔ IsSepClosed F := by
- refine ⟨fun h ↦ IsSepClosed.of_exists_root _ fun p _ hirr hsep ↦ ?_,
- fun _ ↦ IntermediateField.eq_bot_of_isSepClosed_of_isSeparable _⟩
- obtain ⟨x, hx⟩ := IsSepClosed.exists_aeval_eq_zero E p (degree_pos_of_irreducible hirr).ne' hsep
- obtain ⟨x, rfl⟩ := h ▸ mem_separableClosure_iff.2 (hsep.of_dvd <| minpoly.dvd _ x hx)
- exact ⟨x, by simpa [Algebra.ofId_apply] using hx⟩
-
-/-- If `E` is separably closed, then the separable closure of `F` in `E` is an absolute
-separable closure of `F`. -/
-instance separableClosure.isSepClosure [IsSepClosed E] : IsSepClosure F (separableClosure F E) :=
- ⟨(IsSepClosed.separableClosure_eq_bot_iff _ E).mp (separableClosure.separableClosure_eq_bot F E),
- isSeparable F E⟩
-
-/-- The absolute separable closure is defined to be the relative separable closure inside the
-algebraic closure. It is indeed a separable closure (`IsSepClosure`) by
-`separableClosure.isSepClosure`, and it is Galois (`IsGalois`) by `separableClosure.isGalois`
-or `IsSepClosure.isGalois`, and every separable extension embeds into it (`IsSepClosed.lift`). -/
-abbrev SeparableClosure : Type _ := separableClosure F (AlgebraicClosure F)
-
-instance SeparableClosure.isSepClosed : IsSepClosed (SeparableClosure F) :=
- (inferInstance : IsSepClosure F (SeparableClosure F)).sep_closed
-
/-- `F(S) / F` is a separable extension if and only if all elements of `S` are
separable elements. -/
theorem IntermediateField.isSeparable_adjoin_iff_isSeparable {S : Set E} :
Algebra.IsSeparable F (adjoin F S) ↔ ∀ x ∈ S, IsSeparable F x :=
(le_separableClosure_iff F E _).symm.trans adjoin_le_iff
+/-- If `p` is a separable polynomial with splitting field `E` over `F`, then `E / F` is a
+separable extension. -/
+theorem Algebra.isSeparable_of_separable_splitting_field {p : F[X]}
+ [sp : p.IsSplittingField F E] (hp : p.Separable) : Algebra.IsSeparable F E := by
+ rw [← isSeparable_top, ← (isSplittingField_iff_intermediateField.mp sp).2,
+ isSeparable_adjoin_iff_isSeparable]
+ exact fun x hx ↦ hp.of_dvd (minpoly.dvd F x (aeval_eq_zero_of_mem_rootSet hx))
+
/-- The separable closure of `F` in `E` is equal to `E` if and only if `E / F` is
separable. -/
theorem separableClosure.eq_top_iff : separableClosure F E = ⊤ ↔ Algebra.IsSeparable F E :=
diff --git a/Mathlib/FieldTheory/SeparableDegree.lean b/Mathlib/FieldTheory/SeparableDegree.lean
index 5b90913ccb2b6f..8f092165f30c70 100644
--- a/Mathlib/FieldTheory/SeparableDegree.lean
+++ b/Mathlib/FieldTheory/SeparableDegree.lean
@@ -196,6 +196,13 @@ theorem finSepDegree_top : finSepDegree F (⊤ : IntermediateField E K) = finSep
end Tower
+theorem isSeparable_bot : Algebra.IsSeparable F (⊥ : IntermediateField F E) :=
+ AlgEquiv.Algebra.isSeparable (IntermediateField.botEquiv F E).symm
+
+theorem isSeparable_top :
+ Algebra.IsSeparable F (⊤ : IntermediateField F E) ↔ Algebra.IsSeparable F E :=
+ Algebra.IsSeparable.iff_of_equiv_equiv (RingEquiv.refl F) topEquiv.toRingEquiv (by ext; simp)
+
end IntermediateField
namespace Field
From bbed4187a91bb274a4359b92d49b59e32be9c95b Mon Sep 17 00:00:00 2001
From: Jireh Loreaux
Date: Thu, 18 Jun 2026 21:17:05 +0000
Subject: [PATCH 0157/1300] =?UTF-8?q?feat:=20continuous=20linear=20equival?=
=?UTF-8?q?ence=20between=20continuous=20`=E2=84=9D`-=20and=20`?=
=?UTF-8?q?=F0=9D=95=9C`-linear=20functionals=20(in=20either=20the=20stron?=
=?UTF-8?q?g=20or=20weak-=E2=8B=86=20topologies)=20(#34728)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This realizes the map `StrongDual.extendRCLikeₗ`, after pre- and post-composing so that it is an equivalence between the weak duals, as a *continuous* linear equivalence.
In addition, when the space is a topological vector space, we realize `StrongDual.extendRCLikeₗ` as a *continuous* linear equivalence between the strong duals.
---
.../Analysis/Normed/Module/RCLike/Extend.lean | 34 +++++++-
Mathlib/Analysis/Normed/Module/WeakDual.lean | 83 +++++++++++++++++++
Mathlib/Analysis/RCLike/Extend.lean | 3 -
.../Algebra/Module/Spaces/WeakDual.lean | 19 +++++
4 files changed, 135 insertions(+), 4 deletions(-)
diff --git a/Mathlib/Analysis/Normed/Module/RCLike/Extend.lean b/Mathlib/Analysis/Normed/Module/RCLike/Extend.lean
index d85858c2bda0b0..b5269e984b5373 100644
--- a/Mathlib/Analysis/Normed/Module/RCLike/Extend.lean
+++ b/Mathlib/Analysis/Normed/Module/RCLike/Extend.lean
@@ -5,7 +5,7 @@ Authors: Ruben Van de Velde
-/
module
-public import Mathlib.Analysis.Normed.Operator.Basic
+public import Mathlib.Analysis.Normed.Operator.Mul
public import Mathlib.Analysis.RCLike.Extend
/-!
@@ -38,6 +38,28 @@ theorem Module.Dual.norm_extendRCLike_le_seminorm [AddCommGroup E] [Module 𝕜
namespace StrongDual
+/-- The extension `StrongDual.extendRCLike` as a continuous linear equivalence between
+the strong duals when scalar multiplication (by `𝕜`) is jointly continuous. -/
+@[expose, simps! -isSimp apply symm_apply]
+noncomputable def extendRCLikeL {𝕜 F : Type*} [RCLike 𝕜] [TopologicalSpace F]
+ [AddCommGroup F] [Module 𝕜 F] [ContinuousSMul 𝕜 F] [Module ℝ F] [IsScalarTower ℝ 𝕜 F] :
+ StrongDual ℝ F ≃L[ℝ] StrongDual 𝕜 F where
+ toLinearEquiv := extendRCLikeₗ
+ continuous_toFun := by
+ rw [(ContinuousLinearMap.isEmbedding_restrictScalars ℝ).continuous_iff]
+ let smulI : F →L[ℝ] F := (I : 𝕜) • ContinuousLinearMap.id 𝕜 F |>.restrictScalars ℝ
+ let mulI : 𝕜 →L[ℝ] 𝕜 := ContinuousLinearMap.mul ℝ 𝕜 (I : 𝕜)
+ exact ofRealCLM.postcomp F - mulI.postcomp F ∘L smulI.precomp 𝕜 ∘L ofRealCLM.postcomp F
+ |>.continuous
+ continuous_invFun := reCLM.postcomp F |>.continuous.comp <|
+ (ContinuousLinearMap.isEmbedding_restrictScalars ℝ).continuous
+
+@[simp]
+lemma toLinearEquiv_extendRCLikeL {𝕜 F : Type*} [RCLike 𝕜] [TopologicalSpace F]
+ [AddCommGroup F] [Module 𝕜 F] [ContinuousSMul 𝕜 F] [Module ℝ F] [IsScalarTower ℝ 𝕜 F] :
+ (extendRCLikeL (𝕜 := 𝕜) (F := F)).toLinearEquiv = extendRCLikeₗ :=
+ rfl
+
/-- If a continuous real-linear functional is bounded by a `𝕜`-seminorm, then its `𝕜`-linear
extension is bounded by the same seminorm. -/
theorem norm_extendRCLike_le_seminorm [AddCommGroup E] [Module 𝕜 E] [Module ℝ E]
@@ -70,6 +92,16 @@ noncomputable def extendRCLikeₗᵢ : StrongDual ℝ F ≃ₗᵢ[ℝ] StrongDua
toLinearEquiv := StrongDual.extendRCLikeₗ
norm_map' := norm_extendRCLike
+@[simp]
+lemma toLinearEquiv_extendRCLikeₗᵢ :
+ (extendRCLikeₗᵢ (𝕜 := 𝕜) (F := F)).toLinearEquiv = extendRCLikeₗ :=
+ rfl
+
+@[simp]
+lemma toContinuousLinearEquiv_extendRCLikeₗᵢ :
+ (extendRCLikeₗᵢ (F := F) (𝕜 := 𝕜)).toContinuousLinearEquiv = extendRCLikeL :=
+ rfl
+
end StrongDual
namespace ContinuousLinearMap
diff --git a/Mathlib/Analysis/Normed/Module/WeakDual.lean b/Mathlib/Analysis/Normed/Module/WeakDual.lean
index 857a24373b9937..99aeca6008b7bf 100644
--- a/Mathlib/Analysis/Normed/Module/WeakDual.lean
+++ b/Mathlib/Analysis/Normed/Module/WeakDual.lean
@@ -7,6 +7,7 @@ module
public import Mathlib.Analysis.Normed.Module.Dual
public import Mathlib.Analysis.Normed.Operator.Completeness
+public import Mathlib.Analysis.Normed.Operator.Mul
public import Mathlib.Topology.Algebra.Module.Spaces.WeakDual
public import Mathlib.Topology.MetricSpace.PiNat
public import Mathlib.Analysis.Normed.Operator.BanachSteinhaus
@@ -370,3 +371,85 @@ theorem isSeqCompact_closedBall (x' : StrongDual 𝕜 E) (r : ℝ) :
isSeqCompact_of_isBounded_of_isClosed 𝕜 _ (isBounded_closedBall x' r) (isClosed_closedBall x' r)
end WeakDual
+
+section RCLike
+
+open RCLike
+open scoped NNReal Topology
+
+namespace WeakDual
+
+-- we shadow the variables for this section because they don't fit with the rest of the file.
+variable {α 𝕜 E F : Type*} [TopologicalSpace α] [RCLike 𝕜]
+ [AddCommGroup E] [Module 𝕜 E] [AddCommGroup F] [Module 𝕜 F]
+
+/-- A map into `WeakBilin (B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜)` over `𝕜` (with `RCLike 𝕜`) is
+continuous if the real parts of all the evaluation maps `a ↦ B (g a) y` are
+continuous for each `y : F`. -/
+theorem _root_.WeakBilin.continuous_of_continuous_eval_re (B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜)
+ {g : α → WeakBilin B} (h : ∀ y, Continuous fun a ↦ re (B (g a) y)) :
+ Continuous g := by
+ refine WeakBilin.continuous_of_continuous_eval _ fun x ↦ ?_
+ suffices Continuous fun a ↦ (re (B (g a) x) : 𝕜) - re (B (g a) ((I : 𝕜) • x)) * I by simpa
+ fun_prop
+
+variable [TopologicalSpace F]
+
+/-- A map into `WeakDual 𝕜 F` over `𝕜` (with `RCLike 𝕜`) is continuous if the real parts of all
+the evaluation maps `a ↦ g a y` are continuous for each `y : F`. -/
+theorem continuous_of_continuous_eval_re {g : α → WeakDual 𝕜 F}
+ (h : ∀ x, Continuous fun a ↦ re (g a x)) :
+ Continuous g :=
+ WeakBilin.continuous_of_continuous_eval_re _ h
+
+variable [ContinuousConstSMul 𝕜 F] [Module ℝ F] [IsScalarTower ℝ 𝕜 F]
+
+open StrongDual
+
+/-- The extension `StrongDual.extendRCLike` as a continuous linear equivalence between
+the weak duals. -/
+@[simps! -isSimp apply symm_apply]
+noncomputable def extendRCLikeL : WeakDual ℝ F ≃L[ℝ] WeakDual 𝕜 F where
+ toLinearEquiv := toStrongDual ≪≫ₗ extendRCLikeₗ ≪≫ₗ toWeakDual.restrictScalars ℝ
+ continuous_toFun := continuous_of_continuous_eval_re fun x ↦ by
+ simpa [extendRCLikeₗ_apply] using eval_continuous x
+ continuous_invFun :=
+ continuous_of_continuous_eval fun x ↦ RCLike.continuous_re.comp (eval_continuous x)
+
+@[simp]
+lemma toLinearEquiv_extendRCLikeL :
+ (extendRCLikeL (𝕜 := 𝕜) (F := F)).toLinearEquiv =
+ toStrongDual ≪≫ₗ extendRCLikeₗ ≪≫ₗ toWeakDual.restrictScalars ℝ := by
+ rfl
+
+lemma extendRCLikeL_apply_apply (f : WeakDual ℝ F) (x : F) :
+ extendRCLikeL (𝕜 := 𝕜) f x = f x - (I : 𝕜) • f ((I : 𝕜) • x) := by
+ rfl
+
+lemma extendRCLikeL_symm_apply_apply (f : WeakDual 𝕜 F) (x : F) :
+ extendRCLikeL.symm f x = re (f x) :=
+ rfl
+
+@[simp]
+lemma re_extendRCLikeL_apply_apply (f : WeakDual ℝ F) (x : F) :
+ re (extendRCLikeL (𝕜 := 𝕜) f x) = f x := by
+ simp [extendRCLikeL_apply_apply]
+
+@[simp]
+lemma im_extendRCLikeL_apply_apply (f : WeakDual ℝ F) (x : F) :
+ im (extendRCLikeL (𝕜 := 𝕜) f x) = - f ((I : 𝕜) • x) := by
+ simp [extendRCLikeL_apply, extendRCLikeₗ_apply]
+
+@[simp high]
+lemma toStrongDual_extendRCLikeL_apply (f : WeakDual ℝ F) :
+ (extendRCLikeL (𝕜 := 𝕜) f).toStrongDual = extendRCLikeₗ f :=
+ rfl
+
+@[simp high]
+lemma _root_.StrongDual.toWeakDual_extendRCLikeₗ_apply (f : StrongDual ℝ F) :
+ (extendRCLikeₗ f).toWeakDual = extendRCLikeL (𝕜 := 𝕜) f.toWeakDual :=
+ rfl
+
+end WeakDual
+
+end RCLike
diff --git a/Mathlib/Analysis/RCLike/Extend.lean b/Mathlib/Analysis/RCLike/Extend.lean
index 9bfd56a416bf36..f1edf68502006f 100644
--- a/Mathlib/Analysis/RCLike/Extend.lean
+++ b/Mathlib/Analysis/RCLike/Extend.lean
@@ -103,9 +103,6 @@ variable [Module ℝ F] [IsScalarTower ℝ 𝕜 F]
/-- Extend `fr : StrongDual ℝ F` to `StrongDual 𝕜 F`.
-It would be possible to use `LinearMap.mkContinuous` here, but we would need to know that the
-continuity of `fr` implies it has bounded norm and we want to avoid that dependency here.
-
Norm properties of this extension can be found in
`Mathlib/Analysis/Normed/Module/RCLike/Extend.lean`. -/
noncomputable def extendRCLike (fr : StrongDual ℝ F) : StrongDual 𝕜 F where
diff --git a/Mathlib/Topology/Algebra/Module/Spaces/WeakDual.lean b/Mathlib/Topology/Algebra/Module/Spaces/WeakDual.lean
index fc6dcabebd8be6..e7ac7aa3da5de1 100644
--- a/Mathlib/Topology/Algebra/Module/Spaces/WeakDual.lean
+++ b/Mathlib/Topology/Algebra/Module/Spaces/WeakDual.lean
@@ -137,6 +137,25 @@ equivalence `StrongDual.toWeakDual` in the other direction. -/
def toStrongDual : WeakDual 𝕜 E ≃ₗ[𝕜] StrongDual 𝕜 E :=
StrongDual.toWeakDual.symm
+@[simp]
+theorem symm_toStrongDual :
+ (toStrongDual (𝕜 := 𝕜) (E := E)).symm = StrongDual.toWeakDual :=
+ rfl
+
+@[simp]
+theorem _root_.StrongDual.symm_toWeakDual :
+ (StrongDual.toWeakDual (𝕜 := 𝕜) (E := E)).symm = toStrongDual :=
+ rfl
+
+@[simp]
+theorem _root_.StrongDual.toStrongDual_toWeakDual (x : StrongDual 𝕜 E) :
+ x.toWeakDual.toStrongDual = x :=
+ rfl
+
+@[simp]
+theorem toWeakDual_toStrongDual (x : WeakDual 𝕜 E) : x.toStrongDual.toWeakDual = x :=
+ rfl
+
@[simp]
theorem toStrongDual_apply (x : WeakDual 𝕜 E) (y : E) : (toStrongDual x) y = x y := rfl
From e69d3a0c829dab0ba9ff0a5270ae932e9c8492a6 Mon Sep 17 00:00:00 2001
From: Marcelo Lynch
Date: Thu, 18 Jun 2026 22:01:28 +0000
Subject: [PATCH 0158/1300] ci: make lake cache shadow shells uniform (#40744)
Sets one workflow-level default shell (`bash -euo pipefail {0}`) so every `run` step shares the same strict mode, instead of relying on the implicit per-step `shell: bash`. `upload`/`consume`/`report` inherit it; `build_and_stage` keeps its landrun sandbox as its job default, and its setup steps spell out the same flags to opt out of the sandbox.
This also fixes some observed issues related to not passing pipefail in places.
---
.github/workflows/lake_cache_shadow.yml | 37 +++++++++++++------------
1 file changed, 19 insertions(+), 18 deletions(-)
diff --git a/.github/workflows/lake_cache_shadow.yml b/.github/workflows/lake_cache_shadow.yml
index ceb83aa283ec17..b0b0491fe7f769 100644
--- a/.github/workflows/lake_cache_shadow.yml
+++ b/.github/workflows/lake_cache_shadow.yml
@@ -62,6 +62,10 @@ concurrency:
group: lake-cache-shadow-${{ inputs.mathlib_ref || 'master' }}
cancel-in-progress: false
+defaults:
+ run:
+ shell: bash -euo pipefail {0}
+
env:
# Scope prefix for all puts and gets in this workflow. Namespaces the
# workflow's artifacts within the cache bucket.
@@ -87,7 +91,7 @@ jobs:
shell: landrun --rox /usr --ro /etc/timezone --rw /dev --rox /home/lean/.elan --rox /home/lean/actions-runner/_work --rox /home/lean/.cache/mathlib/ --rw pr-branch/.lake/ --env PATH --env HOME --env GITHUB_OUTPUT --env CI --env LAKE_CACHE_DIR --env LAKE_NO_CACHE --env STAGE_TARGETS --env SHADOW_SCOPE -- bash -euxo pipefail {0}
steps:
- name: job info
- shell: bash
+ shell: bash -euo pipefail {0}
run: echo "::notice::Lake cache shadow on ref ${{ inputs.mathlib_ref || 'master' }} run ${{ github.run_id }}"
- name: Setup jq
@@ -108,7 +112,7 @@ jobs:
- name: Resolve sha & toolchain
id: resolve
- shell: bash
+ shell: bash -euo pipefail {0}
run: |
cd pr-branch
SHA="$(git rev-parse HEAD)"
@@ -117,23 +121,22 @@ jobs:
echo "toolchain=$TC" >> "$GITHUB_OUTPUT"
- name: Create empty directories (landrun prerequisites)
- shell: bash
+ shell: bash -euo pipefail {0}
run: |
mkdir -p pr-branch/.lake/
mkdir -p .cache/mathlib/
mkdir -p _work
- name: install elan
- shell: bash
+ shell: bash -euo pipefail {0}
run: |
- set -o pipefail
curl -o elan-init.sh -sSfL https://elan.lean-lang.org/elan-init.sh
chmod +x elan-init.sh
./elan-init.sh -y --default-toolchain none
echo "$HOME/.elan/bin" >> "${GITHUB_PATH}"
- name: set toolchain directory
- shell: bash
+ shell: bash -euo pipefail {0}
run: |
cd pr-branch
LAKE_PATH=$(elan which lake)
@@ -141,7 +144,7 @@ jobs:
echo "TOOLCHAIN_DIR=$TOOLCHAIN_DIR" >> "$GITHUB_ENV"
- name: set LEAN_SRC_PATH
- shell: bash
+ shell: bash -euo pipefail {0}
run: |
cd pr-branch
LEAN_SRC_PATH=".:$TOOLCHAIN_DIR/src/lean/lake"
@@ -154,7 +157,7 @@ jobs:
echo "LEAN_SRC_PATH=$LEAN_SRC_PATH" >> "$GITHUB_ENV"
- name: build tools-branch tools
- shell: bash
+ shell: bash -euo pipefail {0}
run: |
cd tools-branch
lake build cache
@@ -166,13 +169,13 @@ jobs:
lake env
- name: Hydrate .lake/build via legacy cache
- shell: bash
+ shell: bash -euo pipefail {0}
run: |
cd pr-branch
../tools-branch/.lake/build/bin/cache get
- name: Patch lakefile to enable Lake's artifact cache
- shell: bash
+ shell: bash -euo pipefail {0}
run: |
cd pr-branch
if grep -q 'enableArtifactCache' lakefile.lean; then
@@ -228,7 +231,7 @@ jobs:
# write to it. Same pattern as build_template.yml uses for the legacy
# cache-staging dir.
- name: Create staging directory
- shell: bash
+ shell: bash -euo pipefail {0}
run: mkdir -p lake-cache-staging
- name: Stage cache for upload
@@ -245,7 +248,7 @@ jobs:
# free-tier / lifecycle budget. Logs the size first, so a trip still tells
# us how big the build got.
- name: Guard upload size (bail if staging > 1 GB)
- shell: bash
+ shell: bash -euo pipefail {0}
run: |
MAX=$((1 * 1000 * 1000 * 1000)) # 1 GB — ~2.5x the measured ~389 MB full build (runaway guard)
BYTES=$(du -sb lake-cache-staging | cut -f1)
@@ -284,9 +287,7 @@ jobs:
fetch-depth: 1
- name: install elan + matching lake
- shell: bash
run: |
- set -o pipefail
curl -o elan-init.sh -sSfL https://elan.lean-lang.org/elan-init.sh
chmod +x elan-init.sh
./elan-init.sh -y --default-toolchain none
@@ -336,7 +337,8 @@ jobs:
base="${AUTH%/artifacts}/analysis"
sha="${{ needs.build_and_stage.outputs.sha }}"
sig=(--aws-sigv4 aws:amz:auto:s3 --user "$LAKE_CACHE_KEY")
- grep -oE 'uploaded artifact [0-9a-f]+' /tmp/put.log | awk '{print $3}' | sort -u > /tmp/today.txt
+ # grep exits 1 on no match (handled by the total==0 branch); tolerate it under pipefail.
+ grep -oE 'uploaded artifact [0-9a-f]+' /tmp/put.log | awk '{print $3}' | sort -u > /tmp/today.txt || true
total=$(wc -l < /tmp/today.txt)
if [ "$total" -eq 0 ]; then
echo "::warning::no uploaded artifacts parsed from put.log — skipping carryover (manifest/pointer left unchanged)"
@@ -418,7 +420,6 @@ jobs:
- name: install elan
run: |
- set -o pipefail
curl -o elan-init.sh -sSfL https://elan.lean-lang.org/elan-init.sh
chmod +x elan-init.sh
./elan-init.sh -y --default-toolchain none
@@ -458,7 +459,8 @@ jobs:
# Artifacts fetched here = the root-package outputs served from cache
# (a non-verbose build replays them silently). Hand the count to the
# provenance step.
- FETCHED=$(find ~/.elan/toolchains -name '*.ltar' 2>/dev/null | wc -l)
+ # `|| true`: an informational count must not trip pipefail if find fails.
+ FETCHED=$(find ~/.elan/toolchains -name '*.ltar' 2>/dev/null | wc -l || true)
echo "ltar count after get: $FETCHED"
echo "FETCHED_ARTIFACTS=$FETCHED" >> "$GITHUB_ENV"
@@ -514,7 +516,6 @@ jobs:
steps:
- name: Compose Zulip message
id: compose
- shell: bash
env:
BUILD: ${{ needs.build_and_stage.result }}
UPLOAD: ${{ needs.upload.result }}
From e81046e832ef8aac1d13bc7510be8d27d6568e5c Mon Sep 17 00:00:00 2001
From: Thomas Browning <13339017+tb65536@users.noreply.github.com>
Date: Thu, 18 Jun 2026 22:50:24 +0000
Subject: [PATCH 0159/1300] chore(NumberTheory/ArithmeticFunction/LFunction):
add backticks to fix docgen (#40783)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This PR adds backticks to the module docstring of `NumberTheory/ArithmeticFunction/LFunction.lean` to fix the docgen output.
The current output is pretty poor:
```
T=q⁻ˢ s ∈ ℂ
[polynomials in T] ----> [polynomials in q⁻ˢ] ----> [analytic function in s] | | | | (reciprocal) | (reciprocal) | (reciprocal) v T=q⁻ˢ V s ∈ ℂ V [power series in T] ----> [power series in q⁻ˢ] ----> [analytic function in s] (the Euler factor) | | | | (product) | (product) | (product) v T=q⁻ˢ V s ∈ ℂ V [multivariate power series] ----> [Dirichlet series] ----> [L-function in s] (the Euler product)
```
Co-authored-by: tb65536
---
Mathlib/NumberTheory/ArithmeticFunction/LFunction.lean | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/Mathlib/NumberTheory/ArithmeticFunction/LFunction.lean b/Mathlib/NumberTheory/ArithmeticFunction/LFunction.lean
index 15a06a3780598d..bc9919c9511070 100644
--- a/Mathlib/NumberTheory/ArithmeticFunction/LFunction.lean
+++ b/Mathlib/NumberTheory/ArithmeticFunction/LFunction.lean
@@ -32,7 +32,7 @@ We take the following route from polynomials to L-functions:
For example, the Riemann zeta function `ζ(s)` corresponds to taking `1 - T` at each prime `p`.
For context, here is a diagram of the possible routes from polynomials to L-functions:
-
+```
T=q⁻ˢ s ∈ ℂ
[polynomials in T] ----> [polynomials in q⁻ˢ] ----> [analytic function in s]
| | |
@@ -43,6 +43,7 @@ For context, here is a diagram of the possible routes from polynomials to L-func
| (product) | (product) | (product)
v T=q⁻ˢ V s ∈ ℂ V
[multivariate power series] ----> [Dirichlet series] ----> [L-function in s] (the Euler product)
+```
-/
@[expose] public section
From 360da6fa66c1273b76b6b2d8c5666fd5ac2e3b56 Mon Sep 17 00:00:00 2001
From: Garmelon <11077553+Garmelon@users.noreply.github.com>
Date: Thu, 18 Jun 2026 23:59:56 +0000
Subject: [PATCH 0160/1300] chore: bump toolchain to v4.32.0-rc1 (#40732)
Co-authored-by: mathlib-nightly-testing[bot]
Co-authored-by: Kim Morrison
Co-authored-by: mathlib4-bot
Co-authored-by: Joscha
Co-authored-by: Rob23oba
Co-authored-by: leanprover-community-mathlib4-bot
Co-authored-by: Julia Markus Himmel <2065352+TwoFX@users.noreply.github.com>
Co-authored-by: Sebastian Ullrich
---
Counterexamples/DirectSumIsInternal.lean | 6 +-
Mathlib/Algebra/Category/Grp/Basic.lean | 7 +-
.../Algebra/Module/Presentation/Basic.lean | 2 +
.../Algebra/Order/AbsoluteValue/Basic.lean | 4 +-
Mathlib/Algebra/Order/Algebra.lean | 11 +-
.../Algebra/Order/BigOperators/Expect.lean | 10 +-
.../Order/BigOperators/Ring/Finset.lean | 14 +-
Mathlib/Algebra/Order/Field/Basic.lean | 64 ++---
Mathlib/Algebra/Order/Field/Power.lean | 110 ++++-----
Mathlib/Algebra/Order/Floor/Extended.lean | 4 +-
Mathlib/Algebra/Order/Floor/Ring.lean | 12 +-
Mathlib/Algebra/Order/Interval/Basic.lean | 8 +-
Mathlib/Algebra/Order/Module/Field.lean | 4 +-
Mathlib/Analysis/Complex/Exponential.lean | 4 +-
Mathlib/Analysis/Complex/Order.lean | 4 +-
Mathlib/Analysis/Complex/Trigonometric.lean | 4 +-
.../Complex/UpperHalfPlane/Basic.lean | 8 +-
Mathlib/Analysis/Normed/Group/Basic.lean | 24 +-
Mathlib/Analysis/Real/Sqrt.lean | 8 +-
.../Analysis/SpecialFunctions/Bernstein.lean | 4 +-
.../SpecialFunctions/Gamma/Basic.lean | 4 +-
.../Analysis/SpecialFunctions/Log/Basic.lean | 12 +-
.../Analysis/SpecialFunctions/Pow/NNReal.lean | 8 +-
.../Analysis/SpecialFunctions/Pow/Real.lean | 8 +-
.../Trigonometric/Arctan.lean | 10 +-
.../SpecialFunctions/Trigonometric/Basic.lean | 4 +-
.../Trigonometric/DerivHyp.lean | 4 +-
Mathlib/CategoryTheory/Bicategory/Basic.lean | 1 +
Mathlib/CategoryTheory/Category/Cat.lean | 1 +
Mathlib/CategoryTheory/Category/Preorder.lean | 1 +
Mathlib/CategoryTheory/Category/Quiv.lean | 1 +
Mathlib/CategoryTheory/Category/ReflQuiv.lean | 1 +
.../FiberedCategory/BasedCategory.lean | 1 +
.../FiberedCategory/HasFibers.lean | 1 +
.../CategoryTheory/Groupoid/Grpd/Basic.lean | 1 +
Mathlib/CategoryTheory/Limits/Chosen/End.lean | 2 +
Mathlib/CategoryTheory/Limits/Creates.lean | 2 +
.../Limits/Shapes/Multiequalizer.lean | 2 +
.../Monoidal/OfHasFiniteProducts.lean | 2 +
.../MorphismProperty/Local.lean | 16 +-
.../Combinatorics/Enumerative/DyckWord.lean | 4 +-
Mathlib/Combinatorics/Hindman.lean | 4 +
.../SimpleGraph/Regularity/Bound.lean | 8 +-
.../SimpleGraph/Triangle/Removal.lean | 4 +-
Mathlib/Computability/Partrec.lean | 1 +
Mathlib/Data/ENNReal/Basic.lean | 8 +-
Mathlib/Data/ENNReal/Real.lean | 4 +-
Mathlib/Data/EReal/Basic.lean | 16 +-
Mathlib/Data/EReal/Inv.lean | 8 +-
Mathlib/Data/EReal/Operations.lean | 8 +-
Mathlib/Data/Fin/Tuple/Reflection.lean | 12 +-
Mathlib/Data/NNReal/Defs.lean | 12 +-
.../Data/Nat/Factorial/DoubleFactorial.lean | 4 +-
Mathlib/Data/Nat/Find.lean | 1 +
Mathlib/Data/Nat/Sqrt.lean | 106 +--------
Mathlib/Data/Nat/Totient.lean | 3 +-
Mathlib/Data/PFunctor/Univariate/Basic.lean | 1 +
Mathlib/Data/Rat/Cast/Order.lean | 29 ++-
Mathlib/FieldTheory/Galois/IsGaloisGroup.lean | 1 +
Mathlib/Geometry/Euclidean/Altitude.lean | 4 +-
Mathlib/Geometry/RingedSpace/Basic.lean | 1 +
Mathlib/Init.lean | 2 +-
Mathlib/Lean/MessageData/ForExprs.lean | 1 +
.../Lean/Meta/RefinedDiscrTree/Encode.lean | 4 +-
.../MeasureTheory/Covering/Besicovitch.lean | 4 +-
.../MeasureTheory/Integral/Bochner/Basic.lean | 4 +-
Mathlib/MeasureTheory/Measure/Real.lean | 4 +-
Mathlib/ModelTheory/Basic.lean | 1 +
.../NumberTheory/ArithmeticFunction/Misc.lean | 4 +-
.../NumberTheory/ArithmeticFunction/Zeta.lean | 4 +-
Mathlib/NumberTheory/Height/Basic.lean | 16 +-
Mathlib/NumberTheory/Height/NumberField.lean | 4 +-
.../NumberTheory/Height/Projectivization.lean | 8 +-
Mathlib/NumberTheory/LucasLehmer.lean | 4 +-
Mathlib/NumberTheory/Padics/Hensel.lean | 1 +
Mathlib/NumberTheory/SelbergSieve.lean | 4 +-
Mathlib/SetTheory/Ordinal/Univ.lean | 2 +
Mathlib/SetTheory/ZFC/PSet.lean | 1 +
Mathlib/Tactic/Algebra/Basic.lean | 77 +++---
Mathlib/Tactic/CrossRefAttribute.lean | 10 +-
Mathlib/Tactic/DefEqAbuse.lean | 38 +--
Mathlib/Tactic/DeprecateTo.lean | 9 +-
Mathlib/Tactic/DeriveEncodable.lean | 2 +-
Mathlib/Tactic/FieldSimp.lean | 26 +-
Mathlib/Tactic/FieldSimp/Lemmas.lean | 14 +-
.../Oracle/SimplexAlgorithm/Gauss.lean | 4 +-
Mathlib/Tactic/LinearCombinationPrime.lean | 2 +-
Mathlib/Tactic/Linter/HaveLetLinter.lean | 4 +-
Mathlib/Tactic/Linter/Style.lean | 6 +-
Mathlib/Tactic/Linter/Whitespace.lean | 6 +-
Mathlib/Tactic/NormNum/Core.lean | 2 +-
Mathlib/Tactic/NormNum/Irrational.lean | 104 ++++----
Mathlib/Tactic/Positivity/Basic.lean | 222 +++++++++---------
Mathlib/Tactic/Positivity/Core.lean | 47 ++--
Mathlib/Tactic/Positivity/Finset.lean | 26 +-
Mathlib/Tactic/ReduceModChar.lean | 3 +-
Mathlib/Tactic/Ring/Basic.lean | 17 +-
Mathlib/Tactic/Ring/Common.lean | 82 +++----
Mathlib/Tactic/Ring/Compare.lean | 8 +-
Mathlib/Tactic/Simproc/ExistsAndEq.lean | 20 +-
.../Tactic/TacticAnalysis/Declarations.lean | 4 +-
Mathlib/Tactic/Translate/Core.lean | 2 +-
Mathlib/Tactic/Translate/Reorder.lean | 2 +-
Mathlib/Tactic/Variable.lean | 4 +-
.../Topology/Algebra/InfiniteSum/Order.lean | 4 +-
.../Category/CompHausLike/Limits.lean | 1 +
Mathlib/Topology/ContinuousMap/Algebra.lean | 1 +
Mathlib/Topology/MetricSpace/Bounded.lean | 4 +-
Mathlib/Topology/MetricSpace/Pseudo/Defs.lean | 4 +-
Mathlib/Util/CountHeartbeats.lean | 2 +-
Mathlib/Util/GetAllModules.lean | 2 +-
Mathlib/Util/WhatsNew.lean | 2 +-
MathlibTest/Attribute/ToAdditive/Basic.lean | 3 +
MathlibTest/Attribute/ToDual.lean | 3 +
MathlibTest/CategoryTheory/CategoryStar.lean | 2 +
MathlibTest/Linter/DocPrime.lean | 1 +
MathlibTest/MinImports.lean | 2 +
MathlibTest/Simps.lean | 1 +
MathlibTest/Subsingleton.lean | 1 +
MathlibTest/Tactic/Says/Basic.lean | 2 +
MathlibTest/UnusedTactic.lean | 1 +
MathlibTest/globalAttributeIn.lean | 1 +
MathlibTest/symm.lean | 2 +
lake-manifest.json | 16 +-
lean-toolchain | 2 +-
scripts/create_deprecated_modules.lean | 2 +-
scripts/lint-style.lean | 2 +-
scripts/mk_all.lean | 2 +-
128 files changed, 756 insertions(+), 749 deletions(-)
diff --git a/Counterexamples/DirectSumIsInternal.lean b/Counterexamples/DirectSumIsInternal.lean
index c46125e7464b48..b2f95cb70a44d2 100644
--- a/Counterexamples/DirectSumIsInternal.lean
+++ b/Counterexamples/DirectSumIsInternal.lean
@@ -56,8 +56,10 @@ theorem withSign.isCompl : IsCompl ℤ≥0 ℤ≤0 := by
· exact Submodule.mem_sup_left (mem_withSign_one.mpr hp)
· exact Submodule.mem_sup_right (mem_withSign_neg_one.mpr hn)
-lemma withSign.independent : iSupIndep withSign := by
- apply (iSupIndep_pair UnitsInt.one_ne_neg_one _).mpr withSign.isCompl.disjoint
+set_option linter.defProp false in
+def withSign.independent : iSupIndep withSign := by
+ apply
+ (iSupIndep_pair UnitsInt.one_ne_neg_one _).mpr withSign.isCompl.disjoint
intro i
fin_cases i <;> simp
diff --git a/Mathlib/Algebra/Category/Grp/Basic.lean b/Mathlib/Algebra/Category/Grp/Basic.lean
index 3f1342fbd31449..bd4be06cb2de90 100644
--- a/Mathlib/Algebra/Category/Grp/Basic.lean
+++ b/Mathlib/Algebra/Category/Grp/Basic.lean
@@ -600,19 +600,24 @@ instance CommGrpCat.forget_reflects_isos : (forget CommGrpCat.{u}).ReflectsIsomo
-- note: in the following definitions, there is a problem with `@[to_additive]`
-- as the `Category` instance is not found on the additive variant
-- this variant is then renamed with an `Aux` suffix
-
+set_option linter.checkUnivs false in
/-- An alias for `GrpCat.{max u v}`, to deal around unification issues. -/
@[to_additive (attr := nolint checkUnivs) GrpMaxAux
/-- An alias for `AddGrpCat.{max u v}`, to deal around unification issues. -/]
abbrev GrpMax.{u1, u2} := GrpCat.{max u1 u2}
+
+set_option linter.checkUnivs false in
/-- An alias for `AddGrpCat.{max u v}`, to deal around unification issues. -/
@[nolint checkUnivs]
abbrev AddGrpMax.{u1, u2} := AddGrpCat.{max u1 u2}
+set_option linter.checkUnivs false in
/-- An alias for `CommGrpCat.{max u v}`, to deal around unification issues. -/
@[to_additive (attr := nolint checkUnivs) AddCommGrpMaxAux
/-- An alias for `AddCommGrpCat.{max u v}`, to deal around unification issues. -/]
abbrev CommGrpMax.{u1, u2} := CommGrpCat.{max u1 u2}
+
+set_option linter.checkUnivs false in
/-- An alias for `AddCommGrpCat.{max u v}`, to deal around unification issues. -/
@[nolint checkUnivs]
abbrev AddCommGrpMax.{u1, u2} := AddCommGrpCat.{max u1 u2}
diff --git a/Mathlib/Algebra/Module/Presentation/Basic.lean b/Mathlib/Algebra/Module/Presentation/Basic.lean
index c66b20bdc5641d..47775e86dd5dff 100644
--- a/Mathlib/Algebra/Module/Presentation/Basic.lean
+++ b/Mathlib/Algebra/Module/Presentation/Basic.lean
@@ -48,6 +48,7 @@ namespace Module
variable (A : Type u) [Ring A]
+set_option linter.checkUnivs false in
/-- Given a ring `A`, this structure involves a family of elements (indexed by a type `R`)
in a free module `G →₀ A`. This allows to define an `A`-module by generators and relations,
see `Relations.Quotient`. -/
@@ -488,6 +489,7 @@ end Relations
variable (M : Type v) [AddCommGroup M] [Module A M]
+set_option linter.checkUnivs false in
/-- Given an `A`-module `M`, a term in this type is a presentation by `M` by
generators and relations. -/
@[nolint checkUnivs]
diff --git a/Mathlib/Algebra/Order/AbsoluteValue/Basic.lean b/Mathlib/Algebra/Order/AbsoluteValue/Basic.lean
index 2438aca9fe70bd..7caaf3ed13c86c 100644
--- a/Mathlib/Algebra/Order/AbsoluteValue/Basic.lean
+++ b/Mathlib/Algebra/Order/AbsoluteValue/Basic.lean
@@ -416,8 +416,8 @@ open Lean Meta Mathlib Meta Positivity Qq in
For performance reasons, we only attempt to apply this when `abv` is a variable.
If it is an explicit function, e.g. `|_|` or `‖_‖`, another extension should apply. -/
@[positivity _]
-meta def Mathlib.Meta.Positivity.evalAbv : PositivityExt where eval {_ _α} _zα pα? e := do
- let some _ := pα? | pure .none
+meta def Mathlib.Meta.Positivity.evalAbv : PositivityExt where eval {_ _α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
let (.app f a) ← whnfR e | throwError "not abv ·"
if !f.getAppFn.isFVar then
throwError "abv: function is not a variable"
diff --git a/Mathlib/Algebra/Order/Algebra.lean b/Mathlib/Algebra/Order/Algebra.lean
index c5a1cbd199b481..36fe510b349ab7 100644
--- a/Mathlib/Algebra/Order/Algebra.lean
+++ b/Mathlib/Algebra/Order/Algebra.lean
@@ -95,14 +95,13 @@ open Lean Meta Qq Function
/-- Extension for `algebraMap`. -/
@[positivity algebraMap _ _ _]
-meta def evalAlgebraMap : PositivityExt where eval {u β} _zβ pβ? e := do
+meta def evalAlgebraMap : PositivityExt where eval {u β} _zβ pβ? e :=
+ match pβ? with | none => pure .none | some _ => do
let ~q(@algebraMap $α _ $instα $instβ $instαβ $a) := e | throwError "not `algebraMap`"
- let pα ← try? <| synthInstanceQ q(PartialOrder $α)
- match ← core q(inferInstance) pα a with
+ let some pα ← try? <| synthInstanceQ q(PartialOrder $α) | pure .none
+ match ← core q(inferInstance) (some pα) a with
| .positive pa =>
- let some _ := pβ? | pure .none
let _instαSemiring ← synthInstanceQ q(Semiring $α)
- let _instαPartialOrder ← synthInstanceQ q(PartialOrder $α)
try
let _instβSemiring ← synthInstanceQ q(Semiring $β)
let _instβPartialOrder ← synthInstanceQ q(PartialOrder $β)
@@ -118,9 +117,7 @@ meta def evalAlgebraMap : PositivityExt where eval {u β} _zβ pβ? e := do
assertInstancesCommute
return .nonnegative q(algebraMap_nonneg $β <| le_of_lt $pa)
| .nonnegative pa =>
- let some _ := pβ? | pure .none
let _instαSemiring ← synthInstanceQ q(CommSemiring $α)
- let _instαPartialOrder ← synthInstanceQ q(PartialOrder $α)
let _instβSemiring ← synthInstanceQ q(Semiring $β)
let _instβPartialOrder ← synthInstanceQ q(PartialOrder $β)
let _instβIsOrderedRing ← synthInstanceQ q(IsOrderedRing $β)
diff --git a/Mathlib/Algebra/Order/BigOperators/Expect.lean b/Mathlib/Algebra/Order/BigOperators/Expect.lean
index 2f7e1bf8a89631..5b72ba801fa25a 100644
--- a/Mathlib/Algebra/Order/BigOperators/Expect.lean
+++ b/Mathlib/Algebra/Order/BigOperators/Expect.lean
@@ -220,22 +220,22 @@ open scoped BigOperators
attribute [local instance] monadLiftOptionMetaM in
/-- Positivity extension for `Finset.expect`. -/
@[positivity Finset.expect _ _]
-meta def evalFinsetExpect : PositivityExt where eval {u α} zα pα? e := do
- let some pα := pα? | pure .none
+meta def evalFinsetExpect : PositivityExt where eval {u α} zα pα? e :=
+ match pα? with | none => pure .none | some pα => do
match e with
| ~q(@Finset.expect $ι _ $instα $instmod $s $f) =>
let i : Q($ι) ← mkFreshExprMVarQ q($ι) .syntheticOpaque
have body : Q($α) := .betaRev f #[i]
let rbody ← core zα pα body
- let p_pos : Option Q(0 < $e) ← (do
+ let p_pos : Option Q(0 < $e) ← do
let .positive pbody := rbody | pure none -- Fail if the body is not provably positive
let some ps ← proveFinsetNonempty s | pure none
let .some pα' ← trySynthInstanceQ q(IsOrderedCancelAddMonoid $α) | pure none
let .some instαordsmul ← trySynthInstanceQ q(PosSMulStrictMono ℚ≥0 $α) | pure none
assumeInstancesCommute
let pr : Q(∀ i, 0 < $f i) ← mkLambdaFVars #[i] pbody
- return some
- q(@expect_pos $ι $α $instα $pα $pα' $instmod $instαordsmul $s $f (fun i _ ↦ $pr i) $ps))
+ pure <| some
+ q(@expect_pos $ι $α $instα $pα $pα' $instmod $instαordsmul $s $f (fun i _ ↦ $pr i) $ps)
-- Try to show that the sum is positive
if let some p_pos := p_pos then
return .positive p_pos
diff --git a/Mathlib/Algebra/Order/BigOperators/Ring/Finset.lean b/Mathlib/Algebra/Order/BigOperators/Ring/Finset.lean
index f4b2836ef9c166..0d96d6a018402a 100644
--- a/Mathlib/Algebra/Order/BigOperators/Ring/Finset.lean
+++ b/Mathlib/Algebra/Order/BigOperators/Ring/Finset.lean
@@ -225,16 +225,16 @@ example (s : Finset ℕ) (f : ℕ → ℤ) (hf : ∀ n, 0 ≤ f n) : 0 ≤ s.pro
because `compareHyp` can't look for assumptions behind binders.
-/
@[positivity Finset.prod _ _]
-meta def evalFinsetProd : PositivityExt where eval {u α} zα pα? e := do
+meta def evalFinsetProd : PositivityExt where eval {u α} zα pα? e :=
+ match pα? with | none => pure .none | some pα => do
match e with
| ~q(@Finset.prod $ι _ $instα $s $f) =>
- let some pα := pα? | pure .none
let i : Q($ι) ← mkFreshExprMVarQ q($ι) .syntheticOpaque
have body : Q($α) := Expr.betaRev f #[i]
let rbody ← core zα pα body
let _instαmon ← synthInstanceQ q(CommMonoidWithZero $α)
-- Try to show that the product is positive
- let p_pos : Option Q(0 < $e) := ← do
+ let p_pos : Option Q(0 < $e) ← do
let .positive pbody := rbody | pure none -- Fail if the body is not provably positive
-- TODO(https://github.com/leanprover-community/quote4/issues/38):
-- We must name the following, else `assertInstancesCommute` loops.
@@ -243,19 +243,19 @@ meta def evalFinsetProd : PositivityExt where eval {u α} zα pα? e := do
let .some _instαnontriv ← trySynthInstanceQ q(Nontrivial $α) | pure none
assertInstancesCommute
let pr : Q(∀ i, 0 < $f i) ← mkLambdaFVars #[i] pbody (binderInfoForMVars := .default)
- return some q(prod_pos fun i _ ↦ $pr i)
+ pure <| some q(prod_pos fun i _ ↦ $pr i)
if let some p_pos := p_pos then return .positive p_pos
-- Try to show that the product is nonnegative
- let p_nonneg : Option Q(0 ≤ $e) := ← do
+ let p_nonneg : Option Q(0 ≤ $e) ← do
let some pbody := rbody.toNonneg
- | return none -- Fail if the body is not provably nonnegative
+ | pure none -- Fail if the body is not provably nonnegative
let pr : Q(∀ i, 0 ≤ $f i) ← mkLambdaFVars #[i] pbody (binderInfoForMVars := .default)
-- TODO(https://github.com/leanprover-community/quote4/issues/38):
-- We must name the following, else `assertInstancesCommute` loops.
let .some _instαzeroone ← trySynthInstanceQ q(ZeroLEOneClass $α) | pure none
let .some _instαposmul ← trySynthInstanceQ q(PosMulMono $α) | pure none
assertInstancesCommute
- return some q(prod_nonneg fun i _ ↦ $pr i)
+ pure <| some q(prod_nonneg fun i _ ↦ $pr i)
if let some p_nonneg := p_nonneg then return .nonnegative p_nonneg
-- Fall back to showing that the product is nonzero
let pbody ← rbody.toNonzero
diff --git a/Mathlib/Algebra/Order/Field/Basic.lean b/Mathlib/Algebra/Order/Field/Basic.lean
index ca4d841b0657e8..f50b2c06f3020b 100644
--- a/Mathlib/Algebra/Order/Field/Basic.lean
+++ b/Mathlib/Algebra/Order/Field/Basic.lean
@@ -740,26 +740,28 @@ such that `positivity` successfully recognises both `a` and `b`. -/
trace[Tactic.positivity.zeroness] "evalDiv: {a} divided by {b}"
let _a ← synthInstanceQ q(Semifield $α)
let ⟨_f_eq⟩ ← withDefault <| withNewMCtxDepth <| assertDefEqQ q($f) q(HDiv.hDiv)
- let some pα := pα? |
+ match (dependent := true) pα? with
+ | none =>
match ← core zα pα? a, ← core zα pα? b with
| .nonzero pa, .nonzero pb =>
let _a ← synthInstanceQ q(GroupWithZero $α)
assumeInstancesCommute
pure (.nonzero q(div_ne_zero $pa $pb))
| _, _ => pure .none
- let _a ← synthInstanceQ q(GroupWithZero $α)
- let _a ← synthInstanceQ q(PosMulReflectLT $α)
- assumeInstancesCommute
- let ra ← core zα pα a; let rb ← core zα pα b
- match ra, rb with
- | .positive pa, .positive pb => pure (.positive q(div_pos $pa $pb))
- | .positive pa, .nonnegative pb => pure (.nonnegative q(div_nonneg_of_pos_of_nonneg $pa $pb))
- | .nonnegative pa, .positive pb => pure (.nonnegative q(div_nonneg_of_nonneg_of_pos $pa $pb))
- | .nonnegative pa, .nonnegative pb => pure (.nonnegative q(div_nonneg $pa $pb))
- | .positive pa, .nonzero pb => pure (.nonzero q(div_ne_zero_of_pos_of_ne_zero $pa $pb))
- | .nonzero pa, .positive pb => pure (.nonzero q(div_ne_zero_of_ne_zero_of_pos $pa $pb))
- | .nonzero pa, .nonzero pb => pure (.nonzero q(div_ne_zero $pa $pb))
- | _, _ => pure .none
+ | some pα =>
+ let _a ← synthInstanceQ q(GroupWithZero $α)
+ let _a ← synthInstanceQ q(PosMulReflectLT $α)
+ assumeInstancesCommute
+ let ra ← core zα pα a; let rb ← core zα pα b
+ match ra, rb with
+ | .positive pa, .positive pb => pure (.positive q(div_pos $pa $pb))
+ | .positive pa, .nonnegative pb => pure (.nonnegative q(div_nonneg_of_pos_of_nonneg $pa $pb))
+ | .nonnegative pa, .positive pb => pure (.nonnegative q(div_nonneg_of_nonneg_of_pos $pa $pb))
+ | .nonnegative pa, .nonnegative pb => pure (.nonnegative q(div_nonneg $pa $pb))
+ | .positive pa, .nonzero pb => pure (.nonzero q(div_ne_zero_of_pos_of_ne_zero $pa $pb))
+ | .nonzero pa, .positive pb => pure (.nonzero q(div_ne_zero_of_ne_zero_of_pos $pa $pb))
+ | .nonzero pa, .nonzero pb => pure (.nonzero q(div_ne_zero $pa $pb))
+ | _, _ => pure .none
/-- The `positivity` extension which identifies expressions of the form `a⁻¹`,
such that `positivity` successfully recognises `a`. -/
@@ -769,33 +771,35 @@ meta def evalInv : PositivityExt where eval {u α} zα pα? e := do
let _e_eq : $e =Q $f $a := ⟨⟩
let _a ← synthInstanceQ q(Semifield $α)
let ⟨_f_eq⟩ ← withDefault <| withNewMCtxDepth <| assertDefEqQ q($f) q(Inv.inv)
- let some _ := pα? |
+ match (dependent := true) pα? with
+ | none =>
match ← core zα pα? a with
| .nonzero pa =>
let _a ← synthInstanceQ q(GroupWithZero $α)
assumeInstancesCommute
pure (.nonzero q(inv_ne_zero $pa))
| _ => pure .none
- let _a ← synthInstanceQ q(GroupWithZero $α)
- let _a ← synthInstanceQ q(PartialOrder $α)
- let _a ← synthInstanceQ q(PosMulReflectLT $α)
- assumeInstancesCommute
- let ra ← core zα pα? a
- match ra with
- | .positive pa =>
+ | some pα =>
+ let _a ← synthInstanceQ q(GroupWithZero $α)
+ let _a ← synthInstanceQ q(PartialOrder $α)
+ let _a ← synthInstanceQ q(PosMulReflectLT $α)
assumeInstancesCommute
- pure (.positive q(inv_pos_of_pos $pa))
- | .nonnegative pa =>
- assumeInstancesCommute
- pure (.nonnegative q(inv_nonneg_of_nonneg $pa))
- | .nonzero pa => pure (.nonzero q(inv_ne_zero $pa))
- | .none => pure .none
+ let ra ← core zα (some pα) a
+ match ra with
+ | .positive pa =>
+ assumeInstancesCommute
+ pure (.positive q(inv_pos_of_pos $pa))
+ | .nonnegative pa =>
+ assumeInstancesCommute
+ pure (.nonnegative q(inv_nonneg_of_nonneg $pa))
+ | .nonzero pa => pure (.nonzero q(inv_ne_zero $pa))
+ | .none => pure .none
/-- The `positivity` extension which identifies expressions of the form `a ^ (0:ℤ)`. -/
@[positivity _ ^ (0 : ℤ), Pow.pow _ (0 : ℤ)]
-meta def evalPowZeroInt : PositivityExt where eval {u α} _zα pα? e := do
+meta def evalPowZeroInt : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
let .app (.app _ (a : Q($α))) _ ← withReducible (whnf e) | throwError "not ^"
- let some _ := pα? | pure .none
let _a ← synthInstanceQ q(Semifield $α)
let _a ← synthInstanceQ q(LinearOrder $α)
let _a ← synthInstanceQ q(IsStrictOrderedRing $α)
diff --git a/Mathlib/Algebra/Order/Field/Power.lean b/Mathlib/Algebra/Order/Field/Power.lean
index 921d4a96f7f17f..99b8427345bff4 100644
--- a/Mathlib/Algebra/Order/Field/Power.lean
+++ b/Mathlib/Algebra/Order/Field/Power.lean
@@ -125,7 +125,8 @@ such that `positivity` successfully recognises both `a` and `b`. -/
@[positivity _ ^ (_ : ℤ), Pow.pow _ (_ : ℤ)]
meta def evalZPow : PositivityExt where eval {u α} zα pα? e := do
let .app (.app _ (a : Q($α))) (b : Q(ℤ)) ← withReducible (whnf e) | throwError "not ^"
- let some pα := pα? |
+ match (dependent := true) pα? with
+ | none =>
match ← core zα pα? a with
| .nonzero pa =>
let _a ← synthInstanceQ q(GroupWithZero $α)
@@ -133,61 +134,62 @@ meta def evalZPow : PositivityExt where eval {u α} zα pα? e := do
haveI' : $e =Q $a ^ $b := ⟨⟩
pure (.nonzero q(zpow_ne_zero $b $pa))
| _ => pure .none
- let result ← catchNone do
- let _a ← synthInstanceQ q(Field $α)
- let _a ← synthInstanceQ q(LinearOrder $α)
- let _a ← synthInstanceQ q(IsStrictOrderedRing $α)
- assumeInstancesCommute
- match ← whnfR b with
- | .app (.app (.app (.const `OfNat.ofNat _) _) (.lit (Literal.natVal n))) _ =>
- guard (n % 2 = 0)
- have m : Q(ℕ) := mkRawNatLit (n / 2)
- haveI' : $b =Q $m + $m := ⟨⟩
- haveI' : $e =Q $a ^ $b := ⟨⟩
- pure (.nonnegative q(Even.zpow_nonneg (Even.add_self _) $a))
- | .app (.app (.app (.const `Neg.neg _) _) _) b' =>
- let b' ← whnfR b'
- let .true := b'.isAppOfArity ``OfNat.ofNat 3 | throwError "not a ^ -n where n is a literal"
- let some n := (b'.getRevArg! 1).rawNatLit? | throwError "not a ^ -n where n is a literal"
- guard (n % 2 = 0)
- have m : Q(ℕ) := mkRawNatLit (n / 2)
- haveI' : $b =Q (-$m) + (-$m) := ⟨⟩
- haveI' : $e =Q $a ^ $b := ⟨⟩
- pure (.nonnegative q(Even.zpow_nonneg (Even.add_self _) $a))
- | _ => throwError "not a ^ n where n is a literal or a negated literal"
- orElse result do
- let ra ← core zα pα a
- let ofNonneg (pa : Q(0 ≤ $a))
- (_oα : Q(Semifield $α)) (_oα : Q(LinearOrder $α)) (_oα : Q(IsStrictOrderedRing $α)) :
- MetaM (Strictness zα e pα) := do
- haveI' : $e =Q $a ^ $b := ⟨⟩
- assumeInstancesCommute
- pure (.nonnegative q(zpow_nonneg $pa $b))
- let ofNonzero (pa : Q($a ≠ 0)) (_oα : Q(GroupWithZero $α)) : MetaM (Strictness zα e pα) := do
- haveI' : $e =Q $a ^ $b := ⟨⟩
- let _a ← synthInstanceQ q(GroupWithZero $α)
+ | some pα =>
+ let result ← catchNone do
+ let _a ← synthInstanceQ q(Field $α)
+ let _a ← synthInstanceQ q(LinearOrder $α)
+ let _a ← synthInstanceQ q(IsStrictOrderedRing $α)
assumeInstancesCommute
- pure (.nonzero q(zpow_ne_zero $b $pa))
- match ra with
- | .positive pa =>
- try
- let _a ← synthInstanceQ q(Semifield $α)
- let _a ← synthInstanceQ q(LinearOrder $α)
- let _a ← synthInstanceQ q(IsStrictOrderedRing $α)
+ match ← whnfR b with
+ | .app (.app (.app (.const `OfNat.ofNat _) _) (.lit (Literal.natVal n))) _ =>
+ guard (n % 2 = 0)
+ have m : Q(ℕ) := mkRawNatLit (n / 2)
+ haveI' : $b =Q $m + $m := ⟨⟩
+ haveI' : $e =Q $a ^ $b := ⟨⟩
+ pure (.nonnegative q(Even.zpow_nonneg (Even.add_self _) $a))
+ | .app (.app (.app (.const `Neg.neg _) _) _) b' =>
+ let b' ← whnfR b'
+ let .true := b'.isAppOfArity ``OfNat.ofNat 3 | throwError "not a ^ -n where n is a literal"
+ let some n := (b'.getRevArg! 1).rawNatLit? | throwError "not a ^ -n where n is a literal"
+ guard (n % 2 = 0)
+ have m : Q(ℕ) := mkRawNatLit (n / 2)
+ haveI' : $b =Q (-$m) + (-$m) := ⟨⟩
+ haveI' : $e =Q $a ^ $b := ⟨⟩
+ pure (.nonnegative q(Even.zpow_nonneg (Even.add_self _) $a))
+ | _ => throwError "not a ^ n where n is a literal or a negated literal"
+ orElse result do
+ let ra ← core zα pα a
+ let ofNonneg (pa : Q(0 ≤ $a))
+ (_oα : Q(Semifield $α)) (_oα : Q(LinearOrder $α)) (_oα : Q(IsStrictOrderedRing $α)) :
+ MetaM (Strictness zα e pα) := do
+ haveI' : $e =Q $a ^ $b := ⟨⟩
assumeInstancesCommute
+ pure (.nonnegative q(zpow_nonneg $pa $b))
+ let ofNonzero (pa : Q($a ≠ 0)) (_oα : Q(GroupWithZero $α)) : MetaM (Strictness zα e pα) := do
haveI' : $e =Q $a ^ $b := ⟨⟩
- pure (.positive q(zpow_pos $pa $b))
- catch e : Exception =>
- trace[Tactic.positivity.failure] "{e.toMessageData}"
- let sα ← synthInstanceQ q(Semifield $α)
- let oα ← synthInstanceQ q(LinearOrder $α)
- let iα ← synthInstanceQ q(IsStrictOrderedRing $α)
- orElse (← catchNone (ofNonneg q(le_of_lt $pa) sα oα iα))
- (ofNonzero q(ne_of_gt $pa) q(inferInstance))
- | .nonnegative pa =>
- ofNonneg pa (← synthInstanceQ (_ : Q(Type u)))
- (← synthInstanceQ (_ : Q(Type u))) (← synthInstanceQ (_ : Q(Prop)))
- | .nonzero pa => ofNonzero pa (← synthInstanceQ (_ : Q(Type u)))
- | .none => pure .none
+ let _a ← synthInstanceQ q(GroupWithZero $α)
+ assumeInstancesCommute
+ pure (.nonzero q(zpow_ne_zero $b $pa))
+ match ra with
+ | .positive pa =>
+ try
+ let _a ← synthInstanceQ q(Semifield $α)
+ let _a ← synthInstanceQ q(LinearOrder $α)
+ let _a ← synthInstanceQ q(IsStrictOrderedRing $α)
+ assumeInstancesCommute
+ haveI' : $e =Q $a ^ $b := ⟨⟩
+ pure (.positive q(zpow_pos $pa $b))
+ catch e : Exception =>
+ trace[Tactic.positivity.failure] "{e.toMessageData}"
+ let sα ← synthInstanceQ q(Semifield $α)
+ let oα ← synthInstanceQ q(LinearOrder $α)
+ let iα ← synthInstanceQ q(IsStrictOrderedRing $α)
+ orElse (← catchNone (ofNonneg q(le_of_lt $pa) sα oα iα))
+ (ofNonzero q(ne_of_gt $pa) q(inferInstance))
+ | .nonnegative pa =>
+ ofNonneg pa (← synthInstanceQ (_ : Q(Type u)))
+ (← synthInstanceQ (_ : Q(Type u))) (← synthInstanceQ (_ : Q(Prop)))
+ | .nonzero pa => ofNonzero pa (← synthInstanceQ (_ : Q(Type u)))
+ | .none => pure .none
end Mathlib.Meta.Positivity
diff --git a/Mathlib/Algebra/Order/Floor/Extended.lean b/Mathlib/Algebra/Order/Floor/Extended.lean
index 3708cc1a32b067..3b73682846a96d 100644
--- a/Mathlib/Algebra/Order/Floor/Extended.lean
+++ b/Mathlib/Algebra/Order/Floor/Extended.lean
@@ -256,10 +256,10 @@ alias ⟨_, natCeil_pos⟩ := ENat.ceil_pos
/-- Extension for the `positivity` tactic: `ENat.ceil` is positive if its input is. -/
@[positivity ⌈_⌉ₑ]
-meta def evalENatCeil : PositivityExt where eval {u α} _zα pα? e := do
+meta def evalENatCeil : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℕ∞), ~q(ENat.ceil $r) =>
- let some _ := pα? | pure .none
match ← core q(inferInstance) (some q(inferInstance)) r with
| .positive pr =>
assertInstancesCommute
diff --git a/Mathlib/Algebra/Order/Floor/Ring.lean b/Mathlib/Algebra/Order/Floor/Ring.lean
index a7b86a5d802241..8aeced5d83d30d 100644
--- a/Mathlib/Algebra/Order/Floor/Ring.lean
+++ b/Mathlib/Algebra/Order/Floor/Ring.lean
@@ -50,10 +50,10 @@ theorem int_floor_nonneg_of_pos [Ring α] [LinearOrder α] [FloorRing α] {a :
/-- Extension for the `positivity` tactic: `Int.floor` is nonnegative if its input is. -/
@[positivity ⌊_⌋]
-meta def evalIntFloor : PositivityExt where eval {u α} _zα pα? e := do
+meta def evalIntFloor : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℤ), ~q(@Int.floor $α' $ir $io $j $a) =>
- let some _ := pα? | pure .none
match ← core q(inferInstance) (some q(inferInstance)) a with
| .positive pa =>
assertInstancesCommute
@@ -70,10 +70,10 @@ theorem nat_ceil_pos [Semiring α] [LinearOrder α] [FloorSemiring α] {a : α}
/-- Extension for the `positivity` tactic: `Nat.ceil` is positive if its input is. -/
@[positivity ⌈_⌉₊]
-meta def evalNatCeil : PositivityExt where eval {u α} _zα pα? e := do
+meta def evalNatCeil : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℕ), ~q(@Nat.ceil $α' $ir $io $j $a) =>
- let some _ := pα? | pure .none
let _i ← synthInstanceQ q(LinearOrder $α')
let _i ← synthInstanceQ q(IsStrictOrderedRing $α')
assertInstancesCommute
@@ -89,10 +89,10 @@ theorem int_ceil_pos [Ring α] [LinearOrder α] [FloorRing α] {a : α} : 0 < a
/-- Extension for the `positivity` tactic: `Int.ceil` is positive/nonnegative if its input is. -/
@[positivity ⌈_⌉]
-meta def evalIntCeil : PositivityExt where eval {u α} _zα pα? e := do
+meta def evalIntCeil : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℤ), ~q(@Int.ceil $α' $ir $io $j $a) =>
- let some _ := pα? | pure .none
match ← core q(inferInstance) (some q(inferInstance)) a with
| .positive pa =>
assertInstancesCommute
diff --git a/Mathlib/Algebra/Order/Interval/Basic.lean b/Mathlib/Algebra/Order/Interval/Basic.lean
index 620b79a1c6fa82..b833e2ff856d15 100644
--- a/Mathlib/Algebra/Order/Interval/Basic.lean
+++ b/Mathlib/Algebra/Order/Interval/Basic.lean
@@ -660,10 +660,10 @@ open Lean Meta Qq
/-- Extension for the `positivity` tactic: The length of an interval is always nonnegative. -/
@[positivity NonemptyInterval.length _]
meta def evalNonemptyIntervalLength : PositivityExt where
- eval {u α} _ pα? e := do
+ eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
let ~q(@NonemptyInterval.length _ $ig $ipo $a) := e |
throwError "not NonemptyInterval.length"
- let some _ := pα? | pure .none
let _i ← synthInstanceQ q(IsOrderedAddMonoid $α)
assertInstancesCommute
return .nonnegative q(NonemptyInterval.length_nonneg $a)
@@ -671,9 +671,9 @@ meta def evalNonemptyIntervalLength : PositivityExt where
/-- Extension for the `positivity` tactic: The length of an interval is always nonnegative. -/
@[positivity Interval.length _]
meta def evalIntervalLength : PositivityExt where
- eval {u α} _ pα? e := do
+ eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
let ~q(@Interval.length _ $ig $ipo $a) := e | throwError "not Interval.length"
- let some _ := pα? | pure .none
let _i ← synthInstanceQ q(IsOrderedAddMonoid $α)
assumeInstancesCommute
return .nonnegative q(Interval.length_nonneg $a)
diff --git a/Mathlib/Algebra/Order/Module/Field.lean b/Mathlib/Algebra/Order/Module/Field.lean
index f1575550df9058..e09bbc4544fd08 100644
--- a/Mathlib/Algebra/Order/Module/Field.lean
+++ b/Mathlib/Algebra/Order/Module/Field.lean
@@ -103,8 +103,8 @@ end Module.IsTorsionFree
/-- Positivity extension for scalar multiplication. -/
@[positivity HSMul.hSMul _ _]
-meta def evalSMul : PositivityExt where eval {_u α} zα pα? (e : Q($α)) := do
- let some pα := pα? | pure .none
+meta def evalSMul : PositivityExt where eval {_u α} zα pα? (e : Q($α)) :=
+ match pα? with | none => pure .none | some pα => do
let .app (.app (.app (.app (.app (.app
(.const ``HSMul.hSMul [u1, _, _]) (β : Q(Type u1))) _) _) _)
(a : Q($β))) (b : Q($α)) ← whnfR e | throwError "failed to match hSMul"
diff --git a/Mathlib/Analysis/Complex/Exponential.lean b/Mathlib/Analysis/Complex/Exponential.lean
index 5d95d2378a9389..1bf61e0671053f 100644
--- a/Mathlib/Analysis/Complex/Exponential.lean
+++ b/Mathlib/Analysis/Complex/Exponential.lean
@@ -692,10 +692,10 @@ open Lean.Meta Qq
/-- Extension for the `positivity` tactic: `Real.exp` is always positive. -/
@[positivity Real.exp _]
-meta def evalExp : PositivityExt where eval {u α} _ pα? e := do
+meta def evalExp : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(Real.exp $a) =>
- let some _ := pα? | pure .none
assertInstancesCommute
pure (.positive q(Real.exp_pos $a))
| _, _, _ => throwError "not Real.exp"
diff --git a/Mathlib/Analysis/Complex/Order.lean b/Mathlib/Analysis/Complex/Order.lean
index 03bf6ffb6cb7fa..43dc270e625bde 100644
--- a/Mathlib/Analysis/Complex/Order.lean
+++ b/Mathlib/Analysis/Complex/Order.lean
@@ -142,10 +142,10 @@ alias ⟨_, ofReal_ne_zero_of_ne_zero⟩ := ofReal_ne_zero
/-- Extension for the `positivity` tactic: `Complex.ofReal` is positive/nonnegative/nonzero if its
input is. -/
@[positivity Complex.ofReal _, Complex.ofReal _]
-meta def evalComplexOfReal : PositivityExt where eval {u α} _ pα? e := do
+meta def evalComplexOfReal : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℂ), ~q(Complex.ofReal $a) =>
- let some _ := pα? | pure .none
assumeInstancesCommute
match ← core q(inferInstance) (some q(inferInstance)) a with
| .positive pa => return .positive q(ofReal_pos $pa)
diff --git a/Mathlib/Analysis/Complex/Trigonometric.lean b/Mathlib/Analysis/Complex/Trigonometric.lean
index 44d62339fd1e11..554e4153002295 100644
--- a/Mathlib/Analysis/Complex/Trigonometric.lean
+++ b/Mathlib/Analysis/Complex/Trigonometric.lean
@@ -940,10 +940,10 @@ open Lean.Meta Qq
/-- Extension for the `positivity` tactic: `Real.cosh` is always positive. -/
@[positivity Real.cosh _]
-meta def evalCosh : PositivityExt where eval {u α} _ pα? e := do
+meta def evalCosh : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(Real.cosh $a) =>
- let some _ := pα? | pure .none
assertInstancesCommute
return .positive q(Real.cosh_pos $a)
| _, _, _ => throwError "not Real.cosh"
diff --git a/Mathlib/Analysis/Complex/UpperHalfPlane/Basic.lean b/Mathlib/Analysis/Complex/UpperHalfPlane/Basic.lean
index 1ce4914b6dffdb..6786ec87928768 100644
--- a/Mathlib/Analysis/Complex/UpperHalfPlane/Basic.lean
+++ b/Mathlib/Analysis/Complex/UpperHalfPlane/Basic.lean
@@ -148,20 +148,20 @@ open Lean Meta Qq
/-- Extension for the `positivity` tactic: `UpperHalfPlane.im`. -/
@[positivity UpperHalfPlane.im _]
-meta def evalUpperHalfPlaneIm : PositivityExt where eval {u α} _zα pα? e := do
+meta def evalUpperHalfPlaneIm : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(UpperHalfPlane.im $a) =>
- let some _ := pα? | pure .none
assertInstancesCommute
pure (.positive q(@UpperHalfPlane.im_pos $a))
| _, _, _ => throwError "not UpperHalfPlane.im"
/-- Extension for the `positivity` tactic: `UpperHalfPlane.coe`. -/
@[positivity UpperHalfPlane.coe _]
-meta def evalUpperHalfPlaneCoe : PositivityExt where eval {u α} _zα pα? e := do
+meta def evalUpperHalfPlaneCoe : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℂ), ~q(UpperHalfPlane.coe $a) =>
- let some _ := pα? | pure .none
assertInstancesCommute
pure (.nonzero q(@UpperHalfPlane.ne_zero $a))
| _, _, _ => throwError "not UpperHalfPlane.coe"
diff --git a/Mathlib/Analysis/Normed/Group/Basic.lean b/Mathlib/Analysis/Normed/Group/Basic.lean
index 0409bfc27bfc7b..f0789e63185df6 100644
--- a/Mathlib/Analysis/Normed/Group/Basic.lean
+++ b/Mathlib/Analysis/Normed/Group/Basic.lean
@@ -1069,17 +1069,17 @@ open Lean Meta Qq Function
/-- Extension for the `positivity` tactic: multiplicative norms are always nonnegative, and positive
on non-one inputs. -/
@[positivity ‖_‖]
-meta def evalMulNorm : PositivityExt where eval {u α} _ pα? e := do
+meta def evalMulNorm : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(@Norm.norm $E $_n $a) =>
- let some _ := pα? | pure .none
let _seminormedGroup_E ← synthInstanceQ q(SeminormedGroup $E)
assertInstancesCommute
-- Check whether we are in a normed group and whether the context contains a `a ≠ 1` assumption
- let o : Option (Q(NormedGroup $E) × Q($a ≠ 1)) := ← do
- let .some normedGroup_E ← trySynthInstanceQ q(NormedGroup $E) | return none
- let some pa ← findLocalDeclWithTypeQ? q($a ≠ 1) | return none
- return some (normedGroup_E, pa)
+ let o : Option (Q(NormedGroup $E) × Q($a ≠ 1)) ← do
+ let .some normedGroup_E ← trySynthInstanceQ q(NormedGroup $E) | pure none
+ let some pa ← findLocalDeclWithTypeQ? q($a ≠ 1) | pure none
+ pure <| some (normedGroup_E, pa)
match o with
-- If so, return a proof of `0 < ‖a‖`
| some (_normedGroup_E, pa) =>
@@ -1092,17 +1092,17 @@ meta def evalMulNorm : PositivityExt where eval {u α} _ pα? e := do
/-- Extension for the `positivity` tactic: additive norms are always nonnegative, and positive
on non-zero inputs. -/
@[positivity ‖_‖]
-meta def evalAddNorm : PositivityExt where eval {u α} _ pα? e := do
+meta def evalAddNorm : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(@Norm.norm $E $_n $a) =>
- let some _ := pα? | pure .none
let _seminormedAddGroup_E ← synthInstanceQ q(SeminormedAddGroup $E)
assertInstancesCommute
-- Check whether we are in a normed group and whether the context contains a `a ≠ 0` assumption
- let o : Option (Q(NormedAddGroup $E) × Q($a ≠ 0)) := ← do
- let .some normedAddGroup_E ← trySynthInstanceQ q(NormedAddGroup $E) | return none
- let some pa ← findLocalDeclWithTypeQ? q($a ≠ 0) | return none
- return some (normedAddGroup_E, pa)
+ let o : Option (Q(NormedAddGroup $E) × Q($a ≠ 0)) ← do
+ let .some normedAddGroup_E ← trySynthInstanceQ q(NormedAddGroup $E) | pure none
+ let some pa ← findLocalDeclWithTypeQ? q($a ≠ 0) | pure none
+ pure <| some (normedAddGroup_E, pa)
match o with
-- If so, return a proof of `0 < ‖a‖`
| some (_normedAddGroup_E, pa) =>
diff --git a/Mathlib/Analysis/Real/Sqrt.lean b/Mathlib/Analysis/Real/Sqrt.lean
index bb2d0f3c36425a..3b8cd015df8f07 100644
--- a/Mathlib/Analysis/Real/Sqrt.lean
+++ b/Mathlib/Analysis/Real/Sqrt.lean
@@ -312,8 +312,8 @@ open Lean Meta Qq Function
/-- Extension for the `positivity` tactic: a square root of a strictly positive nonnegative real is
positive. -/
@[positivity NNReal.sqrt _]
-meta def evalNNRealSqrt : PositivityExt where eval {u α} _zα pα? e := do
- let some _ := pα? | pure .none
+meta def evalNNRealSqrt : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(NNReal), ~q(NNReal.sqrt $a) =>
assertInstancesCommute
@@ -326,8 +326,8 @@ meta def evalNNRealSqrt : PositivityExt where eval {u α} _zα pα? e := do
/-- Extension for the `positivity` tactic: a square root is nonnegative, and is strictly positive if
its input is. -/
@[positivity √_]
-meta def evalSqrt : PositivityExt where eval {u α} _zα pα? e := do
- let some _ := pα? | pure .none
+meta def evalSqrt : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(√$a) =>
assertInstancesCommute
diff --git a/Mathlib/Analysis/SpecialFunctions/Bernstein.lean b/Mathlib/Analysis/SpecialFunctions/Bernstein.lean
index 2e650b4aa8f761..ca7fffbc5653b5 100644
--- a/Mathlib/Analysis/SpecialFunctions/Bernstein.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Bernstein.lean
@@ -80,9 +80,9 @@ open Lean Meta Qq Function
/-- Extension of the `positivity` tactic for Bernstein polynomials: they are always non-negative. -/
@[positivity DFunLike.coe (bernstein _ _) _]
-meta def evalBernstein : PositivityExt where eval {_ _} _zα pα? e := do
+meta def evalBernstein : PositivityExt where eval {_ _} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
let .app (.app _coe (.app (.app _ n) ν)) x ← whnfR e | throwError "not bernstein polynomial"
- let some _ := pα? | pure .none
let p ← mkAppOptM ``bernstein_nonneg #[n, ν, x]
pure (.nonnegative p)
diff --git a/Mathlib/Analysis/SpecialFunctions/Gamma/Basic.lean b/Mathlib/Analysis/SpecialFunctions/Gamma/Basic.lean
index 16c34c92f0cf0a..a57539a4f3e2de 100644
--- a/Mathlib/Analysis/SpecialFunctions/Gamma/Basic.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Gamma/Basic.lean
@@ -472,10 +472,10 @@ lemma integral_rpow_mul_exp_neg_mul_Ioi {a r : ℝ} (ha : 0 < a) (hr : 0 < r) :
open Lean.Meta Qq Mathlib.Meta.Positivity in
/-- The `positivity` extension which identifies expressions of the form `Gamma a`. -/
@[positivity Gamma (_ : ℝ)]
-meta def _root_.Mathlib.Meta.Positivity.evalGamma : PositivityExt where eval {u α} _zα pα? e := do
+meta def _root_.Mathlib.Meta.Positivity.evalGamma : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(Gamma $a) =>
- let some _ := pα? | pure .none
match ← core q(inferInstance) (some q(inferInstance)) a with
| .positive pa =>
assertInstancesCommute
diff --git a/Mathlib/Analysis/SpecialFunctions/Log/Basic.lean b/Mathlib/Analysis/SpecialFunctions/Log/Basic.lean
index a50cc14b759129..cc7333c231aa1b 100644
--- a/Mathlib/Analysis/SpecialFunctions/Log/Basic.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Log/Basic.lean
@@ -596,30 +596,30 @@ lemma log_nz_of_isRat_neg {n : ℤ} : (NormNum.IsRat e n d) → (decide (n / d <
/-- Extension for the `positivity` tactic: `Real.log` of a natural number is always nonnegative. -/
@[positivity Real.log (Nat.cast _)]
-meta def evalLogNatCast : PositivityExt where eval {u α} _zα pα? e := do
+meta def evalLogNatCast : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(Real.log (Nat.cast $a)) =>
- let some _ := pα? | pure .none
assertInstancesCommute
pure (.nonnegative q(Real.log_natCast_nonneg $a))
| _, _, _ => throwError "not Real.log"
/-- Extension for the `positivity` tactic: `Real.log` of an integer is always nonnegative. -/
@[positivity Real.log (Int.cast _)]
-meta def evalLogIntCast : PositivityExt where eval {u α} _zα pα? e := do
+meta def evalLogIntCast : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(Real.log (Int.cast $a)) =>
- let some _ := pα? | pure .none
assertInstancesCommute
pure (.nonnegative q(Real.log_intCast_nonneg $a))
| _, _, _ => throwError "not Real.log"
/-- Extension for the `positivity` tactic: `Real.log` of a numeric literal. -/
@[positivity Real.log _]
-meta def evalLogNatLit : PositivityExt where eval {u α} _ pα? e := do
+meta def evalLogNatLit : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(Real.log $a) =>
- let some _ := pα? | pure .none
match ← NormNum.derive a with
| .isNat (_ : Q(AddMonoidWithOne ℝ)) lit p =>
assumeInstancesCommute
diff --git a/Mathlib/Analysis/SpecialFunctions/Pow/NNReal.lean b/Mathlib/Analysis/SpecialFunctions/Pow/NNReal.lean
index ccde81c19c3eca..6e98036e4a5139 100644
--- a/Mathlib/Analysis/SpecialFunctions/Pow/NNReal.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Pow/NNReal.lean
@@ -1125,8 +1125,8 @@ open Lean Meta Qq
the base is nonnegative and positive when the base is positive.
This is the `NNReal` analogue of `evalRpow` for `Real`. -/
@[positivity (_ : ℝ≥0) ^ (_ : ℝ)]
-meta def evalNNRealRpow : PositivityExt where eval {u α} _ pα? e := do
- let some _ := pα? | pure .none
+meta def evalNNRealRpow : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ≥0), ~q($a ^ (0 : ℝ)) =>
assertInstancesCommute
@@ -1155,8 +1155,8 @@ private meta def isFiniteM? (x : Q(ℝ≥0∞)) : MetaM (Option Q($x ≠ (⊤ :
the base is nonnegative and positive when the base is positive.
This is the `ENNReal` analogue of `evalRpow` for `Real`. -/
@[positivity (_ : ℝ≥0∞) ^ (_ : ℝ)]
-meta def evalENNRealRpow : PositivityExt where eval {u α} _ pα? e := do
- let some _ := pα? | pure .none
+meta def evalENNRealRpow : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ≥0∞), ~q($a ^ (0 : ℝ)) =>
assertInstancesCommute
diff --git a/Mathlib/Analysis/SpecialFunctions/Pow/Real.lean b/Mathlib/Analysis/SpecialFunctions/Pow/Real.lean
index b220c5e0e31fdb..4fa7722f332deb 100644
--- a/Mathlib/Analysis/SpecialFunctions/Pow/Real.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Pow/Real.lean
@@ -375,8 +375,8 @@ open Lean Meta Qq
/-- Extension for the `positivity` tactic: exponentiation by a real number is positive (namely 1)
when the exponent is zero. The other cases are done in `evalRpow`. -/
@[positivity (_ : ℝ) ^ (0 : ℝ)]
-meta def evalRpowZero : PositivityExt where eval {u α} _ pα? e := do
- let some _ := pα? | pure .none
+meta def evalRpowZero : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q($a ^ (0 : ℝ)) =>
assertInstancesCommute
@@ -386,8 +386,8 @@ meta def evalRpowZero : PositivityExt where eval {u α} _ pα? e := do
/-- Extension for the `positivity` tactic: exponentiation by a real number is nonnegative when
the base is nonnegative and positive when the base is positive. -/
@[positivity (_ : ℝ) ^ (_ : ℝ)]
-meta def evalRpow : PositivityExt where eval {u α} _zα pα? e := do
- let some _ := pα? | pure .none
+meta def evalRpow : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q($a ^ ($b : ℝ)) =>
assertInstancesCommute
diff --git a/Mathlib/Analysis/SpecialFunctions/Trigonometric/Arctan.lean b/Mathlib/Analysis/SpecialFunctions/Trigonometric/Arctan.lean
index db73145896a607..a4f209c1ff56de 100644
--- a/Mathlib/Analysis/SpecialFunctions/Trigonometric/Arctan.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Trigonometric/Arctan.lean
@@ -399,7 +399,8 @@ open Lean Meta Qq
/-- Extension for `Real.arctan`. -/
@[positivity Real.arctan _]
-meta def evalRealArctan : PositivityExt where eval {u α} z p e := do
+meta def evalRealArctan : PositivityExt where eval {u α} z p e :=
+ match p with | none => pure .none | some p => do
match u, α, e with
| 0, ~q(ℝ), ~q(Real.arctan $a) =>
let ra ← core z p a
@@ -418,17 +419,18 @@ meta def evalRealArctan : PositivityExt where eval {u α} z p e := do
/-- Extension for `Real.cos (Real.arctan _)`. -/
@[positivity Real.cos (Real.arctan _)]
-meta def evalRealCosArctan : PositivityExt where eval {u α} _ pα? e := do
+meta def evalRealCosArctan : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(Real.cos (Real.arctan $a)) =>
- let some _ := pα? | pure .none
assumeInstancesCommute
return .positive q(Real.cos_arctan_pos _)
| _ => throwError "not Real.cos (Real.arctan _)"
/-- Extension for `Real.sin (Real.arctan _)`. -/
@[positivity Real.sin (Real.arctan _)]
-meta def evalRealSinArctan : PositivityExt where eval {u α} z p e := do
+meta def evalRealSinArctan : PositivityExt where eval {u α} z p e :=
+ match p with | none => pure .none | some p => do
match u, α, e with
| 0, ~q(ℝ), ~q(Real.sin (Real.arctan $a)) =>
match ← core z p a with
diff --git a/Mathlib/Analysis/SpecialFunctions/Trigonometric/Basic.lean b/Mathlib/Analysis/SpecialFunctions/Trigonometric/Basic.lean
index 6d712d0f1aa57e..86ee5817a80fec 100644
--- a/Mathlib/Analysis/SpecialFunctions/Trigonometric/Basic.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Trigonometric/Basic.lean
@@ -177,10 +177,10 @@ open Lean.Meta Qq
/-- Extension for the `positivity` tactic: `π` is always positive. -/
@[positivity Real.pi]
-meta def evalRealPi : PositivityExt where eval {u α} _zα pα? e := do
+meta def evalRealPi : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(Real.pi) =>
- let some _ := pα? | pure .none
assertInstancesCommute
pure (.positive q(Real.pi_pos))
| _, _, _ => throwError "not Real.pi"
diff --git a/Mathlib/Analysis/SpecialFunctions/Trigonometric/DerivHyp.lean b/Mathlib/Analysis/SpecialFunctions/Trigonometric/DerivHyp.lean
index d4ae1114b1700b..7568fe3549d181 100644
--- a/Mathlib/Analysis/SpecialFunctions/Trigonometric/DerivHyp.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Trigonometric/DerivHyp.lean
@@ -798,8 +798,8 @@ alias ⟨_, sinh_ne_zero_of_ne_zero⟩ := Real.sinh_ne_zero
/-- Extension for the `positivity` tactic: `Real.sinh` is positive/nonnegative/nonzero if its input
is. -/
@[positivity Real.sinh _]
-meta def evalSinh : PositivityExt where eval {u α} _ pα? e := do
- let some _ := pα? | pure .none
+meta def evalSinh : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
let zα : Q(Zero ℝ) := q(inferInstance)
let pα : Q(PartialOrder ℝ) := q(inferInstance)
match u, α, e with
diff --git a/Mathlib/CategoryTheory/Bicategory/Basic.lean b/Mathlib/CategoryTheory/Bicategory/Basic.lean
index 130bb40e313fbe..a715b9ac6890cd 100644
--- a/Mathlib/CategoryTheory/Bicategory/Basic.lean
+++ b/Mathlib/CategoryTheory/Bicategory/Basic.lean
@@ -48,6 +48,7 @@ universe w v u
open Category Iso
-- intended to be used with explicit universe parameters
+set_option linter.checkUnivs false in
/-- In a bicategory, we can compose the 1-morphisms `f : a ⟶ b` and `g : b ⟶ c` to obtain
a 1-morphism `f ≫ g : a ⟶ c`. This composition does not need to be strictly associative,
but there is a specified associator, `α_ f g h : (f ≫ g) ≫ h ≅ f ≫ (g ≫ h)`.
diff --git a/Mathlib/CategoryTheory/Category/Cat.lean b/Mathlib/CategoryTheory/Category/Cat.lean
index f1335563886635..b179e05ca015f0 100644
--- a/Mathlib/CategoryTheory/Category/Cat.lean
+++ b/Mathlib/CategoryTheory/Category/Cat.lean
@@ -31,6 +31,7 @@ namespace CategoryTheory
open Bicategory Functor
-- intended to be used with explicit universe parameters
+set_option linter.checkUnivs false in
/-- Category of categories. -/
@[nolint checkUnivs]
def Cat :=
diff --git a/Mathlib/CategoryTheory/Category/Preorder.lean b/Mathlib/CategoryTheory/Category/Preorder.lean
index 6a0802ed2d49da..8e91d464d4b631 100644
--- a/Mathlib/CategoryTheory/Category/Preorder.lean
+++ b/Mathlib/CategoryTheory/Category/Preorder.lean
@@ -80,6 +80,7 @@ theorem homOfLE_comp {x y z : X} (h : x ≤ y) (k : y ≤ z) :
theorem leOfHom {x y : X} (h : x ⟶ y) : x ≤ y :=
h.down.down
+set_option linter.defProp false in
@[nolint defLemma, inherit_doc leOfHom]
abbrev _root_.Quiver.Hom.le := @leOfHom
diff --git a/Mathlib/CategoryTheory/Category/Quiv.lean b/Mathlib/CategoryTheory/Category/Quiv.lean
index 473229f413a7c4..60b2bf23c8709f 100644
--- a/Mathlib/CategoryTheory/Category/Quiv.lean
+++ b/Mathlib/CategoryTheory/Category/Quiv.lean
@@ -22,6 +22,7 @@ universe v u v₁ v₂ v₃ u₁ u₂ u₃ w
namespace CategoryTheory
-- intended to be used with explicit universe parameters
+set_option linter.checkUnivs false in
/-- Category of quivers. -/
@[nolint checkUnivs]
def Quiv :=
diff --git a/Mathlib/CategoryTheory/Category/ReflQuiv.lean b/Mathlib/CategoryTheory/Category/ReflQuiv.lean
index 9dd8b7a23195f1..f1dc344cb96886 100644
--- a/Mathlib/CategoryTheory/Category/ReflQuiv.lean
+++ b/Mathlib/CategoryTheory/Category/ReflQuiv.lean
@@ -21,6 +21,7 @@ The category `ReflQuiv` of (bundled) reflexive quivers, and the free/forgetful a
namespace CategoryTheory
universe v u v₁ v₂ u₁ u₂
+set_option linter.checkUnivs false in
/-- Category of refl quivers. -/
@[nolint checkUnivs]
def ReflQuiv :=
diff --git a/Mathlib/CategoryTheory/FiberedCategory/BasedCategory.lean b/Mathlib/CategoryTheory/FiberedCategory/BasedCategory.lean
index ed5734b16d5cd4..02f09d2a413c2a 100644
--- a/Mathlib/CategoryTheory/FiberedCategory/BasedCategory.lean
+++ b/Mathlib/CategoryTheory/FiberedCategory/BasedCategory.lean
@@ -36,6 +36,7 @@ open Functor Category NatTrans IsHomLift
variable {𝒮 : Type u₁} [Category.{v₁} 𝒮]
+set_option linter.checkUnivs false in
/-- A based category over `𝒮` is a category `𝒳` together with a functor `p : 𝒳 ⥤ 𝒮`. -/
@[nolint checkUnivs]
structure BasedCategory (𝒮 : Type u₁) [Category.{v₁} 𝒮] where
diff --git a/Mathlib/CategoryTheory/FiberedCategory/HasFibers.lean b/Mathlib/CategoryTheory/FiberedCategory/HasFibers.lean
index 7907d6276d3caa..7dc53e2dabba98 100644
--- a/Mathlib/CategoryTheory/FiberedCategory/HasFibers.lean
+++ b/Mathlib/CategoryTheory/FiberedCategory/HasFibers.lean
@@ -58,6 +58,7 @@ open CategoryTheory Functor Category IsCartesian IsHomLift Fiber
variable {𝒮 : Type u₁} {𝒳 : Type u₂} [Category.{v₁} 𝒮] [Category.{v₂} 𝒳]
+set_option linter.checkUnivs false in
/-- HasFibers is an extrinsic notion of fibers on a functor `p : 𝒳 ⥤ 𝒮`. It is given by a
collection of categories `Fib S` for every `S : 𝒮` (the fiber categories), each equipped with a
functors `ι : Fib S ⥤ 𝒳` which map constantly to `S` on the base such that the induced functor
diff --git a/Mathlib/CategoryTheory/Groupoid/Grpd/Basic.lean b/Mathlib/CategoryTheory/Groupoid/Grpd/Basic.lean
index 9674e4558b323d..9860d8bd1379b6 100644
--- a/Mathlib/CategoryTheory/Groupoid/Grpd/Basic.lean
+++ b/Mathlib/CategoryTheory/Groupoid/Grpd/Basic.lean
@@ -33,6 +33,7 @@ universe v u
namespace CategoryTheory
-- intended to be used with explicit universe parameters
+set_option linter.checkUnivs false in
/-- Category of groupoids -/
@[nolint checkUnivs]
def Grpd :=
diff --git a/Mathlib/CategoryTheory/Limits/Chosen/End.lean b/Mathlib/CategoryTheory/Limits/Chosen/End.lean
index 29a4f819429c10..8bacfbd3374248 100644
--- a/Mathlib/CategoryTheory/Limits/Chosen/End.lean
+++ b/Mathlib/CategoryTheory/Limits/Chosen/End.lean
@@ -30,6 +30,7 @@ class ChosenCoendsOfShape (J : Type*) [Category* J] (C : Type*) [Category* C] wh
/-- The chosen cowedge is colimiting. -/
isCoend (F : Jᵒᵖ ⥤ J ⥤ C) : IsColimit (cowedge F)
+set_option linter.checkUnivs false in
/-- The data of chosen coends in `C`. -/
@[nolint checkUnivs, pp_with_univ]
abbrev ChosenCoends (C : Type*) [Category* C] :=
@@ -104,6 +105,7 @@ class ChosenEndsOfShape (J : Type*) [Category* J] (C : Type*) [Category* C] wher
/-- The chosen wedge is limiting. -/
isEnd (F : Jᵒᵖ ⥤ J ⥤ C) : IsLimit (wedge F)
+set_option linter.checkUnivs false in
/-- The data of chosen ends in `C`. -/
@[nolint checkUnivs, pp_with_univ]
abbrev ChosenEnds (C : Type*) [Category* C] :=
diff --git a/Mathlib/CategoryTheory/Limits/Creates.lean b/Mathlib/CategoryTheory/Limits/Creates.lean
index 6e50771af6bbee..0608f6fb77867b 100644
--- a/Mathlib/CategoryTheory/Limits/Creates.lean
+++ b/Mathlib/CategoryTheory/Limits/Creates.lean
@@ -81,6 +81,7 @@ class CreatesLimitsOfShape (J : Type w) [Category.{w'} J] (F : C ⥤ D) where
CreatesLimit : ∀ {K : J ⥤ C}, CreatesLimit K F := by infer_instance
-- This should be used with explicit universe variables.
+set_option linter.checkUnivs false in
/-- `F` creates limits if it creates limits of shape `J` for any `J`. -/
-- After https://github.com/leanprover/lean4/pull/12286 and
-- https://github.com/leanprover/lean4/pull/12423, the shape universes in
@@ -114,6 +115,7 @@ class CreatesColimitsOfShape (J : Type w) [Category.{w'} J] (F : C ⥤ D) where
CreatesColimit : ∀ {K : J ⥤ C}, CreatesColimit K F := by infer_instance
-- This should be used with explicit universe variables.
+set_option linter.checkUnivs false in
/-- `F` creates colimits if it creates colimits of shape `J` for any small `J`. -/
@[univ_out_params, nolint checkUnivs, pp_with_univ]
class CreatesColimitsOfSize (F : C ⥤ D) where
diff --git a/Mathlib/CategoryTheory/Limits/Shapes/Multiequalizer.lean b/Mathlib/CategoryTheory/Limits/Shapes/Multiequalizer.lean
index aaef3ca24fd17d..7a154e987b310e 100644
--- a/Mathlib/CategoryTheory/Limits/Shapes/Multiequalizer.lean
+++ b/Mathlib/CategoryTheory/Limits/Shapes/Multiequalizer.lean
@@ -35,6 +35,7 @@ namespace CategoryTheory.Limits
universe t w w' v u
+set_option linter.checkUnivs false in
/-- The shape of a multiequalizer diagram. It involves two types `L` and `R`,
and two maps `R → L`. -/
@[nolint checkUnivs]
@@ -58,6 +59,7 @@ def MulticospanShape.prod (ι : Type w) : MulticospanShape where
fst := _root_.Prod.fst
snd := _root_.Prod.snd
+set_option linter.checkUnivs false in
/-- The shape of a multicoequalizer diagram. It involves two types `L` and `R`,
and two maps `L → R`. -/
@[nolint checkUnivs]
diff --git a/Mathlib/CategoryTheory/Monoidal/OfHasFiniteProducts.lean b/Mathlib/CategoryTheory/Monoidal/OfHasFiniteProducts.lean
index e3b93682c62958..db187334c5de14 100644
--- a/Mathlib/CategoryTheory/Monoidal/OfHasFiniteProducts.lean
+++ b/Mathlib/CategoryTheory/Monoidal/OfHasFiniteProducts.lean
@@ -181,6 +181,7 @@ variable [PreservesLimit (Functor.empty.{0} C) F]
[PreservesLimitsOfShape (Discrete WalkingPair) F]
set_option backward.defeqAttrib.useBackward true in
+set_option linter.deprecated false in
@[deprecated inferInstance (since := "2025-10-19")]
instance :
have : HasFiniteProducts C := hasFiniteProducts_of_has_binary_and_terminal
@@ -190,6 +191,7 @@ instance :
IsIso (η F) := by dsimp [η_eq]; apply instIsIsoTerminalComparison
set_option backward.defeqAttrib.useBackward true in
+set_option linter.deprecated false in
@[deprecated inferInstance (since := "2025-10-19")]
instance (X Y : C) :
have : HasFiniteProducts C := hasFiniteProducts_of_has_binary_and_terminal
diff --git a/Mathlib/CategoryTheory/MorphismProperty/Local.lean b/Mathlib/CategoryTheory/MorphismProperty/Local.lean
index ba30a655b47b9a..60a80b23c863ab 100644
--- a/Mathlib/CategoryTheory/MorphismProperty/Local.lean
+++ b/Mathlib/CategoryTheory/MorphismProperty/Local.lean
@@ -135,7 +135,13 @@ instance inf (P Q : MorphismProperty C) [IsLocalAtTarget P K] [IsLocalAtTarget Q
end IsLocalAtTarget
-alias of_zeroHypercover_target := IsLocalAtTarget.of_zeroHypercover
+set_option backward.defeqAttrib.useBackward true in
+lemma of_zeroHypercover_target {P : MorphismProperty C} {K : Precoverage C} [K.HasPullbacks]
+ [P.IsLocalAtTarget K] {X Y : C} {f : X ⟶ Y} (𝒰 : Precoverage.ZeroHypercover.{w} K Y)
+ [Precoverage.ZeroHypercover.Small.{v} 𝒰] (h : ∀ i, P (pullback.snd f (𝒰.f i))) :
+ P f := by
+ rw [IsLocalAtTarget.iff_of_zeroHypercover (P := P) 𝒰.restrictIndexOfSmall]
+ simp [h]
alias iff_of_zeroHypercover_target := IsLocalAtTarget.iff_of_zeroHypercover
@@ -211,7 +217,13 @@ instance inf (P Q : MorphismProperty C) [IsLocalAtSource P K] [IsLocalAtSource Q
end IsLocalAtSource
-alias of_zeroHypercover_source := IsLocalAtSource.of_zeroHypercover
+set_option backward.defeqAttrib.useBackward true in
+lemma of_zeroHypercover_source {P : MorphismProperty C} {K : Precoverage C}
+ [P.IsLocalAtSource K] {X Y : C} {f : X ⟶ Y} (𝒰 : Precoverage.ZeroHypercover.{w} K X)
+ [Precoverage.ZeroHypercover.Small.{v} 𝒰] (h : ∀ i, P (𝒰.f i ≫ f)) :
+ P f := by
+ rw [IsLocalAtSource.iff_of_zeroHypercover (P := P) 𝒰.restrictIndexOfSmall]
+ simp [h]
alias iff_of_zeroHypercover_source := IsLocalAtSource.iff_of_zeroHypercover
diff --git a/Mathlib/Combinatorics/Enumerative/DyckWord.lean b/Mathlib/Combinatorics/Enumerative/DyckWord.lean
index 0dfcee6466b377..b4462eda3c50f6 100644
--- a/Mathlib/Combinatorics/Enumerative/DyckWord.lean
+++ b/Mathlib/Combinatorics/Enumerative/DyckWord.lean
@@ -557,8 +557,8 @@ open Lean Meta Qq
/-- Extension for the `positivity` tactic: `p.firstReturn` is positive if `p` is nonzero. -/
@[positivity DyckWord.firstReturn _]
-meta def evalDyckWordFirstReturn : PositivityExt where eval {u α} _zα pα? e := do
- let some _ := pα? | pure .none
+meta def evalDyckWordFirstReturn : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℕ), ~q(DyckWord.firstReturn $a) =>
assertInstancesCommute
diff --git a/Mathlib/Combinatorics/Hindman.lean b/Mathlib/Combinatorics/Hindman.lean
index 66f54aeee73816..8e12c26ea959be 100644
--- a/Mathlib/Combinatorics/Hindman.lean
+++ b/Mathlib/Combinatorics/Hindman.lean
@@ -104,14 +104,18 @@ section Aliases
we provide match patterns that preserve the defeq correctly in their type. -/
variable {M} [Semigroup M] (a : Stream' M) (m : M) (h : FP a.tail m)
+
+set_option linter.defProp false in
/-- Constructor for `FP`. This is the preferred spelling over `FP.head'`. -/
@[to_additive (attr := match_pattern, nolint defLemma)
/-- Constructor for `FS`. This is the preferred spelling over `FS.head'`. -/]
abbrev FP.head : a.head ∈ FP a := FP.head' a
+set_option linter.defProp false in
/-- Constructor for `FP`. This is the preferred spelling over `FP.tail'`. -/
@[to_additive (attr := match_pattern, nolint defLemma)
/-- Constructor for `FS`. This is the preferred spelling over `FS.tail'`. -/]
abbrev FP.tail : m ∈ FP a := FP.tail' a m h
+set_option linter.defProp false in
/-- Constructor for `FP`. This is the preferred spelling over `FP.cons'`. -/
@[to_additive (attr := match_pattern, nolint defLemma)
/-- Constructor for `FS`. This is the preferred spelling over `FS.cons'`. -/]
diff --git a/Mathlib/Combinatorics/SimpleGraph/Regularity/Bound.lean b/Mathlib/Combinatorics/SimpleGraph/Regularity/Bound.lean
index 10020841234a53..308abad39b08c1 100644
--- a/Mathlib/Combinatorics/SimpleGraph/Regularity/Bound.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/Regularity/Bound.lean
@@ -239,10 +239,10 @@ open Lean.Meta Qq
/-- Extension for the `positivity` tactic: `SzemerediRegularity.initialBound` is always positive. -/
@[positivity SzemerediRegularity.initialBound _ _]
-meta def evalInitialBound : PositivityExt where eval {u α} _ pα? e := do
+meta def evalInitialBound : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℕ), ~q(SzemerediRegularity.initialBound $ε $l) =>
- let some _ := pα? | pure .none
assertInstancesCommute
pure (.positive q(SzemerediRegularity.initialBound_pos $ε $l))
| _, _, _ => throwError "not initialBound"
@@ -252,10 +252,10 @@ example (ε : ℝ) (l : ℕ) : 0 < SzemerediRegularity.initialBound ε l := by p
/-- Extension for the `positivity` tactic: `SzemerediRegularity.bound` is always positive. -/
@[positivity SzemerediRegularity.bound _ _]
-meta def evalBound : PositivityExt where eval {u α} _ pα? e := do
+meta def evalBound : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℕ), ~q(SzemerediRegularity.bound $ε $l) =>
- let some _ := pα? | pure .none
assertInstancesCommute
pure (.positive q(SzemerediRegularity.bound_pos $ε $l))
| _, _, _ => throwError "not bound"
diff --git a/Mathlib/Combinatorics/SimpleGraph/Triangle/Removal.lean b/Mathlib/Combinatorics/SimpleGraph/Triangle/Removal.lean
index 2e4f7bb17a6c94..3377d40e399d70 100644
--- a/Mathlib/Combinatorics/SimpleGraph/Triangle/Removal.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/Triangle/Removal.lean
@@ -176,10 +176,10 @@ if `ε` is.
This exploits the positivity of the junk value of `triangleRemovalBound ε` for `ε ≥ 1`. -/
@[positivity triangleRemovalBound _]
-meta def evalTriangleRemovalBound : PositivityExt where eval {u α} _zα pα? e := do
+meta def evalTriangleRemovalBound : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(triangleRemovalBound $ε) =>
- let some _ := pα? | pure .none
let .positive hε ← core q(inferInstance) (some q(inferInstance)) ε | failure
assertInstancesCommute
pure (.positive q(triangleRemovalBound_pos $hε))
diff --git a/Mathlib/Computability/Partrec.lean b/Mathlib/Computability/Partrec.lean
index f73cb614714702..0e6f71a84e1bc8 100644
--- a/Mathlib/Computability/Partrec.lean
+++ b/Mathlib/Computability/Partrec.lean
@@ -46,6 +46,7 @@ set_option backward.privateInPublic true in
private def lbp (m n : ℕ) : Prop :=
m = n + 1 ∧ ∀ k ≤ n, false ∈ p k
+set_option linter.defProp false in
set_option backward.privateInPublic true in
private def wf_lbp (H : ∃ n, true ∈ p n ∧ ∀ k < n, (p k).Dom) : WellFounded (lbp p) :=
⟨by
diff --git a/Mathlib/Data/ENNReal/Basic.lean b/Mathlib/Data/ENNReal/Basic.lean
index 3b71f90289ab75..8679db698faccf 100644
--- a/Mathlib/Data/ENNReal/Basic.lean
+++ b/Mathlib/Data/ENNReal/Basic.lean
@@ -741,8 +741,8 @@ open Lean Meta Qq
/-- Extension for the `positivity` tactic: `ENNReal.toReal`. -/
@[positivity ENNReal.toReal _]
-meta def evalENNRealtoReal : PositivityExt where eval {u α} _zα pα? e := do
- let some _ := pα? | pure .none
+meta def evalENNRealtoReal : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(ENNReal.toReal $a) =>
assertInstancesCommute
@@ -751,8 +751,8 @@ meta def evalENNRealtoReal : PositivityExt where eval {u α} _zα pα? e := do
/-- Extension for the `positivity` tactic: `ENNReal.ofNNReal`. -/
@[positivity ENNReal.ofNNReal _]
-meta def evalENNRealOfNNReal : PositivityExt where eval {u α} _zα pα? e := do
- let some _ := pα? | pure .none
+meta def evalENNRealOfNNReal : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ≥0∞), ~q(ENNReal.ofNNReal $a) =>
assertInstancesCommute
diff --git a/Mathlib/Data/ENNReal/Real.lean b/Mathlib/Data/ENNReal/Real.lean
index 6da5409e3594ec..2aeca7a1b8dabf 100644
--- a/Mathlib/Data/ENNReal/Real.lean
+++ b/Mathlib/Data/ENNReal/Real.lean
@@ -392,8 +392,8 @@ open Lean Meta Qq
/-- Extension for the `positivity` tactic: `ENNReal.ofReal`. -/
@[positivity ENNReal.ofReal _]
-meta def evalENNRealOfReal : PositivityExt where eval {u α} _zα pα? e := do
- let some _ := pα? | pure .none
+meta def evalENNRealOfReal : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ≥0∞), ~q(ENNReal.ofReal $a) =>
assertInstancesCommute
diff --git a/Mathlib/Data/EReal/Basic.lean b/Mathlib/Data/EReal/Basic.lean
index 9cbb2d70f53709..2e8a1d014a1c2c 100644
--- a/Mathlib/Data/EReal/Basic.lean
+++ b/Mathlib/Data/EReal/Basic.lean
@@ -850,8 +850,8 @@ open Lean Meta Qq Function
/-- Extension for the `positivity` tactic: cast from `ℝ` to `EReal`. -/
@[positivity Real.toEReal _]
-meta def evalRealToEReal : PositivityExt where eval {u α} _zα pα? e := do
- let some _ := pα? | pure .none
+meta def evalRealToEReal : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(EReal), ~q(Real.toEReal $a) =>
assertInstancesCommute
@@ -865,8 +865,8 @@ meta def evalRealToEReal : PositivityExt where eval {u α} _zα pα? e := do
/-- Extension for the `positivity` tactic: cast from `ℝ≥0∞` to `EReal`. -/
@[positivity ENNReal.toEReal _]
-meta def evalENNRealToEReal : PositivityExt where eval {u α} _zα pα? e := do
- let some _ := pα? | pure .none
+meta def evalENNRealToEReal : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(EReal), ~q(ENNReal.toEReal $a) =>
assertInstancesCommute
@@ -883,8 +883,8 @@ We prove that `EReal.toReal x` is nonnegative whenever `x` is nonnegative.
Since `EReal.toReal ⊤ = 0`, we cannot prove a stronger statement,
at least without relying on a tactic like `finiteness`. -/
@[positivity EReal.toReal _]
-meta def evalERealToReal : PositivityExt where eval {u α} _zα pα? e := do
- let some _ := pα? | pure .none
+meta def evalERealToReal : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(Real), ~q(EReal.toReal $a) =>
assertInstancesCommute
@@ -900,8 +900,8 @@ and it is nonnegative otherwise.
We cannot deduce any corollaries from `x ≠ 0`, since `EReal.toENNReal x = 0` for `x < 0`.
-/
@[positivity EReal.toENNReal _]
-meta def evalERealToENNReal : PositivityExt where eval {u α} _zα pα? e := do
- let some _ := pα? | pure .none
+meta def evalERealToENNReal : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ENNReal), ~q(EReal.toENNReal $a) =>
assertInstancesCommute
diff --git a/Mathlib/Data/EReal/Inv.lean b/Mathlib/Data/EReal/Inv.lean
index e61ad93ab0efa8..20f2c4bbc6a20c 100644
--- a/Mathlib/Data/EReal/Inv.lean
+++ b/Mathlib/Data/EReal/Inv.lean
@@ -551,8 +551,8 @@ open Lean Meta Qq Function
/-- Extension for the `positivity` tactic: inverse of an `EReal`. -/
@[positivity (_⁻¹ : EReal)]
-meta def evalERealInv : PositivityExt where eval {u α} zα pα? e := do
- let some pα := pα? | pure .none
+meta def evalERealInv : PositivityExt where eval {u α} zα pα? e :=
+ match pα? with | none => pure .none | some pα => do
match u, α, e with
| 0, ~q(EReal), ~q($a⁻¹) =>
assertInstancesCommute
@@ -563,8 +563,8 @@ meta def evalERealInv : PositivityExt where eval {u α} zα pα? e := do
/-- Extension for the `positivity` tactic: ratio of two `EReal`s. -/
@[positivity (_ / _ : EReal)]
-meta def evalERealDiv : PositivityExt where eval {u α} zα pα? e := do
- let some pα := pα? | pure .none
+meta def evalERealDiv : PositivityExt where eval {u α} zα pα? e :=
+ match pα? with | none => pure .none | some pα => do
match u, α, e with
| 0, ~q(EReal), ~q($a / $b) =>
assertInstancesCommute
diff --git a/Mathlib/Data/EReal/Operations.lean b/Mathlib/Data/EReal/Operations.lean
index 075d47a6994978..53bf367a596b7d 100644
--- a/Mathlib/Data/EReal/Operations.lean
+++ b/Mathlib/Data/EReal/Operations.lean
@@ -821,8 +821,8 @@ open Lean Meta Qq Function
/-- Extension for the `positivity` tactic: sum of two `EReal`s. -/
@[positivity (_ + _ : EReal)]
-meta def evalERealAdd : PositivityExt where eval {u α} zα pα? e := do
- let some pα := pα? | pure .none
+meta def evalERealAdd : PositivityExt where eval {u α} zα pα? e :=
+ match pα? with | none => pure .none | some pα => do
match u, α, e with
| 0, ~q(EReal), ~q($a + $b) =>
assertInstancesCommute
@@ -841,8 +841,8 @@ meta def evalERealAdd : PositivityExt where eval {u α} zα pα? e := do
/-- Extension for the `positivity` tactic: product of two `EReal`s. -/
@[positivity (_ * _ : EReal)]
-meta def evalERealMul : PositivityExt where eval {u α} zα pα? e := do
- let some pα := pα? | pure .none
+meta def evalERealMul : PositivityExt where eval {u α} zα pα? e :=
+ match pα? with | none => pure .none | some pα => do
match u, α, e with
| 0, ~q(EReal), ~q($a * $b) =>
assertInstancesCommute
diff --git a/Mathlib/Data/Fin/Tuple/Reflection.lean b/Mathlib/Data/Fin/Tuple/Reflection.lean
index 0b5d508e05e0b9..e577f7abd086a9 100644
--- a/Mathlib/Data/Fin/Tuple/Reflection.lean
+++ b/Mathlib/Data/Fin/Tuple/Reflection.lean
@@ -175,10 +175,10 @@ open Lean Meta Qq
that shows it is equal to `∏ i, f i`. -/
meta def mkProdEqQ {u : Level} {α : Q(Type u)}
(inst : Q(CommMonoid $α)) (n : ℕ) (f : Q(Fin $n → $α)) :
- MetaM <| (val : Q($α)) × Q(∏ i, $f i = $val) := do
+ MetaM <| (val : Q($α)) × Q(∏ i, $f i = $val) :=
match n with
- | 0 => return ⟨q((1 : $α)), q(Fin.prod_univ_zero $f)⟩
- | m + 1 =>
+ | 0 => do return ⟨q((1 : $α)), q(Fin.prod_univ_zero $f)⟩
+ | m + 1 => do
let nezero : Q(NeZero ($m + 1)) := q(⟨Nat.succ_ne_zero _⟩)
let val ← makeRHS (m + 1) f nezero (m + 1)
let _ : $val =Q FinVec.prod $f := ⟨⟩
@@ -198,10 +198,10 @@ where
that shows it is equal to `∑ i, f i`. -/
meta def mkSumEqQ {u : Level} {α : Q(Type u)}
(inst : Q(AddCommMonoid $α)) (n : ℕ) (f : Q(Fin $n → $α)) :
- MetaM <| (val : Q($α)) × Q(∑ i, $f i = $val) := do
+ MetaM <| (val : Q($α)) × Q(∑ i, $f i = $val) :=
match n with
| 0 => return ⟨q((0 : $α)), q(Fin.sum_univ_zero $f)⟩
- | m + 1 =>
+ | m + 1 => do
let nezero : Q(NeZero ($m + 1)) := q(⟨Nat.succ_ne_zero _⟩)
let val ← makeRHS (m + 1) f nezero (m + 1)
let _ : $val =Q FinVec.sum $f := ⟨⟩
@@ -228,7 +228,7 @@ open Qq Lean FinVec
simproc_decl prod_univ_ofNat (∏ _ : Fin _, _) := .ofQ fun u _ e => do
match u, e with
| .succ _, ~q(@Finset.prod (Fin $n) _ $inst (@Finset.univ _ $instF) $f) => do
- match (generalizing := false) n.nat? with
+ match n.nat? with
| none =>
return .continue
| some nVal =>
diff --git a/Mathlib/Data/NNReal/Defs.lean b/Mathlib/Data/NNReal/Defs.lean
index d1a46797c16806..1e2243a3c8284b 100644
--- a/Mathlib/Data/NNReal/Defs.lean
+++ b/Mathlib/Data/NNReal/Defs.lean
@@ -1006,8 +1006,8 @@ alias ⟨_, nnreal_coe_pos⟩ := coe_pos
/-- Extension for the `positivity` tactic: cast from `ℝ≥0` to `ℝ`. -/
@[positivity NNReal.toReal _]
-meta def evalNNRealtoReal : PositivityExt where eval {u α} _zα pα? e := do
- let some _ := pα? | pure .none
+meta def evalNNRealtoReal : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(NNReal.toReal $a) =>
assertInstancesCommute
@@ -1019,8 +1019,8 @@ meta def evalNNRealtoReal : PositivityExt where eval {u α} _zα pα? e := do
/-- Extension for the `positivity` tactic: `Real.toNNReal` -/
@[positivity Real.toNNReal _]
-meta def evalRealToNNReal : PositivityExt where eval {u α} _zα pα? e := do
- let some _ := pα? | pure .none
+meta def evalRealToNNReal : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ≥0), ~q(Real.toNNReal $a) =>
assertInstancesCommute
@@ -1033,8 +1033,8 @@ alias ⟨_, nnabs_pos_of_pos⟩ := Real.nnabs_pos
/-- Extension for the `positivity` tactic: `Real.nnabs` -/
@[positivity Real.nnabs _]
-meta def evalRealNNAbs : PositivityExt where eval {u α} _zα pα? e := do
- let some _ := pα? | pure .none
+meta def evalRealNNAbs : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ≥0), ~q(Real.nnabs $a) =>
assertInstancesCommute
diff --git a/Mathlib/Data/Nat/Factorial/DoubleFactorial.lean b/Mathlib/Data/Nat/Factorial/DoubleFactorial.lean
index 0d27087ca77713..7bb9f92d299545 100644
--- a/Mathlib/Data/Nat/Factorial/DoubleFactorial.lean
+++ b/Mathlib/Data/Nat/Factorial/DoubleFactorial.lean
@@ -87,8 +87,8 @@ open Lean Meta Qq
/-- Extension for `Nat.doubleFactorial`. -/
@[positivity Nat.doubleFactorial _]
-meta def evalDoubleFactorial : PositivityExt where eval {u α} _ pα? e := do
- let some _ := pα? | pure .none
+meta def evalDoubleFactorial : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℕ), ~q(Nat.doubleFactorial $n) =>
assumeInstancesCommute
diff --git a/Mathlib/Data/Nat/Find.lean b/Mathlib/Data/Nat/Find.lean
index 250e74085224df..1a61e465b21bd9 100644
--- a/Mathlib/Data/Nat/Find.lean
+++ b/Mathlib/Data/Nat/Find.lean
@@ -29,6 +29,7 @@ private def lbp (m n : ℕ) : Prop :=
variable [DecidablePred p] (H : ∃ n, p n)
+set_option linter.defProp false in
set_option backward.privateInPublic true in
private def wf_lbp : WellFounded (@lbp p) :=
⟨let ⟨n, pn⟩ := H
diff --git a/Mathlib/Data/Nat/Sqrt.lean b/Mathlib/Data/Nat/Sqrt.lean
index a2f2013abde8af..6e0e55b225761c 100644
--- a/Mathlib/Data/Nat/Sqrt.lean
+++ b/Mathlib/Data/Nat/Sqrt.lean
@@ -6,7 +6,6 @@ Authors: Floris van Doorn, Leonardo de Moura, Jeremy Avigad, Mario Carneiro
module
public import Mathlib.Data.Nat.Basic
-public import Batteries.Data.Nat.Basic
/-!
# Properties of the natural number square root function.
@@ -28,92 +27,8 @@ See [Wikipedia, *Methods of computing square roots*]
(https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_numeral_system_(base_2)).
-/
-private lemma iter_fp_bound (n k : ℕ) :
- let iter_next (n guess : ℕ) := (guess + n / guess) / 2;
- sqrt.iter n k ≤ iter_next n (sqrt.iter n k) := by
- intro iter_next
- unfold sqrt.iter
- if h : (k + n / k) / 2 < k then
- simpa [if_pos h] using iter_fp_bound _ _
- else
- grind
-
-private lemma AM_GM : {a b : ℕ} → (4 * a * b ≤ (a + b) * (a + b))
- | 0, _ => by rw [Nat.mul_zero, Nat.zero_mul]; exact zero_le _
- | _, 0 => by rw [Nat.mul_zero]; exact zero_le _
- | a + 1, b + 1 => by
- simpa only [Nat.mul_add, Nat.add_mul, show (4 : ℕ) = 1 + 1 + 1 + 1 from rfl, Nat.one_mul,
- Nat.mul_one, Nat.add_assoc, Nat.add_left_comm, Nat.add_le_add_iff_left]
- using Nat.add_le_add_right (@AM_GM a b) 4
-
--- These two lemmas seem like they belong to `Batteries.Data.Nat.Basic`.
-
-lemma sqrt.iter_sq_le (n guess : ℕ) : sqrt.iter n guess * sqrt.iter n guess ≤ n := by
- unfold sqrt.iter
- let next := (guess + n / guess) / 2
- if h : next < guess then
- simpa only [next, dif_pos h] using sqrt.iter_sq_le n next
- else
- apply Nat.mul_le_of_le_div
- grind
-
-lemma sqrt.lt_iter_succ_sq (n guess : ℕ) (hn : n < (guess + 1) * (guess + 1)) :
- n < (sqrt.iter n guess + 1) * (sqrt.iter n guess + 1) := by
- unfold sqrt.iter
- -- m was `next`
- let m := (guess + n / guess) / 2
- dsimp
- split_ifs with h
- · suffices n < (m + 1) * (m + 1) by
- simpa only [dif_pos h] using sqrt.lt_iter_succ_sq n m this
- refine Nat.lt_of_mul_lt_mul_left ?_ (a := 4 * (guess * guess))
- apply Nat.lt_of_le_of_lt AM_GM
- rw [show (4 : ℕ) = 2 * 2 from rfl]
- rw [Nat.mul_mul_mul_comm 2, Nat.mul_mul_mul_comm (2 * guess)]
- refine Nat.mul_self_lt_mul_self (?_ : _ < _ * ((_ / 2) + 1))
- rw [← add_div_right _ (by decide), Nat.mul_comm 2, Nat.mul_assoc,
- show guess + n / guess + 2 = (guess + n / guess + 1) + 1 from rfl]
- have aux_lemma {a : ℕ} : a ≤ 2 * ((a + 1) / 2) := by lia
- refine lt_of_lt_of_le ?_ (Nat.mul_le_mul_left _ aux_lemma)
- rw [Nat.add_assoc, Nat.mul_add]
- exact Nat.add_lt_add_left (lt_mul_div_succ _ (lt_of_le_of_lt (Nat.zero_le m) h)) _
- · exact hn
-
-private def IsSqrt (n q : ℕ) : Prop :=
- q * q ≤ n ∧ n < (q + 1) * (q + 1)
-
-/-
-Sketch of proof:
-Up to rounding, in terms of the definition of `sqrt.iter`,
-
-* By AM-GM inequality, `next² ≥ n` giving one of the bounds.
-* When we terminated, we have `guess ≥ next` from which we deduce the other bound `n ≥ next²`.
-
-To turn this into a lean proof we need to manipulate, use properties of natural number division etc.
--/
-private lemma sqrt_isSqrt (n : ℕ) : IsSqrt n (sqrt n) := by
- match n with
- | 0 => simp [IsSqrt, sqrt]
- | 1 => simp [IsSqrt, sqrt]
- | n + 2 =>
- have h : ¬ (n + 2) ≤ 1 := by simp
- simp only [IsSqrt, sqrt, h, ite_false]
- refine ⟨sqrt.iter_sq_le _ _, sqrt.lt_iter_succ_sq _ _ ?_⟩
- simp only [Nat.mul_add, Nat.add_mul, Nat.one_mul, Nat.mul_one, ← Nat.add_assoc]
- rw [Nat.lt_add_one_iff, Nat.add_assoc, ← Nat.mul_two]
- refine le_trans (Nat.le_of_eq (div_add_mod' (n + 2) 2).symm) ?_
- rw [show (n + 2) / 2 * 2 + (n + 2) % 2 = n + 2 by grind]
- simp only [shiftLeft_eq, Nat.one_mul]
- refine Nat.le_of_lt (Nat.le_trans lt_log2_self (le_add_right_of_le ?_))
- rw [← Nat.pow_add]
- grind
-
-lemma sqrt_le (n : ℕ) : sqrt n * sqrt n ≤ n := (sqrt_isSqrt n).left
-
lemma sqrt_le' (n : ℕ) : sqrt n ^ 2 ≤ n := by simpa [Nat.pow_two] using sqrt_le n
-lemma lt_succ_sqrt (n : ℕ) : n < succ (sqrt n) * succ (sqrt n) := (sqrt_isSqrt n).right
-
lemma lt_succ_sqrt' (n : ℕ) : n < succ (sqrt n) ^ 2 := by simpa [Nat.pow_two] using lt_succ_sqrt n
lemma sqrt_le_add (n : ℕ) : n ≤ sqrt n * sqrt n + sqrt n + sqrt n := by
@@ -134,15 +49,8 @@ lemma sqrt_le_self (n : ℕ) : sqrt n ≤ n := le_trans (le_mul_self _) (sqrt_le
@[gcongr]
lemma sqrt_le_sqrt (h : m ≤ n) : sqrt m ≤ sqrt n := le_sqrt.2 (le_trans (sqrt_le _) h)
-@[simp, grind =] lemma sqrt_zero : sqrt 0 = 0 := rfl
-
-@[simp, grind =] lemma sqrt_one : sqrt 1 = 1 := rfl
-
-lemma sqrt_eq_zero : sqrt n = 0 ↔ n = 0 :=
- ⟨fun h ↦ have := @sqrt_lt n 1; by grind, by grind⟩
-
lemma eq_sqrt : a = sqrt n ↔ a * a ≤ n ∧ n < (a + 1) * (a + 1) :=
- ⟨fun e ↦ e.symm ▸ sqrt_isSqrt n,
+ ⟨fun e ↦ e.symm ▸ ⟨sqrt_le n, lt_succ_sqrt n⟩,
fun ⟨h₁, h₂⟩ ↦ le_antisymm (le_sqrt.2 h₁) (le_of_lt_succ <| sqrt_lt.2 h₂)⟩
lemma eq_sqrt' : a = sqrt n ↔ a ^ 2 ≤ n ∧ n < (a + 1) ^ 2 := by
@@ -178,8 +86,18 @@ lemma sqrt_succ_le_succ_sqrt (n : ℕ) : sqrt n.succ ≤ n.sqrt.succ :=
@[simp]
lemma log2_two : (2 : ℕ).log2 = 1 := by simp [log2_def]
+@[simp, grind =] lemma sqrt_zero : sqrt 0 = 0 :=
+ eq_comm.1 (by simp [eq_sqrt])
+
+@[simp, grind =] lemma sqrt_one : sqrt 1 = 1 :=
+ eq_comm.1 (by simp [eq_sqrt])
+
+lemma sqrt_eq_zero : sqrt n = 0 ↔ n = 0 :=
+ ⟨fun h ↦ have := @sqrt_lt n 1; by grind, by grind⟩
+
@[simp]
-lemma sqrt_two : sqrt 2 = 1 := by simp [sqrt, sqrt.iter]
+lemma sqrt_two : sqrt 2 = 1 :=
+ eq_comm.1 (by simp [eq_sqrt])
lemma add_one_sqrt_le_of_ne_zero {n : ℕ} (hn : n ≠ 0) : (n + 1).sqrt ≤ n :=
le_induction (by simp) (fun n _ ih ↦ le_trans n.succ.sqrt_succ_le_succ_sqrt (succ_le_succ ih)) n
diff --git a/Mathlib/Data/Nat/Totient.lean b/Mathlib/Data/Nat/Totient.lean
index 82b080ac815907..c31f22ad920296 100644
--- a/Mathlib/Data/Nat/Totient.lean
+++ b/Mathlib/Data/Nat/Totient.lean
@@ -443,7 +443,8 @@ open Lean Meta Qq
/-- Extension for `Nat.totient`. -/
@[positivity Nat.totient _]
-meta def evalNatTotient : PositivityExt where eval {u α} z p e := do
+meta def evalNatTotient : PositivityExt where eval {u α} z p e :=
+ match p with | none => pure .none | some p => do
match u, α, e with
| 0, ~q(ℕ), ~q(Nat.totient $n) =>
match ← core z p n with
diff --git a/Mathlib/Data/PFunctor/Univariate/Basic.lean b/Mathlib/Data/PFunctor/Univariate/Basic.lean
index 536ed9683b7150..69a65f27239bf0 100644
--- a/Mathlib/Data/PFunctor/Univariate/Basic.lean
+++ b/Mathlib/Data/PFunctor/Univariate/Basic.lean
@@ -18,6 +18,7 @@ This file defines polynomial functors and the W-type construction as a polynomia
universe u v uA uB uA₁ uB₁ uA₂ uB₂ v₁ v₂ v₃
+set_option linter.checkUnivs false in
/-- A polynomial functor `P` is given by a type `A` and a family `B` of types over `A`. `P` maps
any type `α` to a new type `P α`, which is defined as the sigma type `Σ x, P.B x → α`.
diff --git a/Mathlib/Data/Rat/Cast/Order.lean b/Mathlib/Data/Rat/Cast/Order.lean
index 935ce8e22c6bcf..8fecc133d64446 100644
--- a/Mathlib/Data/Rat/Cast/Order.lean
+++ b/Mathlib/Data/Rat/Cast/Order.lean
@@ -263,15 +263,21 @@ open Lean Meta Qq Function
meta def evalRatCast : PositivityExt where eval {u α} _zα pα? e := do
let ~q(@Rat.cast _ (_) ($a : ℚ)) := e | throwError "not Rat.cast"
match ← core q(inferInstance) (some q(inferInstance)) a with
- | .positive pa =>
- let some _ := pα? | pure .none
- let _oα ← synthInstanceQ q(Field $α)
- let _oα ← synthInstanceQ q(LinearOrder $α)
- let _oα ← synthInstanceQ q(IsStrictOrderedRing $α)
- assumeInstancesCommute
- return .positive q((Rat.cast_pos (K := $α)).mpr $pa)
- | .nonnegative pa =>
- let some _ := pα? | pure .none
+ | .positive pa => id <|
+ match pα? with
+ | none => do
+ let _oα ← synthInstanceQ q(DivisionRing $α)
+ let _cα ← synthInstanceQ q(CharZero $α)
+ assumeInstancesCommute
+ return .nonzero q((Rat.cast_ne_zero (α := $α)).mpr ($pa).ne')
+ | some _ => do
+ let _oα ← synthInstanceQ q(Field $α)
+ let _oα ← synthInstanceQ q(LinearOrder $α)
+ let _oα ← synthInstanceQ q(IsStrictOrderedRing $α)
+ assumeInstancesCommute
+ return .positive q((Rat.cast_pos (K := $α)).mpr $pa)
+ | .nonnegative pa => id <|
+ match pα? with | none => pure .none | some _ => do
let _oα ← synthInstanceQ q(Field $α)
let _oα ← synthInstanceQ q(LinearOrder $α)
let _oα ← synthInstanceQ q(IsStrictOrderedRing $α)
@@ -286,18 +292,17 @@ meta def evalRatCast : PositivityExt where eval {u α} _zα pα? e := do
/-- Extension for NNRat.cast. -/
@[positivity NNRat.cast _]
-meta def evalNNRatCast : PositivityExt where eval {u α} _zα pα? e := do
+meta def evalNNRatCast : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
let ~q(@NNRat.cast _ (_) ($a : ℚ≥0)) := e | throwError "not NNRat.cast"
match ← core q(inferInstance) (some q(inferInstance)) a with
| .positive pa =>
- let some _ := pα? | pure .none
let _oα ← synthInstanceQ q(Semifield $α)
let _oα ← synthInstanceQ q(LinearOrder $α)
let _oα ← synthInstanceQ q(IsStrictOrderedRing $α)
assumeInstancesCommute
return .positive q((NNRat.cast_pos (K := $α)).mpr $pa)
| _ =>
- let some _ := pα? | pure .none
let _oα ← synthInstanceQ q(Semifield $α)
let _oα ← synthInstanceQ q(LinearOrder $α)
let _oα ← synthInstanceQ q(IsStrictOrderedRing $α)
diff --git a/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean b/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean
index dc2cecbf723157..f76a7cbae7f149 100644
--- a/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean
+++ b/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean
@@ -667,6 +667,7 @@ theorem isScalarTower_mulSemiringActionQuotient [MulSemiringAction G B] [SMulDis
⟨fun g q b ↦ Quotient.inductionOn' q fun h ↦ by
simp [mul_smul, mulSemiringActionQuotient_smul_def]⟩
+set_option linter.defProp false in
/-- If `G` acts on `C` commuting with `A`, then the action of `G ⧸ N` on `B` commutes with `A`. -/
@[implicit_reducible]
def smulCommClassQuotient [N.Normal] [Algebra A B] [IsScalarTower A B C] [SMulCommClass G A C]
diff --git a/Mathlib/Geometry/Euclidean/Altitude.lean b/Mathlib/Geometry/Euclidean/Altitude.lean
index 1d100ab3c1c959..cf2c704ce1e916 100644
--- a/Mathlib/Geometry/Euclidean/Altitude.lean
+++ b/Mathlib/Geometry/Euclidean/Altitude.lean
@@ -260,10 +260,10 @@ lemma height_pos {n : ℕ} [NeZero n] (s : Simplex ℝ P n) (i : Fin (n + 1)) :
open Qq Mathlib.Meta.Positivity in
/-- Extension for the `positivity` tactic: the height of a simplex is always positive. -/
@[positivity height _ _]
-meta def evalHeight : PositivityExt where eval {u α} _ pα? e := do
+meta def evalHeight : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(@height $V $P $i1 $i2 $i3 $i4 $n $hn $s $i) =>
- let some _ := pα? | pure .none
assertInstancesCommute
return .positive q(height_pos $s $i)
| _, _, _ => throwError "not Simplex.height"
diff --git a/Mathlib/Geometry/RingedSpace/Basic.lean b/Mathlib/Geometry/RingedSpace/Basic.lean
index ce4c5879fe072e..4a615fbba24138 100644
--- a/Mathlib/Geometry/RingedSpace/Basic.lean
+++ b/Mathlib/Geometry/RingedSpace/Basic.lean
@@ -38,6 +38,7 @@ open TopCat.Presheaf
namespace AlgebraicGeometry
+set_option linter.checkUnivs false in
/-- The type of Ringed spaces, as an abbreviation for `SheafedSpace CommRingCat`. -/
@[nolint checkUnivs] -- The universes appear together in the type, but separately in the value.
abbrev RingedSpace : Type max (u + 1) (v + 1) :=
diff --git a/Mathlib/Init.lean b/Mathlib/Init.lean
index 81b05422e07d29..e49829ccc5766f 100644
--- a/Mathlib/Init.lean
+++ b/Mathlib/Init.lean
@@ -132,7 +132,7 @@ run_cmd liftTermElabM do
let DefinedInScripts : Array Name :=
#[`linter.checkInitImports, `linter.allScriptsDocumented]
let env ← getEnv
- let ls := linterSetsExt.getEntries env
+ let ls := (linterSetsExt.getState env).localEntries
let some (_, mlLinters) := ls.find? (·.1 == ``linter.mathlibStandardSet) |
throwError m!"'linter.mathlibStandardSet' is not defined."
let some (_, nrLinters) := ls.find? (·.1 == ``linter.nightlyRegressionSet) |
diff --git a/Mathlib/Lean/MessageData/ForExprs.lean b/Mathlib/Lean/MessageData/ForExprs.lean
index a67e574dbe10fc..93d155149f7d7a 100644
--- a/Mathlib/Lean/MessageData/ForExprs.lean
+++ b/Mathlib/Lean/MessageData/ForExprs.lean
@@ -68,6 +68,7 @@ where
| .ofFormatWithInfos fwi => do
let some ppCtx := ctx?.map (mkPPContext nctx) | return .yield s
goFmt ppCtx fwi.infos s fwi.fmt
+ | .ofOriginatingSyntax _ m => go nctx ctx? s m
/-- Iterate over the tags of a `Format` using `f`. -/
goFmt (ppCtx : PPContext) (infos) (s : σ) : Format → m (ForInStep σ)
| .tag n fmt => do
diff --git a/Mathlib/Lean/Meta/RefinedDiscrTree/Encode.lean b/Mathlib/Lean/Meta/RefinedDiscrTree/Encode.lean
index 6baef24ebebd58..4650ebd053cd21 100644
--- a/Mathlib/Lean/Meta/RefinedDiscrTree/Encode.lean
+++ b/Mathlib/Lean/Meta/RefinedDiscrTree/Encode.lean
@@ -134,8 +134,8 @@ private def etaPossibilities (e : Expr) (lambdas : List FVarId) (root : Bool)
if isStarWithArg (.fvar fvarId) a && !f.getAppFn.isMVar then
etaPossibilities f lambdas root entry
else
- return []
- | _, _ => return [])
+ pure []
+ | _, _ => pure [])
where
/-- Check whether the expression is represented by `Key.star` and has `arg` as an argument. -/
isStarWithArg (arg : Expr) : Expr → Bool
diff --git a/Mathlib/MeasureTheory/Covering/Besicovitch.lean b/Mathlib/MeasureTheory/Covering/Besicovitch.lean
index c369663b90f5d7..f91ca532768c13 100644
--- a/Mathlib/MeasureTheory/Covering/Besicovitch.lean
+++ b/Mathlib/MeasureTheory/Covering/Besicovitch.lean
@@ -138,10 +138,10 @@ open Lean Meta Qq
/-- Extension for the `positivity` tactic: `Besicovitch.SatelliteConfig.r`. -/
@[positivity Besicovitch.SatelliteConfig.r _ _]
-meta def evalBesicovitchSatelliteConfigR : PositivityExt where eval {u α} _zα pα? e := do
+meta def evalBesicovitchSatelliteConfigR : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(@Besicovitch.SatelliteConfig.r $β $inst $N $τ $self $i) =>
- let some _ := pα? | pure .none
assertInstancesCommute
return .positive q(Besicovitch.SatelliteConfig.rpos $self $i)
| _, _, _ => throwError "not Besicovitch.SatelliteConfig.r"
diff --git a/Mathlib/MeasureTheory/Integral/Bochner/Basic.lean b/Mathlib/MeasureTheory/Integral/Bochner/Basic.lean
index 577389d0ba23ef..87f7e189d926d1 100644
--- a/Mathlib/MeasureTheory/Integral/Bochner/Basic.lean
+++ b/Mathlib/MeasureTheory/Integral/Bochner/Basic.lean
@@ -1367,8 +1367,8 @@ attribute [local instance] monadLiftOptionMetaM in
This extension only proves non-negativity, strict positivity is more delicate for integration and
requires more assumptions. -/
@[positivity MeasureTheory.integral _ _]
-meta def evalIntegral : PositivityExt where eval {u α} zα pα? e := do
- let some pα := pα? | pure .none
+meta def evalIntegral : PositivityExt where eval {u α} zα pα? e :=
+ match pα? with | none => pure .none | some pα => do
match u, α, e with
| 0, ~q(ℝ), ~q(@MeasureTheory.integral $i ℝ _ $inst2 _ _ $f) =>
let i : Q($i) ← mkFreshExprMVarQ q($i) .syntheticOpaque
diff --git a/Mathlib/MeasureTheory/Measure/Real.lean b/Mathlib/MeasureTheory/Measure/Real.lean
index 0d15c02f097b66..ec1f88b8aa8fe4 100644
--- a/Mathlib/MeasureTheory/Measure/Real.lean
+++ b/Mathlib/MeasureTheory/Measure/Real.lean
@@ -493,9 +493,9 @@ open Lean Meta Qq Function
/-- Extension for the `positivity` tactic: applications of `μ.real` are nonnegative. -/
@[positivity MeasureTheory.Measure.real _ _]
-meta def evalMeasureReal : PositivityExt where eval {_ _} _zα pα? e := do
+meta def evalMeasureReal : PositivityExt where eval {_ _} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
let .app (.app _ a) b ← whnfR e | throwError "not measureReal"
- let some _ := pα? | pure .none
let p ← mkAppOptM ``MeasureTheory.measureReal_nonneg #[none, none, a, b]
pure (.nonnegative p)
diff --git a/Mathlib/ModelTheory/Basic.lean b/Mathlib/ModelTheory/Basic.lean
index 461a3c31318c83..bab71eb42a7dd3 100644
--- a/Mathlib/ModelTheory/Basic.lean
+++ b/Mathlib/ModelTheory/Basic.lean
@@ -52,6 +52,7 @@ namespace FirstOrder
-- intended to be used with explicit universe parameters
+set_option linter.checkUnivs false in
/-- A first-order language consists of a type of functions of every natural-number arity and a
type of relations of every natural-number arity. -/
@[nolint checkUnivs]
diff --git a/Mathlib/NumberTheory/ArithmeticFunction/Misc.lean b/Mathlib/NumberTheory/ArithmeticFunction/Misc.lean
index 0978dd9917eae5..11f9445eac20a4 100644
--- a/Mathlib/NumberTheory/ArithmeticFunction/Misc.lean
+++ b/Mathlib/NumberTheory/ArithmeticFunction/Misc.lean
@@ -456,8 +456,8 @@ open Lean Meta Qq
/-- Extension for `ArithmeticFunction.sigma`. -/
@[positivity ArithmeticFunction.sigma _ _]
-meta def evalArithmeticFunctionSigma : PositivityExt where eval {u α} z p? e := do
- let some p := p? | throwError "no PartialOrder instance"
+meta def evalArithmeticFunctionSigma : PositivityExt where eval {u α} z p? e :=
+ match p? with | none => throwError "no PartialOrder instance" | some p => do
match u, α, e with
| 0, ~q(ℕ), ~q(ArithmeticFunction.sigma $k $n) =>
assumeInstancesCommute
diff --git a/Mathlib/NumberTheory/ArithmeticFunction/Zeta.lean b/Mathlib/NumberTheory/ArithmeticFunction/Zeta.lean
index f0595aca62f502..3c7e3486d06ac2 100644
--- a/Mathlib/NumberTheory/ArithmeticFunction/Zeta.lean
+++ b/Mathlib/NumberTheory/ArithmeticFunction/Zeta.lean
@@ -222,8 +222,8 @@ open Lean Meta Qq
/-- Extension for `ArithmeticFunction.zeta`. -/
@[positivity ArithmeticFunction.zeta _]
-meta def evalArithmeticFunctionZeta : PositivityExt where eval {u α} z p? e := do
- let some p := p? | throwError "no PartialOrder instance"
+meta def evalArithmeticFunctionZeta : PositivityExt where eval {u α} z p? e :=
+ match p? with | none => throwError "no PartialOrder instance" | some p => do
match u, α, e with
| 0, ~q(ℕ), ~q(ArithmeticFunction.zeta $n) =>
assumeInstancesCommute
diff --git a/Mathlib/NumberTheory/Height/Basic.lean b/Mathlib/NumberTheory/Height/Basic.lean
index 7118e99a9d8893..e5bdeb555ef6a8 100644
--- a/Mathlib/NumberTheory/Height/Basic.lean
+++ b/Mathlib/NumberTheory/Height/Basic.lean
@@ -189,20 +189,20 @@ open Lean.Meta Qq Height
/-- Extension for the `positivity` tactic: `Height.mulHeight₁` is always positive. -/
@[positivity Height.mulHeight₁ _]
-meta def evalMulHeight₁ : PositivityExt where eval {u α} _ pα? e := do
+meta def evalMulHeight₁ : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(@mulHeight₁ $K $KF $KA $a) =>
- let some _ := pα? | pure .none
assertInstancesCommute
pure (.positive q(mulHeight₁_pos $a))
| _, _, _ => throwError "not Height.mulHeight₁"
/-- Extension for the `positivity` tactic: `Height.logHeight₁` is always nonnegative. -/
@[positivity Height.logHeight₁ _]
-meta def evalLogHeight₁ : PositivityExt where eval {u α} _ pα? e := do
+meta def evalLogHeight₁ : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(@logHeight₁ $K $KF $KA $a) =>
- let some _ := pα? | pure .none
assertInstancesCommute
pure (.nonnegative q(zero_le_logHeight₁ $a))
| _, _, _ => throwError "not Height.logHeight₁"
@@ -510,10 +510,10 @@ open Lean.Meta Qq Height
/-- Extension for the `positivity` tactic: `Height.mulHeight` is always positive. -/
@[positivity Height.mulHeight _]
-meta def evalMulHeight : PositivityExt where eval {u α} _ pα? e := do
+meta def evalMulHeight : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(@mulHeight $K $KF $KA $ι $a) =>
- let some _ := pα? | pure .none
-- Check whether there is a `Finite` instance for `$ι` around.
match ← trySynthInstanceQ q(Finite $ι) with
| .some _instFinite =>
@@ -524,10 +524,10 @@ meta def evalMulHeight : PositivityExt where eval {u α} _ pα? e := do
/-- Extension for the `positivity` tactic: `Height.logHeight` is always nonnegative. -/
@[positivity Height.logHeight _]
-meta def evalLogHeight : PositivityExt where eval {u α} _ pα? e := do
+meta def evalLogHeight : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(@logHeight $K $KF $KA $ι $a) =>
- let some _ := pα? | pure .none
-- Check whether there is a `Finite` instance for `$ι` around.
match ← trySynthInstanceQ q(Finite $ι) with
| .some _instFinite =>
diff --git a/Mathlib/NumberTheory/Height/NumberField.lean b/Mathlib/NumberTheory/Height/NumberField.lean
index 0e3471a25c812c..3c154daa059b4d 100644
--- a/Mathlib/NumberTheory/Height/NumberField.lean
+++ b/Mathlib/NumberTheory/Height/NumberField.lean
@@ -194,10 +194,10 @@ open Lean.Meta Qq
/-- Extension for the `positivity` tactic: `Height.totalWeight` is positive for number fields. -/
@[positivity Height.totalWeight _]
-meta def evalHeightTotalWeight : PositivityExt where eval {u α} _ pα? e := do
+meta def evalHeightTotalWeight : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℕ), ~q(@Height.totalWeight $K $KF $KA) =>
- let some _ := pα? | pure .none
-- Check whether there is a `NumberField` instance for `$K` around.
match ← trySynthInstanceQ q(NumberField $K) with
| .some _inst =>
diff --git a/Mathlib/NumberTheory/Height/Projectivization.lean b/Mathlib/NumberTheory/Height/Projectivization.lean
index 0caa26f4437275..6d7c19f22bc3e6 100644
--- a/Mathlib/NumberTheory/Height/Projectivization.lean
+++ b/Mathlib/NumberTheory/Height/Projectivization.lean
@@ -83,20 +83,20 @@ open Lean.Meta Qq Projectivization
/-- Extension for the `positivity` tactic: `Projectivization.mulHeight` is always positive. -/
@[positivity Projectivization.mulHeight _]
-meta def evalProjMulHeight : PositivityExt where eval {u α} _ pα? e := do
+meta def evalProjMulHeight : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(@mulHeight $K $KF $KA $ι $ιF $a) =>
- let some _ := pα? | pure .none
assertInstancesCommute
pure (.positive q(mulHeight_pos $a))
| _, _, _ => throwError "not Projectivization.mulHeight"
/-- Extension for the `positivity` tactic: `Projectivization.logHeight` is always nonnegative. -/
@[positivity Projectivization.logHeight _]
-meta def evalProjLogHeight : PositivityExt where eval {u α} _ pα? e := do
+meta def evalProjLogHeight : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(@logHeight $K $KF $KA $ι $ιF $a) =>
- let some _ := pα? | pure .none
assertInstancesCommute
pure (.nonnegative q(logHeight_nonneg $a))
| _, _, _ => throwError "not Projectivization.logHeight"
diff --git a/Mathlib/NumberTheory/LucasLehmer.lean b/Mathlib/NumberTheory/LucasLehmer.lean
index cfac67c69e8e3b..137d17b82b52ba 100644
--- a/Mathlib/NumberTheory/LucasLehmer.lean
+++ b/Mathlib/NumberTheory/LucasLehmer.lean
@@ -78,8 +78,8 @@ alias ⟨_, mersenne_pos_of_pos⟩ := mersenne_pos
/-- Extension for the `positivity` tactic: `mersenne`. -/
@[positivity mersenne _]
-meta def evalMersenne : PositivityExt where eval {u α} _zα pα? e := do
- let some _ := pα? | pure .none
+meta def evalMersenne : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℕ), ~q(mersenne $a) =>
assertInstancesCommute
diff --git a/Mathlib/NumberTheory/Padics/Hensel.lean b/Mathlib/NumberTheory/Padics/Hensel.lean
index 98571de58969ce..9031f81aa319ec 100644
--- a/Mathlib/NumberTheory/Padics/Hensel.lean
+++ b/Mathlib/NumberTheory/Padics/Hensel.lean
@@ -227,6 +227,7 @@ private def calc_eval_z' {z z' z1 : ℤ_[p]} (hz' : z' = z - z1) {n} (hz : ih n
_ = -F.aeval z := by simp only [mul_div_cancel₀ _ hdzne', Subtype.coe_eta]
exact ⟨q, by simpa [sub_eq_add_neg, neg_mul_eq_mul_neg, this, hz'] using hq⟩
+set_option linter.defProp false in
private def calc_eval_z'_norm {z z' z1 : ℤ_[p]} {n} (hz : ih n z) {q}
(heq : F.aeval z' = q * z1 ^ 2)
(h1 : ‖(↑(F.aeval z) : ℚ_[p]) / ↑(F.derivative.aeval z)‖ ≤ 1) (hzeq : z1 = ⟨_, h1⟩) :
diff --git a/Mathlib/NumberTheory/SelbergSieve.lean b/Mathlib/NumberTheory/SelbergSieve.lean
index 93cfa77d4eb6ff..316cf8e7ddb7c0 100644
--- a/Mathlib/NumberTheory/SelbergSieve.lean
+++ b/Mathlib/NumberTheory/SelbergSieve.lean
@@ -89,10 +89,10 @@ open Lean Meta Qq
/-- Extension for the `positivity` tactic: `BoundingSieve.weights`. -/
@[positivity BoundingSieve.weights _ _]
-meta def evalBoundingSieveWeights : PositivityExt where eval {u α} _zα pα? e := do
+meta def evalBoundingSieveWeights : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(@BoundingSieve.weights $s $n) =>
- let some _ := pα? | pure .none
assertInstancesCommute
pure (.nonnegative q(BoundingSieve.weights_nonneg $s $n))
| _, _, _ => throwError "not BoundingSieve.weights"
diff --git a/Mathlib/SetTheory/Ordinal/Univ.lean b/Mathlib/SetTheory/Ordinal/Univ.lean
index d7aac64cfe94d2..7abc9881f863c0 100644
--- a/Mathlib/SetTheory/Ordinal/Univ.lean
+++ b/Mathlib/SetTheory/Ordinal/Univ.lean
@@ -28,6 +28,7 @@ This makes the basic API easier to set up. See `Cardinal.mk_cardinal` for a proo
universe u v w
+set_option linter.checkUnivs false in
open Ordinal in
-- intended to be used with explicit universe parameters
/-- The ordinal `univ.{u, v}` is the order type of `Ordinal.{u}` or `Cardinal.{u}`, as an element of
@@ -36,6 +37,7 @@ open Ordinal in
def Ordinal.univ : Ordinal.{max (u + 1) v} :=
lift.{v, u + 1} (typeLT Ordinal)
+set_option linter.checkUnivs false in
open Cardinal in
-- intended to be used with explicit universe parameters
/-- The cardinal `univ.{u, v}` is the cardinality of `Ordinal.{u}` or `Cardinal.{u}`, as an element
diff --git a/Mathlib/SetTheory/ZFC/PSet.lean b/Mathlib/SetTheory/ZFC/PSet.lean
index 70115837262919..ca273f551fb0fe 100644
--- a/Mathlib/SetTheory/ZFC/PSet.lean
+++ b/Mathlib/SetTheory/ZFC/PSet.lean
@@ -441,6 +441,7 @@ protected def Lift : PSet.{u} → PSet.{max u v}
| ⟨α, A⟩ => ⟨ULift.{v, u} α, fun ⟨x⟩ => PSet.Lift (A x)⟩
-- intended to be used with explicit universe parameters
+set_option linter.checkUnivs false in
/-- Embedding of one universe in another -/
@[nolint checkUnivs]
def embed : PSet.{max (u + 1) v} :=
diff --git a/Mathlib/Tactic/Algebra/Basic.lean b/Mathlib/Tactic/Algebra/Basic.lean
index d71ca00b7cd5db..cda5ca25b7cf19 100644
--- a/Mathlib/Tactic/Algebra/Basic.lean
+++ b/Mathlib/Tactic/Algebra/Basic.lean
@@ -164,24 +164,24 @@ namespace RingCompute
/-- Evaluate the sum of two normalized expressions in `R` using `ring`. -/
def add (cR : Common.Cache sR) {a b : Q($A)} (za : BaseType sAlg a) (zb : BaseType sAlg b) :
- MetaM (Common.Result (BaseType sAlg) q($a + $b) × Option Q(IsNat ($a + $b) 0)) := do
- let ⟨r, vr⟩ := za
- let ⟨s, vs⟩ := zb
- let ⟨t, vt, pt⟩ ← Common.evalAdd (Ring.ringCompute cR) rcℕ vr vs
- match vt with
- | .zero =>
- have : $t =Q 0 := ⟨⟩
- return ⟨⟨_, .mk _ vt, q(add_algebraMap $pt)⟩, some q(add_algebraMap_isNat_zero $pt)⟩
- | vt =>
- return ⟨⟨_, .mk _ vt, q(add_algebraMap $pt)⟩, none⟩
+ MetaM (Common.Result (BaseType sAlg) q($a + $b) × Option Q(IsNat ($a + $b) 0)) :=
+ match za, zb with
+ | .mk r vr, .mk s vs => do
+ let ⟨t, vt, pt⟩ ← Common.evalAdd (Ring.ringCompute cR) rcℕ vr vs
+ match (dependent := true) vt with
+ | .zero =>
+ have : $t =Q 0 := ⟨⟩
+ return ⟨⟨_, .mk _ vt, q(add_algebraMap $pt)⟩, some q(add_algebraMap_isNat_zero $pt)⟩
+ | vt =>
+ return ⟨⟨_, .mk _ vt, q(add_algebraMap $pt)⟩, none⟩
/-- Evaluate the product of two normalized expressions in `R` using `ring`. -/
def mul (cR : Common.Cache sR) {a b : Q($A)} (za : BaseType sAlg a) (zb : BaseType sAlg b) :
- MetaM (Common.Result (BaseType sAlg) q($a * $b)) := do
- let ⟨r, vr⟩ := za
- let ⟨s, vs⟩ := zb
- let ⟨t, vt, pt⟩ ← Common.evalMul (Ring.ringCompute cR) rcℕ vr vs
- return ⟨_, .mk _ vt, q(by simp [← $pt, map_mul])⟩
+ MetaM (Common.Result (BaseType sAlg) q($a * $b)) :=
+ match za, zb with
+ | .mk r vr, .mk s vs => do
+ let ⟨t, vt, pt⟩ ← Common.evalMul (Ring.ringCompute cR) rcℕ vr vs
+ return ⟨_, .mk _ vt, q(by simp [← $pt, map_mul])⟩
/-- Take an expression `r'` in a ring `R'` such that `R` is an `R'`-algebra and cast `r'` to `R`
using `algebraMap R' R`, so that the scalar multiplication action on `A` is preserved. -/
@@ -194,7 +194,7 @@ def cast (cR : Algebra.Cache sR) (u' : Level) (R' : Q(Type u'))
let ⟨r, pf_smul⟩ ← evalSMulCast q($sAlg) q($_smul) r'
let ⟨_r'', vr, pr⟩ ←
Common.eval rcℕ (Ring.ringCompute cR.toCache) cR.toCache q($r)
- match vr with
+ match (dependent := true) vr with
| .zero .. =>
assumeInstancesCommute
return ⟨_, .zero, q(cast_zero_smul_eq_zero_mul $pr $pf_smul)⟩
@@ -205,37 +205,40 @@ def cast (cR : Algebra.Cache sR) (u' : Level) (R' : Q(Type u'))
/-- Evaluate the product of two normalized expressions in `R` using `ring`. -/
def neg (cR : Algebra.Cache sR) {a : Q($A)} (_rA : Q(CommRing $A)) (za : BaseType sAlg a) :
- MetaM (Common.Result (BaseType sAlg) q(-$a)) := do
- let ⟨r, vr⟩ := za
- match cR.rα with
- | some rR =>
- let ⟨_, vt, pt⟩ ← Common.evalNeg (Ring.ringCompute cR.toCache) q($rR) vr
- assumeInstancesCommute
- return ⟨_, .mk _ vt, q(neg_algebraMap $pt)⟩
- | none => failure
+ MetaM (Common.Result (BaseType sAlg) q(-$a)) :=
+ match za with
+ | .mk r vr => do
+ match cR.rα with
+ | some rR =>
+ let ⟨_, vt, pt⟩ ← Common.evalNeg (Ring.ringCompute cR.toCache) q($rR) vr
+ assumeInstancesCommute
+ return ⟨_, .mk _ vt, q(neg_algebraMap $pt)⟩
+ | none => failure
/-- Raise a normalized expression in `R` to the power of a normalized natural number expression
using `ring`. -/
def pow (cR : Common.Cache sR) {a : Q($A)} {b : Q(ℕ)} (za : BaseType sAlg a)
(vb : Common.ExProdNat q($b)) :
- OptionT MetaM (Common.Result (BaseType sAlg) q($a ^ $b)) := do
- let ⟨r, vr⟩ := za
- let ⟨_, vs, ps⟩ ← Common.evalPow₁ (Ring.ringCompute cR) rcℕ vr vb
- return ⟨_, ⟨_, vs⟩, q(pow_algebraMap $ps)⟩
+ OptionT MetaM (Common.Result (BaseType sAlg) q($a ^ $b)) :=
+ match za with
+ | .mk r vr => do
+ let ⟨_, vs, ps⟩ ← Common.evalPow₁ (Ring.ringCompute cR) rcℕ vr vb
+ return ⟨_, ⟨_, vs⟩, q(pow_algebraMap $ps)⟩
/-- Evaluate the inverse of two normalized expressions in `R` using `ring`. -/
/- We include the CharZero argument to match the type signature of the ringCompute entry. -/
@[nolint unusedArguments]
def inv (cR : Algebra.Cache sR) {a : Q($A)} (_ : Option Q(CharZero $A)) (fA : Q(Semifield $A))
- (za : BaseType sAlg a) : AtomM (Option (Common.Result (BaseType sAlg) q($a⁻¹))) := do
- match cR.dsα with
- | some fR =>
- let ⟨r, vr⟩ := za
- let ⟨_, vs, ps⟩ ← Common.ExSum.evalInv (Ring.ringCompute cR.toCache) rcℕ q($fR) cR.czα vr
- assumeInstancesCommute
- return some ⟨_, ⟨_, vs⟩, q(inv_algebraMap $ps)⟩
- | none =>
- return none
+ (za : BaseType sAlg a) : AtomM (Option (Common.Result (BaseType sAlg) q($a⁻¹))) :=
+ match za with
+ | .mk r vr => do
+ match cR.dsα with
+ | some fR =>
+ let ⟨_, vs, ps⟩ ← Common.ExSum.evalInv (Ring.ringCompute cR.toCache) rcℕ q($fR) cR.czα vr
+ assumeInstancesCommute
+ return some ⟨_, ⟨_, vs⟩, q(inv_algebraMap $ps)⟩
+ | none =>
+ return none
/-- Evaluate constants in `A` using `norm_num`. -/
def derive (cR : Algebra.Cache sR) (cA : Algebra.Cache sA) (x : Q($A)) :
diff --git a/Mathlib/Tactic/CrossRefAttribute.lean b/Mathlib/Tactic/CrossRefAttribute.lean
index 8b8881d079c5ef..d99fb0748397f9 100644
--- a/Mathlib/Tactic/CrossRefAttribute.lean
+++ b/Mathlib/Tactic/CrossRefAttribute.lean
@@ -224,9 +224,9 @@ initialize Lean.registerBuiltinAttribute {
name := `stacksTag
descr := "Apply a Stacks or Kerodon project tag to a theorem."
add := fun decl stx _attrKind => do
- let (db, tag, comment) := ← match stx with
- | `(attr| stacks $tag $[$comment]?) => return (Database.stacks, tag, comment)
- | `(attr| kerodon $tag $[$comment]?) => return (Database.kerodon, tag, comment)
+ let (db, tag, comment) ← match stx with
+ | `(attr| stacks $tag $[$comment]?) => pure (Database.stacks, tag, comment)
+ | `(attr| kerodon $tag $[$comment]?) => pure (Database.kerodon, tag, comment)
| _ => throwUnsupportedSyntax
addCrossRefDoc db decl (← tag.getStacksTag) ((comment.map (·.getString)).getD "")
-- docstrings are immutable once an asynchronous elaboration task has been started
@@ -247,8 +247,8 @@ initialize Lean.registerBuiltinAttribute {
name := `wikidataTag
descr := "Apply a Wikidata identifier to a declaration."
add := fun decl stx _attrKind => do
- let (id, comment) := ← match stx with
- | `(attr| wikidata $id $[$comment]?) => return (id, comment)
+ let (id, comment) ← match stx with
+ | `(attr| wikidata $id $[$comment]?) => pure (id, comment)
| _ => throwUnsupportedSyntax
addCrossRefDoc .wikidata decl (← id.getWikidataId) ((comment.map (·.getString)).getD "")
-- docstrings are immutable once an asynchronous elaboration task has been started
diff --git a/Mathlib/Tactic/DefEqAbuse.lean b/Mathlib/Tactic/DefEqAbuse.lean
index e5f0546c1573f8..bb376b1bccf820 100644
--- a/Mathlib/Tactic/DefEqAbuse.lean
+++ b/Mathlib/Tactic/DefEqAbuse.lean
@@ -113,7 +113,8 @@ where
return result
| .ascend a? => return a?.getD empty
| .compose a b => return combine (← go a) (← go b)
- | .nest _ m | .group m | .tagged _ m | .withContext _ m | .withNamingContext _ m => go m
+ | .nest _ m | .group m | .tagged _ m | .withContext _ m | .withNamingContext _ m
+ | .ofOriginatingSyntax _ m => go m
| .ofLazy _ _ | .ofWidget _ _ | .ofGoal _ | .ofFormatWithInfos _ => return empty
/-- Convenience wrapper which accumulates the results of `visitM` across `arr`, attempting to
@@ -143,6 +144,7 @@ partial def withPPOptions (msg : MessageData) (modify : Options → Options) : M
| .nest n m => .nest n (withPPOptions m modify)
| .group m => .group (withPPOptions m modify)
| .tagged t m => .tagged t (withPPOptions m modify)
+ | .ofOriginatingSyntax stx m => .ofOriginatingSyntax stx (withPPOptions m modify)
| .withNamingContext nc m => .withNamingContext nc (withPPOptions m modify)
| .trace td header children =>
.trace td (withPPOptions header modify) (children.map (withPPOptions · modify))
@@ -164,19 +166,6 @@ namespace Mathlib.Tactic.DefEqAbuse
unless (`Meta.isDefEq).isPrefixOf td.cls do return .descend
f td header children
-/-- Strip the leading status emoji that `withTraceNodeBefore` prepends to trace headers.
-`withTraceNodeBefore` stores `m!"{result.toEmoji} {content}"` as the header; this strips the
-emoji prefix using the structured `TraceData.result?` to know exactly what was prepended.
-Needed because the same isDefEq check has different emoji prefixes across trace runs
-(✅️ when it succeeds, ❌️ when it fails), but we need to compare the content.
-See https://github.com/leanprover/lean4/pull/13070 for the upstream fix. -/
-private def stripHeaderEmoji (s : String) (result? : Option Lean.TraceResult) : String :=
- match result? with
- | some result =>
- let emojiPrefix := s!"{result.toEmoji} "
- if s.startsWith emojiPrefix then (s.drop emojiPrefix.length).toString else s
- | none => s
-
/-- Find the deepest failing `Meta.isDefEq` trace nodes (leaf failures).
Skips `onFailure` retry nodes and ignores ✅️ branches (recovered failures aren't root causes). -/
partial def findLeafFailures (msg : MessageData) : BaseIO (Array MessageData) :=
@@ -188,13 +177,13 @@ partial def findLeafFailures (msg : MessageData) : BaseIO (Array MessageData) :=
return .ascend <| if childFailures.isEmpty then #[header] else childFailures
/-- Collect rendered check strings from `Meta.isDefEq` trace nodes matching a status predicate.
-Returns a `HashSet` of emoji-stripped header strings. -/
+Returns a `HashSet` of header strings. -/
partial def collectIsDefEqChecks (pred : Lean.TraceResult → Bool)
(msg : MessageData) : BaseIO (Std.HashSet String) :=
msg.visitTraceNodesM <| onlyOnDefEqNodes fun td header children => do
if let some status := td.result? then
if pred status then
- let headerStr := stripHeaderEmoji (← header.toString) td.result?
+ let headerStr ← header.toString
return .descend (butFirst := some {headerStr})
return .descend
@@ -210,7 +199,7 @@ partial def findTransitionFailures (permSuccesses : Std.HashSet String)
if permSuccesses.isEmpty then findLeafFailures msg
else msg.visitTraceNodesM <| onlyOnDefEqNodes fun td header children => do
unless td.result? matches some .failure do return .descend
- let headerStr := stripHeaderEmoji (← header.toString) td.result?
+ let headerStr ← header.toString
if permSuccesses.contains headerStr && !permFailures.contains headerStr then
-- Transition point: fails strict, succeeds permissive, doesn't also fail permissive.
-- Look for deeper transition points among children.
@@ -303,17 +292,13 @@ def analyzeTraces (strictMsgs permMsgs : Array MessageData) (includeSynth : Bool
return (uniqueFailures, dedupedResults)
/-- Check whether a rendered isDefEq check string has syntactically identical LHS and RHS
-(e.g. `"❌️ ⊤ =?= ⊤"` or `"Quiver C =?= Quiver C"`).
+(e.g. `"⊤ =?= ⊤"` or `"Quiver C =?= Quiver C"`).
Comparison is whitespace-insensitive to handle cases where LHS and RHS are semantically identical
but rendered with different line breaks or spacing.
TODO: once https://github.com/leanprover/lean4/pull/12698 is available, refactor to use
`TraceData.result?` and compare the LHS/RHS `Expr`s structurally instead of string-matching. -/
def isIdenticalSidesStr (raw : String) : Bool :=
- if let [lhsRaw, rhs] := raw.splitOn " =?= " then
- -- Strip the leading status emoji/word (first whitespace-delimited token).
- let lhs := match lhsRaw.splitOn " " with
- | _ :: rest => " ".intercalate rest
- | _ => lhsRaw
+ if let [lhs, rhs] := raw.splitOn " =?= " then
-- Compare up to whitespace so that line-break differences don't cause false negatives.
let tokenize (s : String) : List String :=
(s.split Char.isWhitespace).toList.map (·.toString) |>.filter (· ≠ "")
@@ -346,13 +331,14 @@ def disambiguateFailures (failures : Array MessageData) : BaseIO (Array MessageD
def reportDefEqAbuse {m : Type → Type} [Monad m] [MonadLog m] [AddMessageContext m]
[MonadOptions m] (kind : String) (uniqueFailures : Array MessageData)
(synthResults : Array (MessageData × Array MessageData)) : m Unit := do
+ let failureEmoji := Lean.TraceResult.failure.toEmoji
if !synthResults.isEmpty then
-- Structured report: group by instance application
let mut entries : Array MessageData := #[]
for (app, failures) in synthResults do
let failureList := joinSep
- (failures.toList.map fun f => m!" {f}") "\n"
- entries := entries.push m!" {app}\n{failureList}"
+ (failures.toList.map fun f => m!" {failureEmoji} {f}") "\n"
+ entries := entries.push m!" {failureEmoji} {app}\n{failureList}"
let report := joinSep entries.toList "\n"
logWarning
m!"#defeq_abuse: {kind} fails with \
@@ -365,7 +351,7 @@ def reportDefEqAbuse {m : Type → Type} [Monad m] [MonadLog m] [AddMessageConte
Could not identify specific failing isDefEq checks from traces."
else
let failureList := joinSep
- (uniqueFailures.toList.map fun f => m!" {f}") "\n"
+ (uniqueFailures.toList.map fun f => m!" {failureEmoji} {f}") "\n"
logWarning
m!"#defeq_abuse: {kind} fails with \
`backward.isDefEq.respectTransparency true` but succeeds with `false`.\n\
diff --git a/Mathlib/Tactic/DeprecateTo.lean b/Mathlib/Tactic/DeprecateTo.lean
index 6c4805d681021e..96a3d1352a2db9 100644
--- a/Mathlib/Tactic/DeprecateTo.lean
+++ b/Mathlib/Tactic/DeprecateTo.lean
@@ -47,11 +47,10 @@ open Lean Elab Term Command
/-- Produce the syntax for the command `@[deprecated (since := "YYYY-MM-DD")] alias n := id`. -/
def mkDeprecationStx (id : TSyntax `ident) (n : Name) (dat : Option String := none) :
CommandElabM (TSyntax `command) := do
- let dat := ←
- match dat with
- | none => do
- return s!"{(← Std.Time.ZonedDateTime.now).toPlainDate}"
- | some s => return s
+ let dat ← match dat with
+ | none => do
+ pure s!"{← Std.Time.PlainDate.now}"
+ | some s => pure s
let nd := mkNode `str #[mkAtom ("\"" ++ dat.trimAsciiEnd ++ "\"")]
`(command| @[deprecated (since := $nd)] alias $(mkIdent n) := $id)
diff --git a/Mathlib/Tactic/DeriveEncodable.lean b/Mathlib/Tactic/DeriveEncodable.lean
index 52f418e78bf039..7ac67b035ee4e9 100644
--- a/Mathlib/Tactic/DeriveEncodable.lean
+++ b/Mathlib/Tactic/DeriveEncodable.lean
@@ -126,7 +126,7 @@ private def S_equiv : S ≃ ℕ where
· rwa [Nat.one_le_iff_ne_zero]
· exact nat_unpair_lt_2 h
· obtain _ | n' := n
- · exact False.elim (h rfl)
+ · exact False.elim (h (by simp))
· have := Nat.unpair_lt (by lia : 1 ≤ n' + 1)
lia
diff --git a/Mathlib/Tactic/FieldSimp.lean b/Mathlib/Tactic/FieldSimp.lean
index 4848295ba6a635..70cc8bae5385bb 100644
--- a/Mathlib/Tactic/FieldSimp.lean
+++ b/Mathlib/Tactic/FieldSimp.lean
@@ -62,7 +62,7 @@ def onExponent (l : qNF M) (f : ℤ → ℤ) : qNF M :=
/-- Build a transparent expression for the product of powers represented by `l : qNF M`. -/
def evalPrettyMonomial (iM : Q(GroupWithZero $M)) (r : ℤ) (x : Q($M)) :
- MetaM (Σ e : Q($M), Q(zpow' $x $r = $e)) := do
+ MetaM (Σ e : Q($M), Q(zpow' $x $r = $e)) :=
match r with
| 0 => /- If an exponent is zero then we must not have been able to prove that x is nonzero. -/
return ⟨q($x / $x), q(zpow'_zero_eq_div ..)⟩
@@ -111,10 +111,10 @@ def removeZeros
of) the negative powers. -/
def split (iM : Q(CommGroupWithZero $M)) (l : qNF M) :
MetaM (Σ l_n l_d : qNF M, Q(NF.eval $(l.toNF)
- = NF.eval $(l_n.toNF) / NF.eval $(l_d.toNF))) := do
+ = NF.eval $(l_n.toNF) / NF.eval $(l_d.toNF))) :=
match l with
| [] => return ⟨[], [], q(Eq.symm (div_one (1:$M)))⟩
- | ((r, x), i) :: t =>
+ | ((r, x), i) :: t => do
let ⟨t_n, t_d, pf⟩ ← split iM t
if r > 0 then
return ⟨((r, x), i) :: t_n, t_d, (q(NF.cons_eq_div_of_eq_div $r $x $pf):)⟩
@@ -125,13 +125,13 @@ def split (iM : Q(CommGroupWithZero $M)) (l : qNF M) :
return ⟨t_n, ((r', x), i) :: t_d, (q(NF.cons_eq_div_of_eq_div' $r' $x $pf):)⟩
private def evalPrettyAux (iM : Q(CommGroupWithZero $M)) (l : qNF M) :
- MetaM (Σ e : Q($M), Q(NF.eval $(l.toNF) = $e)) := do
+ MetaM (Σ e : Q($M), Q(NF.eval $(l.toNF) = $e)) :=
match l with
| [] => return ⟨q(1), q(rfl)⟩
- | [((r, x), _)] =>
+ | [((r, x), _)] => do
let ⟨e, pf⟩ ← evalPrettyMonomial q(inferInstance) r x
return ⟨e, q(by rw [NF.eval_cons]; exact Eq.trans (one_mul _) $pf)⟩
- | ((r, x), k) :: t =>
+ | ((r, x), k) :: t => do
let ⟨e, pf_e⟩ ← evalPrettyMonomial q(inferInstance) r x
let ⟨t', pf⟩ ← evalPrettyAux iM t
have pf'' : Q(NF.eval $(qNF.toNF (((r, x), k) :: t)) = (NF.eval $(qNF.toNF t)) * zpow' $x $r) :=
@@ -144,7 +144,7 @@ def evalPretty (iM : Q(CommGroupWithZero $M)) (l : qNF M) :
let ⟨l_n, l_d, pf⟩ ← split iM l
let ⟨num, pf_n⟩ ← evalPrettyAux q(inferInstance) l_n
let ⟨den, pf_d⟩ ← evalPrettyAux q(inferInstance) l_d
- match l_d with
+ match (dependent := true) l_d with
| [] => return ⟨num, q(eq_div_of_eq_one_of_subst $pf $pf_n)⟩
| _ =>
let pf_n : Q(NF.eval $(l_n.toNF) = $num) := pf_n
@@ -281,14 +281,14 @@ def mkDenomConditionProofSucc {iM : Q(CommGroupWithZero $M)}
(disch : ∀ {u : Level} (type : Q(Sort u)), MetaM Q($type))
{cond : DenomCondition (M := M) q(inferInstance)}
{L : qNF M} (hL : cond.proof L) (e : Q($M)) (r : ℤ) (i : ℕ) :
- MetaM (Q($e ≠ 0) × cond.proof (((r, e), i) :: L)) := do
+ MetaM (Q($e ≠ 0) × cond.proof (((r, e), i) :: L)) :=
match cond with
| .none => return (← disch q($e ≠ 0), Unit.unit)
- | .nonzero =>
+ | .nonzero => do
let pf ← disch q($e ≠ 0)
let pf₀ : Q(NF.eval $(qNF.toNF L) ≠ 0) := hL
return (pf, q(NF.cons_ne_zero $r $pf $pf₀))
- | .positive _ _ _ _ =>
+ | .positive _ _ _ _ => do
let pf ← disch q(0 < $e)
let pf₀ : Q(0 < NF.eval $(qNF.toNF L)) := hL
let pf' := q(NF.cons_pos $r (x := $e) $pf $pf₀)
@@ -301,14 +301,14 @@ def mkDenomConditionProofSucc' {iM : Q(CommGroupWithZero $M)}
(disch : ∀ {u : Level} (type : Q(Sort u)), MetaM Q($type))
{cond : DenomCondition (M := M) q(inferInstance)}
{L : qNF M} (hL : cond.proof L) (e : Q($M)) (r : ℤ) (i : ℕ) :
- MetaM (cond.proof (((r, e), i) :: L)) := do
+ MetaM (cond.proof (((r, e), i) :: L)) :=
match cond with
| .none => return Unit.unit
- | .nonzero =>
+ | .nonzero => do
let pf ← disch q($e ≠ 0)
let pf₀ : Q(NF.eval $(qNF.toNF L) ≠ 0) := hL
return q(NF.cons_ne_zero $r $pf $pf₀)
- | .positive _ _ _ _ =>
+ | .positive _ _ _ _ => do
let pf ← disch q(0 < $e)
let pf₀ : Q(0 < NF.eval $(qNF.toNF L)) := hL
return q(NF.cons_pos $r (x := $e) $pf $pf₀)
diff --git a/Mathlib/Tactic/FieldSimp/Lemmas.lean b/Mathlib/Tactic/FieldSimp/Lemmas.lean
index 9b5669aa6f68cb..177257c99d0d68 100644
--- a/Mathlib/Tactic/FieldSimp/Lemmas.lean
+++ b/Mathlib/Tactic/FieldSimp/Lemmas.lean
@@ -398,7 +398,7 @@ def Sign.expr : Sign M → Q($M) → Q($M)
the product with `c` of (± `y`) (here taking the specified sign) is ± `c * y`. -/
def Sign.mulRight (iM : Q(CommGroupWithZero $M)) (c y : Q($M)) (g : Sign M) :
MetaM Q($(g.expr q($c * $y)) = $c * $(g.expr y)) := do
- match g with
+ match (dependent := true) g with
| .plus => pure q(rfl)
| .minus _ =>
assumeInstancesCommute
@@ -409,7 +409,7 @@ the product of (± `y₁`) and (± `y₂`) (here taking the specified signs) is
proof and the computed sign. -/
def Sign.mul (iM : Q(CommGroupWithZero $M)) (y₁ y₂ : Q($M)) (g₁ g₂ : Sign M) :
MetaM (Σ (G : Sign M), Q($(g₁.expr y₁) * $(g₂.expr y₂) = $(G.expr q($y₁ * $y₂)))) := do
- match g₁, g₂ with
+ match (dependent := true) g₁, g₂ with
| .plus, .plus => pure ⟨.plus, q(rfl)⟩
| .plus, .minus i =>
assumeInstancesCommute
@@ -425,7 +425,7 @@ def Sign.mul (iM : Q(CommGroupWithZero $M)) (y₁ y₂ : Q($M)) (g₁ g₂ : Sig
the inverse of (± `y`) (here taking the specified sign) is ± `y⁻¹`. -/
def Sign.inv (iM : Q(CommGroupWithZero $M)) (y : Q($M)) (g : Sign M) :
MetaM (Q($(g.expr y)⁻¹ = $(g.expr q($y⁻¹)))) := do
- match g with
+ match (dependent := true) g with
| .plus => pure q(rfl)
| .minus _ =>
assumeInstancesCommute
@@ -436,7 +436,7 @@ the quotient of (± `y₁`) and (± `y₂`) (here taking the specified signs) is
proof and the computed sign. -/
def Sign.div (iM : Q(CommGroupWithZero $M)) (y₁ y₂ : Q($M)) (g₁ g₂ : Sign M) :
MetaM (Σ (G : Sign M), Q($(g₁.expr y₁) / $(g₂.expr y₂) = $(G.expr q($y₁ / $y₂)))) := do
- match g₁, g₂ with
+ match (dependent := true) g₁, g₂ with
| .plus, .plus => pure ⟨.plus, q(rfl)⟩
| .plus, .minus i =>
assumeInstancesCommute
@@ -452,7 +452,7 @@ def Sign.div (iM : Q(CommGroupWithZero $M)) (y₁ y₂ : Q($M)) (g₁ g₂ : Sig
the negation of (± `y`) (here taking the specified sign) is ∓ `y`. -/
def Sign.neg (iM : Q(Field $M)) (y : Q($M)) (g : Sign M) :
MetaM (Σ (G : Sign M), Q(-$(g.expr y) = $(G.expr y))) := do
- match g with
+ match (dependent := true) g with
| .plus => pure ⟨.minus iM, q(rfl)⟩
| .minus _ =>
assumeInstancesCommute
@@ -463,7 +463,7 @@ the exponentiation to power `s : ℕ` of (± `y`) (here taking the specified sig
return this proof and the computed sign. -/
def Sign.pow (iM : Q(CommGroupWithZero $M)) (y : Q($M)) (g : Sign M) (s : ℕ) :
MetaM (Σ (G : Sign M), Q($(g.expr y) ^ $s = $(G.expr q($y ^ $s)))) := do
- match g with
+ match (dependent := true) g with
| .plus => pure ⟨.plus, q(rfl)⟩
| .minus i =>
assumeInstancesCommute
@@ -479,7 +479,7 @@ the exponentiation to power `s : ℤ` of (± `y`) (here taking the specified sig
return this proof and the computed sign. -/
def Sign.zpow (iM : Q(CommGroupWithZero $M)) (y : Q($M)) (g : Sign M) (s : ℤ) :
MetaM (Σ (G : Sign M), Q($(g.expr y) ^ $s = $(G.expr q($y ^ $s)))) := do
- match g with
+ match (dependent := true) g with
| .plus => pure ⟨.plus, q(rfl)⟩
| .minus i =>
assumeInstancesCommute
diff --git a/Mathlib/Tactic/Linarith/Oracle/SimplexAlgorithm/Gauss.lean b/Mathlib/Tactic/Linarith/Oracle/SimplexAlgorithm/Gauss.lean
index d89a10b0fce4e1..190798ae8eab1c 100644
--- a/Mathlib/Tactic/Linarith/Oracle/SimplexAlgorithm/Gauss.lean
+++ b/Mathlib/Tactic/Linarith/Oracle/SimplexAlgorithm/Gauss.lean
@@ -65,13 +65,13 @@ def getTableauImp : GaussM n m matType <| Tableau matType := do
for i in [col:m] do
free := free.push i
- let ansMatrix : matType basic.size free.size := ← do
+ let ansMatrix : matType basic.size free.size ← do
let vals := getValues (← get) |>.filterMap fun (i, j, v) =>
if j == basic[i]! then
none
else
some (i, free.findIdx? (· == j) |>.get!, -v)
- return ofValues vals
+ pure <| ofValues vals
return ⟨basic, free, ansMatrix⟩
diff --git a/Mathlib/Tactic/LinearCombinationPrime.lean b/Mathlib/Tactic/LinearCombinationPrime.lean
index 6b71f8dab9737e..530e066e78ee2e 100644
--- a/Mathlib/Tactic/LinearCombinationPrime.lean
+++ b/Mathlib/Tactic/LinearCombinationPrime.lean
@@ -94,7 +94,7 @@ partial def expandLinearCombo (ty : Expr) (stx : Syntax.Term) : TermElabM Expand
match ← expandLinearCombo ty e with
| .const c => .const <$> `(-$c)
| .proof p => .proof <$> ``(neg_pf $p)
- | `(← $e) => do
+ | `(← $e:term) => do
match ← expandLinearCombo ty e with
| .const c => return .const c
| .proof p => .proof <$> ``(Eq.symm $p)
diff --git a/Mathlib/Tactic/Linter/HaveLetLinter.lean b/Mathlib/Tactic/Linter/HaveLetLinter.lean
index a0322f4601c61d..65a3dfcc4bffbe 100644
--- a/Mathlib/Tactic/Linter/HaveLetLinter.lean
+++ b/Mathlib/Tactic/Linter/HaveLetLinter.lean
@@ -88,7 +88,7 @@ def toFormat_propTypes (ctx : ContextInfo) (lc : LocalContext) (es : Array (Expr
also a `Format`ted version of the corresponding Type. -/
public partial
def nonPropHaves : InfoTree → CommandElabM (Array (Syntax × Format)) :=
- InfoTree.foldInfoM (init := #[]) fun ctx info args => return args ++ (← do
+ InfoTree.foldInfoM (init := #[]) fun ctx info args => return args ++ (← (do
let .ofTacticInfo i := info | return #[]
let stx := i.stx
let .original .. := stx.getHeadInfo | return #[]
@@ -109,7 +109,7 @@ def nonPropHaves : InfoTree → CommandElabM (Array (Syntax × Format)) :=
-- Now, we get the `MetaM` state up and running to find the types of each entry of `newDecls`.
-- For each entry which is a `Type`, we print a warning on `have`.
let fmts ← toFormat_propTypes ctx lc (newDecls.map (fun e ↦ (e.type, e.userName))).toArray
- return fmts.map fun (fmt, na) ↦ (stx, f!"{na} : {fmt}"))
+ return fmts.map fun (fmt, na) ↦ (stx, f!"{na} : {fmt}")))
/-- The main implementation of the `have` vs `let` linter. -/
def haveLetLinter : Linter where run := withSetOptionIn fun _stx => do
diff --git a/Mathlib/Tactic/Linter/Style.lean b/Mathlib/Tactic/Linter/Style.lean
index 5b17726d37d547..d949f89d541593 100644
--- a/Mathlib/Tactic/Linter/Style.lean
+++ b/Mathlib/Tactic/Linter/Style.lean
@@ -458,14 +458,14 @@ def longLineLinter : Linter where run := withSetOptionIn fun stx ↦ do
return
if stx.isOfKind ``Lean.Parser.Module.header then return
-- if the linter reached the end of the file, then we scan the `import` syntax instead
- let stx := ← do
+ let stx ← do
if stx.isOfKind ``Lean.Parser.Command.eoi then
let fileMap ← getFileMap
-- `impMods` is the syntax for the modules imported in the current file
let (impMods, _) ← Parser.parseHeader
{ inputString := fileMap.source, fileName := ← getFileName, fileMap := fileMap }
- return impMods.raw
- else return stx
+ pure impMods.raw
+ else pure stx
let sstr := stx.getSubstring?
let fm ← getFileMap
let maxLineLength := linter.style.longLine.maxLineLength.get (← getOptions)
diff --git a/Mathlib/Tactic/Linter/Whitespace.lean b/Mathlib/Tactic/Linter/Whitespace.lean
index 16a175642c7990..2c6a496e390461 100644
--- a/Mathlib/Tactic/Linter/Whitespace.lean
+++ b/Mathlib/Tactic/Linter/Whitespace.lean
@@ -335,14 +335,14 @@ def whitespaceLinter : Linter where run := withSetOptionIn fun stx ↦ do
return
let some upTo := CommandStart.endPos stx | return
- let fmt : Option Format := ←
+ let fmt : Option Format ←
try
- liftCoreM <| PrettyPrinter.ppCategory `command stx
+ liftCoreM <| some <$> PrettyPrinter.ppCategory `command stx
catch _ =>
Linter.logLintIf linter.style.whitespace.verbose (stx.getHead?.getD stx)
m!"The `whitespace` linter had some parsing issues: \
feel free to silence it and report this error!"
- return none
+ pure none
if let some fmt := fmt then
let st := fmt.pretty
let origSubstring := stx.getSubstring?.getD default
diff --git a/Mathlib/Tactic/NormNum/Core.lean b/Mathlib/Tactic/NormNum/Core.lean
index be79b82e34957e..07d76133491ac1 100644
--- a/Mathlib/Tactic/NormNum/Core.lean
+++ b/Mathlib/Tactic/NormNum/Core.lean
@@ -151,7 +151,7 @@ and returning the truth or falsity of `p' : Prop` from an equivalence `p ↔ p'`
def deriveBoolOfIff (p p' : Q(Prop)) (hp : Q($p ↔ $p')) :
MetaM ((b : Bool) × BoolResult p' b) := do
let ⟨b, pb⟩ ← deriveBool p
- match b with
+ match (dependent := true) b with
| true => return ⟨true, q(Iff.mp $hp $pb)⟩
| false => return ⟨false, q((Iff.not $hp).mp $pb)⟩
diff --git a/Mathlib/Tactic/NormNum/Irrational.lean b/Mathlib/Tactic/NormNum/Irrational.lean
index 8528c4739b4e17..e062206ec53c36 100644
--- a/Mathlib/Tactic/NormNum/Irrational.lean
+++ b/Mathlib/Tactic/NormNum/Irrational.lean
@@ -260,64 +260,64 @@ def findNotPowerCertificate (m n : Q(ℕ)) : MetaM (NotPowerCertificate m n) :=
/-- `norm_num` extension that proves `Irrational x ^ y` for rational `y`. `x` may be
natural or rational. -/
@[norm_num Irrational (_ ^ (_ : ℝ))]
-def evalIrrationalRpow : NormNumExt where eval {u α} e := do
- let 0 := u | failure
- let ~q(Prop) := α | failure
- let ~q(Irrational (($x : ℝ) ^ ($y : ℝ))) := e | failure
- let .isNNRat sℝ _ y_num y_den y_isNNRat ← derive y | failure
- let ⟨gy, hy_coprime⟩ := proveNatGCD y_num y_den
- if gy.natLit! != 1 then failure
- let _ : $gy =Q 1 := ⟨⟩
- match ← derive x with
- | .isNat sℝ ex x_isNat =>
- let cert ← findNotPowerCertificate q($ex) y_den
- assumeInstancesCommute
- return .isTrue q(irrational_rpow_nat_rat $x_isNat $y_isNNRat $hy_coprime
- $cert.pf_left $cert.pf_right)
- | .isNNRat sℝ _ x_num x_den x_isNNRat =>
- let ⟨gx, hx_coprime⟩ := proveNatGCD x_num x_den
- if gx.natLit! != 1 then failure
- let _ : $gx =Q 1 := ⟨⟩
- let hx_isNNRat' : Q(IsNNRat $x $x_num $x_den) := x_isNNRat
- let hy_isNNRat' : Q(IsNNRat $y $y_num $y_den) := y_isNNRat
- try
- let numCert ← findNotPowerCertificate q($x_num) y_den
+def evalIrrationalRpow : NormNumExt where eval {u α} e :=
+ match u, α, e with
+ | 0, ~q(Prop), ~q(Irrational (($x : ℝ) ^ ($y : ℝ))) => do
+ let .isNNRat sℝ _ y_num y_den y_isNNRat ← derive y | failure
+ let ⟨gy, hy_coprime⟩ := proveNatGCD y_num y_den
+ if gy.natLit! != 1 then failure
+ let _ : $gy =Q 1 := ⟨⟩
+ match ← derive x with
+ | .isNat sℝ ex x_isNat =>
+ let cert ← findNotPowerCertificate q($ex) y_den
assumeInstancesCommute
- return Result.isTrue q(irrational_rpow_rat_rat_of_num $hx_isNNRat' $hy_isNNRat'
- $hx_coprime $hy_coprime $numCert.pf_left $numCert.pf_right)
- catch _ =>
- let denCert ← findNotPowerCertificate q($x_den) y_den
- assumeInstancesCommute
- return Result.isTrue q(irrational_rpow_rat_rat_of_den $hx_isNNRat' $hy_isNNRat'
- $hx_coprime $hy_coprime $denCert.pf_left $denCert.pf_right)
- | _ => failure
+ return .isTrue q(irrational_rpow_nat_rat $x_isNat $y_isNNRat $hy_coprime
+ $cert.pf_left $cert.pf_right)
+ | .isNNRat sℝ _ x_num x_den x_isNNRat =>
+ let ⟨gx, hx_coprime⟩ := proveNatGCD x_num x_den
+ if gx.natLit! != 1 then failure
+ let _ : $gx =Q 1 := ⟨⟩
+ let hx_isNNRat' : Q(IsNNRat $x $x_num $x_den) := x_isNNRat
+ let hy_isNNRat' : Q(IsNNRat $y $y_num $y_den) := y_isNNRat
+ try
+ let numCert ← findNotPowerCertificate q($x_num) y_den
+ assumeInstancesCommute
+ return Result.isTrue q(irrational_rpow_rat_rat_of_num $hx_isNNRat' $hy_isNNRat'
+ $hx_coprime $hy_coprime $numCert.pf_left $numCert.pf_right)
+ catch _ =>
+ let denCert ← findNotPowerCertificate q($x_den) y_den
+ assumeInstancesCommute
+ return Result.isTrue q(irrational_rpow_rat_rat_of_den $hx_isNNRat' $hy_isNNRat'
+ $hx_coprime $hy_coprime $denCert.pf_left $denCert.pf_right)
+ | _ => failure
+ | _, _, _ => failure
/-- `norm_num` extension that proves `Irrational √x` for rational `x`. -/
@[norm_num Irrational (Real.sqrt _)]
def evalIrrationalSqrt : NormNumExt where eval {u α} e := do
- let 0 := u | failure
- let ~q(Prop) := α | failure
- let ~q(Irrational (√$x)) := e | failure
- match ← derive x with
- | .isNat sℝ ex pf =>
- let cert ← findNotPowerCertificate ex q(nat_lit 2)
- assumeInstancesCommute
- return .isTrue q(irrational_sqrt_nat $pf $cert.pf_left $cert.pf_right)
- | .isNNRat sℝ eq en ed pf =>
- let ⟨g, pf_coprime⟩ := proveNatGCD en ed
- if g.natLit! != 1 then failure
- let _ : $g =Q 1 := ⟨⟩
- try
- let numCert ← findNotPowerCertificate en q(nat_lit 2)
- assumeInstancesCommute
- return Result.isTrue
- q(irrational_sqrt_rat_of_num $pf $pf_coprime $numCert.pf_left $numCert.pf_right)
- catch _ =>
- let denCert ← findNotPowerCertificate ed q(nat_lit 2)
+ match u, α, e with
+ | 0, ~q(Prop), ~q(Irrational (√$x)) => do
+ match ← derive x with
+ | .isNat sℝ ex pf =>
+ let cert ← findNotPowerCertificate ex q(nat_lit 2)
assumeInstancesCommute
- return Result.isTrue
- q(irrational_sqrt_rat_of_den $pf $pf_coprime $denCert.pf_left $denCert.pf_right)
- | _ => failure
+ return .isTrue q(irrational_sqrt_nat $pf $cert.pf_left $cert.pf_right)
+ | .isNNRat sℝ eq en ed pf =>
+ let ⟨g, pf_coprime⟩ := proveNatGCD en ed
+ if g.natLit! != 1 then failure
+ let _ : $g =Q 1 := ⟨⟩
+ try
+ let numCert ← findNotPowerCertificate en q(nat_lit 2)
+ assumeInstancesCommute
+ return Result.isTrue
+ q(irrational_sqrt_rat_of_num $pf $pf_coprime $numCert.pf_left $numCert.pf_right)
+ catch _ =>
+ let denCert ← findNotPowerCertificate ed q(nat_lit 2)
+ assumeInstancesCommute
+ return Result.isTrue
+ q(irrational_sqrt_rat_of_den $pf $pf_coprime $denCert.pf_left $denCert.pf_right)
+ | _ => failure
+ | _, _, _ => failure
end NormNum
diff --git a/Mathlib/Tactic/Positivity/Basic.lean b/Mathlib/Tactic/Positivity/Basic.lean
index 4e078e63ad35fc..0efd17c7fde774 100644
--- a/Mathlib/Tactic/Positivity/Basic.lean
+++ b/Mathlib/Tactic/Positivity/Basic.lean
@@ -63,6 +63,7 @@ such that `positivity` successfully recognises both `a` and `b`. -/
haveI' : $e =Q ite $p $a $b := ⟨⟩
let ra ← core zα pα? a; let rb ← core zα pα? b
guard <|← withDefault <| withNewMCtxDepth <| isDefEq f q(ite (α := $α))
+ id <|
match ra, rb with
| .positive pa, .positive pb => pure (.positive q(ite_pos $p $pa $pb))
| .positive pa, .nonnegative pb => pure (.nonnegative q(ite_nonneg_of_pos_of_nonneg $p $pa $pb))
@@ -95,7 +96,7 @@ such that `positivity` successfully recognises both `a` and `b`. -/
let _a ← synthInstanceQ q(LinearOrder $α)
let ⟨_f_eq⟩ ← withDefault <| withNewMCtxDepth <| assertDefEqQ q($f) q(min)
assumeInstancesCommute
- match ← core zα pα? a, ← core zα pα? b with
+ match (dependent := true) ← core zα pα? a, ← core zα pα? b with
| .positive (pα := pα') pa, .positive pb =>
assumeInstancesCommute
pure (.positive q(lt_min $pa $pb))
@@ -127,7 +128,7 @@ is nonnegative, strictly positive if at least one is positive, and nonzero if bo
let ⟨_f_eq⟩ ← withDefault <| withNewMCtxDepth <| assertDefEqQ q($f) q(max)
let result : Strictness zα e pα? ← catchNone do
let ra ← core zα pα? a
- match ra with
+ match (dependent := true) ra with
| .positive pa =>
assumeInstancesCommute
pure (.positive q(lt_max_of_lt_left $pa))
@@ -140,7 +141,7 @@ is nonnegative, strictly positive if at least one is positive, and nonzero if bo
| _ => pure .none
orElse result do
let rb ← core zα pα? b
- match rb with
+ match (dependent := true) rb with
| .positive pb =>
assumeInstancesCommute
pure (.positive q(lt_max_of_lt_right $pb))
@@ -155,14 +156,14 @@ is nonnegative, strictly positive if at least one is positive, and nonzero if bo
/-- The `positivity` extension which identifies expressions of the form `a + b`,
such that `positivity` successfully recognises both `a` and `b`. -/
-@[positivity _ + _] def evalAdd : PositivityExt where eval {u α} zα pα? e := do
+@[positivity _ + _] def evalAdd : PositivityExt where eval {u α} zα pα? e :=
+ match pα? with | none => pure .none | some pα => do
let .app (.app (f : Q($α → $α → $α)) (a : Q($α))) (b : Q($α)) ← whnfR e | throwError "not +"
let _e_eq : $e =Q $f $a $b := ⟨⟩
let _a ← synthInstanceQ q(AddZeroClass $α)
assumeInstancesCommute
let ⟨_f_eq⟩ ← withDefault <| withNewMCtxDepth <| assertDefEqQ q($f) q(HAdd.hAdd)
- let ra ← core zα pα? a; let rb ← core zα pα? b
- let some _pα := pα? | pure .none
+ let ra ← core zα pα a; let rb ← core zα pα b
match ra, rb with
| .positive pa, .positive pb =>
let _a ← synthInstanceQ q(AddLeftMono $α)
@@ -186,8 +187,9 @@ such that there is a local hypothesis `b < a`, `b ≤ a`, `a ≠ b` or `b ≠ a`
let _a ← synthInstanceQ q(AddGroup $α)
assumeInstancesCommute
let ⟨_f_eq⟩ ← withDefault <| withNewMCtxDepth <| assertDefEqQ q($f) q(HSub.hSub)
+ id <|
match pα? with
- | some pα =>
+ | some pα => do
let mut result := .none
for decl in ← getLCtx do
unless decl.isImplementationDetail do
@@ -221,7 +223,7 @@ such that there is a local hypothesis `b < a`, `b ≤ a`, `a ≠ b` or `b ≠ a`
return .none
| _ => return .none
return result
- | none =>
+ | none => do
let mut result := .none
for decl in ← getLCtx do
unless decl.isImplementationDetail do
@@ -271,8 +273,9 @@ such that `positivity` successfully recognises both `a` and `b`. -/
let _a ← synthInstanceQ q(PosMulStrictMono $α)
assumeInstancesCommute
pure (.positive q(mul_pos $pa $pb))
+ id <|
match pα? with
- | some pα =>
+ | some pα => do
let mut result : Strictness zα e (some pα) := .none
result ← orElse result (tryProvePositive pα ra.toPositive rb.toPositive)
result ← orElse result (tryProveNonneg pα ra.toNonneg rb.toNonneg)
@@ -295,8 +298,8 @@ lemma int_div_nonneg_of_pos_of_pos {a b : ℤ} (ha : 0 < a) (hb : 0 < b) : 0 ≤
/-- The `positivity` extension which identifies expressions of the form `a / b`,
where `a` and `b` are integers. -/
-@[positivity (_ : ℤ) / (_ : ℤ)] def evalIntDiv : PositivityExt where eval {u α} _ pα? e := do
- let some _ := pα? | throwError "not PartialOrder ℤ"
+@[positivity (_ : ℤ) / (_ : ℤ)] def evalIntDiv : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℤ), ~q($a / $b) =>
let ra ← core q(inferInstance) (some q(inferInstance)) a
@@ -334,78 +337,82 @@ meta def evalPowZeroNat : PositivityExt where eval {u α} _zα pα? e := do
assumeInstancesCommute
haveI' : $e =Q $a ^ 0 := ⟨⟩
let _a ← synthInstanceQ q(Nontrivial $α)
- let some _pα := pα? | pure (.nonzero q(pow_zero_ne_zero $a))
- let _a ← synthInstanceQ q(IsOrderedRing $α)
- pure (.positive q(pow_zero_pos $a))
+ match (dependent := true) pα? with
+ | some _pα =>
+ let _a ← synthInstanceQ q(IsOrderedRing $α)
+ pure (.positive q(pow_zero_pos $a))
+ | none => pure (.nonzero q(pow_zero_ne_zero $a))
/-- The `positivity` extension which identifies expressions of the form `a ^ (b : ℕ)`,
such that `positivity` successfully recognises both `a` and `b`. -/
@[positivity _ ^ (_ : ℕ)]
meta def evalPow : PositivityExt where eval {u α} zα pα? e := do
let .app (.app _ (a : Q($α))) (b : Q(ℕ)) ← whnfR e | throwError "not ^"
- let some pα := pα? | do
+ match (dependent := true) pα? with
+ | none =>
let _a ← synthInstanceQ q(MonoidWithZero $α)
let _a ← synthInstanceQ q(NoZeroDivisors $α)
assumeInstancesCommute
haveI' : $e =Q $a ^ $b := ⟨⟩
let .nonzero nza ← core zα .none a | pure .none
pure (.nonzero q(pow_ne_zero $b $nza))
- let result : Strictness zα e pα ← catchNone do
- let _a ← synthInstanceQ q(Ring $α)
- let _a ← synthInstanceQ q(LinearOrder $α)
- let _a ← synthInstanceQ q(IsStrictOrderedRing $α)
- assumeInstancesCommute
- let .true := b.isAppOfArity ``OfNat.ofNat 3 | throwError "not a ^ n where n is a literal"
- let some n := (b.getRevArg! 1).rawNatLit? | throwError "not a ^ n where n is a literal"
- guard (n % 2 = 0)
- have m : Q(ℕ) := mkRawNatLit (n / 2)
- haveI' : $b =Q 2 * $m := ⟨⟩
- haveI' : $e =Q $a ^ $b := ⟨⟩
- pure (.nonnegative q((even_two_mul $m).pow_nonneg $a))
- orElse result do
- let ra ← core zα pα a
- let ofNonneg (pa : Q(0 ≤ $a)) (_rα : Q(Semiring $α)) (_oα : Q(IsOrderedRing $α)) :
- MetaM (Strictness zα e (some pα)) := do
- haveI' : $e =Q $a ^ $b := ⟨⟩
+ | some pα =>
+ let result : Strictness zα e pα ← catchNone do
+ let _a ← synthInstanceQ q(Ring $α)
+ let _a ← synthInstanceQ q(LinearOrder $α)
+ let _a ← synthInstanceQ q(IsStrictOrderedRing $α)
assumeInstancesCommute
- pure (.nonnegative q(pow_nonneg $pa $b))
- let ofNonzero (pa : Q($a ≠ 0)) (_rα : Q(Semiring $α)) (_oα : Q(IsOrderedRing $α)) :
- MetaM (Strictness zα e (some pα)) := do
+ let .true := b.isAppOfArity ``OfNat.ofNat 3 | throwError "not a ^ n where n is a literal"
+ let some n := (b.getRevArg! 1).rawNatLit? | throwError "not a ^ n where n is a literal"
+ guard (n % 2 = 0)
+ have m : Q(ℕ) := mkRawNatLit (n / 2)
+ haveI' : $b =Q 2 * $m := ⟨⟩
haveI' : $e =Q $a ^ $b := ⟨⟩
- assumeInstancesCommute
- let _a ← synthInstanceQ q(NoZeroDivisors $α)
- pure (.nonzero q(pow_ne_zero $b $pa))
- match ra with
- | .positive pa =>
- try
- let _a ← synthInstanceQ q(Semiring $α)
- let _a ← synthInstanceQ q(IsStrictOrderedRing $α)
+ pure (.nonnegative q((even_two_mul $m).pow_nonneg $a))
+ orElse result do
+ let ra ← core zα pα a
+ let ofNonneg (pa : Q(0 ≤ $a)) (_rα : Q(Semiring $α)) (_oα : Q(IsOrderedRing $α)) :
+ MetaM (Strictness zα e (some pα)) := do
+ haveI' : $e =Q $a ^ $b := ⟨⟩
assumeInstancesCommute
+ pure (.nonnegative q(pow_nonneg $pa $b))
+ let ofNonzero (pa : Q($a ≠ 0)) (_rα : Q(Semiring $α)) (_oα : Q(IsOrderedRing $α)) :
+ MetaM (Strictness zα e (some pα)) := do
haveI' : $e =Q $a ^ $b := ⟨⟩
- pure (.positive q(pow_pos $pa $b))
- catch e : Exception =>
- trace[Tactic.positivity.failure] "{e.toMessageData}"
- let rα ← synthInstanceQ q(Semiring $α)
- let oα ← synthInstanceQ q(IsOrderedRing $α)
- orElse (← catchNone (ofNonneg q(le_of_lt $pa) rα oα)) (ofNonzero q(ne_of_gt $pa) rα oα)
- | .nonnegative pa =>
- let sα ← synthInstanceQ q(Semiring $α)
- let oα ← synthInstanceQ q(IsOrderedRing $α)
- ofNonneg q($pa) q($sα) q($oα)
- | .nonzero pa =>
- let sα ← synthInstanceQ q(Semiring $α)
- let oα ← synthInstanceQ q(IsOrderedRing $α)
- ofNonzero q($pa) q($sα) q($oα)
- | .none => pure .none
+ assumeInstancesCommute
+ let _a ← synthInstanceQ q(NoZeroDivisors $α)
+ pure (.nonzero q(pow_ne_zero $b $pa))
+ match ra with
+ | .positive pa =>
+ try
+ let _a ← synthInstanceQ q(Semiring $α)
+ let _a ← synthInstanceQ q(IsStrictOrderedRing $α)
+ assumeInstancesCommute
+ haveI' : $e =Q $a ^ $b := ⟨⟩
+ pure (.positive q(pow_pos $pa $b))
+ catch e : Exception =>
+ trace[Tactic.positivity.failure] "{e.toMessageData}"
+ let rα ← synthInstanceQ q(Semiring $α)
+ let oα ← synthInstanceQ q(IsOrderedRing $α)
+ orElse (← catchNone (ofNonneg q(le_of_lt $pa) rα oα)) (ofNonzero q(ne_of_gt $pa) rα oα)
+ | .nonnegative pa =>
+ let sα ← synthInstanceQ q(Semiring $α)
+ let oα ← synthInstanceQ q(IsOrderedRing $α)
+ ofNonneg q($pa) q($sα) q($oα)
+ | .nonzero pa =>
+ let sα ← synthInstanceQ q(Semiring $α)
+ let oα ← synthInstanceQ q(IsOrderedRing $α)
+ ofNonzero q($pa) q($sα) q($oα)
+ | .none => pure .none
theorem abs_pos_of_ne_zero {α : Type*} [AddGroup α] [LinearOrder α]
[AddLeftMono α] {a : α} : a ≠ 0 → 0 < |a| := abs_pos.mpr
/-- The `positivity` extension which identifies expressions of the form `|a|`. -/
@[positivity |_|]
-meta def evalAbs : PositivityExt where eval {_u} (α zα pα?) (e : Q($α)) := do
+meta def evalAbs : PositivityExt where eval {_u} (α zα pα?) (e : Q($α)) :=
+ match pα? with | none => pure .none | some pα' => do
let ~q(@abs _ (_) (_) $a) := e | throwError "not |·|"
- let some pα' := pα? | pure .none
try
match ← core zα (some pα') a with
| .positive pa =>
@@ -427,8 +434,8 @@ Since the output type of `Int.natAbs` is `ℕ`, the nonnegative case is handled
`positivity` tactic.
-/
@[positivity Int.natAbs _]
-meta def evalNatAbs : PositivityExt where eval {u α} _zα pα? e := do
- let some _ := pα? | throwError "not PartialOrder ℕ"
+meta def evalNatAbs : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℕ), ~q(Int.natAbs $a) =>
let zα' : Q(Zero Int) := q(inferInstance)
@@ -453,25 +460,27 @@ meta def evalNatCast : PositivityExt where eval {u α} _zα pα? e := do
let ~q(@Nat.cast _ (_) ($a : ℕ)) := e | throwError "not Nat.cast"
let zα' : Q(Zero Nat) := q(inferInstance)
let (_i1 : Q(AddMonoidWithOne $α)) ← synthInstanceQ q(AddMonoidWithOne $α)
- let some _pα := pα? | do
+ match (dependent := true) pα? with
+ | none =>
let (_cz : Q(CharZero $α)) ← synthInstanceQ q(CharZero $α)
assumeInstancesCommute
match ← core zα' .none a with
| .nonzero nza => pure (.nonzero q(Nat.cast_ne_zero.2 $nza))
| _ => pure .none
- let pα' : Q(PartialOrder Nat) := q(inferInstance)
- let (_i2 : Q(AddLeftMono $α)) ← synthInstanceQ q(AddLeftMono $α)
- let (_i3 : Q(ZeroLEOneClass $α)) ← synthInstanceQ q(ZeroLEOneClass $α)
- assumeInstancesCommute
- match ← core zα' pα' a with
- | .positive pa =>
- try
- let _nz ← synthInstanceQ q(NeZero (1 : $α))
- pure (.positive q(Nat.cast_pos'.2 $pa))
- catch _ =>
+ | some _pα =>
+ let pα' : Q(PartialOrder Nat) := q(inferInstance)
+ let (_i2 : Q(AddLeftMono $α)) ← synthInstanceQ q(AddLeftMono $α)
+ let (_i3 : Q(ZeroLEOneClass $α)) ← synthInstanceQ q(ZeroLEOneClass $α)
+ assumeInstancesCommute
+ match ← core zα' pα' a with
+ | .positive pa =>
+ try
+ let _nz ← synthInstanceQ q(NeZero (1 : $α))
+ pure (.positive q(Nat.cast_pos'.2 $pa))
+ catch _ =>
+ pure (.nonnegative q(Nat.cast_nonneg' _))
+ | _ =>
pure (.nonnegative q(Nat.cast_nonneg' _))
- | _ =>
- pure (.nonnegative q(Nat.cast_nonneg' _))
/-- Extension for the `positivity` tactic: `Int.cast` is positive (resp. non-negative)
if its input is. -/
@@ -481,7 +490,7 @@ meta def evalIntCast : PositivityExt where eval {u α} _zα pα? e := do
let zα' : Q(Zero Int) := q(inferInstance)
let pα' : Q(PartialOrder Int) := q(inferInstance)
let ra ← core zα' pα' a
- match ra, pα? with
+ match (dependent := true) ra, pα? with
| .positive pa, some _ =>
let _rα ← synthInstanceQ q(Ring $α)
let _oα ← synthInstanceQ q(IsOrderedRing $α)
@@ -504,8 +513,8 @@ meta def evalIntCast : PositivityExt where eval {u α} _zα pα? e := do
/-- Extension for `Nat.succ`. -/
@[positivity Nat.succ _]
-meta def evalNatSucc : PositivityExt where eval {u α} _zα pα? e := do
- let some _ := pα? | throwError "not PartialOrder ℕ"
+meta def evalNatSucc : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => throwError "not PartialOrder ℕ" | some _ => do
match u, α, e with
| 0, ~q(ℕ), ~q(Nat.succ $a) =>
assertInstancesCommute
@@ -514,8 +523,8 @@ meta def evalNatSucc : PositivityExt where eval {u α} _zα pα? e := do
/-- Extension for `PNat.val`. -/
@[positivity PNat.val _]
-meta def evalPNatVal : PositivityExt where eval {u α} _zα pα? e := do
- let some _ := pα? | throwError "not PartialOrder ℕ"
+meta def evalPNatVal : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => throwError "not PartialOrder ℕ" | some _ => do
match u, α, e with
| 0, ~q(ℕ), ~q(PNat.val $a) =>
assertInstancesCommute
@@ -524,8 +533,8 @@ meta def evalPNatVal : PositivityExt where eval {u α} _zα pα? e := do
/-- Extension for `Nat.factorial`. -/
@[positivity Nat.factorial _]
-meta def evalFactorial : PositivityExt where eval {u α} _ pα? e := do
- let some _ := pα? | throwError "not PartialOrder ℕ"
+meta def evalFactorial : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => throwError "not PartialOrder ℕ" | some _ => do
match u, α, e with
| 0, ~q(ℕ), ~q(Nat.factorial $a) =>
assertInstancesCommute
@@ -534,8 +543,8 @@ meta def evalFactorial : PositivityExt where eval {u α} _ pα? e := do
/-- Extension for `Nat.ascFactorial`. -/
@[positivity Nat.ascFactorial _ _]
-meta def evalAscFactorial : PositivityExt where eval {u α} _ pα? e := do
- let some _ := pα? | throwError "not PartialOrder ℕ"
+meta def evalAscFactorial : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => throwError "not PartialOrder ℕ" | some _ => do
match u, α, e with
| 0, ~q(ℕ), ~q(Nat.ascFactorial ($n + 1) $k) =>
assertInstancesCommute
@@ -547,7 +556,8 @@ Uses positivity of the left term, if available, then tries the right term.
The implementation relies on the fact that `Positivity.core` on `ℕ` never returns `nonzero`. -/
@[positivity Nat.gcd _ _]
-meta def evalNatGCD : PositivityExt where eval {u α} z p e := do
+meta def evalNatGCD : PositivityExt where eval {u α} z p e :=
+ match p with | none => throwError "not PartialOrder ℕ" | some p => do
match u, α, e with
| 0, ~q(ℕ), ~q(Nat.gcd $a $b) =>
assertInstancesCommute
@@ -565,7 +575,8 @@ meta def evalNatGCD : PositivityExt where eval {u α} z p e := do
/-- Extension for `Nat.lcm`. -/
@[positivity Nat.lcm _ _]
-meta def evalNatLCM : PositivityExt where eval {u α} z p e := do
+meta def evalNatLCM : PositivityExt where eval {u α} z p e :=
+ match p with | none => throwError "not PartialOrder ℕ" | some p => do
match u, α, e with
| 0, ~q(ℕ), ~q(Nat.lcm $a $b) =>
match ← core z p a with
@@ -581,7 +592,8 @@ meta def evalNatLCM : PositivityExt where eval {u α} z p e := do
/-- Extension for `Nat.sqrt`. -/
@[positivity Nat.sqrt _]
-meta def evalNatSqrt : PositivityExt where eval {u α} z p e := do
+meta def evalNatSqrt : PositivityExt where eval {u α} z p e :=
+ match p with | none => throwError "not PartialOrder ℕ" | some p => do
match u, α, e with
| 0, ~q(ℕ), ~q(Nat.sqrt $n) =>
match ← core z p n with
@@ -594,8 +606,8 @@ meta def evalNatSqrt : PositivityExt where eval {u α} z p e := do
/-- Extension for `Int.gcd`.
Uses positivity of the left term, if available, then tries the right term. -/
@[positivity Int.gcd _ _]
-meta def evalIntGCD : PositivityExt where eval {u α} _ pα? e := do
- let some _ := pα? | throwError "not PartialOrder ℕ"
+meta def evalIntGCD : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => throwError "not PartialOrder ℕ" | some _ => do
match u, α, e with
| 0, ~q(ℕ), ~q(Int.gcd $a $b) =>
let z ← synthInstanceQ (q(Zero ℤ) : Q(Type))
@@ -611,8 +623,8 @@ meta def evalIntGCD : PositivityExt where eval {u α} _ pα? e := do
/-- Extension for `Int.lcm`. -/
@[positivity Int.lcm _ _]
-meta def evalIntLCM : PositivityExt where eval {u α} _ pα? e := do
- let some _ := pα? | throwError "not PartialOrder ℕ"
+meta def evalIntLCM : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => throwError "not PartialOrder ℕ" | some _ => do
match u, α, e with
| 0, ~q(ℕ), ~q(Int.lcm $a $b) =>
let z ← synthInstanceQ (q(Zero ℤ) : Q(Type))
@@ -635,8 +647,8 @@ alias ⟨_, NNRat.num_ne_zero_of_ne_zero⟩ := num_ne_zero
/-- The `positivity` extension which identifies expressions of the form `NNRat.num q`,
such that `positivity` successfully recognises `q`. -/
@[positivity NNRat.num _]
-meta def evalNNRatNum : PositivityExt where eval {u α} _ pα? e := do
- let some _ := pα? | throwError "not PartialOrder ℕ"
+meta def evalNNRatNum : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => throwError "not PartialOrder ℕ" | some _ => do
match u, α, e with
| 0, ~q(ℕ), ~q(NNRat.num $a) =>
let zα : Q(Zero ℚ≥0) := q(inferInstance)
@@ -652,8 +664,8 @@ meta def evalNNRatNum : PositivityExt where eval {u α} _ pα? e := do
/-- The `positivity` extension which identifies expressions of the form `Rat.den a`. -/
@[positivity NNRat.den _]
-meta def evalNNRatDen : PositivityExt where eval {u α} _ pα? e := do
- let some _ := pα? | throwError "not PartialOrder ℕ"
+meta def evalNNRatDen : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => throwError "not PartialOrder ℕ" | some _ => do
match u, α, e with
| 0, ~q(ℕ), ~q(NNRat.den $a) =>
assumeInstancesCommute
@@ -677,8 +689,8 @@ alias ⟨_, num_ne_zero_of_ne_zero⟩ := num_ne_zero
/-- The `positivity` extension which identifies expressions of the form `Rat.num a`,
such that `positivity` successfully recognises `a`. -/
@[positivity Rat.num _]
-meta def evalRatNum : PositivityExt where eval {u α} _ pα? e := do
- let some _ := pα? | throwError "not PartialOrder ℤ"
+meta def evalRatNum : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => throwError "not PartialOrder ℤ" | some _ => do
match u, α, e with
| 0, ~q(ℤ), ~q(Rat.num $a) =>
let zα : Q(Zero ℚ) := q(inferInstance)
@@ -695,8 +707,8 @@ meta def evalRatNum : PositivityExt where eval {u α} _ pα? e := do
/-- The `positivity` extension which identifies expressions of the form `Rat.den a`. -/
@[positivity Rat.den _]
-meta def evalRatDen : PositivityExt where eval {u α} _ pα? e := do
- let some _ := pα? | throwError "not PartialOrder ℕ"
+meta def evalRatDen : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => throwError "not PartialOrder ℕ" | some _ => do
match u, α, e with
| 0, ~q(ℕ), ~q(Rat.den $a) =>
assumeInstancesCommute
@@ -705,8 +717,8 @@ meta def evalRatDen : PositivityExt where eval {u α} _ pα? e := do
/-- Extension for `posPart`. `a⁺` is always nonnegative, and positive if `a` is. -/
@[positivity _⁺]
-meta def evalPosPart : PositivityExt where eval {u α} zα pα? e := do
- let some pα := pα? | pure .none
+meta def evalPosPart : PositivityExt where eval {u α} zα pα? e :=
+ match pα? with | none => pure .none | some pα => do
match e with
| ~q(@posPart _ $instαpospart $a) =>
let _instαlat ← synthInstanceQ q(Lattice $α)
@@ -723,8 +735,8 @@ meta def evalPosPart : PositivityExt where eval {u α} zα pα? e := do
/-- Extension for `negPart`. `a⁻` is always nonnegative. -/
@[positivity _⁻]
-meta def evalNegPart : PositivityExt where eval {u α} _ pα? e := do
- let some _ := pα? | pure .none
+meta def evalNegPart : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match e with
| ~q(@negPart _ $instαnegpart $a) =>
let _instαlat ← synthInstanceQ q(Lattice $α)
@@ -735,8 +747,8 @@ meta def evalNegPart : PositivityExt where eval {u α} _ pα? e := do
/-- Extension for the `positivity` tactic: nonnegative maps take nonnegative values. -/
@[positivity DFunLike.coe _ _]
-meta def evalMap : PositivityExt where eval {_ β} _ pβ? e := do
- let some _ := pβ? | pure .none
+meta def evalMap : PositivityExt where eval {_ β} _ pβ? e :=
+ match pβ? with | none => pure .none | some _ => do
let .app (.app _ f) a ← whnfR e
| throwError "not ↑f · where f is of NonnegHomClass"
let pa ← mkAppOptM ``apply_nonneg #[none, none, β, none, none, none, none, f, a]
diff --git a/Mathlib/Tactic/Positivity/Core.lean b/Mathlib/Tactic/Positivity/Core.lean
index 45a3faaa15fde1..b69cd0624625f5 100644
--- a/Mathlib/Tactic/Positivity/Core.lean
+++ b/Mathlib/Tactic/Positivity/Core.lean
@@ -417,17 +417,17 @@ It assumes `t₁` has already been run for a result, and runs `t₂` and takes t
It will skip `t₂` if `t₁` is already a proof of `.positive`, and can also combine
`.nonnegative` and `.nonzero` to produce a `.positive` result. -/
def orElse {pα?} {e : Q($α)} (t₁ : Strictness zα e pα?) (t₂ : MetaM (Strictness zα e pα?)) :
- MetaM (Strictness zα e pα?) := do
+ MetaM (Strictness zα e pα?) :=
match t₁ with
| .none => catchNone t₂
| p@(.positive _) => pure p
- | .nonnegative p₁ =>
+ | .nonnegative p₁ => do
match ← catchNone t₂ with
| p@(.positive _) => pure p
| .nonzero p₂ => pure (.positive q(lt_of_le_of_ne' $p₁ $p₂))
| _ => pure (.nonnegative p₁)
- | .nonzero p₁ =>
- match ← catchNone t₂ with
+ | .nonzero p₁ => do
+ match (dependent := true) ← catchNone t₂ with
| p@(.positive _) => pure p
| .nonnegative p₂ => pure (.positive q(lt_of_le_of_ne' $p₂ $p₁))
| _ => pure (.nonzero p₁)
@@ -442,22 +442,22 @@ def core (pα? : Option Q(PartialOrder $α)) (e : Q($α)) : MetaM (Strictness z
catch err =>
trace[Tactic.positivity] "{e} failed: {err.toMessageData}"
trace[Tactic.positivity] "current result from positivity extensions: {result.toString}"
- match pα? with
- | some pα =>
+ match h : pα?, result with
+ | some pα, res =>
trace[Tactic.positivity] "{α} has some {pα}"
- result ← orElse result <| normNumPositivity zα pα e
- trace[Tactic.positivity] "current result from normNum: {result.toString}"
- result ← orElse result <| positivityCanon zα pα e
- trace[Tactic.positivity] "current result from canonicity: {result.toString}"
- if let .positive _ := result then
- trace[Tactic.positivity] "{e} => {result.toString}"
- return result
+ let mut res ← orElse res <| normNumPositivity zα pα e
+ trace[Tactic.positivity] "current result from normNum: {res.toString}"
+ res ← orElse res <| positivityCanon zα pα e
+ trace[Tactic.positivity] "current result from canonicity: {res.toString}"
+ if let .positive _ := res then
+ trace[Tactic.positivity] "{e} => {res.toString}"
+ return h ▸ res
for ldecl in ← getLCtx do
if !ldecl.isImplementationDetail then
- result ← orElse result <| compareHyp zα pα e ldecl
- trace[Tactic.positivity] "{e} => {result.toString}"
- throwNone (pure result)
- | .none =>
+ res ← orElse res <| compareHyp zα pα e ldecl
+ trace[Tactic.positivity] "{e} => {res.toString}"
+ throwNone (pure (h ▸ res))
+ | .none, _ =>
trace[Tactic.positivity] "{α} has no PartialOrder"
if let .nonzero _ := result then
trace[Tactic.positivity] "{e} => {result.toString}"
@@ -486,10 +486,10 @@ def bestResult (e : Expr) : MetaM (Bool × Expr) := do
let zα ← synthInstanceQ q(Zero $α)
let pα? ← try? <| synthInstanceQ q(PartialOrder $α)
assumeInstancesCommute
- match ← try? (Meta.Positivity.core zα pα? e) with
- | some (.positive pf) => pure (true, pf)
- | some (.nonnegative pf) => pure (false, pf)
- | _ => throwError "could not establish the nonnegativity of {e}"
+ match pα?, ← try? (Meta.Positivity.core zα pα? e) with
+ | _, some (.positive pf) => pure (true, pf)
+ | _, some (.nonnegative pf) => pure (false, pf)
+ | _, _ => throwError "could not establish the nonnegativity of {e}"
/-- Given an expression `e`, use the core method of the `positivity` tactic to prove it nonnegative.
-/
@@ -508,7 +508,8 @@ def solve (t : Q(Prop)) : MetaM Expr := do
let r ← catchNone <| Meta.Positivity.core zα pα? e
let throw (a b : String) : MetaM Expr := throwError
"failed to prove {a}, but it would be possible to prove {b} if desired"
- if let some _ := pα? then
+ match (dependent := true) pα? with
+ | some _ =>
match relDesired, r with
| .lt, .positive p
| .le, .nonnegative p
@@ -523,7 +524,7 @@ def solve (t : Q(Prop)) : MetaM Expr := do
| .ne, .nonnegative _
| .ne', .nonnegative _ => throw "nonzeroness" "nonnegativity"
| _, .none => throwError "failed to prove positivity/nonnegativity/nonzeroness"
- else
+ | none =>
match relDesired, r with
| .ne, .nonzero p => pure p
| .ne', .nonzero p => pure q(Ne.symm $p)
diff --git a/Mathlib/Tactic/Positivity/Finset.lean b/Mathlib/Tactic/Positivity/Finset.lean
index 50fb47d5c610e6..139070a33db6d9 100644
--- a/Mathlib/Tactic/Positivity/Finset.lean
+++ b/Mathlib/Tactic/Positivity/Finset.lean
@@ -27,10 +27,10 @@ open Qq Lean Meta Finset
It calls `Mathlib.Meta.proveFinsetNonempty` to attempt proving that the finset is nonempty. -/
@[positivity Finset.card _]
-meta def evalFinsetCard : PositivityExt where eval {u α} _ pα? e := do
+meta def evalFinsetCard : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℕ), ~q(Finset.card $s) =>
- let some _ := pα? | pure .none
let some ps ← proveFinsetNonempty s | return .none
assertInstancesCommute
return .positive q(Finset.Nonempty.card_pos $ps)
@@ -38,10 +38,10 @@ meta def evalFinsetCard : PositivityExt where eval {u α} _ pα? e := do
/-- Extension for `Fintype.card`. `Fintype.card α` is positive if `α` is nonempty. -/
@[positivity Fintype.card _]
-meta def evalFintypeCard : PositivityExt where eval {u α} _ pα? e := do
+meta def evalFintypeCard : PositivityExt where eval {u α} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℕ), ~q(@Fintype.card $β $instβ) =>
- let some _ := pα? | pure .none
let instβno ← synthInstanceQ q(Nonempty $β)
assumeInstancesCommute
return .positive q(@Fintype.card_pos $β $instβ $instβno)
@@ -51,10 +51,10 @@ meta def evalFintypeCard : PositivityExt where eval {u α} _ pα? e := do
It calls `Mathlib.Meta.proveFinsetNonempty` to attempt proving that the finset is nonempty. -/
@[positivity Finset.dens _]
-meta def evalFinsetDens : PositivityExt where eval {u 𝕜} _ pα? e := do
+meta def evalFinsetDens : PositivityExt where eval {u 𝕜} _ pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, 𝕜, e with
| 0, ~q(ℚ≥0), ~q(@Finset.dens $α $instα $s) =>
- let some _ := pα? | pure .none
let some ps ← proveFinsetNonempty s | return .none
assumeInstancesCommute
return .positive q(@Nonempty.dens_pos $α $instα $s $ps)
@@ -71,21 +71,23 @@ example (s : Finset ℕ) (f : ℕ → ℤ) (hf : ∀ n, 0 ≤ f n) : 0 ≤ s.sum
because `compareHyp` can't look for assumptions behind binders.
-/
@[positivity Finset.sum _ _]
-meta def evalFinsetSum : PositivityExt where eval {u α} zα pα? e := do
+meta def evalFinsetSum : PositivityExt where eval {u α} zα pα? e :=
+ match pα? with
+ | none => pure .none -- TODO: the case without PartialOrder
+ | some pα => do
match e with
| ~q(@Finset.sum $ι _ $instα $s $f) =>
let i : Q($ι) ← mkFreshExprMVarQ q($ι) .syntheticOpaque
have body : Q($α) := .betaRev f #[i]
- let rbody ← core zα pα? body
- let some pα := pα? | pure .none -- TODO: the case without PartialOrder
- let p_pos : Option Q(0 < $e) := ← (do
+ let rbody ← core zα pα body
+ let p_pos : Option Q(0 < $e) ← do
let .positive pbody := rbody | pure none -- Fail if the body is not provably positive
let some ps ← proveFinsetNonempty s | pure none
let .some pα' ← trySynthInstanceQ q(IsOrderedCancelAddMonoid $α) | pure none
assertInstancesCommute
let pr : Q(∀ i, 0 < $f i) ← mkLambdaFVars #[i] pbody
- return some q(@sum_pos $ι $α $instα (@PartialOrder.toPreorder _ $pα) $pα' $f $s _
- (fun i _ ↦ $pr i) $ps))
+ pure <| some q(@sum_pos $ι $α $instα (@PartialOrder.toPreorder _ $pα) $pα' $f $s _
+ (fun i _ ↦ $pr i) $ps)
-- Try to show that the sum is positive
if let some p_pos := p_pos then
return .positive p_pos
diff --git a/Mathlib/Tactic/ReduceModChar.lean b/Mathlib/Tactic/ReduceModChar.lean
index 11702f4f5e7b11..115eed60892f84 100644
--- a/Mathlib/Tactic/ReduceModChar.lean
+++ b/Mathlib/Tactic/ReduceModChar.lean
@@ -258,9 +258,10 @@ partial def derive (expensive := false) (e : Expr) : MetaM Simp.Result := do
(simpTheorems := #[← ext.getTheorems])
let discharge := Mathlib.Meta.NormNum.discharge
let r : Simp.Result := {expr := e}
- let pre := Simp.preDefault #[] >> fun e =>
+ let matchAndNorm : Simproc := fun e =>
try return (Simp.Step.done (← matchAndNorm (expensive := expensive) e))
catch _ => pure .continue
+ let pre := Simp.preDefault #[] >> matchAndNorm
let post := Simp.postDefault #[]
let r ← r.mkEqTrans (← Simp.main r.expr ctx (methods := { pre, post, discharge? := discharge })).1
diff --git a/Mathlib/Tactic/Ring/Basic.lean b/Mathlib/Tactic/Ring/Basic.lean
index 6875ffafe604ec..ea4a8ac1fb1c2c 100644
--- a/Mathlib/Tactic/Ring/Basic.lean
+++ b/Mathlib/Tactic/Ring/Basic.lean
@@ -231,7 +231,7 @@ partial def ExProd.evalNatCast {a : Q(ℕ)} (va : ExProd sβ a) : AtomM (Result
-/
partial def ExSum.evalNatCast {a : Q(ℕ)} (va : ExSum sβ a) : AtomM (Result (ExSum sα) q($a)) := do
assumeInstancesCommute
- match va with
+ match (dependent := true) va with
| .zero => pure ⟨_, .zero, q(natCast_zero (R := $α))⟩
| .add va₁ va₂ => do
let ⟨_, vb₁, pb₁⟩ ← ExProd.evalNatCast va₁
@@ -378,14 +378,13 @@ partial def add {u : Lean.Level} {α : Q(Type u)} (sα : Q(CommSemiring $α))
{a b : Q($α)} (za : RatCoeff a) (zb : RatCoeff b) :
MetaM (Result RatCoeff q($a + $b) × Option Q(IsNat ($a + $b) 0)) := do
let res ← za.toResult.add zb.toResult
- let isZero : MetaM (Option Q(IsNat ($a + $b) 0)) ← match res with
- | Result.isNat inst lit pf => do
- if lit.natLit! == 0 then
- have : $lit =Q 0 := ⟨⟩
- pure <| some q($pf)
- else
- pure none
- | _ => pure none
+ let isZero ← match res with
+ | Result.isNat _inst lit pf =>
+ if lit.natLit! == 0 then
+ pure <| some (pf : Q(IsNat ($a + $b) 0))
+ else
+ pure none
+ | _ => pure none
let r ← RatCoeff.ofResult res
return ⟨r, isZero⟩
diff --git a/Mathlib/Tactic/Ring/Common.lean b/Mathlib/Tactic/Ring/Common.lean
index bba23ff5496407..868611fdfd5cc9 100644
--- a/Mathlib/Tactic/Ring/Common.lean
+++ b/Mathlib/Tactic/Ring/Common.lean
@@ -525,7 +525,7 @@ and `xy + -xy = 0` is a `.zero` overlap.
def evalAddOverlap {a b : Q($α)} (va : ExProd bt sα a) (vb : ExProd bt sα b) :
OptionT MetaM (Overlap bt sα q($a + $b)) := do
Lean.Core.checkSystem decl_name%.toString
- match va, vb with
+ match (dependent := true) va, vb with
| .const za, .const zb => do
let ⟨⟨_, zc, pf⟩, isZero⟩ ← rc.add za zb
match isZero with
@@ -568,12 +568,12 @@ theorem add_pf_add_gt (b₁ : R) (_ : a + b₂ = c) : a + (b₁ + b₂) = b₁ +
* `(a₁ + a₂) + (b₁ + b₂) = b₁ + ((a₁ + a₂) + b₂)` (if not `a₁.lt b₁`)
-/
partial def evalAdd {a b : Q($α)} (va : ExSum bt sα a) (vb : ExSum bt sα b) :
- MetaM <| Result (ExSum bt sα) q($a + $b) := do
- Lean.Core.checkSystem decl_name%.toString
+ MetaM <| Result (ExSum bt sα) q($a + $b) :=
+ Lean.Core.checkSystem decl_name%.toString *>
match va, vb with
- | .zero, vb => return ⟨b, vb, q(add_pf_zero_add $b)⟩
- | va, .zero => return ⟨a, va, q(add_pf_add_zero $a)⟩
- | .add (a := a₁) (b := _a₂) va₁ va₂, .add (a := b₁) (b := _b₂) vb₁ vb₂ =>
+ | .zero, vb => do return ⟨b, vb, q(add_pf_zero_add $b)⟩
+ | va, .zero => do return ⟨a, va, q(add_pf_add_zero $a)⟩
+ | .add (a := a₁) (b := _a₂) va₁ va₂, .add (a := b₁) (b := _b₂) vb₁ vb₂ => do
have va := .add va₁ va₂; have vb := .add vb₁ vb₂ -- FIXME: why does `va@(...)` fail?
match ← (evalAddOverlap rc rcℕ va₁ vb₁).run with
| some (.nonzero ⟨_, vc₁, pc₁⟩) =>
@@ -619,20 +619,20 @@ theorem mul_pp_pf_overlap {ea eb e : ℕ} (x : R) (_ : ea + eb = e) (_ : a₂ *
* `(a₁ * a₂) * (b₁ * b₂) = b₁ * ((a₁ * a₂) * b₂)` (if not `a₁.lt b₁`)
-/
partial def evalMulProd {a b : Q($α)} (va : ExProd bt sα a) (vb : ExProd bt sα b) :
- MetaM <| Result (ExProd bt sα) q($a * $b) := do
- Lean.Core.checkSystem decl_name%.toString
+ MetaM <| Result (ExProd bt sα) q($a * $b) :=
+ Lean.Core.checkSystem decl_name%.toString *>
match va, vb with
- | .const za, .const zb =>
+ | .const za, .const zb => do
let ⟨_, zc, pf⟩ ← rc.mul za zb
assumeInstancesCommute
return ⟨_, .const zc, q($pf)⟩
- | .mul (x := a₁) (e := a₂) va₁ va₂ va₃, vb@(.const _) =>
+ | .mul (x := a₁) (e := a₂) va₁ va₂ va₃, vb@(.const _) => do
let ⟨_, vc, pc⟩ ← evalMulProd va₃ vb
return ⟨_, .mul va₁ va₂ vc, q(mul_pf_left $a₁ $a₂ $pc)⟩
- | va@(.const _), .mul (x := b₁) (e := b₂) vb₁ vb₂ vb₃ =>
+ | va@(.const _), .mul (x := b₁) (e := b₂) vb₁ vb₂ vb₃ => do
let ⟨_, vc, pc⟩ ← evalMulProd va vb₃
return ⟨_, .mul vb₁ vb₂ vc, q(mul_pf_right $b₁ $b₂ $pc)⟩
- | .mul (x := xa) (e := ea) vxa vea va₂, .mul (x := xb) (e := eb) vxb veb vb₂ =>
+ | .mul (x := xa) (e := ea) vxa vea va₂, .mul (x := xb) (e := eb) vxb veb vb₂ => do
have va := .mul vxa vea va₂; have vb := .mul vxb veb vb₂ -- FIXME: why does `va@(...)` fail?
let ⟨ea', vea'⟩ := vea.toExProd
let ⟨eb', veb'⟩ := veb.toExProd
@@ -664,10 +664,10 @@ theorem mul_add {d : R} (_ : (a : R) * b₁ = c₁) (_ : a * b₂ = c₂) (_ : c
* `a * (b₁ + b₂) = (a * b₁) + (a * b₂)`
-/
def evalMul₁ {a b : Q($α)} (va : ExProd bt sα a) (vb : ExSum bt sα b) :
- MetaM <| Result (ExSum bt sα) q($a * $b) := do
+ MetaM <| Result (ExSum bt sα) q($a * $b) :=
match vb with
- | .zero => return ⟨_, .zero, q(mul_zero $a)⟩
- | .add vb₁ vb₂ =>
+ | .zero => do return ⟨_, .zero, q(mul_zero $a)⟩
+ | .add vb₁ vb₂ => do
let ⟨_, vc₁, pc₁⟩ ← evalMulProd rc rcℕ va vb₁
let ⟨_, vc₂, pc₂⟩ ← evalMul₁ va vb₂
let ⟨_, vd, pd⟩ ← evalAdd rc rcℕ vc₁.toSum vc₂
@@ -684,10 +684,10 @@ theorem add_mul {d : R} (_ : (a₁ : R) * b = c₁) (_ : a₂ * b = c₂) (_ : c
* `(a₁ + a₂) * b = (a₁ * b) + (a₂ * b)`
-/
def evalMul {a b : Q($α)} (va : ExSum bt sα a) (vb : ExSum bt sα b) :
- MetaM <| Result (ExSum bt sα) q($a * $b) := do
+ MetaM <| Result (ExSum bt sα) q($a * $b) :=
match va with
- | .zero => return ⟨_, .zero, q(zero_mul $b)⟩
- | .add va₁ va₂ =>
+ | .zero => do return ⟨_, .zero, q(zero_mul $b)⟩
+ | .add va₁ va₂ => do
let ⟨_, vc₁, pc₁⟩ ← evalMul₁ rc rcℕ va₁ vb
let ⟨_, vc₂, pc₂⟩ ← evalMul va₂ vb
let ⟨_, vd, pd⟩ ← evalAdd rc rcℕ vc₁ vc₂
@@ -707,13 +707,13 @@ theorem neg_mul {R} [CommRing R] (a₁ : R) (a₂) {a₃ b : R}
* `-(a₁ * a₂) = a₁ * -a₂`
-/
def evalNegProd {a : Q($α)} (rα : Q(CommRing $α)) (va : ExProd bt sα a) :
- MetaM <| Result (ExProd bt sα) q(-$a) := do
- Lean.Core.checkSystem decl_name%.toString
+ MetaM <| Result (ExProd bt sα) q(-$a) :=
+ Lean.Core.checkSystem decl_name%.toString *>
match va with
- | .const za =>
+ | .const za => do
let ⟨b, zb, pb⟩ ← rc.neg q($rα) za
return ⟨b, .const zb, q($pb)⟩
- | .mul (x := a₁) (e := a₂) va₁ va₂ va₃ =>
+ | .mul (x := a₁) (e := a₂) va₁ va₂ va₃ => do
let ⟨_, vb, pb⟩ ← evalNegProd rα va₃
assumeInstancesCommute
return ⟨_, .mul va₁ va₂ vb, q(neg_mul $a₁ $a₂ $pb)⟩
@@ -730,11 +730,13 @@ theorem neg_add {R} [CommRing R] {a₁ a₂ b₁ b₂ : R}
* `-(a₁ + a₂) = -a₁ + -a₂`
-/
def evalNeg {a : Q($α)} (rα : Q(CommRing $α)) (va : ExSum bt sα a) :
- MetaM <| Result (ExSum bt sα) q(-$a) := do
- assumeInstancesCommute
+ MetaM <| Result (ExSum bt sα) q(-$a) :=
match va with
- | .zero => return ⟨_, .zero, q(neg_zero (R := $α))⟩
- | .add va₁ va₂ =>
+ | .zero => do
+ assumeInstancesCommute
+ return ⟨_, .zero, q(neg_zero (R := $α))⟩
+ | .add va₁ va₂ => do
+ assumeInstancesCommute
let ⟨_, vb₁, pb₁⟩ ← evalNegProd rc rα va₁
let ⟨_, vb₂, pb₂⟩ ← evalNeg rα va₂
return ⟨_, .add vb₁ vb₂, q(neg_add $pb₁ $pb₂)⟩
@@ -913,9 +915,9 @@ In all other cases we use `evalPowProdAtom`.
def evalPowProd {a : Q($α)} {b : Q(ℕ)} (va : ExProd bt sα a) (vb : ExProdNat b) :
MetaM <| Result (ExProd bt sα) q($a ^ $b) := do
Lean.Core.checkSystem decl_name%.toString
- let res : OptionT MetaM (Result (ExProd bt sα) q($a ^ $b)) := do
+ let res : OptionT MetaM (Result (ExProd bt sα) q($a ^ $b)) :=
match va with
- | va@(.const za) =>
+ | va@(.const za) => do
match rc.isOne za with
| .some pf =>
return ⟨_, va, q(one_pow $b $pf)⟩
@@ -923,7 +925,7 @@ def evalPowProd {a : Q($α)} {b : Q(ℕ)} (va : ExProd bt sα a) (vb : ExProdNat
-- NOTE: rc.pow may fail, e.g. for `ring` when `vb` is not a constant.
let ⟨_, zc, pc⟩ ← rc.pow za vb
return ⟨_, .const zc, q($pc)⟩
- | .mul vxa₁ (e := ea₁) vea₁ va₂ =>
+ | .mul vxa₁ (e := ea₁) vea₁ va₂ => do
let ⟨ea₁', vea₁'⟩ := vea₁.toExProd
let ⟨b', vb'⟩ := vb.toExProd
let ⟨c₁, vc₁, pc₁⟩ ← evalMulProd rcℕ rcℕ vea₁' vb'
@@ -1002,15 +1004,15 @@ Otherwise `a ^ b` is just encoded as `a ^ b * 1 + 0` using `evalPowAtom`.
-/
partial def evalPow₁ {a : Q($α)} {b : Q(ℕ)} (va : ExSum bt sα a) (vb : ExProdNat b) :
MetaM <| Result (ExSum bt sα) q($a ^ $b) := do
- let notPowOne : MetaM <| Result (ExSum bt sα) q($a ^ $b) := do
+ let notPowOne : MetaM <| Result (ExSum bt sα) q($a ^ $b) :=
match va with
- | .zero => match vb.evalPos with
+ | .zero => do match vb.evalPos with
| some p => return ⟨_, .zero, q(zero_pow (R := $α) $p)⟩
| none => return evalPowAtom rc (.sum .zero) vb
- | ExSum.add va .zero => -- TODO: using `.add` here takes a while to compile?
+ | ExSum.add va .zero => do -- TODO: using `.add` here takes a while to compile?
let ⟨_, vc, pc⟩ ← evalPowProd rc rcℕ va vb
return ⟨_, vc.toSum, q(single_pow $pc)⟩
- | va =>
+ | va => do
-- FIXME: condition used to be k.coeff > 1. Should go back to something like this.
let ⟨k, _, vc, pc⟩ := extractCoeff rcℕ vb
if k.natLit! > 1 then
@@ -1020,7 +1022,7 @@ partial def evalPow₁ {a : Q($α)} {b : Q(ℕ)} (va : ExSum bt sα a) (vb : ExP
else
return evalPowAtom rc (.sum va) vb
match vb with
- | .const zb =>
+ | .const zb => do
match rcℕ.isOne zb with
| .some pf =>
assumeInstancesCommute
@@ -1042,13 +1044,13 @@ theorem pow_add {b₁ b₂ : ℕ} {d : R}
* `a ^ (b₁ + b₂) = a ^ b₁ * a ^ b₂`
-/
def evalPow {a : Q($α)} {b : Q(ℕ)} (va : ExSum bt sα a) (vb : ExSumNat b) :
- MetaM <| Result (ExSum bt sα) q($a ^ $b) := do
+ MetaM <| Result (ExSum bt sα) q($a ^ $b) :=
match vb with
- | .zero =>
+ | .zero => do
let ⟨_, one, pf⟩ := rc.one
assumeInstancesCommute
return ⟨_, (ExProd.const (one)).toSum, q(pow_zero $a $pf)⟩
- | .add vb₁ vb₂ =>
+ | .add vb₁ vb₂ => do
let ⟨_, vc₁, pc₁⟩ ← evalPow₁ rc rcℕ va vb₁
let ⟨_, vc₂, pc₂⟩ ← evalPow va vb₂
let ⟨_, vd, pd⟩ ← evalMul rc rcℕ vc₁ vc₂
@@ -1130,10 +1132,10 @@ def evalInvAtom (a : Q($α)) : AtomM (Result (ExBase bt sα) q($a⁻¹)) := do
* `(a ^ b * c)⁻¹ = a⁻¹ ^ b * c⁻¹`
-/
def ExProd.evalInv {a : Q($α)} (czα : Option Q(CharZero $α)) (va : ExProd bt sα a) :
- AtomM (Result (ExProd bt sα) q($a⁻¹)) := do
- Lean.Core.checkSystem decl_name%.toString
+ AtomM (Result (ExProd bt sα) q($a⁻¹)) :=
+ Lean.Core.checkSystem decl_name%.toString *>
match va with
- | .const c =>
+ | .const c => do
match ← rc.inv czα q($dsα) c with
| some ⟨_, vd, pd⟩ => pure ⟨_, .const vd, q($pd)⟩
| none =>
diff --git a/Mathlib/Tactic/Ring/Compare.lean b/Mathlib/Tactic/Ring/Compare.lean
index 08c15416b30deb..7d3f9da5a05e6a 100644
--- a/Mathlib/Tactic/Ring/Compare.lean
+++ b/Mathlib/Tactic/Ring/Compare.lean
@@ -134,8 +134,8 @@ def evalLE {v : Level} {α : Q(Type v)}
let ⟨_, pz⟩ ← NormNum.mkOfNat α q(addMonoidWithOneOfCommSemiring $α) q(nat_lit 0)
let rz : NormNum.Result q((0:$α)) :=
NormNum.Result.isNat q(addMonoidWithOneOfCommSemiring $α) q(nat_lit 0)
- (q(NormNum.isNat_ofNat $α $pz):)
- match va, vb with
+ (q(NormNum.isNat_ofNat $α $pz):)
+ match (dependent := true) va, vb with
/- `0 ≤ 0` -/
| .zero, .zero => pure <| .ok (q(le_refl (0:$α)):)
/- For numerals `ca` and `cb`, `ca + x ≤ cb + x` if `ca ≤ cb` -/
@@ -173,8 +173,8 @@ def evalLT {v : Level} {α : Q(Type v)}
let ⟨_, pz⟩ ← NormNum.mkOfNat α q(addMonoidWithOneOfCommSemiring $α) q(nat_lit 0)
let rz : NormNum.Result q((0:$α)) :=
NormNum.Result.isNat q(addMonoidWithOneOfCommSemiring $α) q(nat_lit 0)
- (q(NormNum.isNat_ofNat $α $pz):)
- match va, vb with
+ (q(NormNum.isNat_ofNat $α $pz):)
+ match (dependent := true) va, vb with
/- `0 < 0` -/
| .zero, .zero => return .error tooSmall
/- For numerals `ca` and `cb`, `ca + x < cb + x` if `ca < cb` -/
diff --git a/Mathlib/Tactic/Simproc/ExistsAndEq.lean b/Mathlib/Tactic/Simproc/ExistsAndEq.lean
index bd82a4b79ea7c0..0684540ca62d4d 100644
--- a/Mathlib/Tactic/Simproc/ExistsAndEq.lean
+++ b/Mathlib/Tactic/Simproc/ExistsAndEq.lean
@@ -127,7 +127,7 @@ where
assertUnreachable
"findEq: some side of equality must be `a`, and the other must not depend on `a`"
| ~q($L ∧ $R) =>
- match (generalizing := false) path with
+ match path with
| [] => assertUnreachable "findEq: P is conjunction but path is empty"
| .left :: tl =>
let (fvars, lctx, P', a') ← go a q($L) tl
@@ -212,17 +212,17 @@ where
MetaM Q($goal) := do
match goal with
| ~q(@Exists $β $pb) =>
- match (generalizing := false) exs with
+ match exs with
| [] => assertUnreachable "mkAfterToBefore: goal is `Exists` but `exs` is empty"
| ⟨v, γ, c⟩ :: exsTail =>
let _ : u_1 =QL v := ⟨⟩
let _ : $γ =Q $β := ⟨⟩
- let pf1 : Q($pb $c) := ← go h exsTail path
+ let pf1 : Q($pb $c) ← go h exsTail path
return q(Exists.intro $c $pf1)
| ~q(And $L $R) =>
let ~q($L' ∧ $R') := P
| assertUnreachable "mkAfterToBefore: goal is `And` but `P` is not `And`"
- match (generalizing := false) path with
+ match path with
| [] => assertUnreachable "mkAfterToBefore: goal is `And` but `exs` is empty"
| .left :: tl =>
let _ : $R =Q $R' := ⟨⟩
@@ -250,7 +250,7 @@ partial def withExistsElimAlongPathImp {u : Level} {α : Q(Sort u)}
MetaM Q($goal) := do
match P with
| ~q(@Exists $β $pb) =>
- match (generalizing := false) exs with
+ match exs with
| [] => assertUnreachable "withExistsElimAlongPathImp: `P` is `Exists` but `exs` is empty"
| ⟨v, γ, b⟩ :: exsTail =>
let _ : u_1 =QL v := ⟨⟩
@@ -261,7 +261,7 @@ partial def withExistsElimAlongPathImp {u : Level} {α : Q(Sort u)}
let pf2 : Q(∀ b, $pb b → $goal) ← mkLambdaFVars #[b, hb] pf1
return q(Exists.elim $h $pf2)
| ~q(And $L' $R') =>
- match (generalizing := false) path with
+ match path with
| [] => assertUnreachable "withExistsElimAlongPathImp: `P` is `And` but `path` is empty"
| .left :: tl =>
withExistsElimAlongPathImp q(And.left $h) exs tl hs act
@@ -372,21 +372,21 @@ where
MetaM Q($goal) := do
match P with
| ~q(@Exists $β $pb) =>
- match (generalizing := false) exs with
+ match exs with
| [] => assertUnreachable "mkBeforeToAfter: `P` is `Exists` but `exs` is empty"
| ⟨v, γ, b⟩ :: exsTail =>
let _ : u_1 =QL v := ⟨⟩
let _ : $γ =Q $β := ⟨⟩
- match (generalizing := false) hs with
+ match hs with
| [] => assertUnreachable "mkBeforeToAfter: `P` is `Exists` but `hs` is empty"
| ⟨H, hb⟩ :: hsTail =>
let _ : $H =Q $pb $b := ⟨⟩
- let pf : Q($goal) := ← go hb exsTail hsTail path h_eq
+ let pf : Q($goal) ← go hb exsTail hsTail path h_eq
return pf
| ~q(And $L $R) =>
let ~q($L' ∧ $R') := goal
| assertUnreachable "mkBeforeToAfter: `P` is `And` but `goal` is not `And`"
- match (generalizing := false) path with
+ match path with
| [] => assertUnreachable "mkBeforeToAfter: `P` is `And` but `path` is empty"
| .left :: tl =>
let pa : Q($α → Prop) ← mkLambdaFVars #[a] R
diff --git a/Mathlib/Tactic/TacticAnalysis/Declarations.lean b/Mathlib/Tactic/TacticAnalysis/Declarations.lean
index b4dc95d07bb6da..16e41e534055f6 100644
--- a/Mathlib/Tactic/TacticAnalysis/Declarations.lean
+++ b/Mathlib/Tactic/TacticAnalysis/Declarations.lean
@@ -412,9 +412,9 @@ def Mathlib.TacticAnalysis.tryAtEachStepCore
-- Extract just the tactic name, ignoring trailing comments/whitespace
-- Use try/catch because ppTactic can fail on certain syntax (e.g., `congr($h x)`)
let oldTacticPP := (← try
- return ((← liftCoreM <| PrettyPrinter.ppTactic ⟨i.tacI.stx⟩).pretty.splitOn "\n")[0]!.trimAscii
+ pure (((← liftCoreM <| PrettyPrinter.ppTactic ⟨i.tacI.stx⟩).pretty.splitOn "\n")[0]!.trimAscii)
catch _ =>
- return i.tacI.stx.reprint.getD "???")
+ pure (i.tacI.stx.reprint.getD "???"))
let newTacticPP ← label.getDM (try
return ((← liftCoreM <| PrettyPrinter.ppTactic tac).pretty.splitOn "\n")[0]!.trimAscii.copy
catch _ =>
diff --git a/Mathlib/Tactic/Translate/Core.lean b/Mathlib/Tactic/Translate/Core.lean
index 7e2d6eb4249e9e..39e16b28bd0583 100644
--- a/Mathlib/Tactic/Translate/Core.lean
+++ b/Mathlib/Tactic/Translate/Core.lean
@@ -1084,7 +1084,7 @@ def elabTranslationAttr (declName : Name) (stx : Syntax) : CoreM Config := do
| `(bracketedOption| (reorder := $reorder)) =>
if reorder?.isSome then
throwErrorAt opt "cannot specify `reorder` multiple times"
- reorder? ← elabReorder reorder argNames xs (.ofConstName declName)
+ reorder? ← some <$> elabReorder reorder argNames xs (.ofConstName declName)
| `(bracketedOption| (relevant_arg := $n)) =>
if relevantArg?.isSome then
throwErrorAt opt "cannot specify `relevant_arg` multiple times"
diff --git a/Mathlib/Tactic/Translate/Reorder.lean b/Mathlib/Tactic/Translate/Reorder.lean
index c2c614edeb632f..bf27e65901608e 100644
--- a/Mathlib/Tactic/Translate/Reorder.lean
+++ b/Mathlib/Tactic/Translate/Reorder.lean
@@ -242,7 +242,7 @@ private def decomposePerm {n} (map : Vector (Option (Fin n)) n) : Permutation :=
map := map.set! j none
if j' = i then break
j := j'
- cycle := ⟨cycle.1 ++ [↑j], by grind⟩
+ cycle := ⟨cycle.1 ++ [j.val], by grind⟩
perm := cycle :: perm
return perm
diff --git a/Mathlib/Tactic/Variable.lean b/Mathlib/Tactic/Variable.lean
index cf6d13901ce531..0988a05e6a9210 100644
--- a/Mathlib/Tactic/Variable.lean
+++ b/Mathlib/Tactic/Variable.lean
@@ -191,7 +191,7 @@ partial def completeBinders' (maxSteps : Nat) (gas : Nat)
trace[«variable?»] m!"elaborated binder types array = {types}"
Term.synthesizeSyntheticMVarsNoPostponing -- checkpoint for withAutoBoundImplicit
Term.withoutAutoBoundImplicit do
- let (binders, toOmit) := ← do
+ let (binders, toOmit) ← (do
match binder with
| `(bracketedBinderF|[$[$ident? :]? $ty]) =>
-- Check if it's an alias
@@ -213,7 +213,7 @@ partial def completeBinders' (maxSteps : Nat) (gas : Nat)
return (binders, toOmit.push true)
else
return (binders, toOmit.push false)
- | _ => return (binders, toOmit.push false)
+ | _ => return (binders, toOmit.push false))
completeBinders' maxSteps gas checkRedundant binders toOmit (i + 1)
else
if h : gas = 0 ∧ i < binders.size then
diff --git a/Mathlib/Topology/Algebra/InfiniteSum/Order.lean b/Mathlib/Topology/Algebra/InfiniteSum/Order.lean
index ddd26a88733d28..97017871e4fcc6 100644
--- a/Mathlib/Topology/Algebra/InfiniteSum/Order.lean
+++ b/Mathlib/Topology/Algebra/InfiniteSum/Order.lean
@@ -371,8 +371,8 @@ attribute [local instance] monadLiftOptionMetaM in
This extension only proves non-negativity, strict positivity is more delicate for infinite sums and
requires more assumptions. -/
@[positivity tsum _]
-meta def evalTsum : PositivityExt where eval {u α} zα pα? e := do
- let some pα := pα? | pure .none
+meta def evalTsum : PositivityExt where eval {u α} zα pα? e :=
+ match pα? with | none => pure .none | some pα => do
match e with
| ~q(@tsum _ $ι $instCommMonoid $instTopSpace $f $L) =>
lambdaBoundedTelescope f 1 fun args (body : Q($α)) => do
diff --git a/Mathlib/Topology/Category/CompHausLike/Limits.lean b/Mathlib/Topology/Category/CompHausLike/Limits.lean
index 48241d88790508..a608a17cabb86d 100644
--- a/Mathlib/Topology/Category/CompHausLike/Limits.lean
+++ b/Mathlib/Topology/Category/CompHausLike/Limits.lean
@@ -120,6 +120,7 @@ lemma finiteCoproduct.ι_desc_apply {B : CompHausLike P} {π : (a : α) → X a
instance : HasCoproduct X where
exists_colimit := ⟨finiteCoproduct.cofan X, finiteCoproduct.isColimit X⟩
+set_option linter.checkUnivs false in
variable (P) in
/--
A typeclass describing the property that forming all finite disjoint unions is stable under the
diff --git a/Mathlib/Topology/ContinuousMap/Algebra.lean b/Mathlib/Topology/ContinuousMap/Algebra.lean
index 5d39eabc7e3970..ecac820768283a 100644
--- a/Mathlib/Topology/ContinuousMap/Algebra.lean
+++ b/Mathlib/Topology/ContinuousMap/Algebra.lean
@@ -492,6 +492,7 @@ end ContinuousMap
end RingStructure
+set_option linter.deprecated false in
attribute [local ext] Subtype.eq
section ModuleStructure
diff --git a/Mathlib/Topology/MetricSpace/Bounded.lean b/Mathlib/Topology/MetricSpace/Bounded.lean
index 50d0a217cdaf41..bd4e06e4f1504f 100644
--- a/Mathlib/Topology/MetricSpace/Bounded.lean
+++ b/Mathlib/Topology/MetricSpace/Bounded.lean
@@ -589,10 +589,10 @@ open Lean Meta Qq Function
/-- Extension for the `positivity` tactic: the diameter of a set is always nonnegative. -/
@[positivity Metric.diam _]
-meta def evalDiam : PositivityExt where eval {u α} _zα pα? e := do
+meta def evalDiam : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(@Metric.diam _ $inst $s) =>
- let some _ := pα? | pure .none
assertInstancesCommute
pure (.nonnegative q(Metric.diam_nonneg))
| _, _, _ => throwError "not ‖ · ‖"
diff --git a/Mathlib/Topology/MetricSpace/Pseudo/Defs.lean b/Mathlib/Topology/MetricSpace/Pseudo/Defs.lean
index aabb1cb2b5e4d6..0de76b0b857754 100644
--- a/Mathlib/Topology/MetricSpace/Pseudo/Defs.lean
+++ b/Mathlib/Topology/MetricSpace/Pseudo/Defs.lean
@@ -258,10 +258,10 @@ open Lean Meta Qq Function
/-- Extension for the `positivity` tactic: distances are nonnegative. -/
@[positivity Dist.dist _ _]
-meta def evalDist : PositivityExt where eval {u α} _zα pα? e := do
+meta def evalDist : PositivityExt where eval {u α} _zα pα? e :=
+ match pα? with | none => pure .none | some _ => do
match u, α, e with
| 0, ~q(ℝ), ~q(@Dist.dist $β $inst $a $b) =>
- let some _ := pα? | pure .none
let _inst ← synthInstanceQ q(PseudoMetricSpace $β)
assertInstancesCommute
pure (.nonnegative q(dist_nonneg))
diff --git a/Mathlib/Util/CountHeartbeats.lean b/Mathlib/Util/CountHeartbeats.lean
index 4180ad42d159ef..72c4fe8b668c19 100644
--- a/Mathlib/Util/CountHeartbeats.lean
+++ b/Mathlib/Util/CountHeartbeats.lean
@@ -153,7 +153,7 @@ elab "#count_heartbeats " approx:(&"approximately ")? "in" ppLine cmd:command :
let m : TSyntax `num := quote max'
Command.liftCoreM <| MetaM.run' do
Lean.Meta.Tactic.TryThis.addSuggestion (← getRef)
- (← set_option hygiene false in `(command| set_option maxHeartbeats $m in $cmd))
+ (← (set_option hygiene false in `(command| set_option maxHeartbeats $m in $cmd)))
set_option linter.style.maxHeartbeats false in
/--
diff --git a/Mathlib/Util/GetAllModules.lean b/Mathlib/Util/GetAllModules.lean
index 5da0463be9851c..09461a0d52f3ab 100644
--- a/Mathlib/Util/GetAllModules.lean
+++ b/Mathlib/Util/GetAllModules.lean
@@ -54,5 +54,5 @@ In addition, these names are sorted in a platform-independent order. -/
def getAllModulesSorted (git : Bool) (ml : String) : IO (Array String) := do
let files ← getAllFiles git ml
let names ← files.mapM fun f => do
- return (← moduleNameOfFileName f none).toString
+ return (← moduleNameOfFileName f none).toString
return names.qsort (· < ·)
diff --git a/Mathlib/Util/WhatsNew.lean b/Mathlib/Util/WhatsNew.lean
index a14267afd8b85e..b833d1f05b79f0 100644
--- a/Mathlib/Util/WhatsNew.lean
+++ b/Mathlib/Util/WhatsNew.lean
@@ -108,7 +108,7 @@ def whatsNew (old new : Environment) : CoreM MessageData := do
diffs := diffs.push (← printIdCore c i)
for ext in ← persistentEnvExtensionsRef.get do
- if let some diff := ← diffExtension old new ext then
+ if let some diff ← diffExtension old new ext then
diffs := diffs.push diff
if diffs.isEmpty then return "no new constants"
diff --git a/MathlibTest/Attribute/ToAdditive/Basic.lean b/MathlibTest/Attribute/ToAdditive/Basic.lean
index 9496d27dc41f71..f4e097c49bfab5 100644
--- a/MathlibTest/Attribute/ToAdditive/Basic.lean
+++ b/MathlibTest/Attribute/ToAdditive/Basic.lean
@@ -142,6 +142,7 @@ def foo4 {α : Type u} : Type v → Type (max u v) := @my_has_pow α
@[to_additive bar4_test]
lemma foo4_test {α β : Type u} : @foo4 α β = @my_has_pow α β := rfl
+set_option linter.defProp false in
@[to_additive bar5]
def foo5 {α} [my_has_pow α ℕ] [my_has_pow ℕ ℤ] : True := True.intro
@@ -304,6 +305,7 @@ attribute [to_additive add_some_def] some_def
run_cmd do liftCoreM <| successIfFail (getConstInfo `Test.add_some_def.in_namespace)
+set_option linter.defProp false in
set_option linter.unusedVariables false in
def foo_mul {I J K : Type} (n : ℕ) {f : I → Type} (L : Type) [∀ i, One (f i)]
[Add I] [Mul L] : true := by trivial
@@ -911,6 +913,7 @@ def monoidAlgebraFoo₂ {k G : Type} [Inhabited k] : MonoidAlgebra k G × Nat :=
(⟨fun _ ↦ default⟩, 2)
-- Proofs in types aren't abstracted:
+set_option linter.defProp false in
@[to_additive]
def abstractMul : Function.const _ True (id Nat.zero_lt_one) := trivial
diff --git a/MathlibTest/Attribute/ToDual.lean b/MathlibTest/Attribute/ToDual.lean
index 890c9c996640e3..7a3676cc8633b0 100644
--- a/MathlibTest/Attribute/ToDual.lean
+++ b/MathlibTest/Attribute/ToDual.lean
@@ -71,6 +71,7 @@ attribute [to_dual existing] Semilattice.toSemilatticeSup
-- when reordering arguments in arguments that are being reordered,
-- there is a convenient syntax to specify this at the same time:
+set_option linter.defProp false in
@[to_dual self (reorder := h₁ h₂ (a b))]
def SemilatticeSup.foo {α} [Semilattice α]
(h₁ : ∀ a b : α, a ⊔ b ≤ b) (h₂ : ∀ a b : α, a ≤ b ⊓ a) (my_sorry : ∀ {p : Prop}, p) : False :=
@@ -310,6 +311,7 @@ info: theorem Cov.Ioc_def : ∀ {α : Type} [inst : PartialOrder α] {a b x : α
/-! Test that translated autoparams are marked with `meta`. -/
+set_option linter.defProp false in
@[to_dual]
def Top.autoParamTest {a b : α} (h : a ≤ b := by grind) : a ≤ b := h
@@ -362,6 +364,7 @@ private theorem WithBotPrivate.coe_le_top : WithTop.coe a ≤ .top := .le_top (W
run_meta guard <| (← getEnv).contains ``WithTopPrivate.coe_le_bot
+set_option linter.defProp false in
set_option linter.unusedVariables false in
@[to_dual (rename := x → y, Pbot ↔ Ptop) renameTest']
def renameTest [Top α] [Bot α] (x : α) {P : α → Prop} (Ptop : P ⊤) (Pbot : P ⊥) : True := trivial
diff --git a/MathlibTest/CategoryTheory/CategoryStar.lean b/MathlibTest/CategoryTheory/CategoryStar.lean
index c5f5ccbf709e1d..5e0f143a13f2d0 100644
--- a/MathlibTest/CategoryTheory/CategoryStar.lean
+++ b/MathlibTest/CategoryTheory/CategoryStar.lean
@@ -66,7 +66,9 @@ universe u₁ u₂
variable (E : Sort (imax (u₁ + 1) (u₂ + 1))) [Category* E]
variable (F : Sort (max (u₁ + 1) (u₂ + 1))) [Category* F]
+set_option linter.checkUnivs false in
def barE := E ⥤ E
+set_option linter.checkUnivs false in
def barF := F ⥤ F
/--
diff --git a/MathlibTest/Linter/DocPrime.lean b/MathlibTest/Linter/DocPrime.lean
index bc217c8686bc65..7f3139c7d0e153 100644
--- a/MathlibTest/Linter/DocPrime.lean
+++ b/MathlibTest/Linter/DocPrime.lean
@@ -88,6 +88,7 @@ Declarations whose name ends with a `'` are expected to contain an explanation f
Note: This linter can be disabled with `set_option linter.docPrime false`
-/
#guard_msgs in
+set_option linter.defProp false in
def def_no_doc' : True := .intro
-- Anonymous declarations in a primed namespace should not get flagged by the linter.
diff --git a/MathlibTest/MinImports.lean b/MathlibTest/MinImports.lean
index e9b070f1862af7..5d1ce54b16f228 100644
--- a/MathlibTest/MinImports.lean
+++ b/MathlibTest/MinImports.lean
@@ -86,6 +86,7 @@ lemma hi (n : ℕ) : n = n := by extract_goal; rfl
section Variables
+set_option linter.defProp false in
/-- info: public import Mathlib.Data.Nat.Notation -/
#guard_msgs in
#min_imports in
@@ -98,6 +99,7 @@ variable {K : Type*} [Field K]
namespace Namespace
+set_option linter.defProp false in
-- The dependency on `Semiring` is only found in the `variable` declaration.
-- We find it by looking up the declaration by name and checking the term,
-- which used to get confused if running in a namespace.
diff --git a/MathlibTest/Simps.lean b/MathlibTest/Simps.lean
index b315b0cb6598ad..e37261b0427cc4 100644
--- a/MathlibTest/Simps.lean
+++ b/MathlibTest/Simps.lean
@@ -977,6 +977,7 @@ instance has_PropClass (n : ℕ) : PropClass n := ⟨trivial⟩
structure NeedsPropClass (n : ℕ) [PropClass n] where
(t : True)
+set_option linter.defProp false in
@[simps] def test_PropClass : NeedsPropClass 1 :=
{ t := trivial }
diff --git a/MathlibTest/Subsingleton.lean b/MathlibTest/Subsingleton.lean
index f139fe25b410b0..eca165c5f4f5ac 100644
--- a/MathlibTest/Subsingleton.lean
+++ b/MathlibTest/Subsingleton.lean
@@ -141,6 +141,7 @@ example {α : Type} [BEq α] (f : ∀ {β : Type} [BEq β], Subsingleton β) (x
The same, but now there's a universe level metavariable.
-/
set_option warn.classDefReducibility false in
+set_option linter.defProp false in
def fdef : ∀ {β : Type _} [BEq β], Subsingleton β := test_sorry
example {α : Type} [BEq α] (x y : α) : x = y := by
diff --git a/MathlibTest/Tactic/Says/Basic.lean b/MathlibTest/Tactic/Says/Basic.lean
index e21eac40686df1..1ab8f8f01ae9b7 100644
--- a/MathlibTest/Tactic/Says/Basic.lean
+++ b/MathlibTest/Tactic/Says/Basic.lean
@@ -97,8 +97,10 @@ example : True := by
-- Check that verification works even with multi-line suggestions, as produced by aesop
def P : Prop := True
def Q : Prop := True
+set_option linter.defProp false in
@[simp]
def very_long_lemma_name_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa : Q → P := fun _ => trivial
+set_option linter.defProp false in
@[simp]
def very_long_lemma_name_bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb : Q := trivial
/--
diff --git a/MathlibTest/UnusedTactic.lean b/MathlibTest/UnusedTactic.lean
index 812ffe92e2c33b..2991ffbb29eaca 100644
--- a/MathlibTest/UnusedTactic.lean
+++ b/MathlibTest/UnusedTactic.lean
@@ -20,6 +20,7 @@ example : 1 = 1 := by
change 1 = 1
rfl
+set_option linter.defProp false in
def why2 : True → True := (by refine ·)
example : True := by
diff --git a/MathlibTest/globalAttributeIn.lean b/MathlibTest/globalAttributeIn.lean
index 1c124f2b4d4ed0..ce50c8fece0e58 100644
--- a/MathlibTest/globalAttributeIn.lean
+++ b/MathlibTest/globalAttributeIn.lean
@@ -13,6 +13,7 @@ import Mathlib.Tactic.Linter.GlobalAttributeIn
class Dummy where
field : True
+set_option linter.defProp false in
@[reducible] def dummyInst : Dummy := ⟨True.intro⟩
/--
diff --git a/MathlibTest/symm.lean b/MathlibTest/symm.lean
index 4ae3ef96355bc3..6e660b12b59b5b 100644
--- a/MathlibTest/symm.lean
+++ b/MathlibTest/symm.lean
@@ -3,6 +3,7 @@ import Mathlib.Logic.Equiv.Basic
set_option autoImplicit true
-- testing that the attribute is recognized
+set_option linter.defProp false in
@[symm] def eq_symm {α : Type} (a b : α) : a = b → b = a := Eq.symm
example (a b : Nat) : a = b → b = a := by intros; symm; assumption
@@ -11,6 +12,7 @@ example (a b : Nat) : a = b → True → b = a := by intro h _; symm at h; assum
def sameParity : Nat → Nat → Prop
| n, m => n % 2 = m % 2
+set_option linter.defProp false in
@[symm] def sameParity_symm (n m : Nat) : sameParity n m → sameParity m n := Eq.symm
example (a b : Nat) : sameParity a b → sameParity b a := by intros; symm; assumption
diff --git a/lake-manifest.json b/lake-manifest.json
index aff8d1dac87c1c..993ff072b9c621 100644
--- a/lake-manifest.json
+++ b/lake-manifest.json
@@ -5,7 +5,7 @@
"type": "git",
"subDir": null,
"scope": "leanprover-community",
- "rev": "63045536fe95024e6c18fc7b48e03f506701c5bc",
+ "rev": "f3f26cc72646205ca167117487c008ee1dafe816",
"name": "plausible",
"manifestFile": "lake-manifest.json",
"inputRev": "main",
@@ -25,7 +25,7 @@
"type": "git",
"subDir": null,
"scope": "leanprover-community",
- "rev": "5c7542ed018c78194f1e2b903eaf6a792b74c03d",
+ "rev": "41f407a8e85b0fdc00910633a8f14754139b63f4",
"name": "importGraph",
"manifestFile": "lake-manifest.json",
"inputRev": "main",
@@ -35,7 +35,7 @@
"type": "git",
"subDir": null,
"scope": "leanprover-community",
- "rev": "24b0d9dc081c5423f8eec7e866c441e5184f29d9",
+ "rev": "e6518a674e62de322b8f79eebeda7bcae2a36bc3",
"name": "proofwidgets",
"manifestFile": "lake-manifest.json",
"inputRev": "main",
@@ -45,7 +45,7 @@
"type": "git",
"subDir": null,
"scope": "leanprover-community",
- "rev": "e3cb2f741431ce31bf73549fb52316a57368b06f",
+ "rev": "b5b9e2bb45ce91e4bc44eaa738c3a8910404ab82",
"name": "aesop",
"manifestFile": "lake-manifest.json",
"inputRev": "master",
@@ -55,7 +55,7 @@
"type": "git",
"subDir": null,
"scope": "leanprover-community",
- "rev": "f46324995fca5f0483b742e4eb4daec7f4ee50d2",
+ "rev": "7a62bd13860cd39ac98da16ffc8c24d601353f69",
"name": "Qq",
"manifestFile": "lake-manifest.json",
"inputRev": "master",
@@ -65,7 +65,7 @@
"type": "git",
"subDir": null,
"scope": "leanprover-community",
- "rev": "fc38104235ab6cf8a448a74405aa258804ef4e36",
+ "rev": "954dbc9873f3b4534dc9896604593406d0383520",
"name": "batteries",
"manifestFile": "lake-manifest.json",
"inputRev": "main",
@@ -75,10 +75,10 @@
"type": "git",
"subDir": null,
"scope": "leanprover",
- "rev": "92564e5770e4d09f2d86dfbf8ada1e9c715b384c",
+ "rev": "406ebb8c8e2f7e852a1b47764b42494022ce652c",
"name": "Cli",
"manifestFile": "lake-manifest.json",
- "inputRev": "v4.31.0",
+ "inputRev": "v4.32.0-rc1",
"inherited": true,
"configFile": "lakefile.toml"}],
"name": "mathlib",
diff --git a/lean-toolchain b/lean-toolchain
index 18640c8b066b18..2694eb767c1d5d 100644
--- a/lean-toolchain
+++ b/lean-toolchain
@@ -1 +1 @@
-leanprover/lean4:v4.31.0
+leanprover/lean4:v4.32.0-rc1
diff --git a/scripts/create_deprecated_modules.lean b/scripts/create_deprecated_modules.lean
index 31705550029ad0..92138e86c56e5d 100644
--- a/scripts/create_deprecated_modules.lean
+++ b/scripts/create_deprecated_modules.lean
@@ -85,7 +85,7 @@ def mkDeprecationWithDate (date : String)
def mkDeprecation (customMessage : Option String := some "Auto-generated deprecation") :
CommandElabM Format := do
-- Get the current date in UTC: we don't want this to depend on the user computer's time zone.
- let date := s!"{(← Std.Time.DateTime.now (tz := .UTC)).toPlainDate}"
+ let date := s!"{(Std.Time.DateTime.ofTimestamp (← Std.Time.Timestamp.now) .UTC).toPlainDate}"
mkDeprecationWithDate date customMessage
/--
diff --git a/scripts/lint-style.lean b/scripts/lint-style.lean
index 727dcd1c7dd608..693913299410b9 100644
--- a/scripts/lint-style.lean
+++ b/scripts/lint-style.lean
@@ -54,7 +54,7 @@ instance : ToExpr LinterSets := inferInstanceAs <| ToExpr (NameMap _)
/-- Return the linter sets defined at this point of elaborating the current file. -/
elab "linter_sets%" : term => do
- return toExpr <| linterSetsExt.getState (← getEnv)
+ return toExpr <| (linterSetsExt.getState (← getEnv)).merged
end LinterSetsElab
diff --git a/scripts/mk_all.lean b/scripts/mk_all.lean
index 4e3e0125670610..cbd76c092b401e 100644
--- a/scripts/mk_all.lean
+++ b/scripts/mk_all.lean
@@ -52,7 +52,7 @@ def mkAllCLI (args : Parsed) : IO UInt32 := do
-- If the package is `mathlib`, then it removes the libraries `Cache` and `MathlibTest` and it
-- adds `Mathlib/Tactic`.
let libs := ← match args.flag? "lib" with
- | some lib => return #[lib.as! String]
+ | some lib => pure #[lib.as! String]
| none => getLeanLibs
let mut updates := 0
for d in libs.reverse do -- reverse to create `Mathlib/Tactic.lean` before `Mathlib.lean`
From 331ad87c32e76aca427277165a0855bd2bbc925d Mon Sep 17 00:00:00 2001
From: Moritz Doll <21366319+mcdoll@users.noreply.github.com>
Date: Fri, 19 Jun 2026 00:58:00 +0000
Subject: [PATCH 0161/1300] feat(Analysis): Taylor's theorem with the integral
remainder (#31176)
Prove Taylor's theorem with the integral remainder in higher dimensions.
---
Mathlib.lean | 1 +
.../Calculus/ContDiff/FTaylorSeries.lean | 9 ++
Mathlib/Analysis/Calculus/TaylorIntegral.lean | 139 ++++++++++++++++++
docs/100.yaml | 1 +
docs/undergrad.yaml | 2 +-
5 files changed, 151 insertions(+), 1 deletion(-)
create mode 100644 Mathlib/Analysis/Calculus/TaylorIntegral.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 73d920207c69ad..066f7d79c13745 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -1843,6 +1843,7 @@ public import Mathlib.Analysis.Calculus.TangentCone.ProperSpace
public import Mathlib.Analysis.Calculus.TangentCone.Real
public import Mathlib.Analysis.Calculus.TangentCone.Seq
public import Mathlib.Analysis.Calculus.Taylor
+public import Mathlib.Analysis.Calculus.TaylorIntegral
public import Mathlib.Analysis.Calculus.UniformLimitsDeriv
public import Mathlib.Analysis.Calculus.VectorField
public import Mathlib.Analysis.Complex.AbelLimit
diff --git a/Mathlib/Analysis/Calculus/ContDiff/FTaylorSeries.lean b/Mathlib/Analysis/Calculus/ContDiff/FTaylorSeries.lean
index 53ded4d8e91d6e..084664a10b855d 100644
--- a/Mathlib/Analysis/Calculus/ContDiff/FTaylorSeries.lean
+++ b/Mathlib/Analysis/Calculus/ContDiff/FTaylorSeries.lean
@@ -7,6 +7,7 @@ module
public import Mathlib.Analysis.Calculus.FDeriv.Add
public import Mathlib.Analysis.Calculus.FDeriv.Equiv
+public import Mathlib.Analysis.Calculus.FDeriv.CompCLM
public import Mathlib.Analysis.Calculus.FormalMultilinearSeries
public import Mathlib.Data.ENat.Lattice
@@ -829,6 +830,14 @@ theorem iteratedFDeriv_succ_apply_left {n : ℕ} (m : Fin (n + 1) → E) :
(fderiv 𝕜 (iteratedFDeriv 𝕜 n f) x : E → E [×n]→L[𝕜] F) (m 0) (tail m) :=
rfl
+/-- The iterated derivative is given by the derivative of the `n-1` iterated derivative. -/
+theorem DifferentiableAt.iteratedFDeriv_succ_apply_left' {n : ℕ} {m : Fin (n + 1) → E}
+ (hf : DifferentiableAt 𝕜 (iteratedFDeriv 𝕜 n f) x) :
+ iteratedFDeriv 𝕜 (n + 1) f x m =
+ fderiv 𝕜 (fun y ↦ iteratedFDeriv 𝕜 n f y (Fin.tail m)) x (m 0) := by
+ convert iteratedFDeriv_succ_apply_left m
+ simp [fderiv_continuousMultilinear_apply_const hf]
+
/-- Writing explicitly the `n+1`-th derivative as the composition of a currying linear equiv,
and the derivative of the `n`-th derivative. -/
theorem iteratedFDeriv_succ_eq_comp_left {n : ℕ} :
diff --git a/Mathlib/Analysis/Calculus/TaylorIntegral.lean b/Mathlib/Analysis/Calculus/TaylorIntegral.lean
new file mode 100644
index 00000000000000..cfec073db45c76
--- /dev/null
+++ b/Mathlib/Analysis/Calculus/TaylorIntegral.lean
@@ -0,0 +1,139 @@
+/-
+Copyright (c) 2025 Moritz Doll. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Moritz Doll
+-/
+module
+
+public import Mathlib.MeasureTheory.Integral.IntervalIntegral.IntegrationByParts
+public import Mathlib.Analysis.Calculus.ContDiff.Basic
+public import Mathlib.Analysis.Calculus.Deriv.Pow
+public import Mathlib.Analysis.Calculus.IteratedDeriv.Defs
+
+/-!
+# Taylor's formula with an integral remainder in higher dimensions
+
+In this file we prove Taylor's formula with the remainder term in integral form.
+
+* `map_add_eq_sum_add_integral_iteratedFDeriv`: version for higher dimensions with `iteratedFDeriv`
+
+TODO: add a version that assumes `ContDiffOn f (closedBall x (‖y‖))`
+
+-/
+
+@[expose] public section
+
+open Nat
+
+variable {𝕜 E F : Type*}
+variable [NormedAddCommGroup E] [NormedAddCommGroup F]
+
+section NontriviallyNormedField
+
+variable [NontriviallyNormedField 𝕜] [NormedSpace 𝕜 E] [NormedSpace 𝕜 F]
+
+variable {f : E → F} {x y : E} {t : 𝕜} {n : ℕ}
+
+theorem DifferentiableAt.deriv_comp_add_smul (hf : DifferentiableAt 𝕜 f (x + t • y)) :
+ deriv (fun (s : 𝕜) ↦ f (x + s • y)) t = fderiv 𝕜 f (x + t • y) y := by
+ have hg : Differentiable 𝕜 (fun (s : 𝕜) ↦ (x + s • y)) := by fun_prop
+ convert fderiv_comp_deriv t hf hg.differentiableAt
+ · simp
+ · simpa using (deriv_smul_const (x := t) differentiableAt_id y).symm
+
+theorem ContDiffAt.deriv_fderiv_add_smul (hf : ContDiffAt 𝕜 (n + 1) f (x + t • y)) :
+ deriv (fun (s : 𝕜) ↦ iteratedFDeriv 𝕜 n f (x + s • y) (fun _ ↦ y)) t =
+ iteratedFDeriv 𝕜 (n + 1) f (x + t • y) (fun _ ↦ y) := by
+ have hf' : DifferentiableAt 𝕜 (iteratedFDeriv 𝕜 n f) (x + t • y) := by
+ apply hf.differentiableAt_iteratedFDeriv
+ norm_cast
+ exact lt_add_one n
+ convert (hf'.continuousMultilinear_apply_const _).deriv_comp_add_smul
+ exact hf'.iteratedFDeriv_succ_apply_left'
+
+end NontriviallyNormedField
+
+variable [NormedSpace ℝ E] [NormedSpace ℝ F]
+
+variable {f : E → F} {x y : E} {n : ℕ}
+
+variable [CompleteSpace F]
+
+/-- *Taylor's theorem with remainder in integral form*. If `f` is `n + 1` times continuously
+differentiable, then `f (x + y)` is given by
+`∑ k in 0..n, D^k f(x; y,..,y) / k! + 1/n! ∫ t in 0..1, (1 - t) ^ n • D^{n+1}f (x + t • y; y,..,y)`,
+where `D^k f` denotes the iterated derivative of `f`.
+
+In the case that `n = 1`, this is a reformulation of the fundamental theorem of calculus, namely
+`f (x + y) = f x + ∫ t in 0..1, D f(x + t • y; y)`. -/
+theorem map_add_eq_sum_add_integral_iteratedFDeriv (hf : ∀ (t : ℝ) (_ht : t ∈ Set.Icc 0 1),
+ ContDiffAt ℝ (n + 1) f (x + t • y)) :
+ f (x + y) = ∑ k ∈ Finset.range (n + 1), (k ! : ℝ)⁻¹ • (iteratedFDeriv ℝ k f x (fun _ ↦ y)) +
+ (n ! : ℝ)⁻¹ • ∫ t in 0..1, (1 - t)^n • iteratedFDeriv ℝ (n + 1) f (x + t • y) (fun _ ↦ y) := by
+ simp_rw [← Set.uIcc_of_le zero_le_one] at hf
+ induction n with
+ | zero =>
+ -- The base case follows from the fundamental theorem of calculus
+ have h_eq : Set.EqOn (fun t ↦ (fderiv ℝ f (x + t • y)) y) (deriv fun (s : ℝ) ↦ f (x + s • y))
+ (Set.uIcc 0 1) := by
+ intro t ht
+ rw [DifferentiableAt.deriv_comp_add_smul]
+ exact (hf t ht).differentiableAt (by simp)
+ simp only [zero_add, Finset.range_one, Finset.sum_singleton, factorial_zero, cast_one, inv_one,
+ iteratedFDeriv_zero_apply, one_smul, pow_zero, reduceAdd, iteratedFDeriv_one_apply]
+ rw [← sub_eq_iff_eq_add', Eq.comm, intervalIntegral.integral_congr h_eq]
+ have hf' : ∀ (t : ℝ) (ht : t ∈ Set.uIcc 0 1), DifferentiableAt ℝ (fun s ↦ f (x + s • y)) t :=
+ fun t ht ↦ ((hf t ht).differentiableAt (by simp)).comp t (by fun_prop)
+ have hint : IntervalIntegrable (deriv (fun s ↦ f (x + s • y))) MeasureTheory.volume 0 1 := by
+ have h₁ : ContinuousOn (fderiv ℝ f) ((fun t ↦ x + t • y) '' Set.uIcc (0 : ℝ) 1) := by
+ intro z ⟨t, ht, hz⟩
+ rw [← hz]
+ exact (((hf t ht).fderiv_right (le_refl _)).continuousAt (n := 0)).continuousWithinAt
+ have h₂ : ContinuousOn (fun x_1 ↦ fderiv ℝ f (x + x_1 • y)) (Set.uIcc (0 : ℝ) 1) := by
+ apply h₁.comp (t := (fun t ↦ x + t • y) '' (Set.uIcc (0 : ℝ) 1)) (by fun_prop)
+ intro t ht
+ use t
+ apply (ContinuousOn.congr _ h_eq.symm).intervalIntegrable
+ fun_prop
+ simpa using intervalIntegral.integral_deriv_eq_sub hf' hint
+ | succ n ih =>
+ -- We use the inductive hypothesis to cancel all lower order terms
+ specialize ih (fun t ht ↦ (hf t ht).of_le (by simp))
+ rw [Finset.sum_range_succ, add_assoc]
+ convert ih using 2
+ -- We define the functions u and v that we will integrate by parts
+ set u := fun (k : ℕ) (t : ℝ) ↦ (k ! : ℝ)⁻¹ * (1 - t) ^ k
+ have hu : ∀ (t : ℝ), HasDerivAt (u (n + 1)) (-u n t) t := by
+ intro t
+ unfold u
+ have : (-((n ! : ℝ)⁻¹ * (1 - t) ^ n)) =
+ ((n + 1) ! : ℝ)⁻¹ * ((n + 1) * (1 - t) ^ n * (-1)) := by
+ field_simp
+ congr 1
+ rw [Nat.factorial_succ]
+ grind
+ rw [this]
+ apply HasDerivAt.const_mul
+ convert ((hasDerivAt_id t).const_sub 1).pow (n + 1)
+ all_goals norm_cast
+ have hu' : Continuous (u n) := by fun_prop
+ set v := fun (k : ℕ) (t : ℝ) ↦ iteratedFDeriv ℝ k f (x + t • y) (fun _ ↦ y)
+ have hv : ∀ (t : ℝ) (ht : t ∈ Set.uIcc 0 1), HasDerivAt (v (n + 1)) (v (n + 1 + 1) t) t := by
+ intro t ht
+ unfold v
+ rw [← (hf t ht).deriv_fderiv_add_smul]
+ have h_diff : DifferentiableAt ℝ (iteratedFDeriv ℝ (n + 1) f) (x + t • y) := by
+ apply (hf t ht).differentiableAt_iteratedFDeriv
+ norm_cast
+ grind
+ refine DifferentiableAt.hasDerivAt ?_
+ fun_prop
+ have hv' : ContinuousOn (v (n + 1 + 1)) (Set.uIcc 0 1) := by
+ intro t ht
+ have h_cont : ContinuousAt (iteratedFDeriv ℝ (n + 1 + 1) f) (x + t • y) :=
+ ((hf t ht).iteratedFDeriv_right (i := n + 1 + 1) (m := 0) (by simp)).continuousAt
+ exact (h_cont.comp (x := t) (by fun_prop)).continuousWithinAt.eval_const _
+ -- Now we apply integration by parts and simplify
+ simpa [← eq_neg_add_iff_add_eq, ← intervalIntegral.integral_smul, smul_smul, u, v] using
+ intervalIntegral.integral_smul_deriv_eq_deriv_smul (fun t _ ↦ hu t) hv
+ (hu'.neg.intervalIntegrable _ _) hv'.intervalIntegrable
diff --git a/docs/100.yaml b/docs/100.yaml
index 84f6227e311369..17a09b6ceb64c1 100644
--- a/docs/100.yaml
+++ b/docs/100.yaml
@@ -146,6 +146,7 @@
decls :
- taylor_mean_remainder_lagrange
- taylor_mean_remainder_cauchy
+ - map_add_eq_sum_add_integral_iteratedFDeriv
authors: Moritz Doll
36:
title : Brouwer Fixed Point Theorem
diff --git a/docs/undergrad.yaml b/docs/undergrad.yaml
index 710b7695d179c4..9c97063d8ff3cb 100644
--- a/docs/undergrad.yaml
+++ b/docs/undergrad.yaml
@@ -466,7 +466,7 @@ Multivariable calculus:
$k$-th order partial derivatives: ''
partial derivatives commute: 'second_derivative_symmetric'
Taylor's theorem with little-o remainder: ''
- Taylor's theorem with integral form for remainder: ''
+ Taylor's theorem with integral form for remainder: 'map_add_eq_sum_add_integral_iteratedFDeriv'
local extrema: 'IsLocalMin.fderiv_eq_zero'
convexity of functions on an open convex subset of $\R^n$: 'ConvexOn'
diffeomorphisms: 'Structomorph'
From bbd0fbe5754c524969fd92429f849ae1f975a253 Mon Sep 17 00:00:00 2001
From: "mathlib-update-dependencies[bot]"
<258990618+mathlib-update-dependencies[bot]@users.noreply.github.com>
Date: Fri, 19 Jun 2026 02:52:21 +0000
Subject: [PATCH 0162/1300] chore: update Mathlib dependencies 2026-06-19
(#40790)
This PR updates the Mathlib dependencies.
---
lake-manifest.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lake-manifest.json b/lake-manifest.json
index 993ff072b9c621..83a478e0413ecb 100644
--- a/lake-manifest.json
+++ b/lake-manifest.json
@@ -65,7 +65,7 @@
"type": "git",
"subDir": null,
"scope": "leanprover-community",
- "rev": "954dbc9873f3b4534dc9896604593406d0383520",
+ "rev": "125807f43a86b5d58892b7ea6972eec0d6c164d2",
"name": "batteries",
"manifestFile": "lake-manifest.json",
"inputRev": "main",
From 39ca77e919f8582647530b35b8677c26fe679d1d Mon Sep 17 00:00:00 2001
From: Vlad Tsyrklevich
Date: Fri, 19 Jun 2026 03:50:53 +0000
Subject: [PATCH 0163/1300] doc(RingTheory): fix local ring doc comment
(#39765)
The predicate for local rings was updated to the current definition on non-commutative semirings back in mathlib3, but the outdated comment stating that local rings are commutative rings with a unique maximal ideal has not been updated since.
---
Mathlib/RingTheory/LocalRing/Defs.lean | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/Mathlib/RingTheory/LocalRing/Defs.lean b/Mathlib/RingTheory/LocalRing/Defs.lean
index 211784be77b0d3..9538400b1f4589 100644
--- a/Mathlib/RingTheory/LocalRing/Defs.lean
+++ b/Mathlib/RingTheory/LocalRing/Defs.lean
@@ -12,14 +12,14 @@ public import Mathlib.Algebra.Ring.Defs
# Local rings
-Define local rings as commutative rings having a unique maximal ideal.
+Define the notion of a local ring for non-commutative semirings. In the commutative case,
+this is shown to be equivalent to the familiar definition that there exists a unique
+maximal ideal in `IsLocalRing.of_unique_max_ideal` and `IsLocalRing.maximal_ideal_unique`.
## Main definitions
* `IsLocalRing`: A predicate on semirings, stating that for any pair of elements that
- adds up to `1`, one of them is a unit. In the commutative case this is shown to be equivalent
- to the condition that there exists a unique maximal ideal, see
- `IsLocalRing.of_unique_max_ideal` and `IsLocalRing.maximal_ideal_unique`.
+ adds up to `1`, one of them is a unit.
-/
From 99e7e2c20b9c709ca1a32de2942db9cae7810a90 Mon Sep 17 00:00:00 2001
From: Jiedong Jiang <107380768+jjdishere@users.noreply.github.com>
Date: Fri, 19 Jun 2026 05:50:18 +0000
Subject: [PATCH 0164/1300] chore(RingTheory/ValuativeRel): fix deprecation
message of `ValueGroupWithZero.embed` (#40794)
`embed` should be `orderMonoidIso`.
---
Mathlib/RingTheory/Valuation/ValuativeRel/Basic.lean | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Mathlib/RingTheory/Valuation/ValuativeRel/Basic.lean b/Mathlib/RingTheory/Valuation/ValuativeRel/Basic.lean
index 09454d77510d4b..26f6b4663daee7 100644
--- a/Mathlib/RingTheory/Valuation/ValuativeRel/Basic.lean
+++ b/Mathlib/RingTheory/Valuation/ValuativeRel/Basic.lean
@@ -1333,7 +1333,7 @@ lemma leftInverse_embedding_orderMonoidIso : Function.LeftInverse embedding
embedding_orderMonoidIso_valuation_eq
/-- The isomorphism between `ValueGroupWithZero R` and `ValueGroup₀ (valuation R)`. -/
-@[deprecated "use ValueGroupWithZero.embed (valuation R) instead" (since := "2026-03-17")]
+@[deprecated "use ValueGroupWithZero.orderMonoidIso instead" (since := "2026-03-17")]
def valueGroupWithZero_equiv_valueGroup₀ := orderMonoidIso (valuation R)
end ValueGroupWithZero
From e88fd8b40bb0b42b10ad5875c5b5e8b70fa79bd3 Mon Sep 17 00:00:00 2001
From: Michael Rothgang <10105016+grunweg@users.noreply.github.com>
Date: Fri, 19 Jun 2026 06:21:24 +0000
Subject: [PATCH 0165/1300] chore: remove two superfluous `IsManifold`
hypotheses (#40786)
---
Mathlib/Geometry/Manifold/ContMDiff/Defs.lean | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/Mathlib/Geometry/Manifold/ContMDiff/Defs.lean b/Mathlib/Geometry/Manifold/ContMDiff/Defs.lean
index 8d9f95219ccf58..a963ba00712ffb 100644
--- a/Mathlib/Geometry/Manifold/ContMDiff/Defs.lean
+++ b/Mathlib/Geometry/Manifold/ContMDiff/Defs.lean
@@ -324,7 +324,7 @@ theorem contMDiffAt_iff_source :
section IsManifold
theorem contMDiffWithinAt_iff_source_of_mem_maximalAtlas
- [IsManifold I n M] (he : e ∈ maximalAtlas I n M) (hx : x ∈ e.source) :
+ (he : e ∈ maximalAtlas I n M) (hx : x ∈ e.source) :
ContMDiffWithinAt I I' n f s x ↔
ContMDiffWithinAt 𝓘(𝕜, E) I' n (f ∘ (e.extend I).symm) ((e.extend I).symm ⁻¹' s ∩ range I)
(e.extend I x) := by
@@ -505,8 +505,7 @@ theorem contMDiffOn_iff [IsManifold I n M] [IsManifold I' n M'] :
mfld_set_tac
/-- zero-smoothness on a set is equivalent to continuity on this set. -/
-theorem contMDiffOn_zero_iff :
- ContMDiffOn I I' 0 f s ↔ ContinuousOn f s := by
+theorem contMDiffOn_zero_iff : ContMDiffOn I I' 0 f s ↔ ContinuousOn f s := by
rw [contMDiffOn_iff]
refine ⟨fun h ↦ h.1, fun h ↦ ⟨h, ?_⟩⟩
intro x y
@@ -713,7 +712,7 @@ protected theorem ContMDiffOn.contMDiffAt (h : ContMDiffOn I I' n f s) (hx : s
ContMDiffAt I I' n f x :=
(h x (mem_of_mem_nhds hx)).contMDiffAt hx
-theorem contMDiffOn_iff_source_of_mem_maximalAtlas [IsManifold I n M]
+theorem contMDiffOn_iff_source_of_mem_maximalAtlas
(he : e ∈ maximalAtlas I n M) (hs : s ⊆ e.source) :
ContMDiffOn I I' n f s ↔
ContMDiffOn 𝓘(𝕜, E) I' n (f ∘ (e.extend I).symm) (e.extend I '' s) := by
From 8b94556e352c04a3d23faa327a71e911cde53774 Mon Sep 17 00:00:00 2001
From: Moritz Doll <21366319+mcdoll@users.noreply.github.com>
Date: Fri, 19 Jun 2026 07:45:10 +0000
Subject: [PATCH 0166/1300] chore(Analysis/TemperedDistribution): add coercion
and fix existing one (#40094)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Adds a missing coercion from Schwartz functions to `Lp` functions and changes the
current coercion from `Lp` functions to tempered distributions from `CoeDep` to `CoeHead`.
The second coercion can only appear at the head of a coercion chain, because `𝓢'(E, F)` can't infer the value of `p` or `μ`.
---
Mathlib/Analysis/Distribution/SchwartzSpace/Basic.lean | 4 ++++
Mathlib/Analysis/Distribution/TemperedDistribution.lean | 9 +++++----
2 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/Mathlib/Analysis/Distribution/SchwartzSpace/Basic.lean b/Mathlib/Analysis/Distribution/SchwartzSpace/Basic.lean
index bbd069ea784a13..8216465d129539 100644
--- a/Mathlib/Analysis/Distribution/SchwartzSpace/Basic.lean
+++ b/Mathlib/Analysis/Distribution/SchwartzSpace/Basic.lean
@@ -1312,6 +1312,10 @@ theorem memLp (f : 𝓢(E, F)) (p : ℝ≥0∞) (μ : Measure E := by volume_tac
def toLp (f : 𝓢(E, F)) (p : ℝ≥0∞) (μ : Measure E := by volume_tac) [hμ : μ.HasTemperateGrowth] :
Lp F p μ := (f.memLp p μ).toLp
+instance instCoeToLp {p : ℝ≥0∞} {μ : Measure E} [hμ : μ.HasTemperateGrowth] :
+ Coe 𝓢(E, F) (Lp F p μ) where
+ coe := (SchwartzMap.toLp · p μ)
+
theorem coeFn_toLp (f : 𝓢(E, F)) (p : ℝ≥0∞) (μ : Measure E := by volume_tac)
[hμ : μ.HasTemperateGrowth] : f.toLp p μ =ᵐ[μ] f := (f.memLp p μ).coeFn_toLp
diff --git a/Mathlib/Analysis/Distribution/TemperedDistribution.lean b/Mathlib/Analysis/Distribution/TemperedDistribution.lean
index 5be14c0e45ed36..871955ee5ae633 100644
--- a/Mathlib/Analysis/Distribution/TemperedDistribution.lean
+++ b/Mathlib/Analysis/Distribution/TemperedDistribution.lean
@@ -175,13 +175,14 @@ theorem toTemperedDistribution_apply {p : ℝ≥0∞} [hp : Fact (1 ≤ p)] (f :
filter_upwards [g.coeFn_toLp (1 - p⁻¹)⁻¹ μ] with x hg
rw [hg]
-instance instCoeDep {p : ℝ≥0∞} [hp : Fact (1 ≤ p)] (f : Lp F p μ) :
- CoeDep (Lp F p μ) f 𝓢'(E, F) where
- coe := toTemperedDistribution f
+/-- This coercion has to be a `CoeHead`, because `𝓢'(E, F)` can't infer the value of `p` or `μ`. -/
+instance instCoeToTemperedDistribution {p : ℝ≥0∞} [hp : Fact (1 ≤ p)] :
+ CoeHead (Lp F p μ) 𝓢'(E, F) where
+ coe := toTemperedDistribution
@[simp]
theorem toTemperedDistribution_toLp_eq [SecondCountableTopology E] {p : ℝ≥0∞} [hp : Fact (1 ≤ p)]
- (f : 𝓢(E, F)) : ((f.toLp p μ) : 𝓢'(E, F)) = f.toTemperedDistributionCLM E F μ := by
+ (f : 𝓢(E, F)) : ((f : Lp F p μ) : 𝓢'(E, F)) = f.toTemperedDistributionCLM E F μ := by
ext g
simp only [Lp.toTemperedDistribution_apply, toTemperedDistributionCLM_apply_apply]
apply integral_congr_ae
From e41065ae98bda505946885a844b968856a096054 Mon Sep 17 00:00:00 2001
From: Eric Wieser <425260+eric-wieser@users.noreply.github.com>
Date: Fri, 19 Jun 2026 08:44:23 +0000
Subject: [PATCH 0167/1300] chore: rename `TwoSidedIdeal.mk` (#40776)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Since this is an implemention detail and doesn't match the docstring, let's give it a more explicit name.
If we in future refactor `mk'` to be the real constructor (to generalize to semirings, for instance), then we can keep this new `ofRingCon` name for the conversion function.
Zulip: [#mathlib4 > Two sided ideals and Ring congruences @ 💬](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Two.20sided.20ideals.20and.20Ring.20congruences/near/604588344)
---
Mathlib/LinearAlgebra/Matrix/Ideal.lean | 2 +-
Mathlib/RingTheory/TwoSidedIdeal/Basic.lean | 18 +++++++++++++++---
Mathlib/RingTheory/TwoSidedIdeal/Kernel.lean | 3 ++-
3 files changed, 18 insertions(+), 5 deletions(-)
diff --git a/Mathlib/LinearAlgebra/Matrix/Ideal.lean b/Mathlib/LinearAlgebra/Matrix/Ideal.lean
index c3bc08418c5df5..1ed4a0c51d8756 100644
--- a/Mathlib/LinearAlgebra/Matrix/Ideal.lean
+++ b/Mathlib/LinearAlgebra/Matrix/Ideal.lean
@@ -261,7 +261,7 @@ theorem matrix_monotone : Monotone (matrix (R := R) n) :=
theorem matrix_strictMono_of_nonempty [h : Nonempty n] :
StrictMono (matrix (R := R) n) :=
matrix_monotone n |>.strictMono_of_injective <|
- .comp (fun _ _ => mk.inj) <| (RingCon.matrix_injective n).comp ringCon_injective
+ .comp (fun _ _ => ofRingCon.inj) <| (RingCon.matrix_injective n).comp ringCon_injective
@[simp]
theorem matrix_bot : (⊥ : TwoSidedIdeal R).matrix n = ⊥ :=
diff --git a/Mathlib/RingTheory/TwoSidedIdeal/Basic.lean b/Mathlib/RingTheory/TwoSidedIdeal/Basic.lean
index 543ded7a05f1ee..d9de6ad31362b3 100644
--- a/Mathlib/RingTheory/TwoSidedIdeal/Basic.lean
+++ b/Mathlib/RingTheory/TwoSidedIdeal/Basic.lean
@@ -36,7 +36,9 @@ A two-sided ideal of a ring `R` is a subset of `R` that contains `0` and is clos
negation, and absorbs multiplication on both sides.
-/
structure TwoSidedIdeal (R : Type*) [NonUnitalNonAssocRing R] where
- /-- every two-sided-ideal is induced by a congruence relation on the ring. -/
+ /-- In a ring, every two-sided ideal is induced by a ring congruence relation. -/
+ ofRingCon ::
+ /-- The congruence relation induced by this ideal. -/
ringCon : RingCon R
end definitions
@@ -69,9 +71,19 @@ instance : PartialOrder (TwoSidedIdeal R) := .ofSetLike (TwoSidedIdeal R) R
lemma mem_iff (x : R) : x ∈ I ↔ I.ringCon x 0 := Iff.rfl
@[simp]
-lemma mem_mk {x : R} {c : RingCon R} : x ∈ mk c ↔ c x 0 := Iff.rfl
+lemma mem_ofRingCon {x : R} {c : RingCon R} : x ∈ ofRingCon c ↔ c x 0 := Iff.rfl
@[simp, norm_cast]
+lemma coe_ofRingCon {c : RingCon R} : (ofRingCon c : Set R) = {x | c x 0} := rfl
+
+/-- A deprecated alias for `ofRingCon`. -/
+@[deprecated mk (since := "2026-06-18")]
+abbrev mk (c : RingCon R) : TwoSidedIdeal R := ofRingCon c
+
+@[deprecated mem_ofRingCon (since := "2026-06-18")]
+lemma mem_mk {x : R} {c : RingCon R} : x ∈ mk c ↔ c x 0 := Iff.rfl
+
+@[deprecated coe_ofRingCon (since := "2026-06-18")]
lemma coe_mk {c : RingCon R} : (mk c : Set R) = {x | c x 0} := rfl
lemma rel_iff (x y : R) : I.ringCon x y ↔ x - y ∈ I := by
@@ -95,7 +107,7 @@ lemma le_iff {I J : TwoSidedIdeal R} : I ≤ J ↔ (I : Set R) ⊆ (J : Set R) :
@[simps apply symm_apply]
def orderIsoRingCon : TwoSidedIdeal R ≃o RingCon R where
toFun := TwoSidedIdeal.ringCon
- invFun := .mk
+ invFun := ofRingCon
map_rel_iff' {I J} := Iff.symm <| le_iff.trans ⟨fun h x y r => by rw [rel_iff] at r ⊢; exact h r,
fun h x hx => by rw [SetLike.mem_coe, mem_iff] at hx ⊢; exact h hx⟩
diff --git a/Mathlib/RingTheory/TwoSidedIdeal/Kernel.lean b/Mathlib/RingTheory/TwoSidedIdeal/Kernel.lean
index ad1b1e4d90c2ef..bc366c492530f2 100644
--- a/Mathlib/RingTheory/TwoSidedIdeal/Kernel.lean
+++ b/Mathlib/RingTheory/TwoSidedIdeal/Kernel.lean
@@ -31,7 +31,8 @@ variable (f : F)
The kernel of a ring homomorphism, as a two-sided ideal.
-/
def ker : TwoSidedIdeal R :=
- .mk
+ .ofRingCon
+ -- TODO: use `RingCon.ker`
{ r := fun x y ↦ f x = f y
iseqv := by constructor <;> aesop
mul' := by intro; simp_all
From 65e1648ab2084b2481bcb1ddce09c7d070a4d1d7 Mon Sep 17 00:00:00 2001
From: Bingyu Xia <71547343+BryceT233@users.noreply.github.com>
Date: Fri, 19 Jun 2026 09:20:31 +0000
Subject: [PATCH 0168/1300] feat(Algebra/Category/Ring): `IsLocalRing` for
limits (#37008)
This PR introduces theorems establishing that limits in `CommRingCat` (specifically pullbacks and equalizers) preserve local homomorphisms and `IsLocalRing` properties under suitable conditions.
---
Mathlib.lean | 1 +
.../Algebra/Category/Ring/Constructions.lean | 133 ++++++++++-----
Mathlib/RingTheory/LocalRing/Pullback.lean | 153 ++++++++++++++++++
.../RingTheory/LocalRing/RingHom/Basic.lean | 13 +-
4 files changed, 254 insertions(+), 46 deletions(-)
create mode 100644 Mathlib/RingTheory/LocalRing/Pullback.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 066f7d79c13745..347da5096894ef 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -6713,6 +6713,7 @@ public import Mathlib.RingTheory.LocalRing.MaximalIdeal.Defs
public import Mathlib.RingTheory.LocalRing.MaximalIdeal.Square
public import Mathlib.RingTheory.LocalRing.Module
public import Mathlib.RingTheory.LocalRing.NonLocalRing
+public import Mathlib.RingTheory.LocalRing.Pullback
public import Mathlib.RingTheory.LocalRing.Quotient
public import Mathlib.RingTheory.LocalRing.ResidueField.Basic
public import Mathlib.RingTheory.LocalRing.ResidueField.Defs
diff --git a/Mathlib/Algebra/Category/Ring/Constructions.lean b/Mathlib/Algebra/Category/Ring/Constructions.lean
index cf49ecd37cfa4a..ff793325838353 100644
--- a/Mathlib/Algebra/Category/Ring/Constructions.lean
+++ b/Mathlib/Algebra/Category/Ring/Constructions.lean
@@ -5,13 +5,17 @@ Authors: Andrew Yang
-/
module
-public import Mathlib.Algebra.Category.Ring.Colimits
+public import Mathlib.Algebra.Category.Ring.Adjunctions
public import Mathlib.Algebra.Category.Ring.Instances
public import Mathlib.Algebra.Category.Ring.Limits
public import Mathlib.CategoryTheory.Limits.Shapes.StrictInitial
public import Mathlib.RingTheory.Localization.BaseChange
public import Mathlib.CategoryTheory.Limits.Shapes.Pullback.IsPullback.Basic
+import Mathlib.RingTheory.FreeCommRing
+import Mathlib.Algebra.Ring.Subring.Units
+import Mathlib.CategoryTheory.Adjunction.Limits
+
/-!
# Constructions of (co)limits in `CommRingCat`
@@ -335,6 +339,53 @@ noncomputable def _root_.RingEquiv.piEquivPi (R : ι → Type u) [∀ i, CommRin
end Pi
+namespace Limits
+
+variable {J : Type u'} [SmallCategory J] (F : J ⥤ CommRingCat.{u}) {c : Cone F}
+
+set_option backward.isDefEq.respectTransparency false in
+theorem isUnit_iff_forall_isUnit (hc : IsLimit c) (r : c.pt) : IsUnit r ↔
+ ∀ (j : J), IsUnit (c.π.app j r) := by
+ refine ⟨fun h _ ↦ h.map _, fun h ↦ ?_⟩
+ simp only [isUnit_iff_exists_inv] at h ⊢
+ choose inv h_inv using h
+ have map_inv {j k : J} (f : j ⟶ k) : F.map f (inv j) = inv k := by
+ have h := congr(F.map f $(h_inv j))
+ have : F.map f (c.π.app j r) = c.π.app k r :=
+ DFunLike.congr_fun (congr(Hom.hom $(c.w f))) r
+ rw [map_mul, map_one, this] at h
+ rw [← mul_one (F.map f (inv j)), ← h_inv k, ← mul_assoc]
+ nth_rw 2 [mul_comm]; rw [h, one_mul]
+ let inv_r : Cone F := .mk (CommRingCat.of (FreeCommRing PUnit)) {
+ app j := ConcreteCategory.ofHom (FreeCommRing.lift (fun _ ↦ inv j))
+ naturality j k f := by
+ ext1; change FreeCommRing.lift (fun _ => inv k) = _
+ ext; simp [map_inv f] }
+ use hc.lift inv_r (FreeCommRing.of PUnit.unit)
+ refine Concrete.isLimit_ext _ hc _ _ fun j ↦ ?_
+ rw [RingHom.map_mul, RingHom.map_one]; convert h_inv j
+ change (hc.lift inv_r ≫ c.π.app j) (FreeCommRing.of PUnit.unit) = inv j
+ rw [IsLimit.fac]; exact FreeCommRing.lift_of ..
+
+-- The assumption `hj` can be generalized to a zigzag-like assumption of finite steps.
+theorem π_isLocalHom (hc : IsLimit c) (j : J) (hj : ∀ (x : c.pt), IsUnit (c.π.app j x) →
+ ∀ (i : J), ∃ (k : J) (f : i ⟶ k) (g : j ⟶ k), IsLocalHom (F.map f).hom ∧
+ F.map f (c.π.app i x) = F.map g (c.π.app j x)) :
+ IsLocalHom (c.π.app j).hom := by
+ refine ⟨fun (x : c.pt) hx ↦ (?_ : IsUnit x)⟩
+ rw [isUnit_iff_forall_isUnit F hc]; intro i
+ obtain ⟨k, f, g, lh, eq⟩ := hj x hx i
+ exact lh.map_nonunit _ (eq ▸ hx.map _)
+
+theorem isLocalRing (hc : IsLimit c) (j : J) [IsLocalRing (F.obj j)]
+ (hj : ∀ (x : c.pt), IsUnit (c.π.app j x) → ∀ (i : J), ∃ (k : J) (f : i ⟶ k) (g : j ⟶ k),
+ IsLocalHom (F.map f).hom ∧ F.map f (c.π.app i x) = F.map g (c.π.app j x)) :
+ IsLocalRing c.pt := by
+ have := π_isLocalHom F hc j hj
+ apply RingHom.domain_isLocalRing (c.π.app j).hom
+
+end Limits
+
section Equalizer
variable {A B : CommRingCat.{u}} (f g : A ⟶ B)
@@ -357,55 +408,44 @@ def equalizerForkIsLimit : IsLimit (equalizerFork f g) := by
ext x
exact Subtype.ext <| RingHom.congr_fun (congrArg Hom.hom hm) x
-instance : IsLocalHom (equalizerFork f g).ι.hom := by
- constructor
- rintro ⟨a, h₁ : _ = _⟩ (⟨⟨x, y, h₃, h₄⟩, rfl : x = _⟩ : IsUnit a)
- have : y ∈ RingHom.eqLocus f.hom g.hom := by
- apply (f.hom.isUnit_map ⟨⟨x, y, h₃, h₄⟩, rfl⟩ : IsUnit (f x)).mul_left_inj.mp
- conv_rhs => rw [h₁]
- rw [← f.hom.map_mul, ← g.hom.map_mul, h₄, f.hom.map_one, g.hom.map_one]
- rw [isUnit_iff_exists_inv]
- exact ⟨⟨y, this⟩, Subtype.ext h₃⟩
-
-@[instance]
-theorem equalizer_ι_isLocalHom (F : WalkingParallelPair ⥤ CommRingCat.{u}) :
- IsLocalHom (limit.π F WalkingParallelPair.zero).hom := by
- have := limMap_π (diagramIsoParallelPair F).hom WalkingParallelPair.zero
- rw [← IsIso.comp_inv_eq] at this
- rw [← this]
- rw [← limit.isoLimitCone_hom_π
- ⟨_,
- equalizerForkIsLimit (F.map WalkingParallelPairHom.left)
- (F.map WalkingParallelPairHom.right)⟩
- WalkingParallelPair.zero]
- change IsLocalHom ((lim.map _ ≫ _ ≫ (equalizerFork _ _).ι) ≫ _).hom
- infer_instance
+instance : IsLocalHom (equalizerFork f g).ι.hom :=
+ inferInstanceAs <| IsLocalHom (f.hom.eqLocus g.hom).subtype
-open CategoryTheory.Limits.WalkingParallelPair Opposite
+open WalkingParallelPair WalkingParallelPairHom Opposite
-open CategoryTheory.Limits.WalkingParallelPairHom
+instance equalizer_ι_isLocalHom (F : WalkingParallelPair ⥤ CommRingCat.{u}) :
+ IsLocalHom (limit.π F WalkingParallelPair.zero).hom := by
+ refine Limits.π_isLocalHom _ (limit.isLimit _) zero fun x hx i ↦ ?_
+ rcases i with _ | _
+ · exact ⟨zero, 𝟙 _, 𝟙 _, inferInstance, by simp⟩
+ · refine ⟨one, 𝟙 _, left, inferInstance, ?_⟩
+ simp only [CategoryTheory.Functor.map_id, hom_id, limit.cone_x, limit.cone_π, RingHom.id_apply]
+ exact (limit.w_apply F left x).symm
+
+theorem equalizer_limit_isLocalRing (F : WalkingParallelPair ⥤ CommRingCat.{u})
+ [IsLocalRing (F.obj zero)] : IsLocalRing ↑(limit F) :=
+ RingHom.domain_isLocalRing (limit.π F WalkingParallelPair.zero).hom
-set_option backward.defeqAttrib.useBackward true in
-set_option backward.isDefEq.respectTransparency false in
instance equalizer_ι_isLocalHom' (F : WalkingParallelPairᵒᵖ ⥤ CommRingCat.{u}) :
- IsLocalHom (limit.π F (Opposite.op WalkingParallelPair.one)).hom := by
- have := limit.isoLimitCone_inv_π
- ⟨_, IsLimit.whiskerEquivalence (limit.isLimit F) walkingParallelPairOpEquiv⟩
- WalkingParallelPair.zero
- dsimp at this
- rw [← this]
- -- note: this was not needed before https://github.com/leanprover-community/mathlib4/pull/19757
- have : IsLocalHom (limit.π (walkingParallelPairOp ⋙ F) zero).hom := by infer_instance
- infer_instance
+ IsLocalHom (limit.π F (op one)).hom := by
+ refine Limits.π_isLocalHom _ (limit.isLimit _) (op one) fun x hx i ↦ ?_
+ rcases i with _ | _
+ · refine ⟨op zero, 𝟙 _, op left, inferInstance, ?_⟩
+ simp only [CategoryTheory.Functor.map_id, hom_id, limit.cone_x, limit.cone_π,
+ RingHom.id_apply]
+ exact (limit.w_apply F (op left) x).symm
+ · exact ⟨op one, 𝟙 _, 𝟙 _, inferInstance, by simp⟩
end Equalizer
section Pullback
+variable {A B C : CommRingCat.{u}}
+
/-- In the category of `CommRingCat`, the pullback of `f : A ⟶ C` and `g : B ⟶ C` is the `eqLocus`
of the two maps `A × B ⟶ C`. This is the constructed pullback cone.
-/
-def pullbackCone {A B C : CommRingCat.{u}} (f : A ⟶ C) (g : B ⟶ C) : PullbackCone f g :=
+def pullbackCone (f : A ⟶ C) (g : B ⟶ C) : PullbackCone f g :=
PullbackCone.mk
(CommRingCat.ofHom <|
(RingHom.fst A B).comp
@@ -418,7 +458,7 @@ def pullbackCone {A B C : CommRingCat.{u}} (f : A ⟶ C) (g : B ⟶ C) : Pullbac
simpa [CommRingCat.ofHom] using e)
/-- The constructed pullback cone is indeed the limit. -/
-def pullbackConeIsLimit {A B C : CommRingCat.{u}} (f : A ⟶ C) (g : B ⟶ C) :
+def pullbackConeIsLimit (f : A ⟶ C) (g : B ⟶ C) :
IsLimit (pullbackCone f g) := by
fapply PullbackCone.IsLimit.mk
· intro s
@@ -439,6 +479,21 @@ def pullbackConeIsLimit {A B C : CommRingCat.{u}} (f : A ⟶ C) (g : B ⟶ C) :
rw [← eq1, ← eq2]
rfl
+open WalkingCospan
+
+instance pullbackFst_isLocalHom (f : A ⟶ C) (g : B ⟶ C) [IsLocalHom g.hom] :
+ IsLocalHom (pullback.fst f g).hom := by
+ refine Limits.π_isLocalHom _ (limit.isLimit _) left fun x hx i ↦ ?_
+ rcases i with _ | _ | _
+ · exact ⟨one, 𝟙 _, Hom.inl, inferInstance, by simp; rfl⟩
+ · exact ⟨left, 𝟙 _, 𝟙 _, inferInstance, by simp⟩
+ · refine ⟨one, Hom.inr, Hom.inl, ‹_›, ?_⟩
+ exact DFunLike.congr_fun (congr(Hom.hom $(pullback.condition (f := f) (g := g)))) x |>.symm
+
+theorem pullback_isLocalRing (f : A ⟶ C) (g : B ⟶ C) [IsLocalHom g.hom] [IsLocalRing A] :
+ IsLocalRing ↑(pullback f g) :=
+ RingHom.domain_isLocalRing (pullback.fst f g).hom
+
end Pullback
end CommRingCat
diff --git a/Mathlib/RingTheory/LocalRing/Pullback.lean b/Mathlib/RingTheory/LocalRing/Pullback.lean
new file mode 100644
index 00000000000000..544f6634a4a90c
--- /dev/null
+++ b/Mathlib/RingTheory/LocalRing/Pullback.lean
@@ -0,0 +1,153 @@
+/-
+Copyright (c) 2026 Bingyu Xia. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Bingyu Xia
+-/
+
+module
+
+public import Mathlib.Algebra.Torsor.Defs
+public import Mathlib.RingTheory.LocalRing.MaximalIdeal.Basic
+
+import Mathlib.Algebra.Ring.Subring.Units
+import Mathlib.RingTheory.LocalRing.RingHom.Basic
+
+/-!
+# Local Ring Properties of Equalizers and Pullbacks
+
+In this file we provide basic lemmas for the equalizers the pullbacks and of ring homomorphisms
+and algebra homomorphisms. We show that they preserve the property of being a local ring under
+suitable conditions.
+
+## Main definitions
+
+* `RingHom.pullback`: The pullback of two ring homomorphisms `f : R →+* T` and `g : S →+* T`,
+ defined as the subring of `R × S` consisting of pairs `(r, s)` such that `f r = g s`.
+
+* `RingHom.pullbackFst`, `RingHom.pullbackSnd`: The canonical projection maps from the
+ pullback to `R` and `S`.
+
+## Main results
+
+* `RingHom.isLocalRing_eqLocus`: The equalizer of two ring homomorphisms from a local
+ ring is again a local ring.
+
+* `RingHom.isLocalRing_pullback`: The pullback of `f : R →+* T` and `g : S →+* T` is a
+ local ring, provided that `R` is a local ring and `g` is a local homomorphism.
+
+-/
+
+@[expose] public section
+
+namespace RingHom
+
+variable {R S T : Type*} [Ring R] [Ring S] [Semiring T]
+
+theorem isLocalRing_eqLocus [IsLocalRing R] (f g : R →+* T) : IsLocalRing (f.eqLocus g) :=
+ (f.eqLocus g).subtype.domain_isLocalRing
+
+/-- The subring of pairs `(r, s) : R × S` such that `f r = g s`, i.e.,
+ the pullback of `f : R →+* T` and `g : S →+* T` as a subring of `R × S`. -/
+abbrev pullback (f : R →+* T) (g : S →+* T) : Subring (R × S) :=
+ (f.comp (RingHom.fst R S)).eqLocus <| g.comp (RingHom.snd R S)
+
+/-- The first projection from the pullback of `f : R →+* T` and `g : S →+* T` to `R`. -/
+abbrev pullbackFst (f : R →+* T) (g : S →+* T) : f.pullback g →+* R :=
+ (RingHom.fst R S).comp (RingHom.pullback f g).subtype
+
+/-- The second projection from the pullback of `f : R →+* T` and `g : S →+* T` to `S`. -/
+abbrev pullbackSnd (f : R →+* T) (g : S →+* T) : f.pullback g →+* S :=
+ (RingHom.snd R S).comp (f.pullback g).subtype
+
+theorem pullback_comm_sq (f : R →+* T) (g : S →+* T) :
+ f.comp (f.pullbackFst g) = g.comp (f.pullbackSnd g) :=
+ ext fun x ↦ x.prop
+
+theorem isUnit_pullback_mk_iff (f : R →+* T) (g : S →+* T) {a : R × S} (a_in : a ∈ f.pullback g) :
+ IsUnit (⟨a, a_in⟩ : f.pullback g) ↔ IsUnit a.1 ∧ IsUnit a.2 := by
+ rw [isUnit_eqLocus_mk_iff, Prod.isUnit_iff]
+
+instance isLocalHom_pullbackFst (f : R →+* T) (g : S →+* T) [IsLocalHom g] :
+ IsLocalHom (f.pullbackFst g) where
+ map_nonunit := fun ⟨⟨_, _⟩, h_in⟩ ha ↦
+ (isUnit_pullback_mk_iff f g h_in).mpr ⟨ha, isUnit_of_map_unit g _ (h_in ▸ ha.map f)⟩
+
+instance isLocalHom_pullbackSnd (f : R →+* T) (g : S →+* T) [IsLocalHom f] :
+ IsLocalHom (f.pullbackSnd g) where
+ map_nonunit := fun ⟨⟨_, _⟩, h_in⟩ ha ↦
+ (isUnit_pullback_mk_iff f g h_in).mpr ⟨isUnit_of_map_unit f _ (h_in.symm ▸ ha.map g), ha⟩
+
+theorem surjective_pullbackFst_of_surjective (f : R →+* T) (g : S →+* T)
+ (h : Function.Surjective g) : Function.Surjective (f.pullbackFst g) :=
+ fun r ↦ by simpa [eq_comm] using h (f r)
+
+theorem surjective_pullbackSnd_of_surjective (f : R →+* T) (g : S →+* T)
+ (h : Function.Surjective f) : Function.Surjective (f.pullbackSnd g) :=
+ fun s ↦ by simpa [eq_comm] using h (g s)
+
+theorem map_pullbackSnd_ker_pullbackFst_eq (f : R →+* T) (g : S →+* T) :
+ Ideal.map (f.pullbackSnd g) (RingHom.ker (f.pullbackFst g)) = RingHom.ker g := by
+ apply le_antisymm
+ · rw [Ideal.map_le_iff_le_comap]
+ rintro ⟨⟨_, _⟩, h⟩
+ simp at h ⊢; grind
+ · intro s hs
+ exact Ideal.mem_map_of_mem (f.pullbackSnd g) (x := ⟨(0, s), by simpa using hs.symm⟩)
+ (I := RingHom.ker (f.pullbackFst g)) (by simp)
+
+theorem isLocalRing_pullback [IsLocalRing R] (f : R →+* T) (g : S →+* T) [IsLocalHom g] :
+ IsLocalRing (f.pullback g) := (f.pullbackFst g).domain_isLocalRing
+
+end RingHom
+
+namespace AlgHom
+
+variable {R A B C : Type*} [CommSemiring R]
+
+section Semiring
+
+variable [Semiring A] [Algebra R A] [Semiring B] [Algebra R B] [Semiring C] [Algebra R C]
+
+/-- The subalgebra of pairs `(a, b) : A × B` such that `f a = g b`, i.e.,
+ the pullback of f and g as a subalgebra of A × B. -/
+abbrev pullback (f : A →ₐ[R] C) (g : B →ₐ[R] C) : Subalgebra R (A × B) := equalizer
+ (f.comp (fst R A B)) (g.comp (snd R A B))
+
+/-- The first projection from the pullback of `f` and `g` to `A`. -/
+abbrev pullbackFst (f : A →ₐ[R] C) (g : B →ₐ[R] C) : pullback f g →ₐ[R] A :=
+ (fst R A B).comp (pullback f g).val
+
+/-- The second projection from the pullback of `f` and `g` to `B`. -/
+abbrev pullbackSnd (f : A →ₐ[R] C) (g : B →ₐ[R] C) : pullback f g →ₐ[R] B :=
+ (snd R A B).comp (pullback f g).val
+
+theorem pullback_comm_sq (f : A →ₐ[R] C) (g : B →ₐ[R] C) :
+ f.comp (pullbackFst f g) = g.comp (pullbackSnd f g) :=
+ AlgHom.ext fun x ↦ x.prop
+
+end Semiring
+
+section Ring
+
+variable [Ring A] [Algebra R A] [Ring B] [Algebra R B] [Semiring C] [Algebra R C]
+
+theorem isUnit_pullback_mk_iff (f : A →ₐ[R] C) (g : B →ₐ[R] C) {a : A × B}
+ (a_in : a ∈ f.pullback g) : IsUnit (⟨a, a_in⟩ : f.pullback g) ↔
+ IsUnit a.1 ∧ IsUnit a.2 :=
+ RingHom.isUnit_pullback_mk_iff (f : A →+* C) (g : B →+* C) a_in
+
+theorem surjective_pullbackFst_of_surjective (f : A →ₐ[R] C) (g : B →ₐ[R] C)
+ (h : Function.Surjective g) : Function.Surjective (pullbackFst f g) :=
+ RingHom.surjective_pullbackFst_of_surjective (f : A →+* C) (g : B →+* C) h
+
+theorem surjective_pullbackSnd_of_surjective (f : A →ₐ[R] C) (g : B →ₐ[R] C)
+ (h : Function.Surjective f) : Function.Surjective (pullbackSnd f g) :=
+ RingHom.surjective_pullbackSnd_of_surjective (f : A →+* C) (g : B →+* C) h
+
+theorem isLocalRing_pullback [IsLocalRing A] (f : A →ₐ[R] C) (g : B →ₐ[R] C)
+ [IsLocalHom g] : IsLocalRing (f.pullback g) :=
+ RingHom.isLocalRing_pullback (f : A →+* C) (g : B →+* C)
+
+end Ring
+
+end AlgHom
diff --git a/Mathlib/RingTheory/LocalRing/RingHom/Basic.lean b/Mathlib/RingTheory/LocalRing/RingHom/Basic.lean
index 1ab85fd57885fd..deeaae7fdb0531 100644
--- a/Mathlib/RingTheory/LocalRing/RingHom/Basic.lean
+++ b/Mathlib/RingTheory/LocalRing/RingHom/Basic.lean
@@ -45,13 +45,12 @@ theorem isLocalHom_of_comp (f : R →+* S) (g : S →+* T) [IsLocalHom (g.comp f
⟨fun _ ha => (isUnit_map_iff (g.comp f) _).mp (g.isUnit_map ha)⟩
/-- If `f : R →+* S` is a local ring hom, then `R` is a local ring if `S` is. -/
-theorem RingHom.domain_isLocalRing {R S : Type*} [Semiring R] [CommSemiring S] [IsLocalRing S]
- (f : R →+* S) [IsLocalHom f] : IsLocalRing R := by
- haveI : Nontrivial R := f.domain_nontrivial
- apply IsLocalRing.of_nonunits_add
- intro a b
- simp_rw [← map_mem_nonunits_iff f, f.map_add]
- exact IsLocalRing.nonunits_add
+theorem RingHom.domain_isLocalRing [IsLocalRing S] (f : R →+* S) [IsLocalHom f] :
+ IsLocalRing R where
+ toNontrivial := f.domain_nontrivial
+ isUnit_or_isUnit_of_add_one {a b} h := Or.imp
+ (isUnit_of_map_unit f a) (isUnit_of_map_unit f b)
+ (IsLocalRing.isUnit_or_isUnit_of_add_one (by rw [← map_add, h, map_one]))
end
From 189bef07b6d8ba64b925c62349afaa70be7b14b3 Mon Sep 17 00:00:00 2001
From: Eric Wieser <425260+eric-wieser@users.noreply.github.com>
Date: Fri, 19 Jun 2026 09:20:33 +0000
Subject: [PATCH 0169/1300] feat: more lemmas about
`{Setoid,Con,AddCon,RingCon}.comap` (#40697)
---
Mathlib/Data/Setoid/Basic.lean | 60 +++++++++++++++++++++--
Mathlib/GroupTheory/Congruence/Basic.lean | 55 +++++++--------------
Mathlib/GroupTheory/Congruence/Defs.lean | 17 +++++++
Mathlib/RingTheory/Congruence/Basic.lean | 36 ++++++++++++--
Mathlib/RingTheory/Congruence/Defs.lean | 20 ++++++++
5 files changed, 145 insertions(+), 43 deletions(-)
diff --git a/Mathlib/Data/Setoid/Basic.lean b/Mathlib/Data/Setoid/Basic.lean
index 0547b8285f1049..29e735ede15619 100644
--- a/Mathlib/Data/Setoid/Basic.lean
+++ b/Mathlib/Data/Setoid/Basic.lean
@@ -38,7 +38,7 @@ attribute [refl, simp] Setoid.refl
attribute [symm] Setoid.symm
attribute [trans] Setoid.trans
-variable {α : Type*} {β : Type*}
+variable {α β γ : Type*}
namespace Setoid
@@ -55,6 +55,8 @@ instance : LE (Setoid α) :=
theorem le_def {r s : Setoid α} : r ≤ s ↔ ∀ {x y}, r x y → s x y :=
Iff.rfl
+theorem le_iff_rel_le {r₁ r₂ : Setoid α} : r₁ ≤ r₂ ↔ ⇑r₁ ≤ ⇑r₂ := Iff.rfl
+
@[refl]
theorem refl' (r : Setoid α) (x) : r x x := r.iseqv.refl x
@@ -189,11 +191,11 @@ instance completeLattice : CompleteLattice (Setoid α) :=
bot := ⟨(· = ·), ⟨fun _ => rfl, fun h => h.symm, fun h1 h2 => h1.trans h2⟩⟩
bot_le := fun r x _ h => h ▸ r.2.1 x }
-@[simp]
+@[simp, grind =]
theorem top_def : ⇑(⊤ : Setoid α) = ⊤ :=
rfl
-@[simp]
+@[simp, grind =]
theorem bot_def : ⇑(⊥ : Setoid α) = (· = ·) :=
rfl
@@ -207,6 +209,9 @@ theorem eq_top_iff {s : Setoid α} : s = (⊤ : Setoid α) ↔ ∀ x y : α, s x
rw [_root_.eq_top_iff, Setoid.le_def, Setoid.top_def]
simp only [Pi.top_apply, Prop.top_eq_true, forall_true_left]
+@[simp]
+theorem ker_eq_bot_iff {f : α → β} : ker f = ⊥ ↔ f.Injective := le_bot_iff.symm
+
lemma sInf_equiv {S : Set (Setoid α)} {x y : α} :
letI := sInf S
x ≈ y ↔ ∀ s ∈ S, s x y := Iff.rfl
@@ -401,6 +406,21 @@ related to the elements of `f⁻¹(y)` by `r`.' -/
def map (r : Setoid α) (f : α → β) : Setoid β :=
Relation.EqvGen.setoid (Relation.Map r f f)
+theorem coe_map_of_ker_le (r : Setoid α) (f : α → β) (hf : ker f ≤ r) :
+ ⇑(map r f) = Relation.Map r f f ⊔ (· = ·) := by
+ refine le_antisymm ?_ (sup_le Relation.EqvGen.rel (by rintro _ _ rfl; exact .refl _))
+ rintro _ _ hxy
+ induction hxy with
+ | rel _ _ hab => exact .inl hab
+ | refl _ => exact .inr rfl
+ | symm _ _ _ ih => exact ih.imp (Std.Symm.symm _ _) (Std.Symm.symm _ _)
+ | trans _ _ _ _ _ ih1 ih2 =>
+ rcases ih1 with ih1 | rfl
+ · rcases ih2 with ih2 | rfl
+ · exact .inl <| r.iseqv.isTrans.map hf |>.trans _ _ _ ih1 ih2
+ · exact .inl ih1
+ · exact ih2
+
/-- Given a surjective function f whose kernel is contained in an equivalence relation r, the
equivalence relation on f's codomain defined by x ≈ y ↔ the elements of f⁻¹(x) are related to
the elements of f⁻¹(y) by r. -/
@@ -420,6 +440,9 @@ See note [reducible non-instances]. -/
abbrev comap (f : α → β) (r : Setoid β) : Setoid α :=
⟨r on f, r.iseqv.comap _⟩
+theorem comap_rel_eq (f : α → β) (r : Setoid β) : ⇑(comap f r) = (⇑r on f) :=
+ rfl
+
theorem comap_rel (f : α → β) (r : Setoid β) (x y : α) : comap f r x y ↔ r (f x) (f y) :=
Iff.rfl
@@ -428,6 +451,37 @@ induced on `α` by `f` equals the kernel of `r`'s quotient map composed with `f`
theorem comap_eq {f : α → β} {r : Setoid β} : comap f r = ker (@Quotient.mk'' _ r ∘ f) :=
ext fun x y => show _ ↔ ⟦_⟧ = ⟦_⟧ by rw [Quotient.eq]; rfl
+@[simp]
+theorem comap_id (c : Setoid α) : c.comap id = c := rfl
+
+@[simp]
+theorem comap_comp (c : Setoid γ) (g : β → γ) (f : α → β) : c.comap (g ∘ f) = (c.comap g).comap f :=
+ rfl
+
+theorem comap_injective (f : α → β) (hf : Function.Surjective f) :
+ Function.Injective (comap f) :=
+ fun _ _ h => ext <| hf.forall₂.2 <| Setoid.ext_iff.1 h
+
+theorem le_comap_map {r : Setoid α} {f : α → β} : r ≤ comap f (r.map f) :=
+ fun _ _ h => Relation.EqvGen.rel _ _ ⟨_, _, h, rfl, rfl⟩
+
+theorem comap_map_of_ker_le (f : α → β) (r : Setoid α) (hf : ker f ≤ r) :
+ comap f (r.map f) = r := by
+ apply le_antisymm _ le_comap_map
+ rw [le_iff_rel_le, comap_rel_eq, coe_map_of_ker_le _ _ hf]
+ rintro x y (⟨a, b, h, ha, hb⟩ | h)
+ · replace ha := hf ha
+ replace hb := hf hb
+ exact trans (symm ha) (trans h hb)
+ · exact hf h
+
+theorem comap_map_eq (f : α → β) (r : Setoid α) (hf : f.Injective) : comap f (r.map f) = r :=
+ comap_map_of_ker_le f r <| ker_eq_bot_iff.2 hf ▸ bot_le
+
+theorem comap_surjective (f : α → β) (hf : Function.Injective f) :
+ Function.Surjective (Setoid.comap f) :=
+ fun r => ⟨_, comap_map_eq f r hf⟩
+
/-- The second isomorphism theorem for sets. -/
noncomputable def comapQuotientEquiv (f : α → β) (r : Setoid β) :
Quotient (comap f r) ≃ Set.range (@Quotient.mk'' _ r ∘ f) :=
diff --git a/Mathlib/GroupTheory/Congruence/Basic.lean b/Mathlib/GroupTheory/Congruence/Basic.lean
index 407821655bdedd..3700a2bd93fbdf 100644
--- a/Mathlib/GroupTheory/Congruence/Basic.lean
+++ b/Mathlib/GroupTheory/Congruence/Basic.lean
@@ -75,48 +75,29 @@ protected def congr {c d : Con M} (h : c = d) : c.Quotient ≃* d.Quotient :=
theorem congr_mk {c d : Con M} (h : c = d) (a : M) :
Con.congr h (a : c.Quotient) = (a : d.Quotient) := rfl
-@[to_additive]
-theorem le_comap_conGen {M N : Type*} [Mul M] [Mul N] (f : M → N)
- (H : ∀ (x y : M), f (x * y) = f x * f y) (rel : N → N → Prop) :
- conGen (fun x y ↦ rel (f x) (f y)) ≤ Con.comap f H (conGen rel) := by
- intro x y h
- simp only [Con.comap_rel]
- exact .rec (fun x y h ↦ .of (f x) (f y) h) (fun x ↦ .refl (f x))
- (fun _ h ↦ .symm h) (fun _ _ h1 h2 ↦ h1.trans h2) (fun {w x y z} _ _ h1 h2 ↦
- (congrArg (fun a ↦ conGen rel a (f (x * z))) (H w y)).mpr
- (((congrArg (fun a ↦ conGen rel (f w * f y) a) (H x z))).mpr
- (.mul h1 h2))) h
-
@[to_additive]
theorem comap_conGen_equiv {M N : Type*} [Mul M] [Mul N] (f : MulEquiv M N) (rel : N → N → Prop) :
Con.comap f (map_mul f) (conGen rel) = conGen (fun x y ↦ rel (f x) (f y)) := by
- apply le_antisymm _ (le_comap_conGen f (map_mul f) rel)
+ apply le_antisymm _ (le_comap_conGen rel f (map_mul f))
intro a b h
simp only [Con.comap_rel] at h
- have H : ∀ n1 n2, (conGen rel) n1 n2 → ∀ a b, f a = n1 → f b = n2 →
- (conGen fun x y ↦ rel (f x) (f y)) a b := by
- intro n1 n2 h
- induction h with
- | of x y h =>
- intro _ _ fa fb
- apply ConGen.Rel.of
- rwa [fa, fb]
- | refl x =>
- intro _ _ fc fd
- rw [f.injective (fc.trans fd.symm)]
- exact ConGen.Rel.refl _
- | symm _ h => exact fun a b fs fb ↦ ConGen.Rel.symm (h b a fb fs)
- | trans _ _ ih ih1 =>
- exact fun a b fa fb ↦ Exists.casesOn (f.surjective _) fun c' hc' ↦
- ConGen.Rel.trans (ih a c' fa hc') (ih1 c' b hc' fb)
- | mul _ _ ih ih1 =>
- rename_i w x y z _ _
- intro a b fa fb
- rw [← f.eq_symm_apply, map_mul] at fa fb
- rw [fa, fb]
- exact ConGen.Rel.mul (ih (f.symm w) (f.symm x) (by simp) (by simp))
- (ih1 (f.symm y) (f.symm z) (by simp) (by simp))
- exact H (f a) (f b) h a b (refl _) (refl _)
+ unfold Function.onFun
+ generalize fa : f a = n1 at h
+ generalize fb : f b = n2 at h
+ induction h generalizing a b with
+ | of x y h =>
+ apply ConGen.Rel.of
+ rwa [fa, fb]
+ | refl x =>
+ rw [f.injective (fa.trans fb.symm)]
+ exact ConGen.Rel.refl _
+ | symm _ h => exact ConGen.Rel.symm (h fb fa)
+ | trans _ _ ih ih1 =>
+ exact Exists.casesOn (f.surjective _) fun c' hc' ↦ ConGen.Rel.trans (ih fa hc') (ih1 hc' fb)
+ | @mul w x y z _ _ ih ih1 =>
+ rw [← f.eq_symm_apply, map_mul] at fa fb
+ rw [fa, fb]
+ exact ConGen.Rel.mul (ih (by simp) (by simp)) (ih1 (by simp) (by simp))
@[to_additive]
theorem comap_conGen_of_bijective {M N : Type*} [Mul M] [Mul N] (f : M → N)
diff --git a/Mathlib/GroupTheory/Congruence/Defs.lean b/Mathlib/GroupTheory/Congruence/Defs.lean
index b47a3672e90a92..499a5e8086a810 100644
--- a/Mathlib/GroupTheory/Congruence/Defs.lean
+++ b/Mathlib/GroupTheory/Congruence/Defs.lean
@@ -517,6 +517,23 @@ theorem comap_rel {f : M → N} (H : ∀ x y, f (x * y) = f x * f y) {c : Con N}
comap f H c x y ↔ c (f x) (f y) :=
Iff.rfl
+@[to_additive (attr := simp)]
+theorem comap_id (c : Con M) : c.comap id (by intros; rfl) = c := rfl
+
+@[to_additive (attr := simp)]
+theorem comap_comp (c : Con P) (g : N → P) (f : M → N) (hg) (hf) :
+ c.comap (g ∘ f) (by grind) = (c.comap g hg).comap f hf := rfl
+
+@[to_additive]
+theorem le_comap_conGen (r : N → N → Prop) (f : M → N) (hf) :
+ conGen (r.onFun f) ≤ (conGen r).comap f hf :=
+ conGen_le.2 fun _ _ h => ConGen.Rel.of _ _ h
+
+@[to_additive]
+theorem comap_injective (f : M → N) (hf : Function.Surjective f) (hf') :
+ Function.Injective (comap f hf') :=
+ .of_comp (f := toSetoid) <| (Setoid.comap_injective f hf).comp toSetoid_injective
+
end
section
diff --git a/Mathlib/RingTheory/Congruence/Basic.lean b/Mathlib/RingTheory/Congruence/Basic.lean
index 40ad52ee203bd3..43628cefaaa913 100644
--- a/Mathlib/RingTheory/Congruence/Basic.lean
+++ b/Mathlib/RingTheory/Congruence/Basic.lean
@@ -32,7 +32,7 @@ Most of the time you likely want to use the `Ideal.Quotient` API that is built o
@[expose] public section
-variable {α β R : Type*}
+variable {α β R R' : Type*}
namespace RingCon
@@ -132,7 +132,7 @@ The API in this section is copied from `Mathlib/GroupTheory/Congruence/Defs.lean
section Lattice
-variable [Add R] [Mul R] {c d : RingCon R}
+variable [Add R] [Mul R] [Add R'] [Mul R'] {c d : RingCon R}
/-- For congruence relations `c, d` on a type `M` with multiplication and addition, `c ≤ d` iff
`∀ x y ∈ M`, `x` is related to `y` by `d` if `x` is related to `y` by `c`. -/
@@ -143,7 +143,7 @@ instance : LE (RingCon R) where
theorem le_def : c ≤ d ↔ ∀ {x y}, c x y → d x y := .rfl
@[gcongr]
-theorem comap_mono {R' : Type*} [Add R'] [Mul R']
+theorem comap_mono
{F : Type*} [FunLike F R R'] [AddHomClass F R R'] [MulHomClass F R R']
{J J' : RingCon R'} {f : F} (h : J ≤ J') :
J.comap f ≤ J'.comap f :=
@@ -326,6 +326,36 @@ theorem sSup_eq_ringConGen (S : Set (RingCon R)) :
congr! with x y
simp
+open scoped Function
+
+theorem le_comap_ringConGen {F} [FunLike F R' R] [MulHomClass F R' R] [AddHomClass F R' R]
+ (r : R → R → Prop) (f : F) :
+ ringConGen (r on f) ≤ (ringConGen r).comap f :=
+ ringConGen_le.2 fun _ _ h => RingConGen.Rel.of _ _ h
+
+theorem comap_injective {F} [FunLike F R' R] [MulHomClass F R' R] [AddHomClass F R' R]
+ (f : F) (hf : Function.Surjective f) :
+ Function.Injective (comap · f) :=
+ .of_comp (f := toCon) <| (Con.comap_injective f hf <| map_mul f).comp toCon_injective
+
+theorem comap_ringConGen_ringEquiv {R R'} [NonAssocSemiring R] [NonAssocSemiring R']
+ (r : R' → R' → Prop) (f : R ≃+* R') :
+ (ringConGen r).comap f = ringConGen (r on f) := by
+ refine le_antisymm ?_ (le_comap_ringConGen _ _)
+ trans (ringConGen (r on ⇑f) |>.comap f.symm.toNonUnitalRingHom).comap f.toNonUnitalRingHom
+ · apply comap_mono
+ grw [← le_comap_ringConGen]
+ gcongr
+ simp [Function.onFun, RingEquiv.coe_toNonUnitalRingHom']
+ · rw [← comap_nonUnitalRingHomComp]
+ simp
+
+-- This one probably needs the RingCon version of `Setoid.comap_surjective`
+proof_wanted comap_ringConGen_equiv
+ {F} [FunLike F R' R] [MulHomClass F R' R] [AddHomClass F R' R] [EquivLike F R' R]
+ (r : R → R → Prop) (f : F) :
+ (ringConGen r).comap f = ringConGen (r on f)
+
end Lattice
end RingCon
diff --git a/Mathlib/RingTheory/Congruence/Defs.lean b/Mathlib/RingTheory/Congruence/Defs.lean
index 16f78f06af0245..1ef19c1e92172e 100644
--- a/Mathlib/RingTheory/Congruence/Defs.lean
+++ b/Mathlib/RingTheory/Congruence/Defs.lean
@@ -159,6 +159,26 @@ def comap (J : RingCon R') (f : F) :
theorem comap_rel {J : RingCon R'} {f : F} {x y : R} :
J.comap f x y ↔ J (f x) (f y) := Iff.rfl
+@[simp]
+theorem comap_nonUnitalRingHomId {R} [NonUnitalNonAssocSemiring R] (J : RingCon R) :
+ J.comap (NonUnitalRingHom.id _) = J := rfl
+
+@[simp]
+theorem comap_nonUnitalRingHomComp {R R' R''}
+ [NonUnitalNonAssocSemiring R] [NonUnitalNonAssocSemiring R'] [NonUnitalNonAssocSemiring R'']
+ (J : RingCon R) (g : R' →ₙ+* R) (f : R'' →ₙ+* R') :
+ J.comap (g.comp f) = (J.comap g).comap f := rfl
+
+@[simp]
+theorem comap_ringHomId {R} [NonAssocSemiring R] (J : RingCon R) :
+ J.comap (RingHom.id _) = J := rfl
+
+@[simp]
+theorem comap_ringHomComp {R R' R''}
+ [NonAssocSemiring R] [NonAssocSemiring R'] [NonAssocSemiring R'']
+ (J : RingCon R) (g : R' →+* R) (f : R'' →+* R') :
+ J.comap (g.comp f) = (J.comap g).comap f := rfl
+
end Basic
section Quotient
From 7f9226b30ad3ed0a756b7acaaea3dd191d1e4089 Mon Sep 17 00:00:00 2001
From: Sebastien Gouezel <10818434+sgouezel@users.noreply.github.com>
Date: Fri, 19 Jun 2026 10:05:48 +0000
Subject: [PATCH 0170/1300] feat: set integral for vector measures (#40330)
The API is completely copied from the one for the Bochner integral. I have kept and adapted to vector measures all the lemmas that made sense in this context.
To keep the PR at a manageable size, it corresponds only to the first half of the file `SetIntegral`. Second half coming in a second PR once this one is merged.
Co-authored-by: sgouezel
---
Mathlib.lean | 1 +
.../VectorMeasure/SetIntegral.lean | 280 ++++++++++++++++++
2 files changed, 281 insertions(+)
create mode 100644 Mathlib/MeasureTheory/VectorMeasure/SetIntegral.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 347da5096894ef..4e0d5a61092878 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -5622,6 +5622,7 @@ public import Mathlib.MeasureTheory.VectorMeasure.Decomposition.JordanSub
public import Mathlib.MeasureTheory.VectorMeasure.Decomposition.Lebesgue
public import Mathlib.MeasureTheory.VectorMeasure.Decomposition.RadonNikodym
public import Mathlib.MeasureTheory.VectorMeasure.Integral
+public import Mathlib.MeasureTheory.VectorMeasure.SetIntegral
public import Mathlib.MeasureTheory.VectorMeasure.Variation.Basic
public import Mathlib.MeasureTheory.VectorMeasure.Variation.Defs
public import Mathlib.MeasureTheory.VectorMeasure.WithDensity
diff --git a/Mathlib/MeasureTheory/VectorMeasure/SetIntegral.lean b/Mathlib/MeasureTheory/VectorMeasure/SetIntegral.lean
new file mode 100644
index 00000000000000..9bf93cfca754e6
--- /dev/null
+++ b/Mathlib/MeasureTheory/VectorMeasure/SetIntegral.lean
@@ -0,0 +1,280 @@
+/-
+Copyright (c) 2026 Sébastien Gouëzel. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Sébastien Gouëzel
+-/
+module
+
+public import Mathlib.MeasureTheory.VectorMeasure.Integral
+
+/-!
+# Set integral
+
+In this file we prove properties of `∫ᵛ x in s, f x ∂[B; μ]`. Recall that this notation
+is defined as `∫ᵛ x, f x ∂[B; μ.restrict s]`.
+
+The API in this file is modelled on the API for the Bochner integral.
+-/
+
+@[expose] public section
+
+assert_not_exists InnerProductSpace
+
+open Filter Function MeasureTheory RCLike Set TopologicalSpace Topology ContinuousLinearMap
+open scoped ENNReal NNReal Finset
+
+variable {ι X E F G H : Type*} {mX : MeasurableSpace X}
+ [NormedAddCommGroup E] [NormedAddCommGroup F] [NormedAddCommGroup G] [NormedAddCommGroup H]
+ {μ ν : VectorMeasure X F} {f g : X → E} {s t : Set X}
+
+namespace MeasureTheory.VectorMeasure
+
+theorem IntegrableOn.mono (hs : MeasurableSet s) (hts : t ⊆ s) (h : μ.IntegrableOn f s) :
+ μ.IntegrableOn f t := by
+ by_cases ht : MeasurableSet t; swap
+ · simp [VectorMeasure.IntegrableOn, restrict_not_measurable _ ht]
+ apply Integrable.mono_measure h
+ simp [variation_restrict, hs, ht, Measure.restrict_mono hts le_rfl]
+
+theorem IntegrableOn.union (hs : MeasurableSet s) (ht : MeasurableSet t)
+ (hf : μ.IntegrableOn f s) (h'f : μ.IntegrableOn f t) :
+ μ.IntegrableOn f (s ∪ t) := by
+ apply Integrable.mono_measure (hf.add_measure h'f)
+ grw [variation_restrict_le, Measure.restrict_union_le]
+ simp [variation_restrict, hs, ht]
+
+/- `simpNF` complains that this lemma can be proved by `simp`, because the `simp`-generated lemma
+unfolds the abbrev `VectorMeasure.Integrable`. TODO: fix `simp`. See lean4#13958. -/
+@[simp, nolint simpNF] theorem IntegrableOn.empty : μ.IntegrableOn f ∅ := by
+ simp [VectorMeasure.IntegrableOn]
+
+theorem IntegrableOn.biUnion_finite
+ {s : Set ι} (hs : s.Finite) {t : ι → Set X} (ht : ∀ i ∈ s, MeasurableSet (t i))
+ (h't : ∀ i ∈ s, μ.IntegrableOn f (t i)) :
+ μ.IntegrableOn f (⋃ i ∈ s, t i) := by
+ induction s, hs using Set.Finite.induction_on with
+ | empty => simp
+ | insert _ h's hf =>
+ simp only [mem_insert_iff, forall_eq_or_imp, iUnion_iUnion_eq_or_left] at ht h't ⊢
+ exact IntegrableOn.union ht.1 (h's.measurableSet_biUnion ht.2) h't.1 (hf ht.2 h't.2)
+
+theorem IntegrableOn.biUnion_finset {s : Finset ι} {t : ι → Set X}
+ (ht : ∀ i ∈ s, MeasurableSet (t i)) (h't : ∀ i ∈ s, μ.IntegrableOn f (t i)) :
+ μ.IntegrableOn f (⋃ i ∈ s, t i) :=
+ IntegrableOn.biUnion_finite s.finite_toSet ht h't
+
+theorem IntegrableOn.iUnion_finite [Finite ι] {t : ι → Set X}
+ (ht : ∀ i, MeasurableSet (t i)) (h't : ∀ i, μ.IntegrableOn f (t i)) :
+ μ.IntegrableOn f (⋃ i, t i) := by
+ cases nonempty_fintype ι
+ simpa using IntegrableOn.biUnion_finset (f := f) (μ := μ) (s := Finset.univ) (t := t)
+ (fun i hi ↦ ht i) (fun i hi ↦ h't i)
+
+@[simp] theorem integrableOn_univ : μ.IntegrableOn f univ ↔ μ.Integrable f := by
+ simp [VectorMeasure.IntegrableOn]
+
+theorem Integrable.integrableOn (h : μ.Integrable f) : μ.IntegrableOn f s := by
+ rw [← integrableOn_univ] at h
+ exact h.mono MeasurableSet.univ (subset_univ _)
+
+theorem integrable_indicator_iff (hs : MeasurableSet s) :
+ μ.Integrable (indicator s f) ↔ μ.IntegrableOn f s := by
+ simp [VectorMeasure.Integrable, VectorMeasure.IntegrableOn, MeasureTheory.IntegrableOn,
+ MeasureTheory.integrable_indicator_iff hs, variation_restrict hs]
+
+theorem IntegrableOn.integrable_indicator (h : μ.IntegrableOn f s) (hs : MeasurableSet s) :
+ μ.Integrable (indicator s f) :=
+ (integrable_indicator_iff hs).2 h
+
+variable [NormedSpace ℝ E] [NormedSpace ℝ F] [NormedSpace ℝ G] [NormedSpace ℝ H]
+ {B : E →L[ℝ] F →L[ℝ] G}
+
+theorem setIntegral_eq_zero_of_not_measurableSet (hs : ¬MeasurableSet s) :
+ ∫ᵛ x in s, f x ∂[B; μ] = 0 := by
+ simp [restrict_not_measurable _ hs]
+
+theorem setIntegral_congr_ae (h : ∀ᵐ x ∂μ.variation, x ∈ s → f x = g x) :
+ ∫ᵛ x in s, f x ∂[B; μ] = ∫ᵛ x in s, g x ∂[B; μ] := by
+ by_cases hs : MeasurableSet s; swap
+ · simp [setIntegral_eq_zero_of_not_measurableSet hs]
+ apply integral_congr_ae
+ rw [variation_restrict hs]
+ exact (ae_restrict_iff' hs).2 h
+
+theorem setIntegral_congr_fun (h : EqOn f g s) :
+ ∫ᵛ x in s, f x ∂[B; μ] = ∫ᵛ x in s, g x ∂[B; μ] :=
+ setIntegral_congr_ae <| Eventually.of_forall h
+
+theorem setIntegral_union (hst : Disjoint s t) (hs : MeasurableSet s) (ht : MeasurableSet t)
+ (hfs : μ.IntegrableOn f s) (hft : μ.IntegrableOn f t) :
+ ∫ᵛ x in s ∪ t, f x ∂[B; μ] = ∫ᵛ x in s, f x ∂[B; μ] + ∫ᵛ x in t, f x ∂[B; μ] := by
+ rw [← integral_add_vectorMeasure hfs hft, μ.restrict_union hst hs ht]
+
+theorem setIntegral_sdiff (hs : MeasurableSet s) (ht : MeasurableSet t)
+ (hfs : μ.IntegrableOn f s) (hts : t ⊆ s) :
+ ∫ᵛ x in s \ t, f x ∂[B; μ] = ∫ᵛ x in s, f x ∂[B; μ] - ∫ᵛ x in t, f x ∂[B; μ] := by
+ rw [eq_sub_iff_add_eq, ← setIntegral_union (by grind) (hs.diff ht) ht (hfs.mono hs sdiff_subset)
+ (hfs.mono hs hts), sdiff_union_of_subset hts]
+
+theorem setIntegral_inter_add_sdiff (hs : MeasurableSet s) (ht : MeasurableSet t)
+ (hfs : μ.IntegrableOn f s) :
+ ∫ᵛ x in s ∩ t, f x ∂[B; μ] + ∫ᵛ x in s \ t, f x ∂[B; μ] = ∫ᵛ x in s, f x ∂[B; μ] := by
+ rw [← μ.restrict_inter_add_sdiff hs ht,
+ integral_add_vectorMeasure (hfs.mono hs inter_subset_left) (hfs.mono hs sdiff_subset)]
+
+theorem setIntegral_biUnion_finset {ι : Type*} (t : Finset ι) {s : ι → Set X}
+ (hs : ∀ i ∈ t, MeasurableSet (s i)) (h's : Set.Pairwise (↑t) (Disjoint on s))
+ (hf : ∀ i ∈ t, μ.IntegrableOn f (s i)) :
+ ∫ᵛ x in ⋃ i ∈ t, s i, f x ∂[B; μ] = ∑ i ∈ t, ∫ᵛ x in s i, f x ∂[B; μ] := by
+ classical
+ induction t using Finset.induction_on with
+ | empty => simp
+ | insert _ _ hat IH =>
+ simp only [Finset.coe_insert, Finset.forall_mem_insert, Set.pairwise_insert,
+ Finset.set_biUnion_insert] at hs hf h's ⊢
+ rw [setIntegral_union]
+ · rw [Finset.sum_insert hat, IH hs.2 h's.1 hf.2]
+ · simp only [disjoint_iUnion_right]
+ exact fun i hi => (h's.2 i hi (ne_of_mem_of_not_mem hi hat).symm).1
+ · exact hs.1
+ · exact Finset.measurableSet_biUnion _ hs.2
+ · exact hf.1
+ · apply IntegrableOn.biUnion_finset hs.2 hf.2
+
+theorem setIntegral_iUnion_fintype {ι : Type*} [Fintype ι] {s : ι → Set X}
+ (hs : ∀ i, MeasurableSet (s i)) (h's : Pairwise (Disjoint on s))
+ (hf : ∀ i, μ.IntegrableOn f (s i)) :
+ ∫ᵛ x in ⋃ i, s i, f x ∂[B; μ] = ∑ i, ∫ᵛ x in s i, f x ∂[B; μ] := by
+ convert setIntegral_biUnion_finset Finset.univ (fun i _ => hs i) _ fun i _ => hf i
+ · simp
+ · simp [pairwise_univ, h's]
+
+theorem setIntegral_empty : ∫ᵛ x in ∅, f x ∂[B; μ] = 0 := by simp
+
+theorem setIntegral_univ : ∫ᵛ x in univ, f x ∂[B; μ] = ∫ᵛ x, f x ∂[B; μ] := by simp
+
+theorem setIntegral_add_compl (hs : MeasurableSet s) (hfi : μ.Integrable f) :
+ ∫ᵛ x in s, f x ∂[B; μ] + ∫ᵛ x in sᶜ, f x ∂[B; μ] = ∫ᵛ x, f x ∂[B; μ] := by
+ rw [← setIntegral_union disjoint_compl_right
+ hs hs.compl hfi.integrableOn hfi.integrableOn, union_compl_self, setIntegral_univ]
+
+theorem setIntegral_compl (hs : MeasurableSet s) (hfi : μ.Integrable f) :
+ ∫ᵛ x in sᶜ, f x ∂[B; μ] = ∫ᵛ x, f x ∂[B; μ] - ∫ᵛ x in s, f x ∂[B; μ] := by
+ rw [← setIntegral_add_compl (μ := μ) hs hfi, add_sub_cancel_left]
+
+/-- For a function `f` and a measurable set `s`, the integral of `indicator s f`
+over the whole space is equal to `∫ᵛ x in s, f x ∂[B; μ]`
+defined as `∫ᵛ x, f x ∂[B; μ.restrict s]`. -/
+theorem integral_indicator (hs : MeasurableSet s) :
+ ∫ᵛ x, indicator s f x ∂[B; μ] = ∫ᵛ x in s, f x ∂[B; μ] := by
+ by_cases hfi : μ.IntegrableOn f s; swap
+ · rw [integral_undef hfi, integral_undef]
+ rw [integrable_indicator_iff hs]
+ simpa [transpose_restrict, variation_restrict hs] using hfi
+ calc
+ ∫ᵛ x, indicator s f x ∂[B; μ]
+ _ = ∫ᵛ x in s, indicator s f x ∂[B; μ] + ∫ᵛ x in sᶜ, indicator s f x ∂[B; μ] :=
+ (setIntegral_add_compl hs (hfi.integrable_indicator hs)).symm
+ _ = ∫ᵛ x in s, f x ∂[B; μ] + ∫ᵛ x in sᶜ, 0 ∂[B; μ] := by
+ apply congr_arg₂ (· + ·) (integral_congr_ae ?_) (integral_congr_ae ?_)
+ · rw [variation_restrict hs]
+ exact indicator_ae_eq_restrict hs
+ · rw [variation_restrict hs.compl]
+ exact indicator_ae_eq_restrict_compl hs
+ _ = ∫ᵛ x in s, f x ∂[B; μ] := by simp
+
+theorem setIntegral_indicator (hs : MeasurableSet s) (ht : MeasurableSet t) :
+ ∫ᵛ x in s, t.indicator f x ∂[B; μ] = ∫ᵛ x in s ∩ t, f x ∂[B; μ] := by
+ rw [integral_indicator ht, μ.restrict_restrict ht hs, Set.inter_comm]
+
+theorem setIntegral_congr_set
+ (hs : MeasurableSet s) (ht : MeasurableSet t) (hst : s =ᵐ[μ.variation] t) :
+ ∫ᵛ x in s, f x ∂[B; μ] = ∫ᵛ x in t, f x ∂[B; μ] := by
+ rw [← integral_indicator hs, ← integral_indicator ht]
+ apply integral_congr_ae
+ filter_upwards [hst] with x hx
+ replace hx : x ∈ s ↔ x ∈ t := by simpa using! hx
+ simp [indicator]
+ grind
+
+theorem integral_piecewise [DecidablePred (· ∈ s)]
+ (hs : MeasurableSet s) (hf : μ.IntegrableOn f s) (hg : μ.IntegrableOn g sᶜ) :
+ ∫ᵛ x, s.piecewise f g x ∂[B; μ] = ∫ᵛ x in s, f x ∂[B; μ] + ∫ᵛ x in sᶜ, g x ∂[B; μ] := by
+ rw [← Set.indicator_add_compl_eq_piecewise,
+ integral_add (hf.integrable_indicator hs) (hg.integrable_indicator hs.compl),
+ integral_indicator hs, integral_indicator hs.compl]
+
+theorem setIntegral_eq_zero_of_ae_eq_zero
+ (ht_eq : ∀ᵐ x ∂μ.variation, x ∈ t → f x = 0) :
+ ∫ᵛ x in t, f x ∂[B; μ] = 0 := by
+ by_cases ht : MeasurableSet t; swap
+ · simp [setIntegral_eq_zero_of_not_measurableSet ht]
+ by_cases hf : AEStronglyMeasurable f (μ.restrict t).variation; swap
+ · rw [integral_undef]
+ contrapose hf
+ exact hf.1
+ simp only [variation_restrict ht] at hf
+ have : ∫ᵛ x in t, hf.mk f x ∂[B; μ] = 0 := by
+ refine integral_eq_zero_of_ae ?_
+ simp only [variation_restrict ht]
+ apply (ae_restrict_iff' ht).2
+ filter_upwards [ae_imp_of_ae_restrict hf.ae_eq_mk, ht_eq] with x hx h'x h''x
+ rw [← hx h''x]
+ exact h'x h''x
+ rw [← this]
+ apply integral_congr_ae
+ simp only [variation_restrict ht]
+ exact hf.ae_eq_mk
+
+theorem setIntegral_eq_zero_of_forall_eq_zero (ht_eq : ∀ x ∈ t, f x = 0) :
+ ∫ᵛ x in t, f x ∂[B; μ] = 0 :=
+ setIntegral_eq_zero_of_ae_eq_zero (Eventually.of_forall ht_eq)
+
+theorem frequently_ae_ne_zero_of_setIntegral_ne_zero (hU : ∫ᵛ x in t, f x ∂[B; μ] ≠ 0) :
+ ∃ᶠ x in ae (μ.variation.restrict t), f x ≠ 0 := by
+ have ht : MeasurableSet t := by
+ contrapose! hU
+ simp [setIntegral_eq_zero_of_not_measurableSet hU]
+ rw [← variation_restrict ht]
+ exact frequently_ae_ne_zero_of_integral_ne_zero hU
+
+theorem exists_ne_zero_of_setIntegral_ne_zero (hU : ∫ᵛ x in t, f x ∂[B; μ] ≠ 0) :
+ ∃ x, x ∈ t ∧ f x ≠ 0 := by
+ contrapose! hU; exact setIntegral_eq_zero_of_forall_eq_zero hU
+
+theorem setIntegral_of_variation_apply_eq_zero (f : X → E) {s : Set X}
+ (hs : μ.variation s = 0) :
+ ∫ᵛ x in s, f x ∂[B; μ] = 0 := by
+ by_cases h's : MeasurableSet s; swap
+ · simp [restrict_not_measurable μ h's]
+ have : (μ.restrict s).variation = 0 := by
+ rw [variation_restrict h's]
+ apply Measure.restrict_eq_zero.2 hs
+ have : μ.restrict s = 0 := variation_eq_zero.1 this
+ simpa [integral_eq_setToFun, this] using! setToFun_zero_left
+
+theorem setIntegral_dirac' {mX : MeasurableSpace X} [CompleteSpace G] {a : X} {v : F}
+ (hf : StronglyMeasurable f) {s : Set X} (hs : MeasurableSet s) [Decidable (a ∈ s)] :
+ ∫ᵛ x in s, f x ∂[B; VectorMeasure.dirac a v] = if a ∈ s then B (f a) v else 0 := by
+ rw [restrict_dirac hs]
+ split_ifs
+ · exact integral_dirac' hf
+ · exact integral_zero_vectorMeasure
+
+theorem setIntegral_dirac [MeasurableSpace X] [MeasurableSingletonClass X] [CompleteSpace G]
+ {a : X} {v : F} {s : Set X} (hs : MeasurableSet s) [Decidable (a ∈ s)] :
+ ∫ᵛ x in s, f x ∂[B; VectorMeasure.dirac a v] = if a ∈ s then B (f a) v else 0 := by
+ rw [restrict_dirac hs]
+ split_ifs
+ · exact integral_dirac
+ · exact integral_zero_vectorMeasure
+
+theorem integral_singleton' [CompleteSpace G] {a : X} (hf : StronglyMeasurable f) :
+ ∫ᵛ a in {a}, f a ∂[B; μ] = B (f a) (μ {a}) := by
+ simp only [restrict_singleton, integral_dirac' hf]
+
+theorem integral_singleton [MeasurableSingletonClass X] {a : X} [CompleteSpace G] :
+ ∫ᵛ a in {a}, f a ∂[B; μ] = B (f a) (μ {a}) := by
+ simp only [restrict_singleton, integral_dirac]
+
+end MeasureTheory.VectorMeasure
From 4db60865e6954700dc3c471830212740525f36a8 Mon Sep 17 00:00:00 2001
From: Sebastien Gouezel <10818434+sgouezel@users.noreply.github.com>
Date: Fri, 19 Jun 2026 10:05:50 +0000
Subject: [PATCH 0171/1300] chore: move strong measurability proof to
`setToFun` (#40602)
The goal is to be able to use the very same lemma for the vector measure integral, in a forthcoming PR.
Co-authored-by: sgouezel
---
.../Integral/FinMeasAdditive.lean | 12 ++++
Mathlib/MeasureTheory/Integral/Prod.lean | 66 +++----------------
Mathlib/MeasureTheory/Integral/SetToL1.lean | 66 +++++++++++++++++++
3 files changed, 87 insertions(+), 57 deletions(-)
diff --git a/Mathlib/MeasureTheory/Integral/FinMeasAdditive.lean b/Mathlib/MeasureTheory/Integral/FinMeasAdditive.lean
index d6a80a4a9e1495..1a9b39c030e3d8 100644
--- a/Mathlib/MeasureTheory/Integral/FinMeasAdditive.lean
+++ b/Mathlib/MeasureTheory/Integral/FinMeasAdditive.lean
@@ -322,6 +322,18 @@ theorem setToSimpleFunc_eq_sum_filter [DecidablePred fun x ↦ x ≠ (0 : F)]
rw [hx0]
exact map_zero _
+/-- The `setToSimpleFunc` is equal to a sum over any set that includes `f.range` (except `0`). -/
+theorem setToSimpleFunc_eq_sum_of_subset [DecidablePred fun x : F => x ≠ 0]
+ (T : Set α → F →L[ℝ] F') (hT : T ∅ = 0) {f : α →ₛ F} {s : Finset F}
+ (hs : {x ∈ f.range | x ≠ 0} ⊆ s) :
+ setToSimpleFunc T f = ∑ x ∈ s, T (f ⁻¹' {x}) x := by
+ rw [setToSimpleFunc_eq_sum_filter, Finset.sum_subset hs]
+ rintro x - hx; rw [Finset.mem_filter, not_and_or, Ne, Classical.not_not] at hx
+ rcases hx.symm with (rfl | hx)
+ · simp
+ rw [SimpleFunc.mem_range] at hx
+ rw [preimage_eq_empty] <;> simp [Set.disjoint_singleton_left, hx, hT]
+
theorem map_setToSimpleFunc (T : Set α → F →L[ℝ] F') (h_add : FinMeasAdditive μ T) {f : α →ₛ G}
(hf : Integrable f μ) {g : G → F} (hg : g 0 = 0) :
(f.map g).setToSimpleFunc T = ∑ x ∈ f.range, T (f ⁻¹' {x}) (g x) := by
diff --git a/Mathlib/MeasureTheory/Integral/Prod.lean b/Mathlib/MeasureTheory/Integral/Prod.lean
index 6dd14fda021671..da0f8ba97080c1 100644
--- a/Mathlib/MeasureTheory/Integral/Prod.lean
+++ b/Mathlib/MeasureTheory/Integral/Prod.lean
@@ -37,7 +37,6 @@ product measure, Fubini's theorem, Fubini-Tonelli theorem
public section
-
noncomputable section
open scoped Topology ENNReal MeasureTheory
@@ -60,12 +59,6 @@ functions. We show that if `f` is a binary measurable function, then the functio
along one of the variables (using either the Lebesgue or Bochner integral) is measurable.
-/
-
-theorem measurableSet_integrable [SFinite ν] ⦃f : α → β → E⦄
- (hf : StronglyMeasurable (uncurry f)) : MeasurableSet {x | Integrable (f x) ν} := by
- simp_rw [Integrable, hf.of_uncurry_left.aestronglyMeasurable, true_and]
- exact measurableSet_lt (Measurable.lintegral_prod_right hf.enorm) measurable_const
-
section
variable [NormedSpace ℝ E]
@@ -75,49 +68,10 @@ variable [NormedSpace ℝ E]
This version has `f` in curried form. -/
theorem MeasureTheory.StronglyMeasurable.integral_prod_right [SFinite ν] ⦃f : α → β → E⦄
(hf : StronglyMeasurable (uncurry f)) : StronglyMeasurable fun x => ∫ y, f x y ∂ν := by
- classical
- by_cases hE : CompleteSpace E; swap; · simp [integral, hE, stronglyMeasurable_const]
- borelize E
- haveI : SeparableSpace (range (uncurry f) ∪ {0} : Set E) :=
- hf.separableSpace_range_union_singleton
- let s : ℕ → SimpleFunc (α × β) E :=
- SimpleFunc.approxOn _ hf.measurable (range (uncurry f) ∪ {0}) 0 (by simp)
- let s' : ℕ → α → SimpleFunc β E := fun n x => (s n).comp (Prod.mk x) measurable_prodMk_left
- let f' : ℕ → α → E := fun n => {x | Integrable (f x) ν}.indicator fun x => (s' n x).integral ν
- have hf' : ∀ n, StronglyMeasurable (f' n) := by
- intro n; refine StronglyMeasurable.indicator ?_ (measurableSet_integrable hf)
- have : ∀ x, ((s' n x).range.filter fun x => x ≠ 0) ⊆ (s n).range := by
- intro x; refine Finset.Subset.trans (Finset.filter_subset _ _) ?_; intro y
- simp_rw [SimpleFunc.mem_range]; rintro ⟨z, rfl⟩; exact ⟨(x, z), rfl⟩
- simp only [SimpleFunc.integral_eq_sum_of_subset (this _)]
- refine Finset.stronglyMeasurable_fun_sum _ fun x _ => ?_
- refine (Measurable.ennreal_toReal ?_).stronglyMeasurable.smul_const _
- simp only [s', SimpleFunc.coe_comp, preimage_comp]
- apply measurable_measure_prodMk_left
- exact (s n).measurableSet_fiber x
- have h2f' : Tendsto f' atTop (𝓝 fun x : α => ∫ y : β, f x y ∂ν) := by
- rw [tendsto_pi_nhds]; intro x
- by_cases hfx : Integrable (f x) ν
- · have (n : _) : Integrable (s' n x) ν := by
- apply (hfx.norm.add hfx.norm).mono' (s' n x).aestronglyMeasurable
- filter_upwards with y
- simp_rw [s', SimpleFunc.coe_comp]; exact SimpleFunc.norm_approxOn_zero_le _ _ (x, y) n
- simp only [f', hfx, SimpleFunc.integral_eq_integral _ (this _), indicator_of_mem,
- mem_setOf_eq]
- refine
- tendsto_integral_of_dominated_convergence (fun y => ‖f x y‖ + ‖f x y‖)
- (fun n => (s' n x).aestronglyMeasurable) (hfx.norm.add hfx.norm) ?_ ?_
- · refine fun n => Eventually.of_forall fun y =>
- SimpleFunc.norm_approxOn_zero_le ?_ ?_ (x, y) n
- · exact hf.measurable
- · simp
- · refine Eventually.of_forall fun y => SimpleFunc.tendsto_approxOn ?_ ?_ ?_
- · exact hf.measurable.of_uncurry_left
- · simp
- apply subset_closure
- simp [-uncurry_apply_pair]
- · simp [f', hfx, integral_undef]
- exact stronglyMeasurable_of_tendsto _ hf' h2f'
+ simp only [integral_eq_setToFun]
+ apply StronglyMeasurable.setToFun_prod_right _ (fun s hs ↦ ?_) hf
+ refine (Measurable.ennreal_toReal ?_).stronglyMeasurable.smul_const _
+ exact measurable_measure_prodMk_left hs
/-- The Bochner integral is measurable. This shows that the integrand of (the right-hand-side of)
Fubini's theorem is measurable. -/
@@ -380,13 +334,11 @@ end
variable [NormedSpace ℝ E]
theorem Integrable.integral_prod_left ⦃f : α × β → E⦄ (hf : Integrable f (μ.prod ν)) :
- Integrable (fun x => ∫ y, f (x, y) ∂ν) μ :=
- Integrable.mono hf.integral_norm_prod_left hf.aestronglyMeasurable.integral_prod_right' <|
- Eventually.of_forall fun x =>
- (norm_integral_le_integral_norm _).trans_eq <|
- (norm_of_nonneg <|
- integral_nonneg_of_ae <|
- Eventually.of_forall fun y => (norm_nonneg (f (x, y)) :)).symm
+ Integrable (fun x => ∫ y, f (x, y) ∂ν) μ := by
+ apply Integrable.mono hf.integral_norm_prod_left hf.aestronglyMeasurable.integral_prod_right'
+ filter_upwards with x
+ grw [norm_integral_le_integral_norm]
+ exact le_abs_self _
theorem Integrable.integral_prod_right [SFinite μ] ⦃f : α × β → E⦄
(hf : Integrable f (μ.prod ν)) : Integrable (fun y => ∫ x, f (x, y) ∂μ) ν :=
diff --git a/Mathlib/MeasureTheory/Integral/SetToL1.lean b/Mathlib/MeasureTheory/Integral/SetToL1.lean
index 1af06506765fed..bd8c0a097c415a 100644
--- a/Mathlib/MeasureTheory/Integral/SetToL1.lean
+++ b/Mathlib/MeasureTheory/Integral/SetToL1.lean
@@ -844,6 +844,12 @@ theorem setToFun_simpleFunc [CompleteSpace F] (hT : DominatedFinMeasAdditive μ
apply (SimpleFunc.setToSimpleFunc_congr T (fun s ↦ hT.eq_zero_of_measure_zero) hT.1 hf _).symm
grw [A, Lp.simpleFunc.toSimpleFunc_eq_toFun]
+theorem setToFun_simpleFunc_eq_setToSimpleFunc [CompleteSpace F]
+ (hT : DominatedFinMeasAdditive μ T C) (f : SimpleFunc α E) (hf : Integrable f μ) :
+ setToFun μ T hT f = f.setToSimpleFunc T := by
+ rw [setToFun_simpleFunc hT f hf]
+ rfl
+
section Order
variable {G' G'' : Type*}
@@ -1384,6 +1390,66 @@ theorem tendsto_setToFun_filter_of_norm_le_const (hT : DominatedFinMeasAdditive
exact tendsto_setToFun_filter_of_dominated_convergence hT
C h_meas h_boundc (integrable_const c) h_lim
+omit [NormedSpace ℝ E] in
+theorem _root_.measurableSet_integrable {β : Type*} {mβ : MeasurableSpace β} [SFinite μ]
+ ⦃f : β → α → E⦄ (hf : StronglyMeasurable (Function.uncurry f)) :
+ MeasurableSet {x | Integrable (f x) μ} := by
+ simp_rw [Integrable, hf.of_uncurry_left.aestronglyMeasurable, true_and]
+ exact measurableSet_lt (Measurable.lintegral_prod_right hf.enorm) measurable_const
+
+/-- The `setToFun` operation is measurable. This shows that the integrand of (the right-hand-side
+of) Fubini's theorem is measurable. This version has `f` in curried form. -/
+theorem StronglyMeasurable.setToFun_prod_right {β : Type*} {mβ : MeasurableSpace β} [SFinite μ]
+ (hT : DominatedFinMeasAdditive μ T C)
+ (h'T : ∀ (s : Set (β × α)), MeasurableSet s → StronglyMeasurable fun x => T (Prod.mk x ⁻¹' s))
+ ⦃f : β → α → E⦄ (hf : StronglyMeasurable (Function.uncurry f)) :
+ StronglyMeasurable fun x => setToFun μ T hT (f x) := by
+ classical
+ by_cases hF : CompleteSpace F; swap;
+ · simp [setToFun, hF, stronglyMeasurable_const]
+ borelize E
+ haveI : SeparableSpace (range (Function.uncurry f) ∪ {0} : Set E) :=
+ hf.separableSpace_range_union_singleton
+ let s : ℕ → SimpleFunc (β × α) E :=
+ SimpleFunc.approxOn _ hf.measurable (range (Function.uncurry f) ∪ {0}) 0 (by simp)
+ let s' : ℕ → β → SimpleFunc α E := fun n x => (s n).comp (Prod.mk x) measurable_prodMk_left
+ let f' : ℕ → β → F := fun n =>
+ {x | Integrable (f x) μ}.indicator fun x => (s' n x).setToSimpleFunc T
+ have hf' n : StronglyMeasurable (f' n) := by
+ refine StronglyMeasurable.indicator ?_ (measurableSet_integrable hf)
+ have : ∀ x, ((s' n x).range.filter fun x => x ≠ 0) ⊆ (s n).range := by
+ intro x; refine Finset.Subset.trans (Finset.filter_subset _ _) ?_; intro y
+ simp_rw [SimpleFunc.mem_range]; rintro ⟨z, rfl⟩; exact ⟨(x, z), rfl⟩
+ simp_rw [SimpleFunc.setToSimpleFunc_eq_sum_of_subset T hT.1.map_empty_eq_zero (this _)]
+ refine Finset.stronglyMeasurable_fun_sum _ fun x _ => ?_
+ simp only [s', SimpleFunc.coe_comp, preimage_comp]
+ apply StronglyMeasurable.apply_continuousLinearMap
+ apply h'T
+ exact (s n).measurableSet_fiber x
+ have h2f' : Tendsto f' atTop (𝓝 fun x : β => setToFun μ T hT (f x)) := by
+ apply tendsto_pi_nhds.2 fun x ↦ ?_
+ by_cases hfx : Integrable (f x) μ
+ · have (n : _) : Integrable (s' n x) μ := by
+ apply (hfx.norm.add hfx.norm).mono' (s' n x).aestronglyMeasurable
+ filter_upwards with y
+ simp_rw [s', SimpleFunc.coe_comp]; exact SimpleFunc.norm_approxOn_zero_le _ _ (x, y) n
+ simp only [mem_setOf_eq, hfx, indicator_of_mem, this,
+ ← setToFun_simpleFunc_eq_setToSimpleFunc hT, f']
+ refine
+ tendsto_setToFun_of_dominated_convergence hT (fun y => ‖f x y‖ + ‖f x y‖)
+ (fun n => (s' n x).aestronglyMeasurable) (hfx.norm.add hfx.norm) ?_ ?_
+ · refine fun n => Eventually.of_forall fun y =>
+ SimpleFunc.norm_approxOn_zero_le ?_ ?_ (x, y) n
+ · exact hf.measurable
+ · simp
+ · refine Eventually.of_forall fun y => SimpleFunc.tendsto_approxOn ?_ ?_ ?_
+ · exact hf.measurable.of_uncurry_left
+ · simp
+ apply subset_closure
+ simp [-Function.uncurry_apply_pair]
+ · simp [f', hfx, setToFun_undef]
+ exact stronglyMeasurable_of_tendsto _ hf' h2f'
+
variable {X : Type*} [TopologicalSpace X] [FirstCountableTopology X]
theorem continuousWithinAt_setToFun_of_dominated (hT : DominatedFinMeasAdditive μ T C)
From 9aa409007908aecdb6a12049b8d664360ebaa9f7 Mon Sep 17 00:00:00 2001
From: Sebastien Gouezel <10818434+sgouezel@users.noreply.github.com>
Date: Fri, 19 Jun 2026 10:24:01 +0000
Subject: [PATCH 0172/1300] feat: criterion for being smooth in terms of a
maximal atlas element in the target (#40795)
We already had this version in the source, but not in the target
Co-authored-by: sgouezel
---
Mathlib/Geometry/Manifold/ContMDiff/Defs.lean | 28 +++++++++++--------
1 file changed, 16 insertions(+), 12 deletions(-)
diff --git a/Mathlib/Geometry/Manifold/ContMDiff/Defs.lean b/Mathlib/Geometry/Manifold/ContMDiff/Defs.lean
index a963ba00712ffb..4520d963cfe915 100644
--- a/Mathlib/Geometry/Manifold/ContMDiff/Defs.lean
+++ b/Mathlib/Geometry/Manifold/ContMDiff/Defs.lean
@@ -349,20 +349,24 @@ theorem contMDiffAt_iff_source_of_mem_source
ContMDiffWithinAt 𝓘(𝕜, E) I' n (f ∘ (extChartAt I x).symm) (range I) (extChartAt I x x') := by
simp_rw [ContMDiffAt, contMDiffWithinAt_iff_source_of_mem_source hx', preimage_univ, univ_inter]
+theorem contMDiffWithinAt_iff_target_of_mem_maximalAtlas
+ (he' : e' ∈ maximalAtlas I' n M') (hx : f x ∈ e'.source) :
+ ContMDiffWithinAt I I' n f s x ↔
+ ContinuousWithinAt f s x ∧ ContMDiffWithinAt I 𝓘(𝕜, E') n ((e'.extend I') ∘ f) s x := by
+ simp_rw [ContMDiffWithinAt,
+ (contDiffWithinAt_localInvariantProp n).liftPropWithinAt_indep_chart_target he' hx]
+ apply and_congr_right (fun h ↦ ?_)
+ have A : ContinuousWithinAt ((e'.extend I') ∘ f) s x :=
+ (e'.continuousAt_extend hx).comp_continuousWithinAt h
+ have A' : ContinuousWithinAt (e' ∘ f) s x := (e'.continuousAt hx).comp_continuousWithinAt h
+ simp_rw [StructureGroupoid.liftPropWithinAt_self_target, A, A']
+ simp [ContDiffWithinAtProp, comp_assoc]
+
theorem contMDiffWithinAt_iff_target_of_mem_source
[IsManifold I' n M'] {x : M} {y : M'} (hy : f x ∈ (chartAt H' y).source) :
ContMDiffWithinAt I I' n f s x ↔
- ContinuousWithinAt f s x ∧ ContMDiffWithinAt I 𝓘(𝕜, E') n (extChartAt I' y ∘ f) s x := by
- simp_rw [ContMDiffWithinAt]
- rw [(contDiffWithinAt_localInvariantProp n).liftPropWithinAt_indep_chart_target
- (chart_mem_maximalAtlas y) hy,
- and_congr_right]
- intro hf
- simp_rw [StructureGroupoid.liftPropWithinAt_self_target]
- simp_rw [((chartAt H' y).continuousAt hy).comp_continuousWithinAt hf]
- rw [← extChartAt_source (I := I')] at hy
- simp_rw [(continuousAt_extChartAt' hy).comp_continuousWithinAt hf]
- rfl
+ ContinuousWithinAt f s x ∧ ContMDiffWithinAt I 𝓘(𝕜, E') n (extChartAt I' y ∘ f) s x :=
+ contMDiffWithinAt_iff_target_of_mem_maximalAtlas (chart_mem_maximalAtlas _) hy
theorem contMDiffAt_iff_target_of_mem_source
[IsManifold I' n M'] {x : M} {y : M'} (hy : f x ∈ (chartAt H' y).source) :
@@ -380,7 +384,7 @@ theorem contMDiffWithinAt_iff_of_mem_maximalAtlas {x : M} (he : e ∈ maximalAtl
(contDiffWithinAt_localInvariantProp n).liftPropWithinAt_indep_chart he hx he' hy
/-- An alternative formulation of `contMDiffWithinAt_iff_of_mem_maximalAtlas`
-if the set if `s` lies in `e.source`. -/
+if the set `s` lies in `e.source`. -/
theorem contMDiffWithinAt_iff_image {x : M} (he : e ∈ maximalAtlas I n M)
(he' : e' ∈ maximalAtlas I' n M')
(hs : s ⊆ e.source) (hx : x ∈ e.source) (hy : f x ∈ e'.source) :
From 9fbe5e0065da0f234feecd62195593c91fc2ff89 Mon Sep 17 00:00:00 2001
From: Sebastien Gouezel <10818434+sgouezel@users.noreply.github.com>
Date: Fri, 19 Jun 2026 10:56:51 +0000
Subject: [PATCH 0173/1300] feat: the semivariation of a vector measure
(#40384)
We define the semivariation of a vector measure (i.e., the supremum of the variation of its push-forwards by linear forms), and establish basic properties. We deduce that a vector measure is always bounded.
Co-authored-by: sgouezel
---
Mathlib.lean | 1 +
Mathlib/Analysis/Normed/Group/Continuity.lean | 11 ++
.../VectorMeasure/Variation/Basic.lean | 86 ++++++++
.../Variation/Semivariation.lean | 187 ++++++++++++++++++
Mathlib/Topology/UniformSpace/Dini.lean | 2 +-
docs/references.bib | 15 ++
6 files changed, 301 insertions(+), 1 deletion(-)
create mode 100644 Mathlib/MeasureTheory/VectorMeasure/Variation/Semivariation.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 4e0d5a61092878..68d485cacf48e4 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -5625,6 +5625,7 @@ public import Mathlib.MeasureTheory.VectorMeasure.Integral
public import Mathlib.MeasureTheory.VectorMeasure.SetIntegral
public import Mathlib.MeasureTheory.VectorMeasure.Variation.Basic
public import Mathlib.MeasureTheory.VectorMeasure.Variation.Defs
+public import Mathlib.MeasureTheory.VectorMeasure.Variation.Semivariation
public import Mathlib.MeasureTheory.VectorMeasure.WithDensity
public import Mathlib.ModelTheory.Algebra.Field.Basic
public import Mathlib.ModelTheory.Algebra.Field.CharP
diff --git a/Mathlib/Analysis/Normed/Group/Continuity.lean b/Mathlib/Analysis/Normed/Group/Continuity.lean
index 609c44b07dde86..bce9c2ecb79818 100644
--- a/Mathlib/Analysis/Normed/Group/Continuity.lean
+++ b/Mathlib/Analysis/Normed/Group/Continuity.lean
@@ -7,6 +7,7 @@ module
public import Mathlib.Analysis.Normed.Group.Basic
public import Mathlib.Topology.Algebra.Ring.Real
+public import Mathlib.Topology.Instances.ENNReal.Lemmas
public import Mathlib.Topology.Metrizable.Uniformity
public import Mathlib.Topology.Sequences
@@ -68,6 +69,16 @@ theorem tendsto_one_iff_norm_tendsto_zero {f : α → E} {a : Filter α} :
Tendsto f a (𝓝 1) ↔ Tendsto (‖f ·‖) a (𝓝 0) :=
tendsto_iff_norm_inv_mul_tendsto_zero.trans <| by simp
+@[to_additive]
+theorem tendsto_iff_enorm_inv_mul_tendsto_zero {f : α → E} {a : Filter α} {b : E} :
+ Tendsto f a (𝓝 b) ↔ Tendsto (fun e => ‖(f e)⁻¹ * b‖ₑ) a (𝓝 0) := by
+ simp only [← edist_eq_enorm_inv_mul, ← tendsto_iff_edist_tendsto_0]
+
+@[to_additive]
+theorem tendsto_one_iff_enorm_tendsto_zero {f : α → E} {a : Filter α} :
+ Tendsto f a (𝓝 1) ↔ Tendsto (‖f ·‖ₑ) a (𝓝 0) :=
+ tendsto_iff_enorm_inv_mul_tendsto_zero.trans <| by simp
+
@[to_additive (attr := simp 1100)]
theorem comap_norm_nhds_one : comap norm (𝓝 0) = 𝓝 (1 : E) := by
simpa only [dist_one_right] using nhds_comap_dist (1 : E)
diff --git a/Mathlib/MeasureTheory/VectorMeasure/Variation/Basic.lean b/Mathlib/MeasureTheory/VectorMeasure/Variation/Basic.lean
index de8e791a846bee..27c729bf7e976e 100644
--- a/Mathlib/MeasureTheory/VectorMeasure/Variation/Basic.lean
+++ b/Mathlib/MeasureTheory/VectorMeasure/Variation/Basic.lean
@@ -75,6 +75,31 @@ lemma le_variation (μ : VectorMeasure X V) {s : Set X} (hs : MeasurableSet s) {
simp only [sup_set_eq_biUnion, id_eq]
exact hs.diff <| .biUnion (Finset.countable_toSet _) (by simp)
+/-- Measure version of `preVariation.exists_Finpartition_sum_gt`. -/
+lemma exists_lt_sum_of_lt_variation (μ : VectorMeasure X V) {s : Set X} (hs : MeasurableSet s)
+ {a : ℝ≥0∞} (ha : a < μ.variation s) :
+ ∃ (P : Finset (Set X)), (∀ t ∈ P, t ⊆ s) ∧ ((P : Set (Set X)).PairwiseDisjoint id) ∧
+ (∀ t ∈ P, MeasurableSet t) ∧ a < ∑ p ∈ P, ‖μ p‖ₑ := by
+ simp only [variation_apply, preVariation, ennrealToMeasure_apply hs, ennrealPreVariation_apply]
+ at ha ⊢
+ obtain ⟨P, hP⟩ : ∃ P : Finpartition (⟨s, hs⟩ : Subtype MeasurableSet),
+ a < ∑ p ∈ P.parts, (fun x ↦ ‖μ x‖ₑ) p :=
+ preVariation.exists_Finpartition_sum_gt (‖μ ·‖ₑ) _ ha
+ refine ⟨P.parts.map (Function.Embedding.subtype _), ?_, ?_, ?_, ?_⟩
+ · simp only [mem_map, Function.Embedding.subtype_apply, Subtype.exists, exists_and_right,
+ exists_eq_right, forall_exists_index]
+ intro t ht h't
+ exact P.le h't
+ · intro i hi j hj hij
+ simp only [coe_map, Function.Embedding.subtype_apply, Set.mem_image, SetLike.mem_coe,
+ Subtype.exists, exists_and_right, exists_eq_right] at hi hj
+ rcases hi with ⟨h'i, i_mem⟩
+ rcases hj with ⟨h'j, j_mem⟩
+ exact (disjoint_subtype_iff (fun _ _ hs ht ↦ hs.inter ht) _).1
+ (P.disjoint i_mem j_mem (by simpa using hij))
+ · simp +contextual
+ · rwa [Finset.sum_map]
+
/-- Measure version of `preVariation.exists_Finpartition_sum_ge'`. -/
lemma exists_variation_le_add' (μ : VectorMeasure X V) {s : Set X} (hs : MeasurableSet s)
{ε : ℝ≥0∞} (hε : 0 < ε) (hμ : μ.variation s ≠ ∞) :
@@ -316,6 +341,67 @@ instance {x : X} {v : V} : IsFiniteMeasure (VectorMeasure.dirac x v).variation :
apply le_trans ?_ (enorm_measure_le_variation _ _)
simp [hs, Measure.real, Real.enorm_eq_ofReal]
+/-- For a signed measure, the variation is realized by the norm of the measure of a single set, up
+to a factor of `2` and an arbitrarily small error. -/
+lemma _root_.MeasureTheory.SignedMeasure.exists_subset_lt_enorm_apply_of_lt_variation
+ (μ : SignedMeasure X) {s : Set X} (hs : MeasurableSet s)
+ {a : ℝ≥0∞} (ha : a < μ.variation s) :
+ ∃ t ⊆ s, MeasurableSet t ∧ a < 2 * ‖μ t‖ₑ := by
+ /- One may almost realize the variation through a partition into finitely many sets.
+ As their measures are real numbers, we can group together those of positive measure, and
+ also those of negative measure. This gives two measurable sets. Among these two, the one with the
+ largest measure in absolute value satisfies the result. -/
+ obtain ⟨P, Ps, P_disj, P_meas, hP⟩ : ∃ (P : Finset (Set X)), (∀ t ∈ P, t ⊆ s) ∧
+ ((P : Set (Set X)).PairwiseDisjoint id) ∧
+ (∀ t ∈ P, MeasurableSet t) ∧ a < ∑ p ∈ P, ‖μ p‖ₑ := exists_lt_sum_of_lt_variation _ hs ha
+ have I : (∑ p ∈ P.filter (fun p ↦ 0 ≤ μ p), ‖μ p‖ₑ) =
+ ‖μ (⋃ p ∈ P.filter (fun p ↦ 0 ≤ μ p), p)‖ₑ := by
+ simp only [Real.norm_eq_abs, enorm_eq_nnnorm,
+ ← ENNReal.ofNNReal_finsetSum, ENNReal.coe_inj, ← NNReal.coe_inj,
+ NNReal.coe_sum, coe_nnnorm, Real.norm_eq_abs]
+ have A : ∑ x ∈ P with 0 ≤ μ x, |μ x| = μ (⋃ x ∈ P.filter (fun x ↦ 0 ≤ μ x), x) := calc
+ _ = ∑ x ∈ P with 0 ≤ μ x, μ x := by
+ apply Finset.sum_congr rfl (fun p hp ↦ ?_)
+ simp only [Finset.mem_filter] at hp
+ simp [hp]
+ _ = μ (⋃ x ∈ P.filter (fun x ↦ 0 ≤ μ x), x) := by
+ rw [of_biUnion_finset]
+ · apply P_disj.subset (by grind)
+ · grind
+ rw [A, abs_of_nonneg]
+ rw [← A]
+ exact Finset.sum_nonneg (fun p hp ↦ by positivity)
+ have J : (∑ p ∈ P.filter (fun p ↦ ¬ 0 ≤ μ p), ‖μ p‖ₑ) =
+ ‖μ (⋃ p ∈ P.filter (fun p ↦ ¬ 0 ≤ μ p), p)‖ₑ := by
+ simp only [not_le, enorm_eq_nnnorm, ← ENNReal.ofNNReal_finsetSum,
+ ENNReal.coe_inj, ← NNReal.coe_inj, NNReal.coe_sum, coe_nnnorm, Real.norm_eq_abs]
+ have A : ∑ x ∈ P with μ x < 0, |μ x| = - μ (⋃ x ∈ P.filter (fun x ↦ μ x < 0), x) := calc
+ ∑ x ∈ P with μ x < 0, |μ x|
+ _ = ∑ x ∈ P with μ x < 0, -μ x := by
+ refine Finset.sum_congr rfl (fun p hp ↦ ?_)
+ simp only [Finset.mem_filter] at hp
+ simp [hp.2.le]
+ _ = -μ (⋃ x ∈ P.filter (fun x ↦ μ x < 0), x) := by
+ rw [of_biUnion_finset]
+ · simp
+ · apply P_disj.subset (by grind)
+ · grind
+ rw [A, abs_of_nonpos]
+ rw [← neg_nonneg, ← A]
+ exact Finset.sum_nonneg (fun p hp ↦ by positivity)
+ simp_rw [two_mul]
+ rw [← Finset.sum_filter_add_sum_filter_not _ (fun p ↦ 0 ≤ μ p), I, J] at hP
+ rcases le_total (‖μ (⋃ p ∈ P.filter (fun p ↦ ¬ 0 ≤ μ p), p)‖ₑ)
+ (‖μ (⋃ p ∈ P.filter (fun p ↦ 0 ≤ μ p), p)‖ₑ) with h | h
+ · refine ⟨⋃ p ∈ P.filter (fun p ↦ 0 ≤ μ p), p, ?_, ?_, ?_⟩
+ · simp; grind
+ · exact Finset.measurableSet_biUnion _ (by grind)
+ · exact hP.trans_le (by gcongr)
+ · refine ⟨⋃ p ∈ P.filter (fun p ↦ ¬ 0 ≤ μ p), p, ?_, ?_, ?_⟩
+ · simp; grind
+ · exact Finset.measurableSet_biUnion _ (by grind)
+ · exact hP.trans_le (by gcongr)
+
end NormedAddCommGroup
end MeasureTheory.VectorMeasure
diff --git a/Mathlib/MeasureTheory/VectorMeasure/Variation/Semivariation.lean b/Mathlib/MeasureTheory/VectorMeasure/Variation/Semivariation.lean
new file mode 100644
index 00000000000000..06c83d0549c4b9
--- /dev/null
+++ b/Mathlib/MeasureTheory/VectorMeasure/Variation/Semivariation.lean
@@ -0,0 +1,187 @@
+/-
+Copyright (c) 2026 Sébastien Gouëzel. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Sébastien Gouëzel
+-/
+module
+
+public import Mathlib.MeasureTheory.VectorMeasure.Variation.Basic
+
+import Mathlib.Analysis.Normed.Module.HahnBanach
+import Mathlib.Analysis.Normed.Operator.NormedSpace
+
+/-!
+# The semivariation of a vector measure
+
+The semivariation of a vector measure is the supremum of the variations of its push-forwards
+to `ℝ` through all linear forms of norm at most `1`. The interest of this notion is that, in the
+reals, any set has nonnegative or nonpositive measure, so that the variation is realized by
+a subset (up to a factor of at most `2`). This property is inherited by the semivariation in
+general: one has the inequalities
+```
+‖μ s‖ₑ ≤ μ.semivariation s ≤ 2 sup_{t ⊆ s} ‖μ t‖ₑ
+```
+
+The notion of semivariation can in particular be used to show that any vector measure is bounded:
+there exists `C < ∞` such that `‖μ s‖ ≤ C` for all `s`.
+
+## Main results
+
+* `μ.semivariation`: the semivariation of the vector measure `μ`.
+* `exists_subset_lt_enorm_apply_of_lt_semivariation`: given `s`, there exists `t ⊆ s` such that
+ `μ.semivariation s ≤ 2 ‖μ t‖ₑ` up to an arbitrarily small error.
+* `μ.bound`: the semivariation of `univ`, in `ℝ≥0`. It is finite by definition.
+* `enorm_apply_le_bound`: the inequality `‖μ s‖ₑ ≤ μ.bound`, uniformly in `s`.
+
+## References
+
+* [J. Diestel and J.J. Uhl, Vector Measures][DiestelUhl1977]
+
+-/
+
+public section
+
+open scoped ENNReal Function Topology NNReal
+open Set Filter
+
+namespace MeasureTheory.VectorMeasure
+
+variable {X E : Type*} [NormedAddCommGroup E] [NormedSpace ℝ E] {mX : MeasurableSpace X}
+ {μ : VectorMeasure X E} {s t : Set X}
+
+/-- The semivariation of a vector measure, defined as the supremum of the variations
+of the images of the vector measures under continuous linear forms of norm at most `1`. -/
+noncomputable def semivariation (μ : VectorMeasure X E) (s : Set X) : ℝ≥0∞ :=
+ ⨆ ℓ ∈ {ℓ : StrongDual ℝ E | ‖ℓ‖ₑ ≤ 1}, (μ.mapRange (ℓ : E →+ ℝ) ℓ.continuous).variation s
+
+lemma semivariation_union_le :
+ μ.semivariation (s ∪ t) ≤ μ.semivariation s + μ.semivariation t := by
+ simp only [semivariation, iSup_le_iff]
+ intro ℓ hℓ
+ apply (measure_union_le _ _).trans
+ gcongr <;> apply le_biSup _ hℓ
+
+lemma semivariation_mono (hst : s ⊆ t) : μ.semivariation s ≤ μ.semivariation t := by
+ simp only [semivariation, iSup_le_iff]
+ intro ℓ hℓ
+ apply (measure_mono hst).trans
+ apply le_biSup _ hℓ
+
+lemma semivariation_le_variation : μ.semivariation s ≤ μ.variation s := by
+ simp only [semivariation, iSup_le_iff]
+ intro ℓ hℓ
+ suffices (μ.mapRange (ℓ : E →+ ℝ) ℓ.continuous).variation ≤ μ.variation from this s
+ apply variation_le_of_forall_enorm_le (fun t ht ↦ ?_)
+ simp only [mapRange_apply, AddMonoidHom.coe_coe]
+ apply le_trans ?_ (enorm_measure_le_variation _ _)
+ exact (ContinuousLinearMap.le_opNorm_enorm _ _).trans (mul_le_of_le_one_left (by positivity) hℓ)
+
+lemma enorm_apply_le_semivariation : ‖μ s‖ₑ ≤ μ.semivariation s := by
+ by_cases hs : MeasurableSet s; swap
+ · simp [not_measurable, hs]
+ obtain ⟨ℓ, ℓ_norm, hℓ⟩ : ∃ ℓ : StrongDual ℝ E, ‖ℓ‖ ≤ 1 ∧ ℓ (μ s) = ‖μ s‖ :=
+ exists_dual_vector'' _ _
+ have h'ℓ : ℓ ∈ {ℓ : StrongDual ℝ E | ‖ℓ‖ₑ ≤ 1} := by
+ simp [enorm_eq_nnnorm, ← NNReal.coe_le_one, ℓ_norm]
+ calc ‖μ s‖ₑ
+ _ = ‖(μ.mapRange (ℓ : E →+ ℝ) ℓ.continuous) s‖ₑ := by simp [← ofReal_norm, hℓ]
+ _ ≤ (μ.mapRange (ℓ : E →+ ℝ) ℓ.continuous).variation s := enorm_measure_le_variation _ _
+ _ ≤ μ.semivariation s := by apply le_biSup _ h'ℓ
+
+lemma enorm_apply_le_semivariation_of_subset (hst : s ⊆ t) :
+ ‖μ s‖ₑ ≤ μ.semivariation t :=
+ enorm_apply_le_semivariation.trans (semivariation_mono hst)
+
+lemma exists_subset_lt_enorm_apply_of_lt_semivariation (hs : MeasurableSet s)
+ {a : ℝ≥0∞} (ha : a < μ.semivariation s) :
+ ∃ t ⊆ s, MeasurableSet t ∧ a < 2 * ‖μ t‖ₑ := by
+ obtain ⟨ℓ, hℓ, h'ℓ⟩ : ∃ ℓ ∈ {ℓ : StrongDual ℝ E | ‖ℓ‖ₑ ≤ 1},
+ a < (μ.mapRange (ℓ : E →+ ℝ) ℓ.continuous).variation s := lt_biSup_iff.1 ha
+ obtain ⟨t, ts, t_meas, ht⟩ :
+ ∃ t ⊆ s, MeasurableSet t ∧ a < 2 * ‖μ.mapRange (ℓ : E →+ ℝ) ℓ.continuous t‖ₑ :=
+ SignedMeasure.exists_subset_lt_enorm_apply_of_lt_variation _ hs h'ℓ
+ refine ⟨t, ts, t_meas, ht.trans_le ?_⟩
+ gcongr
+ exact (ContinuousLinearMap.le_opNorm_enorm _ _).trans (mul_le_of_le_one_left (by positivity) hℓ)
+
+private lemma exists_one_le_enorm_apply_of_semivariation_eq_top
+ (hs : MeasurableSet s) (h's : μ.semivariation s = ∞) :
+ ∃ t, MeasurableSet t ∧ t ⊆ s ∧ μ.semivariation t = ∞ ∧ 1 ≤ ‖μ (s \ t)‖ₑ := by
+ obtain ⟨t, ts, t_meas, ht⟩ : ∃ t ⊆ s, MeasurableSet t ∧ 2 * ‖μ s‖ₑ + 2 < 2 * ‖μ t‖ₑ := by
+ apply exists_subset_lt_enorm_apply_of_lt_semivariation hs
+ rw [h's]
+ finiteness
+ have h't : 1 + ‖μ s‖ₑ ≤ ‖μ t‖ₑ := by
+ apply (ENNReal.mul_le_mul_iff_right (a := 2) (by simp) (by simp)).1
+ rw [mul_add, add_comm, mul_one]
+ exact ht.le
+ have I : ∞ ≤ μ.semivariation t + μ.semivariation (s \ t) := by
+ rw [← h's]
+ apply le_trans (semivariation_mono (by simp)) semivariation_union_le
+ simp only [top_le_iff, ENNReal.add_eq_top] at I
+ rcases I with hI | hI
+ · refine ⟨t, t_meas, ts, hI, ?_⟩
+ have : 1 + ‖μ s‖ₑ ≤ ‖μ (s \ t)‖ₑ + ‖μ s‖ₑ := by
+ apply h't.trans
+ have : μ t = μ s - μ (s \ t) := by rw [← of_add_of_sdiff t_meas hs ts]; abel
+ rw [this, add_comm]
+ exact enorm_sub_le
+ rwa [ENNReal.add_le_add_iff_right (by simp)] at this
+ · refine ⟨s \ t, hs.diff t_meas, sdiff_subset, hI, ?_⟩
+ simp only [_root_.sdiff_sdiff_right_self, le_eq_subset, ts, inf_of_le_right]
+ exact le_trans (by simp) h't
+
+private lemma semivariation_univ_lt_top : μ.semivariation univ < ∞ := by
+ apply Ne.lt_top (fun h ↦ ?_)
+ have A (s : Set X) (hs : MeasurableSet s) (h's : μ.semivariation s = ∞) :
+ ∃ t, MeasurableSet t ∧ t ⊆ s ∧ μ.semivariation t = ∞ ∧ 1 ≤ ‖μ (s \ t)‖ₑ :=
+ exists_one_le_enorm_apply_of_semivariation_eq_top hs h's
+ choose! t t_meas t_subs t_var ht using A
+ let s n := t^[n] univ
+ have hs n : MeasurableSet (s n) ∧ μ.semivariation (s n) = ∞ := by
+ induction n with
+ | zero => simp [s, h]
+ | succ n ih =>
+ simp only [Function.iterate_succ', Function.comp_apply, s]
+ exact ⟨t_meas _ ih.1 ih.2, t_var _ ih.1 ih.2⟩
+ let u n := s n \ s (n + 1)
+ have hu n : 1 ≤ ‖μ (u n)‖ₑ := by
+ simp only [Function.iterate_succ', Function.comp_apply, u, s]
+ exact ht _ (hs n).1 (hs n).2
+ have s_anti : Antitone s := by
+ apply antitone_nat_of_succ_le (fun n ↦ ?_)
+ simp only [Function.iterate_succ', Function.comp_apply, s]
+ apply t_subs _ (hs n).1 (hs n).2
+ have u_disj : Pairwise (Disjoint on u) := by
+ apply (pairwise_disjoint_on _).2 (fun m n hmn ↦ ?_)
+ have : Disjoint (u m) (s (m + 1)) := by simp [u, disjoint_sdiff_left]
+ apply this.mono_right
+ simp only [sdiff_le_iff, sup_eq_union, le_eq_subset, u]
+ exact Subset.trans (s_anti (by grind)) subset_union_right
+ have : HasSum (fun i => μ (u i)) (μ (⋃ i, u i)) :=
+ hasSum_of_disjoint_iUnion (fun n ↦ (hs n).1.diff (hs (n + 1)).1) u_disj
+ have : Tendsto (fun x ↦ ‖μ (u x)‖ₑ) atTop (𝓝 0) :=
+ tendsto_zero_iff_enorm_tendsto_zero.1 this.summable.tendsto_atTop_zero
+ obtain ⟨n, hn⟩ : ∃ n, ‖μ (u n)‖ₑ < 1 := ((tendsto_order.1 this).2 _ zero_lt_one).exists
+ order [hu n]
+
+variable (μ) in
+/-- A constant bounding the norm of `μ s` for any set `s`. -/
+protected noncomputable def bound : ℝ≥0 := (μ.semivariation univ).toNNReal
+
+lemma semivariation_apply_le_bound : μ.semivariation s ≤ μ.bound := by
+ apply (semivariation_mono (subset_univ _)).trans_eq
+ simp only [VectorMeasure.bound]
+ rw [ENNReal.coe_toNNReal semivariation_univ_lt_top.ne]
+
+lemma enorm_apply_le_bound : ‖μ s‖ₑ ≤ μ.bound :=
+ (enorm_apply_le_semivariation).trans semivariation_apply_le_bound
+
+lemma nnnorm_apply_le_bound : ‖μ s‖₊ ≤ μ.bound := by
+ rw [← ENNReal.coe_le_coe, ← enorm_eq_nnnorm]
+ exact enorm_apply_le_bound
+
+lemma norm_apply_le_bound : ‖μ s‖ ≤ μ.bound := by
+ simpa [← coe_nnnorm] using nnnorm_apply_le_bound
+
+end MeasureTheory.VectorMeasure
diff --git a/Mathlib/Topology/UniformSpace/Dini.lean b/Mathlib/Topology/UniformSpace/Dini.lean
index e5daead8c0747d..cd6fd6672de1b0 100644
--- a/Mathlib/Topology/UniformSpace/Dini.lean
+++ b/Mathlib/Topology/UniformSpace/Dini.lean
@@ -56,7 +56,7 @@ lemma tendstoLocallyUniformly_of_forall_tendsto
refine (atTop : Filter ι).eq_or_neBot.elim (fun h ↦ ?eq_bot) (fun _ ↦ ?_)
case eq_bot => simp [h, tendstoLocallyUniformly_iff_forall_tendsto]
have F_le_f (x : α) (n : ι) : F n x ≤ f x := by
- refine ge_of_tendsto (h_tendsto x) ?_
+ refine _root_.ge_of_tendsto (h_tendsto x) ?_
filter_upwards [Ici_mem_atTop n] with m hnm
exact hF_mono hnm x
simp_rw [Metric.tendstoLocallyUniformly_iff, dist_eq_norm']
diff --git a/docs/references.bib b/docs/references.bib
index 064ad6e45eed78..dc0ca63035b90c 100644
--- a/docs/references.bib
+++ b/docs/references.bib
@@ -1670,6 +1670,21 @@ @Book{ diamondshurman2005
zbl = {1062.11022}
}
+@Book{ DiestelUhl1977,
+ author = {Diestel, J. and Uhl, J. J. jun.},
+ title = {Vector measures},
+ fseries = {Mathematical Surveys},
+ series = {Math. Surv.},
+ issn = {0076-5376},
+ volume = {15},
+ year = {1977},
+ publisher = {American Mathematical Society (AMS), Providence, RI},
+ language = {English},
+ keywords = {46G10,28B05,28A15,28A20,46-02,46B10,46B99,46E15,46E30,46G05,47A65,47B06,47B10,47B99},
+ zbmath = {3576139},
+ zbl = {0369.46039}
+}
+
@Article{ dieudonne1953,
author = {Dieudonn\'{e}, Jean},
title = {On semi-simple {L}ie algebras},
From a446e171c086e2f6c2f40fc811445b7acc4bd88e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Iv=C3=A1n=20Renison?=
<85908989+IvanRenison@users.noreply.github.com>
Date: Fri, 19 Jun 2026 11:22:46 +0000
Subject: [PATCH 0174/1300] feat(Data/Fin): add several lemmas about
subtraction of `Fin.{castLT, castAdd, castSucc, castPred}` (#35610)
---
Mathlib/Data/Fin/Basic.lean | 15 ++++++++++++
Mathlib/Data/Fin/SuccPred.lean | 43 ++++++++++++++++++++++++++++++++++
2 files changed, 58 insertions(+)
diff --git a/Mathlib/Data/Fin/Basic.lean b/Mathlib/Data/Fin/Basic.lean
index e5e643bffb4319..344ac23ae0b0e3 100644
--- a/Mathlib/Data/Fin/Basic.lean
+++ b/Mathlib/Data/Fin/Basic.lean
@@ -84,6 +84,21 @@ lemma ne_zero_of_lt {a b : Fin (n + 1)} (hab : a < b) : b ≠ 0 :=
lemma ne_last_of_lt {a b : Fin (n + 1)} (hab : a < b) : a ≠ last n :=
Fin.ne_of_lt <| Fin.lt_of_lt_of_le hab b.le_last
+lemma ne_last_of_ne_last_of_le {a b : Fin (n + 1)} (hb : b ≠ last n) (hab : a ≤ b) :
+ a ≠ last n := by
+ intro rfl
+ exact Nat.not_lt_of_le hab (lt_last_iff_ne_last.mpr hb)
+
+lemma val_sub_lt_of_lt_of_le {a b : Fin n} (ha : a.val < m) (hab : b ≤ a) :
+ (a - b).val < m := by
+ rw [Fin.sub_val_of_le hab]
+ exact sub_lt_of_lt ha
+
+lemma sub_ne_last_of_ne_last_of_le {a b : Fin (n + 1)} (ha : a ≠ last n) (hab : b ≤ a) :
+ a - b ≠ last n := by
+ rw [← lt_last_iff_ne_last, lt_def]
+ exact val_sub_lt_of_lt_of_le (val_lt_last ha) hab
+
/-- Equivalence between `Fin n` and `{ i // i < n }`. -/
@[simps apply symm_apply]
def equivSubtype : Fin n ≃ { i // i < n } where
diff --git a/Mathlib/Data/Fin/SuccPred.lean b/Mathlib/Data/Fin/SuccPred.lean
index e3672dc3d438d9..99d0eb5599a338 100644
--- a/Mathlib/Data/Fin/SuccPred.lean
+++ b/Mathlib/Data/Fin/SuccPred.lean
@@ -264,6 +264,13 @@ theorem succ_castAdd (i : Fin n) : succ (castAdd m i) =
theorem succ_natAdd (i : Fin m) : succ (natAdd n i) = natAdd n (succ i) := rfl
+theorem sub_castAdd_eq_castAdd_sub_of_le {n : ℕ} {a b : Fin n} (h : b ≤ a) :
+ a.castAdd m - b.castAdd m = (a - b).castAdd m := by
+ grind [Fin.sub_val_of_le]
+
+theorem sub_castSucc_eq_castSucc_sub_of_le {n : ℕ} {a b : Fin n} (h : b ≤ a) :
+ a.castSucc - b.castSucc = (a - b).castSucc := sub_castAdd_eq_castAdd_sub_of_le h
+
end Succ
section Pred
@@ -457,6 +464,42 @@ theorem pred_lt_castPred {a : Fin (n + 1)} (h₁ : a ≠ 0) (h₂ : a ≠ last n
pred a h₁ < castPred a h₂ := by
rw [pred_lt_castPred_iff, le_def]
+theorem val_sub_castLT_of_le {a b : Fin m} (ha : a.val < n) (h : b ≤ a) :
+ (a.castLT ha - b.castLT (lt_of_le_of_lt h ha)).val = (a - b).val := by
+ have : b.castLT (lt_of_le_of_lt h ha) ≤ a.castLT ha := by simpa [← val_fin_le] using h
+ simp [sub_val_of_le, h, this]
+
+theorem sub_castLT_eq_castLT_sub_of_le {a b : Fin m} (ha : a.val < n) (h : b ≤ a) :
+ a.castLT ha - b.castLT (lt_of_le_of_lt h ha) =
+ (a - b).castLT (val_sub_lt_of_lt_of_le ha h) := by
+ rw [Fin.ext_iff]
+ exact val_sub_castLT_of_le ha h
+
+theorem val_sub_castLT_of_lt {a b : Fin m} (hb : b < n) (h : a < b) :
+ (a.castLT (lt_trans h hb) - b.castLT hb).val = (a - b).val + n - m := by
+ simp only [val_sub, val_castLT]
+ repeat rw [Nat.mod_eq_of_lt (by omega)]
+ have h' : a.val < b.val := h
+ omega
+
+theorem val_sub_castPred_of_le {a b : Fin (n + 1)} (ha : a ≠ last n)
+ (h : b ≤ a) :
+ (a.castPred ha - b.castPred (ne_last_of_ne_last_of_le ha h)).val = (a - b).val :=
+ val_sub_castLT_of_le (lt_last_iff_ne_last.mpr ha) h
+
+theorem sub_castPred_eq_castPred_sub_of_le {a b : Fin (n + 1)} (ha : a ≠ last n)
+ (h : b ≤ a) :
+ a.castPred ha - b.castPred (ne_last_of_ne_last_of_le ha h) =
+ (a - b).castPred (sub_ne_last_of_ne_last_of_le ha h) :=
+ sub_castLT_eq_castLT_sub_of_le (lt_last_iff_ne_last.mpr ha) h
+
+theorem val_sub_castPred_of_ge {a b : Fin (n + 1)} (hb : b ≠ last n)
+ (h : a ≤ b) :
+ (a.castPred (ne_last_of_ne_last_of_le hb h) - b.castPred hb).val = (a - b).val - 1 := by
+ obtain (rfl | h') := Fin.eq_or_lt_of_le h
+ · simp [val_sub, Nat.sub_add_cancel a.is_le]
+ grind [castPred, val_sub_castLT_of_lt]
+
end CastPred
section SuccAbove
From f68433175a06e4ff891d592dd601564ce0fe7124 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9my=20Degenne?=
<4094732+RemyDegenne@users.noreply.github.com>
Date: Fri, 19 Jun 2026 11:22:48 +0000
Subject: [PATCH 0175/1300] feat(Probability): add `HasCondDistrib` (#40291)
This PR adds a predicate that states that a random variable has a given conditional distribution given an other random variable.
From Lean Machine Learning.
Co-authored by: Paulo Rauber
Co-authored-by: Remy Degenne
---
Mathlib.lean | 1 +
Mathlib/Probability/HasCondDistrib.lean | 124 ++++++++++++++++++++++++
2 files changed, 125 insertions(+)
create mode 100644 Mathlib/Probability/HasCondDistrib.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 68d485cacf48e4..b04a0f888973c9 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -6249,6 +6249,7 @@ public import Mathlib.Probability.Distributions.Poisson.PoissonLimitThm
public import Mathlib.Probability.Distributions.SetBernoulli
public import Mathlib.Probability.Distributions.TwoValued
public import Mathlib.Probability.Distributions.Uniform
+public import Mathlib.Probability.HasCondDistrib
public import Mathlib.Probability.HasLaw
public import Mathlib.Probability.HasLawExists
public import Mathlib.Probability.IdentDistrib
diff --git a/Mathlib/Probability/HasCondDistrib.lean b/Mathlib/Probability/HasCondDistrib.lean
new file mode 100644
index 00000000000000..b70e76eb7ace7b
--- /dev/null
+++ b/Mathlib/Probability/HasCondDistrib.lean
@@ -0,0 +1,124 @@
+/-
+Copyright (c) 2026 Rémy Degenne. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Rémy Degenne, Paulo Rauber
+-/
+
+module
+
+public import Mathlib.Probability.HasLaw
+
+import Mathlib.Probability.Kernel.Composition.Lemmas
+
+/-!
+# A predicate for having a specified conditional distribution
+
+We introduce a predicate `HasCondDistrib Y X κ P` stating that the conditional distribution of `Y`
+given `X` under the measure `P` is equal to the kernel `κ`.
+The statement uses `HasLaw` to express that the law of the pair `(X, Y)` under `P` is equal to
+`(P.map X) ⊗ₘ κ`, the product of the law of `X` under `P` and the kernel `κ`.
+The use of `HasLaw` also implies that `Y` and `X` are a.e. measurable.
+
+## Main definitions
+
+* `HasCondDistrib Y X κ P` : predicate stating that the conditional distribution of `Y` given `X`
+ under the measure `P` is equal to the kernel `κ`.
+
+-/
+
+@[expose] public section
+
+open MeasureTheory
+
+namespace ProbabilityTheory
+
+variable {Ω 𝓧 𝓨 𝓩 : Type*} {mΩ : MeasurableSpace Ω}
+ {m𝓧 : MeasurableSpace 𝓧} {m𝓨 : MeasurableSpace 𝓨} {m𝓩 : MeasurableSpace 𝓩}
+ {P : Measure Ω} {X : Ω → 𝓧} {Y : Ω → 𝓨} {κ : Kernel 𝓧 𝓨}
+
+/-- Predicate stating that the conditional distribution of `Y` given `X` under the measure `P`
+is equal to the kernel `κ`. -/
+def HasCondDistrib (Y : Ω → 𝓨) (X : Ω → 𝓧) (κ : Kernel 𝓧 𝓨) (P : Measure Ω) : Prop :=
+ HasLaw (fun ω ↦ (X ω, Y ω)) ((P.map X) ⊗ₘ κ) P
+
+@[fun_prop]
+lemma HasCondDistrib.aemeasurable_fst (h : HasCondDistrib Y X κ P) :
+ AEMeasurable X P := h.aemeasurable.fst
+
+@[fun_prop]
+lemma HasCondDistrib.aemeasurable_snd (h : HasCondDistrib Y X κ P) :
+ AEMeasurable Y P := h.aemeasurable.snd
+
+lemma HasLaw.prodMk_of_hasCondDistrib {Q : Measure 𝓧} [IsSFiniteKernel κ]
+ (h1 : HasLaw X Q P) (h2 : HasCondDistrib Y X κ P) :
+ HasLaw (fun ω ↦ (X ω, Y ω)) (Q ⊗ₘ κ) P := by rwa [← h1.map_eq]
+
+lemma HasCondDistrib.hasLaw_of_const [IsProbabilityMeasure P] {Q : Measure 𝓨} [SFinite Q]
+ (h : HasCondDistrib Y X (Kernel.const 𝓧 Q) P) :
+ HasLaw Y Q P where
+ map_eq := by
+ have h_snd : (P.map (fun ω ↦ (X ω, Y ω))).snd = Q := by
+ rw [h.map_eq, Measure.snd_compProd]
+ simp [Measure.map_apply_of_aemeasurable h.aemeasurable_fst]
+ rwa [Measure.snd_map_prodMk₀ h.aemeasurable_fst] at h_snd
+
+variable [SFinite P] [IsSFiniteKernel κ]
+
+lemma HasCondDistrib.comp_left (h : HasCondDistrib Y X κ P) {f : 𝓨 → 𝓩} (hf : Measurable f) :
+ HasCondDistrib (f ∘ Y) X (κ.map f) P where
+ map_eq := calc
+ P.map (fun ω ↦ (X ω, f (Y ω)))
+ _ = (P.map (fun ω ↦ (X ω, Y ω))).map (Prod.map id f) := by
+ rw [AEMeasurable.map_map_of_aemeasurable (by fun_prop) (by fun_prop)]
+ congr
+ _ = (P.map X ⊗ₘ κ).map (Prod.map id f) := by rw [h.map_eq]
+ _ = P.map X ⊗ₘ κ.map f := by rw [Measure.compProd_map hf]
+
+lemma HasCondDistrib.fst {Y : Ω → 𝓨 × 𝓩} {κ : Kernel 𝓧 (𝓨 × 𝓩)} [IsSFiniteKernel κ]
+ (h : HasCondDistrib Y X κ P) :
+ HasCondDistrib (fun ω ↦ (Y ω).1) X κ.fst P := by
+ rw [Kernel.fst_eq]
+ exact h.comp_left measurable_fst
+
+lemma HasCondDistrib.snd {Y : Ω → 𝓨 × 𝓩} {κ : Kernel 𝓧 (𝓨 × 𝓩)} [IsSFiniteKernel κ]
+ (h : HasCondDistrib Y X κ P) :
+ HasCondDistrib (fun ω ↦ (Y ω).2) X κ.snd P := by
+ rw [Kernel.snd_eq]
+ exact h.comp_left measurable_snd
+
+lemma HasCondDistrib.comp_right {f : 𝓩 → 𝓧}
+ {hf : Measurable f} {Z : Ω → 𝓩} (h : HasCondDistrib Y Z (κ.comap f hf) P) :
+ HasCondDistrib Y (f ∘ Z) κ P where
+ map_eq := calc
+ P.map (fun a ↦ ((f ∘ Z) a, Y a))
+ _ = (P.map (fun a ↦ (Z a, Y a))).map (Prod.map f id) := by
+ rw [AEMeasurable.map_map_of_aemeasurable (by fun_prop) (by fun_prop)]
+ rfl
+ _ = (P.map Z ⊗ₘ κ.comap f hf).map (Prod.map f id) := by rw [h.map_eq]
+ _ = (P.map Z).map f ⊗ₘ κ := by
+ ext s hs
+ rw [Measure.map_apply (by fun_prop) hs, Measure.compProd_apply (by measurability),
+ Measure.compProd_apply hs, lintegral_map (Kernel.measurable_kernel_prodMk_left hs) hf]
+ rfl
+ _ = P.map (f ∘ Z) ⊗ₘ κ := by
+ rw [AEMeasurable.map_map_of_aemeasurable hf.aemeasurable (by fun_prop)]
+
+lemma HasCondDistrib.measurableEquiv_comp_right (h : HasCondDistrib Y X κ P) (f : 𝓧 ≃ᵐ 𝓩) :
+ HasCondDistrib Y (f ∘ X) (κ.comap f.symm f.symm.measurable) P := by
+ apply HasCondDistrib.comp_right (hf := f.measurable)
+ simpa [← Kernel.comap_comp_right]
+
+lemma HasCondDistrib.of_compProd {Z : Ω → 𝓩} {η : Kernel (𝓧 × 𝓨) 𝓩} [IsMarkovKernel η]
+ (h : HasCondDistrib (fun a ↦ (Y a, Z a)) X (κ ⊗ₖ η) P) :
+ HasCondDistrib Z (fun a ↦ (X a, Y a)) η P := by
+ have hZ : AEMeasurable Z P := h.aemeasurable_snd.snd
+ have hY : AEMeasurable Y P := h.aemeasurable_snd.fst
+ refine ⟨by fun_prop, ?_⟩
+ calc P.map (fun a ↦ ((X a, Y a), Z a))
+ _ = (P.map X ⊗ₘ (κ ⊗ₖ η)).map MeasurableEquiv.prodAssoc.symm := by
+ rw [← h.map_eq, AEMeasurable.map_map_of_aemeasurable (by fun_prop) (by fun_prop)]
+ rfl
+ _ = P.map X ⊗ₘ κ ⊗ₘ η := Measure.compProd_assoc
+ _ = P.map (fun a ↦ (X a, Y a)) ⊗ₘ η := by simp [h.fst.map_eq]
+
+end ProbabilityTheory
From 07f4b8dcd071cb3a38a57d99a505ec56667c6be4 Mon Sep 17 00:00:00 2001
From: Dennj
Date: Fri, 19 Jun 2026 11:22:50 +0000
Subject: [PATCH 0176/1300] refactor(Analysis/Fourier/Convolution): drop
continuity hypotheses from the convolution theorems (#40583)
drop continuity hypotheses from the convolution theorems
---
Mathlib/Analysis/Fourier/Convolution.lean | 75 ++++++-------------
.../Analysis/Fourier/FourierTransform.lean | 2 +-
2 files changed, 23 insertions(+), 54 deletions(-)
diff --git a/Mathlib/Analysis/Fourier/Convolution.lean b/Mathlib/Analysis/Fourier/Convolution.lean
index b072ac2c74f100..72357bbda4f4af 100644
--- a/Mathlib/Analysis/Fourier/Convolution.lean
+++ b/Mathlib/Analysis/Fourier/Convolution.lean
@@ -41,26 +41,11 @@ variable [NontriviallyNormedField 𝕜] [NormedAddCommGroup E]
[InnerProductSpace ℝ E] [FiniteDimensional ℝ E] [MeasurableSpace E] [BorelSpace E]
[NormedSpace 𝕜 F₁] [NormedSpace 𝕜 F₂] [NormedSpace 𝕜 F₃]
-/-- The norm of the integrant of the convolution is integrable if the functions are integrable
-and continuous. -/
+/-- The norm of the integrand of the convolution is integrable if the functions are integrable. -/
theorem integrable_prod_sub (B : F₁ →L[𝕜] F₂ →L[𝕜] F₃) {f₁ : E → F₁} {f₂ : E → F₂}
- (hf₁ : Integrable f₁) (hf₂ : Integrable f₂) (hf₁' : Continuous f₁) (hf₂' : Continuous f₂) :
+ (hf₁ : Integrable f₁) (hf₂ : Integrable f₂) :
Integrable (fun (p : E × E) ↦ ‖B‖ * (‖f₁ (p.1 - p.2)‖ * ‖f₂ p.2‖)) (volume.prod volume) := by
- apply Integrable.const_mul
- rw [integrable_prod_iff' (by fun_prop)]
- constructor
- · filter_upwards with x
- exact (hf₁.comp_sub_right x).norm.mul_const _
- have : Integrable (fun x ↦ ((∫ y, ‖f₁ y‖) * ‖f₂ x‖)) := by
- apply hf₂.norm.bdd_mul (by fun_prop) (c := ‖(∫ y, ‖f₁ y‖)‖)
- filter_upwards with; rfl
- convert! this using 1
- ext x
- simp_rw [norm_mul, norm_norm]
- rw [integral_mul_const]
- congr 1
- convert! integral_sub_right_eq_self _ x (μ := volume)
- rfl
+ simpa [mul_comm] using (hf₂.norm.convolution_integrand (.mul ℝ ℝ) hf₁.norm).const_mul ‖B‖
open FourierTransform
@@ -68,8 +53,7 @@ variable [NormedSpace ℂ F₃]
/-- Calculate the Fourier transform of the convolution as a symmetric integral. -/
theorem fourier_bilin_convolution_eq_integral (B : F₁ →L[𝕜] F₂ →L[𝕜] F₃) {f₁ : E → F₁} {f₂ : E → F₂}
- (hf₁ : Integrable f₁) (hf₂ : Integrable f₂) (hf₁' : Continuous f₁) (hf₂' : Continuous f₂)
- (ξ : E) :
+ (hf₁ : Integrable f₁) (hf₂ : Integrable f₂) (ξ : E) :
𝓕 (f₁ ⋆[B] f₂) ξ = ∫ y, ∫ x, 𝐞 (-inner ℝ (y + x) ξ) • B (f₁ x) (f₂ y) := calc
_ = 𝓕 (f₂ ⋆[B.flip] f₁) ξ := by
rw [convolution_flip]
@@ -80,11 +64,11 @@ theorem fourier_bilin_convolution_eq_integral (B : F₁ →L[𝕜] F₂ →L[
simp_rw [Circle.smul_def, integral_smul]
_ = ∫ y, ∫ x, 𝐞 (-inner ℝ x ξ) • B (f₁ (x - y)) (f₂ y) := by
refine integral_integral_swap ?_
- apply (integrable_prod_sub B hf₁ hf₂ hf₁' hf₂').mono (by measurability)
- filter_upwards with ⟨y, x⟩
- have : ‖(B (f₁ (y - x))) (f₂ x)‖ ≤ ‖B‖ * (‖f₁ (y - x)‖ * ‖f₂ x‖) := by
- grw [B.le_opNorm₂ (f₁ (y - x)) (f₂ x), mul_assoc]
- simpa
+ have hB := hf₂.convolution_integrand B.flip hf₁
+ refine hB.mono ?_ ?_
+ · exact continuous_fourierChar.comp (by fun_prop) |>.aestronglyMeasurable.smul
+ hB.aestronglyMeasurable
+ · filter_upwards with ⟨x, y⟩ using by simp
_ = ∫ y, ∫ x, 𝐞 (-inner ℝ (y + x) ξ) • B (f₁ x) (f₂ y) := by
congr
ext y
@@ -101,31 +85,18 @@ open ContinuousLinearMap
/-- The Fourier transform of the convolution is given by the bilinear map applied to the Fourier
transform of the individual functions. -/
theorem fourier_bilin_convolution_eq (B : F₁ →L[ℂ] F₂ →L[ℂ] F₃) {f₁ : E → F₁} {f₂ : E → F₂}
- (hf₁ : Integrable f₁) (hf₂ : Integrable f₂) (hf₁' : Continuous f₁) (hf₂' : Continuous f₂)
- (ξ : E) :
+ (hf₁ : Integrable f₁) (hf₂ : Integrable f₂) (ξ : E) :
𝓕 (f₁ ⋆[B] f₂) ξ = B (𝓕 f₁ ξ) (𝓕 f₂ ξ) := calc
_ = ∫ y, ∫ x, 𝐞 (-inner ℝ (y + x) ξ) • B (f₁ x) (f₂ y) :=
- fourier_bilin_convolution_eq_integral B hf₁ hf₂ hf₁' hf₂' _
+ fourier_bilin_convolution_eq_integral B hf₁ hf₂ _
_ = ∫ y, ∫ x, 𝐞 (-inner ℝ y ξ) • 𝐞 (-inner ℝ x ξ) • B (f₁ x) (f₂ y) := by
- congr
- ext y
- congr
- ext x
- rw [smul_smul, ← AddChar.map_add_eq_mul, inner_add_left]
- congr
- grind
+ simp_rw [inner_add_left, neg_add, AddChar.map_add_eq_mul, smul_smul]
_ = ∫ y, (∫ x, B (𝐞 (-inner ℝ x ξ) • f₁ x)) (𝐞 (-inner ℝ y ξ) • f₂ y) := by
- congr
- ext y
- simp_rw [Circle.smul_def, map_smul, MeasureTheory.integral_smul]
- congr
- rw [integral_apply ?_ (f₂ y)]
- · simp
- have : MeasureTheory.Integrable (fun x ↦ ‖B‖ * ‖f₁ x‖) MeasureTheory.volume :=
- hf₁.norm.const_mul _
- apply this.mono (by fun_prop)
- filter_upwards with x
- simpa [← Circle.smul_def] using le_opNorm B (f₁ x)
+ congr with y
+ have : Integrable (fun x ↦ (𝐞 (-inner ℝ x ξ) : ℂ) • B (f₁ x)) volume := by
+ simpa [Circle.smul_def] using
+ (Real.fourierIntegral_convergent_iff ξ).2 (B.integrable_comp hf₁)
+ simp [Circle.smul_def, MeasureTheory.integral_smul, integral_apply this (f₂ y)]
_ = B (∫ x, 𝐞 (-inner ℝ x ξ) • f₁ x) (∫ y, 𝐞 (-inner ℝ y ξ) • f₂ y) := by
rw [← integral_comp_comm _ (by simpa using hf₂), ← integral_comp_comm _ (by simpa using hf₁)]
@@ -134,10 +105,9 @@ of the individual functions.
Version for scalar multiplication. -/
theorem fourier_smul_convolution_eq {f₁ : E → ℂ} {f₂ : E → F₁}
- (hf₁ : Integrable f₁) (hf₂ : Integrable f₂) (hf₁' : Continuous f₁) (hf₂' : Continuous f₂)
- (ξ : E) :
+ (hf₁ : Integrable f₁) (hf₂ : Integrable f₂) (ξ : E) :
𝓕 (f₁ ⋆[lsmul ℂ ℂ] f₂) ξ = (𝓕 f₁ ξ) • (𝓕 f₂ ξ) :=
- fourier_bilin_convolution_eq (lsmul ℂ ℂ) hf₁ hf₂ hf₁' hf₂' ξ
+ fourier_bilin_convolution_eq (lsmul ℂ ℂ) hf₁ hf₂ ξ
variable [NormedRing R] [NormedSpace ℂ R] [IsScalarTower ℂ R R] [SMulCommClass ℂ R R]
[CompleteSpace R]
@@ -147,10 +117,9 @@ of the individual functions.
Version for multiplication. -/
theorem fourier_mul_convolution_eq {f₁ : E → R} {f₂ : E → R}
- (hf₁ : Integrable f₁) (hf₂ : Integrable f₂) (hf₁' : Continuous f₁) (hf₂' : Continuous f₂)
- (ξ : E) :
+ (hf₁ : Integrable f₁) (hf₂ : Integrable f₂) (ξ : E) :
𝓕 (f₁ ⋆[mul ℂ R] f₂) ξ = (𝓕 f₁ ξ) * (𝓕 f₂ ξ) :=
- fourier_bilin_convolution_eq (mul ℂ R) hf₁ hf₂ hf₁' hf₂' ξ
+ fourier_bilin_convolution_eq (mul ℂ R) hf₁ hf₂ ξ
end Real
@@ -198,7 +167,7 @@ open MeasureTheory
theorem fourier_convolution_apply (B : F₁ →L[ℂ] F₂ →L[ℂ] F₃) (f : 𝓢(E, F₁)) (g : 𝓢(E, F₂)) (x : E) :
𝓕 (convolution B f g) x = 𝓕 (f ⋆[B] g) x := by
simp [fourier_convolution, fourier_coe,
- Real.fourier_bilin_convolution_eq B f.integrable g.integrable f.continuous g.continuous]
+ Real.fourier_bilin_convolution_eq B f.integrable g.integrable]
/-- The convolution on Schwartz functions is equal to the convolution on functions. -/
theorem convolution_apply (B : F₁ →L[ℂ] F₂ →L[ℂ] F₃) (f : 𝓢(E, F₁)) (g : 𝓢(E, F₂)) (x : E) :
diff --git a/Mathlib/Analysis/Fourier/FourierTransform.lean b/Mathlib/Analysis/Fourier/FourierTransform.lean
index 7023f5af93e170..6e4d3c70e1a79f 100644
--- a/Mathlib/Analysis/Fourier/FourierTransform.lean
+++ b/Mathlib/Analysis/Fourier/FourierTransform.lean
@@ -177,7 +177,7 @@ section Fubini
variable [TopologicalSpace 𝕜] [IsTopologicalRing 𝕜] [TopologicalSpace V] [BorelSpace V]
[TopologicalSpace W] [MeasurableSpace W] [BorelSpace W]
{e : AddChar 𝕜 𝕊} {μ : Measure V} {L : V →ₗ[𝕜] W →ₗ[𝕜] 𝕜}
- {ν : Measure W} [SigmaFinite μ] [SigmaFinite ν] [SecondCountableTopology V]
+ {ν : Measure W} [SigmaFinite μ] [SigmaFinite ν] [SecondCountableTopologyEither W V]
variable {σ : ℂ →+* ℂ} [RingHomIsometric σ]
From 6d8c75573d90d8801f36d35f97b5165fe912fbfd Mon Sep 17 00:00:00 2001
From: Junyan Xu
Date: Fri, 19 Jun 2026 12:19:41 +0000
Subject: [PATCH 0177/1300] =?UTF-8?q?feat(Topology):=20`=CF=80=E2=82=81(E?=
=?UTF-8?q?=E2=A7=B8G)=20=E2=89=83*=20G`=20for=20`E`=20simply=20connected?=
=?UTF-8?q?=20(#33108)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Oliver Nash
Co-authored-by: Oliver Nash <7734364+ocfnash@users.noreply.github.com>
---
.../FundamentalGroupoid/FundamentalGroup.lean | 16 +-
.../FundamentalGroupoid/SimplyConnected.lean | 2 +-
Mathlib/Topology/Homotopy/Lifting.lean | 237 ++++++++++++++++--
3 files changed, 221 insertions(+), 34 deletions(-)
diff --git a/Mathlib/AlgebraicTopology/FundamentalGroupoid/FundamentalGroup.lean b/Mathlib/AlgebraicTopology/FundamentalGroupoid/FundamentalGroup.lean
index b65e9b431853f1..a4dd9abc9cce9c 100644
--- a/Mathlib/AlgebraicTopology/FundamentalGroupoid/FundamentalGroup.lean
+++ b/Mathlib/AlgebraicTopology/FundamentalGroupoid/FundamentalGroup.lean
@@ -32,17 +32,19 @@ variable (X)
/-- The fundamental group is the automorphism group (vertex group) of the basepoint
in the fundamental groupoid. -/
-def FundamentalGroup (x : X) :=
+abbrev FundamentalGroup (x : X) :=
End (FundamentalGroupoid.mk x)
-instance (x : X) : Group (FundamentalGroup X x) := inferInstanceAs (Group (End _))
-
-instance (x : X) : Inhabited (FundamentalGroup X x) := inferInstanceAs (Inhabited (End _))
-
variable {X}
namespace FundamentalGroup
+variable {x : X} {p q : FundamentalGroup X x}
+
+theorem one_def : (1 : FundamentalGroup X x) = .refl x := rfl
+theorem mul_def : p * q = q.trans p := rfl
+theorem inv_def : p⁻¹ = p.symm := rfl
+
/-- Get an isomorphism between the fundamental groups at two points given a path -/
def fundamentalGroupMulEquivOfPath (p : Path x₀ x₁) :
FundamentalGroup X x₀ ≃* FundamentalGroup X x₁ :=
@@ -84,8 +86,8 @@ variable (f : C(X, Y)) {x : X} {y : Y} (h : f x = y)
def mapOfEq : FundamentalGroup X x →* FundamentalGroup Y y :=
(eqToIso <| congr_arg FundamentalGroupoid.mk h).conj.toMonoidHom.comp (map f x)
-theorem mapOfEq_apply (p : Path x x) :
- mapOfEq f h (fromPath <| .mk p) = fromPath (.mk <| (p.map f.continuous).cast h.symm h.symm) :=
+theorem mapOfEq_apply (p : FundamentalGroup X x) :
+ mapOfEq f h p = (Path.Homotopic.Quotient.map p f).cast h.symm h.symm :=
FundamentalGroupoid.conj_eqToHom ..
end FundamentalGroup
diff --git a/Mathlib/AlgebraicTopology/FundamentalGroupoid/SimplyConnected.lean b/Mathlib/AlgebraicTopology/FundamentalGroupoid/SimplyConnected.lean
index c22d642451c080..568422865c68c9 100644
--- a/Mathlib/AlgebraicTopology/FundamentalGroupoid/SimplyConnected.lean
+++ b/Mathlib/AlgebraicTopology/FundamentalGroupoid/SimplyConnected.lean
@@ -67,7 +67,7 @@ instance (x y : X) : Subsingleton (Path.Homotopic.Quotient x y) :=
rw [simply_connected_iff_unique_homotopic] at *; tauto))
instance (x : X) : Subsingleton (FundamentalGroup X x) :=
- show Subsingleton (Path.Homotopic.Quotient x x) from inferInstance
+ inferInstanceAs <| Subsingleton (Path.Homotopic.Quotient x x)
instance (priority := 100) : PathConnectedSpace X :=
let unique_homotopic := (simply_connected_iff_unique_homotopic X).mp inferInstance
diff --git a/Mathlib/Topology/Homotopy/Lifting.lean b/Mathlib/Topology/Homotopy/Lifting.lean
index 1178bed0e81c2b..a98ee8d80d3032 100644
--- a/Mathlib/Topology/Homotopy/Lifting.lean
+++ b/Mathlib/Topology/Homotopy/Lifting.lean
@@ -8,7 +8,7 @@ module
public import Mathlib.AlgebraicTopology.FundamentalGroupoid.FundamentalGroup
public import Mathlib.AlgebraicTopology.FundamentalGroupoid.SimplyConnected
public import Mathlib.Topology.Connected.LocPathConnected
-public import Mathlib.Topology.Covering.Basic
+public import Mathlib.Topology.Covering.Quotient
public import Mathlib.Topology.Homotopy.Path
public import Mathlib.Topology.UnitInterval
@@ -26,9 +26,11 @@ public import Mathlib.Topology.UnitInterval
arbitrary point).
-/
+noncomputable section
+
@[expose] public section
-open Topology unitInterval
+open Function Topology unitInterval
variable {E X A : Type*} [TopologicalSpace E] [TopologicalSpace X] [TopologicalSpace A] {p : E → X}
@@ -101,7 +103,7 @@ theorem exists_lift_nhds {f : C(I × A, X)} {g : I × A → E} (g_lifts : p ∘
rw [if_pos this]
-- here we use that {tₙ} × Nₙ₊₁ is mapped to the domain of `q e`
apply (q e).injOn (by rwa [← ta.eta, ht]) ((q e).map_target this)
- rw [(q e).right_inv this, ← hpq e]; exact congr_fun g'_lifts ta
+ rw [(q e).right_inv this, ← hpq e]; exact congr($g'_lifts ta)
· rw [closure_le_eq continuous_fst continuous_const] at ht
exact ⟨⟨hta.1.1, ht⟩, hta.2.2.1⟩
· simp_rw [not_le]; exact (ContinuousOn.congr ((q e).continuousOn_invFun.comp f.2.continuousOn
@@ -109,14 +111,14 @@ theorem exists_lift_nhds {f : C(I × A, X)} {g : I × A → E} (g_lifts : p ∘
fun _ h ↦ if_pos <| huv ⟨hu ⟨h.2, h.1.1.2⟩, h.1.2.1⟩).mono
(Set.inter_subset_inter_right _ <| closure_lt_subset_le continuous_const continuous_fst)
· ext ta; rw [Function.comp_apply]; split_ifs with _ hv
- · exact congr_fun g'_lifts ta
+ · exact congr($g'_lifts ta)
· rw [hpq e, (q e).right_inv hv]
- · exact congr_fun g_lifts ta
+ · exact congr($g_lifts ta)
· rw [← g'_0]; exact if_pos bot_le
· dsimp only; split_ifs with htn hf
· exact g'_a t0 htn
· apply (q e).injOn ((q e).map_target hf) (h_sub ⟨le_of_not_ge htn, htn1⟩)
- rw [(q e).right_inv hf, ← hpq e]; exact (congr_fun g_lifts _).symm
+ rw [(q e).right_inv hf, ← hpq e]; exact congr($g_lifts _).symm
· rfl
variable (sep : IsSeparatedMap p)
@@ -254,7 +256,7 @@ theorem exists_path_lifts : ∃ Γ : C(I, E), p ∘ Γ = γ ∧ Γ 0 = e := by
· dsimp only; rwa [if_pos (t_0 ▸ t_mono n.zero_le)]
/-- The lift of a path to a covering space given a lift of the left endpoint. -/
-noncomputable def liftPath : C(I, E) := (cov.exists_path_lifts γ e γ_0).choose
+def liftPath : C(I, E) := (cov.exists_path_lifts γ e γ_0).choose
lemma liftPath_lifts : p ∘ cov.liftPath γ e γ_0 = γ := (cov.exists_path_lifts γ e γ_0).choose_spec.1
lemma liftPath_zero : cov.liftPath γ e γ_0 0 = e := (cov.exists_path_lifts γ e γ_0).choose_spec.2
@@ -277,13 +279,13 @@ lemma liftPath_const {x : X} (hpe : x = p e) : cov.liftPath (.const I x) e hpe =
lemma liftPath_trans {x y z : X} {e : E} (hpe : x = p e) (γ : Path x y) (γ' : Path y z) :
letI Γ := cov.liftPath γ e (γ.source.trans hpe)
cov.liftPath (γ.trans γ') e (by simpa) = (⟨Γ, liftPath_zero .., rfl⟩ : Path e (Γ 1)).trans
- ⟨cov.liftPath γ' (Γ 1) (by simpa using (congr_fun (cov.liftPath_lifts γ ..) 1).symm),
+ ⟨cov.liftPath γ' (Γ 1) (by simpa using congr($(cov.liftPath_lifts γ ..) 1).symm),
liftPath_zero .., rfl⟩ := by
refine .symm <| (cov.eq_liftPath_iff' _).mpr ⟨funext fun _ ↦ ?_, by simp⟩
simp only [ContinuousMap.coe_coe, Function.comp_apply, Path.trans_apply]; split_ifs
· exact congr_fun (cov.liftPath_lifts γ e (γ.source.trans hpe)) _
· refine congr_fun (cov.liftPath_lifts γ' _ ?_) _
- simpa using (congr_fun (cov.liftPath_lifts γ ..) 1).symm
+ simpa using congr($(cov.liftPath_lifts γ ..) 1).symm
end path_lifting
@@ -294,7 +296,7 @@ variable (H : C(I × A, X)) (f : C(A, E)) (H_0 : ∀ a, H (0, a) = p (f a))
the homotopy lifting property for covering maps.
In other words, a covering map is a Hurewicz fibration.
Proposition 1.30 of [hatcher02]. -/
-@[simps] noncomputable def liftHomotopy : C(I × A, E) where
+@[simps] def liftHomotopy : C(I × A, E) where
toFun ta := cov.liftPath (H.comp <| (ContinuousMap.id I).prodMk <| .const I ta.2)
(f ta.2) (H_0 ta.2) ta.1
continuous_toFun := cov.isLocalHomeomorph.continuous_lift cov.isSeparatedMap H
@@ -326,7 +328,7 @@ variable {f₀ f₁ : C(A, X)} {S : Set A} (F : f₀.HomotopyRel f₁ S)
open ContinuousMap in
/-- The lift to a covering space of a homotopy between two continuous maps relative to a set
given compatible lifts of the continuous maps. -/
-noncomputable def liftHomotopyRel [PreconnectedSpace A]
+def liftHomotopyRel [PreconnectedSpace A]
{f₀' f₁' : C(A, E)} (he : ∃ a ∈ S, f₀' a = f₁' a)
(h₀ : p ∘ f₀' = f₀) (h₁ : p ∘ f₁' = f₁) : f₀'.HomotopyRel f₁' S :=
have F_0 : ∀ a, F (0, a) = p (f₀' a) := fun a ↦ (F.apply_zero a).trans (congr_fun h₀ a).symm
@@ -339,10 +341,10 @@ noncomputable def liftHomotopyRel [PreconnectedSpace A]
map_zero_left := cov.liftHomotopy_zero F f₀' F_0
map_one_left := by
obtain ⟨a, ha, he⟩ := he
- simp_rw [toFun_eq_coe, ← curry_apply]
+ simp_rw [toFun_eq_coe, ← ContinuousMap.curry_apply]
refine congr_fun (cov.eq_of_comp_eq
(ContinuousMap.continuous _) f₁'.continuous ?_ a <| (rel 1 a ha).trans he)
- ext a; rw [h₁, Function.comp_apply, curry_apply]
+ ext a; rw [h₁, Function.comp_apply, ContinuousMap.curry_apply]
exact (congr_fun (cov.liftHomotopy_lifts F f₀' _) (1, a)).trans (F.apply_one a)
prop' := rel }
@@ -354,44 +356,90 @@ theorem homotopicRel_iff_comp [PreconnectedSpace A] {f₀ f₁ : C(A, E)} {S : S
(ContinuousMap.comp ⟨p, cov.continuous⟩ f₀).HomotopicRel (.comp ⟨p, cov.continuous⟩ f₁) S :=
⟨fun ⟨F⟩ ↦ ⟨F.compContinuousMap _⟩, fun ⟨F⟩ ↦ ⟨cov.liftHomotopyRel F he rfl rfl⟩⟩
+theorem homotopicRel_liftPath {γ₀ γ₁ : C(I, X)}
+ (h : γ₀.HomotopicRel γ₁ {0,1}) (e : E) (h₀ : γ₀ 0 = p e) (h₁ : γ₁ 0 = p e) :
+ (cov.liftPath γ₀ e h₀).HomotopicRel (cov.liftPath γ₁ e h₁) {0,1} :=
+ h.map fun H ↦ cov.liftHomotopyRel (f₀' := cov.liftPath γ₀ e h₀) (f₁' := cov.liftPath γ₁ e h₁) H
+ ⟨0, .inl rfl, by simp_rw [liftPath_zero]⟩ (liftPath_lifts ..) (liftPath_lifts ..)
+
/-- Lifting two paths that are homotopic relative to `{0,1}`
starting from the same point also ends up in the same point. -/
theorem liftPath_apply_one_eq_of_homotopicRel {γ₀ γ₁ : C(I, X)}
(h : γ₀.HomotopicRel γ₁ {0,1}) (e : E) (h₀ : γ₀ 0 = p e) (h₁ : γ₁ 0 = p e) :
cov.liftPath γ₀ e h₀ 1 = cov.liftPath γ₁ e h₁ 1 := by
- obtain ⟨H⟩ := h
- have := cov.liftHomotopyRel (f₀' := cov.liftPath γ₀ e h₀) (f₁' := cov.liftPath γ₁ e h₁) H
- ⟨0, .inl rfl, by simp_rw [liftPath_zero]⟩ (liftPath_lifts ..) (liftPath_lifts ..)
+ have := (cov.homotopicRel_liftPath h e h₀ h₁).some
rw [← this.eq_fst 0 (.inr rfl), ← this.eq_snd 0 (.inr rfl)]
/-- The monodromy of a covering map `p : E → X`, which sends a lift of the starting point of a
path in `X` to the endpoint of the lifted path in `E`. It only depends on the homotopy class
of the path. -/
-noncomputable def monodromy {x y : X} (γ : Path.Homotopic.Quotient x y) :
+def monodromy {x y : X} (γ : Path.Homotopic.Quotient x y) :
p ⁻¹' {x} → p ⁻¹' {y} :=
fun e ↦ γ.lift (fun γ : Path x y ↦ ⟨cov.liftPath γ e (γ.source.trans e.2.symm) 1,
- (congr_fun (cov.liftPath_lifts ..) 1).trans γ.target⟩)
+ congr($(cov.liftPath_lifts ..) 1).trans γ.target⟩)
fun _ _ h ↦ Subtype.ext (cov.liftPath_apply_one_eq_of_homotopicRel h ..)
+/-- Lift a homotopy class of paths to a covering space. -/
+def liftPathQuotient {x y : X} (γ : Path.Homotopic.Quotient x y) (e : p ⁻¹' {x}) :
+ Path.Homotopic.Quotient e.1 (cov.monodromy γ e) :=
+ have he (γ : Path x y) : γ 0 = p (e : E) := by aesop
+ let g (γ : Path x y) : Path.Homotopic.Quotient (e : E) (cov.liftPath γ (e : E) (he γ) 1) :=
+ .mk ⟨cov.liftPath γ (e : E) (he γ), cov.liftPath_zero .., rfl⟩
+ let _i : Setoid (Path x y) := Path.Homotopic.setoid x y
+ have hg (γ γ' : Path x y) (hγ : γ ≈ γ') : g γ ≍ g γ' := by
+ refine .trans (heq_of_eq ?_) (Path.Homotopic.Quotient.cast_heq rfl
+ (cov.liftPath_apply_one_eq_of_homotopicRel hγ _ (he γ) _))
+ rw [← Path.Homotopic.Quotient.mk_cast, Path.Homotopic.Quotient.eq]
+ exact cov.homotopicRel_liftPath hγ _ (by aesop) (by aesop)
+ γ.hrecOn g hg
+
+theorem map_liftPathQuotient {x y : X} (γ : Path.Homotopic.Quotient x y) (e : p ⁻¹' {x}) :
+ (cov.liftPathQuotient γ e).map ⟨p, cov.continuous⟩ = γ.cast e.2 (cov.monodromy γ e).2 := by
+ obtain ⟨γ⟩ := γ
+ refine congr_arg Path.Homotopic.Quotient.mk ?_
+ ext1
+ exact cov.liftPath_lifts _ _ (γ.source.trans e.2.symm)
+
theorem monodromy_map {x y : E} (γ : Path.Homotopic.Quotient x y) :
cov.monodromy (γ.map ⟨p, cov.continuous⟩) ⟨x, rfl⟩ = ⟨y, rfl⟩ := Subtype.ext <| by
obtain ⟨γ⟩ := γ
- exact (DFunLike.congr_fun ((cov.eq_liftPath_iff' _).mpr ⟨rfl, γ.source⟩).symm 1).trans γ.target
+ exact congr($((cov.eq_liftPath_iff' _).mpr ⟨rfl, γ.source⟩) 1).symm.trans γ.target
+
+theorem monodromy_eq_of_map_eq {x y : X} {γ : Path.Homotopic.Quotient x y}
+ {ex : p ⁻¹' {x}} {ey : p ⁻¹' {y}} (Γ : Path.Homotopic.Quotient ex.1 ey)
+ (eq : Γ.map ⟨p, cov.continuous⟩ = γ.cast ex.2 ey.2) :
+ cov.monodromy γ ex = ey := by
+ convert ← cov.monodromy_map Γ
+ exacts [ey.2, ex.2, ey.2, by rw [eq]; exact γ.cast_heq .., ey.2]
theorem monodromy_refl {x : X} : cov.monodromy (.refl x) = id :=
- funext fun e ↦ Subtype.ext <| DFunLike.congr_fun (cov.liftPath_const e.2.symm) 1
+ funext fun e ↦ Subtype.ext congr($(cov.liftPath_const e.2.symm) 1)
theorem monodromy_trans_apply {x y z : X}
(γ : Path.Homotopic.Quotient x y) (γ' : Path.Homotopic.Quotient y z) (e) :
cov.monodromy (γ.trans γ') e = cov.monodromy γ' (cov.monodromy γ e) := by
obtain ⟨γ⟩ := γ; obtain ⟨γ'⟩ := γ'
- exact Subtype.ext ((DFunLike.congr_fun (cov.liftPath_trans e.2.symm ..) 1).trans (Path.target _))
+ exact Subtype.ext (congr($(cov.liftPath_trans e.2.symm ..) 1).trans (Path.target _))
+
+/-- The monodromy action of the fundamental group at `x` on the fiber over `x`. -/
+@[reducible] def fundamentalGroupMulAction (x : X) :
+ MulAction (FundamentalGroup X x) (p ⁻¹' {x}) :=
+ { smul := cov.monodromy (x := x) (y := x)
+ mul_smul _ _ _ := cov.monodromy_trans_apply ..
+ one_smul := congr_fun cov.monodromy_refl }
+
+/-- The monodromy action of the fundamental group at `x` on the fiber over `x`. -/
+def monodromyPerm (x : X) : FundamentalGroup X x →* Equiv.Perm (p ⁻¹' {x}) :=
+ letI := cov.fundamentalGroupMulAction x
+ MulAction.toPermHom _ _
+
+@[simp] theorem coe_monodromyPerm {x γ} : cov.monodromyPerm x γ = cov.monodromy γ := rfl
open CategoryTheory
/-- Monodromy of a covering map as a functor. Definition 2.1 in
https://ncatlab.org/nlab/show/monodromy. -/
-@[simps] noncomputable def monodromyFunctor : FundamentalGroupoid X ⥤ Type _ where
+@[simps] def monodromyFunctor : FundamentalGroupoid X ⥤ Type _ where
obj x := p ⁻¹' {x.as}
map f := ↾(cov.monodromy f)
map_id _ := by ext x : 3; simpa using! congr_fun cov.monodromy_refl x
@@ -404,7 +452,7 @@ theorem monodromy_bijective {x y : X} (γ : Path.Homotopic.Quotient x y) :
/-- A covering map induces an injection on all Hom-sets of the fundamental groupoid,
in particular on the fundamental group. The first part of Proposition 1.31 of [hatcher02]. -/
lemma injective_path_homotopic_map (e₀ e₁ : E) :
- Function.Injective fun γ : Path.Homotopic.Quotient e₀ e₁ ↦ γ.map ⟨p, cov.continuous⟩ := by
+ Injective fun γ : Path.Homotopic.Quotient e₀ e₁ ↦ γ.map ⟨p, cov.continuous⟩ := by
refine Quotient.ind₂ fun γ₀ γ₁ ↦ ?_
dsimp only
simp only [Path.Homotopic.Quotient.mk''_eq_mk]
@@ -459,10 +507,9 @@ theorem existsUnique_continuousMap_lifts_of_range_le
conv_rhs => rw [← eq.2 ⟨.reflTransSymm _⟩, mk_refl, monodromy_refl]
rw [Path.map_symm, ← Path.map_trans]
set pγγ' : Path a₀ a₀ := pγ.trans pγ'.symm
- have ⟨pΓΓ', eq⟩ := le ⟨fromPath (.mk pγγ'), rfl⟩
- obtain ⟨pΓΓ', rfl⟩ := mk_surjective pΓΓ'
+ obtain ⟨⟨pΓΓ'⟩, eq⟩ := le ⟨fromPath (.mk pγγ'), rfl⟩
rw [mapOfEq_apply, map_apply, ← mk_map] at eq
- exact eq ▸ Subtype.ext (congr_arg (·.1) (cov.monodromy_map (.mk pΓΓ')))
+ exact eq ▸ Subtype.ext congr($(cov.monodromy_map <| .mk _))
end homotopy_lifting
@@ -496,3 +543,141 @@ theorem IsCoveringMapOn.existsUnique_continuousMap_lifts [SimplyConnectedSpace A
⟨Subtype.ext hF'₁, ?_⟩
· ext; simp [← hF'₂]
· ext; simp [← hF_unique]
+
+namespace IsQuotientCoveringMap
+
+variable {G : Type*} [Group G] [MulAction G E] (hp : IsQuotientCoveringMap p G) {g : G}
+
+/-- The monodromy action of a quotient covering map commutes with the group action. -/
+theorem monodromy_toPermFiber {x y : X} {γ : Path.Homotopic.Quotient x y} {e : p ⁻¹' {x}} :
+ letI monodromy := hp.isCoveringMap.monodromy
+ monodromy γ (hp.toPermFiber x g e) = hp.toPermFiber y g (monodromy γ e) :=
+ let Γ := hp.isCoveringMap.liftPathQuotient γ e
+ let g' : C(E, E) := ⟨_, hp.toContinuousConstSMul.continuous_const_smul g⟩
+ let p' : C(E, X) := ⟨p, hp.continuous⟩
+ have hgp : p'.comp g' = p' := by ext; simp [g', p', hp.map_smul]
+ hp.isCoveringMap.monodromy_eq_of_map_eq (Γ.map g') <| show (Γ.map g').map p' = _ by
+ rw [← Path.Homotopic.Quotient.map_comp]
+ convert hp.isCoveringMap.map_liftPathQuotient γ e using 2
+ · simp [g', p', hp.map_smul]
+ · simp [g', p', hp.map_smul]
+ · grind
+ · grind
+
+theorem commute_monodromyPerm_toPermFiber {x : X} {γ : FundamentalGroup X x} :
+ Commute (hp.isCoveringMap.monodromyPerm x γ) (hp.toPermFiber x g) := by
+ ext; exact congr($hp.monodromy_toPermFiber)
+
+theorem monodromy_ext_iff {x y : X} {γ γ' : Path.Homotopic.Quotient x y} (e : p ⁻¹' {x}) :
+ letI monodromy := hp.isCoveringMap.monodromy
+ monodromy γ e = monodromy γ' e ↔ monodromy γ = monodromy γ' where
+ mp eq := by
+ ext e'
+ obtain ⟨g, rfl⟩ := hp.exists_toPermFiber_eq e e'
+ simp_rw [monodromy_toPermFiber, eq]
+ mpr := (congr_fun · _)
+
+alias ⟨monodromy_ext, _⟩ := monodromy_ext_iff
+
+variable {x : X} (e : p ⁻¹' {x}) {γ : FundamentalGroup X x}
+
+theorem monodromy_eq_id_iff :
+ hp.isCoveringMap.monodromy γ = id ↔ hp.isCoveringMap.monodromy γ e = e where
+ mp := (congr_fun · _)
+ mpr eq := (hp.monodromy_ext e (eq.trans congr($hp.isCoveringMap.monodromy_refl e).symm)).trans
+ hp.isCoveringMap.monodromy_refl
+
+theorem ker_monodromyPerm :
+ (hp.isCoveringMap.monodromyPerm x).ker =
+ (FundamentalGroup.mapOfEq ⟨p, hp.continuous⟩ e.2).range := by
+ ext γ; constructor <;> intro h
+ · refine ⟨(hp.isCoveringMap.liftPathQuotient γ e).cast rfl congr($h.symm e), ?_⟩
+ rw [FundamentalGroup.mapOfEq_apply,
+ Path.Homotopic.Quotient.map_cast, IsCoveringMap.map_liftPathQuotient]
+ aesop
+ · obtain ⟨γ, rfl⟩ := h
+ refine DFunLike.ext' <|
+ (hp.monodromy_eq_id_iff e).mpr <| hp.isCoveringMap.monodromy_eq_of_map_eq γ ?_
+ aesop (add simp FundamentalGroup.mapOfEq_apply)
+
+theorem monodromyPerm_injective [SimplyConnectedSpace E] :
+ Injective (hp.isCoveringMap.monodromyPerm x) := by
+ let e : p⁻¹' {x} := ⟨(hp.surjective x).choose, (hp.surjective x).choose_spec⟩
+ rw [← MonoidHom.ker_eq_bot_iff, hp.ker_monodromyPerm e]
+ set f : FundamentalGroup E (e : E) →* FundamentalGroup X x :=
+ FundamentalGroup.mapOfEq ⟨p, hp.continuous⟩ e.2
+ have : Subsingleton f.range := (Set.subsingleton_coe _).mpr f.subsingleton_coe_range
+ exact Subgroup.eq_bot_of_subsingleton _
+
+open MulOpposite in
+/-- Choosing an arbitrary basepoint `e ∈ f ⁻¹' {x}` induces a bijection `f ⁻¹' {x} ≃ G`, and the
+`G`-action on `f ⁻¹' {x}` corresponds to left multiplication. The monodromy action commutes
+with the `G`-action, so each monodromy must corresponds must correspond to a right multiplication.
+-/
+def fundamentalGroupToMulOpposite : FundamentalGroup X x →* Gᵐᵒᵖ where
+ toFun γ := op <| hp.fiberEquivGroup e (hp.isCoveringMap.monodromy γ e)
+ map_one' := by rw [FundamentalGroup.one_def, IsCoveringMap.monodromy_refl]; simp
+ map_mul' γ γ' := by
+ rw [FundamentalGroup.mul_def, IsCoveringMap.monodromy_trans_apply, ← op_mul, op_inj]
+ apply hp.isCancelSMul.right_cancel _ _ e.1
+ simp_rw [mul_smul, fiberEquivGroup_smul_self, ← hp.toPermFiber_apply_apply_coe]
+ congr
+ refine .trans ?_ hp.monodromy_toPermFiber
+ congr
+ exact Subtype.ext (fiberEquivGroup_smul_self ..).symm
+
+variable {e} in
+theorem fundamentalGroupToMulOpposite_apply_eq_Iff {g : Gᵐᵒᵖ} :
+ hp.fundamentalGroupToMulOpposite e γ = g ↔ g.unop • e.1 = hp.isCoveringMap.monodromy γ e := by
+ rw [fundamentalGroupToMulOpposite, ← MulOpposite.unop_injective.eq_iff, iff_comm, eq_comm,
+ ← hp.fiberEquivGroup_smul_self e]
+ have := hp.isCancelSMul.right_cancel'
+ aesop
+
+variable {e} in
+theorem unop_fundamentalGroupToMulOpposite_smul :
+ (hp.fundamentalGroupToMulOpposite e γ).unop • e.1 = hp.isCoveringMap.monodromy γ e := by
+ simp [fundamentalGroupToMulOpposite, fiberEquivGroup_smul_self]
+
+variable {e} in
+theorem fundamentalGroupToMulOpposite_eq_one_iff :
+ hp.fundamentalGroupToMulOpposite e γ = 1 ↔ hp.isCoveringMap.monodromy γ e = e where
+ mp h := Subtype.ext <| by rw [← hp.unop_fundamentalGroupToMulOpposite_smul, h]; apply one_smul
+ mpr h := MulOpposite.unop_injective <| hp.isCancelSMul.right_cancel _ _ e.1 <| by
+ simp [fundamentalGroupToMulOpposite, h]
+
+theorem ker_fundamentalGroupToMulOpposite :
+ (hp.fundamentalGroupToMulOpposite e).ker = (hp.isCoveringMap.monodromyPerm x).ker := by
+ ext; simp [fundamentalGroupToMulOpposite_eq_one_iff, DFunLike.ext'_iff, ← hp.monodromy_eq_id_iff]
+
+theorem fundamentalGroupToMulOpposite_surjective [PathConnectedSpace E] :
+ Surjective (hp.fundamentalGroupToMulOpposite e) := by
+ intro g
+ set e' : p⁻¹' {x} := ⟨MulOpposite.unop g • (e : E), by
+ have := hp.map_smul (e := e) (MulOpposite.unop g); aesop⟩ with he'
+ set Γ : Path (e : E) (e' : E) :=
+ { toFun := PathConnectedSpace.somePath (e : E) (e' : E)
+ continuous_toFun := by fun_prop
+ source' := by simp
+ target' := by simp }
+ set γ : Path x x := (Γ.map hp.continuous).cast
+ (by simpa using e.property.symm) (by simpa using e'.property.symm)
+ use .fromPath ⟦γ⟧
+ rw [fundamentalGroupToMulOpposite_apply_eq_Iff]
+ change (e' : E) = _
+ rw [← hp.isCoveringMap.monodromy_eq_of_map_eq (γ := ⟦γ⟧) (Γ := ⟦Γ⟧) rfl]
+
+lemma fundamentalGroupToMulOpposite_injective [SimplyConnectedSpace E] :
+ Injective (hp.fundamentalGroupToMulOpposite e) := by
+ rw [← MonoidHom.ker_eq_bot_iff, ker_fundamentalGroupToMulOpposite, MonoidHom.ker_eq_bot_iff]
+ exact hp.monodromyPerm_injective
+
+/-- The fundamental group of the base of simply-connected covering map is contravariantly
+equivalent to the group of the covering map. -/
+def fundamentalGroupEquiv [SimplyConnectedSpace E] :
+ FundamentalGroup X x ≃* Gᵐᵒᵖ :=
+ MulEquiv.ofBijective (hp.fundamentalGroupToMulOpposite e)
+ ⟨hp.fundamentalGroupToMulOpposite_injective e,
+ hp.fundamentalGroupToMulOpposite_surjective e⟩
+
+end IsQuotientCoveringMap
From 7c309b0e29db78e07335916aa82148910b5689eb Mon Sep 17 00:00:00 2001
From: Whysoserioushah <109107491+Whysoserioushah@users.noreply.github.com>
Date: Fri, 19 Jun 2026 12:19:43 +0000
Subject: [PATCH 0178/1300] feat(Projectivization/PSL/Stabilizer): Add
stabilizer lemmas (#39999)
---
Mathlib.lean | 1 +
Mathlib/LinearAlgebra/Matrix/Action.lean | 3 +
.../Projectivization/PSL/Stabilizer.lean | 142 ++++++++++++++++++
3 files changed, 146 insertions(+)
create mode 100644 Mathlib/LinearAlgebra/Projectivization/PSL/Stabilizer.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index b04a0f888973c9..f2a1f9bc7ef3f4 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -5165,6 +5165,7 @@ public import Mathlib.LinearAlgebra.Projectivization.Cardinality
public import Mathlib.LinearAlgebra.Projectivization.Collinear
public import Mathlib.LinearAlgebra.Projectivization.Constructions
public import Mathlib.LinearAlgebra.Projectivization.Independence
+public import Mathlib.LinearAlgebra.Projectivization.PSL.Stabilizer
public import Mathlib.LinearAlgebra.Projectivization.Subspace
public import Mathlib.LinearAlgebra.QuadraticForm.AlgClosed
public import Mathlib.LinearAlgebra.QuadraticForm.Basic
diff --git a/Mathlib/LinearAlgebra/Matrix/Action.lean b/Mathlib/LinearAlgebra/Matrix/Action.lean
index 0a432ad73fef8e..bf7e4f980e4f63 100644
--- a/Mathlib/LinearAlgebra/Matrix/Action.lean
+++ b/Mathlib/LinearAlgebra/Matrix/Action.lean
@@ -47,6 +47,9 @@ instance [DistribSMul S R] [SMulCommClass S R R] : SMulCommClass S (Matrix n n R
instance [DistribSMul S R] [IsScalarTower S R R] : IsScalarTower S (Matrix n n R) (n → R) where
smul_assoc := smul_mulVec
+lemma ext_iff_smul {A B : Matrix n n R} :
+ A = B ↔ ∀ v : n → R, A • v = B • v := Matrix.ext_iff_mulVec
+
end mulVec
/-! ## `*ᵥ` as a right-module -/
diff --git a/Mathlib/LinearAlgebra/Projectivization/PSL/Stabilizer.lean b/Mathlib/LinearAlgebra/Projectivization/PSL/Stabilizer.lean
new file mode 100644
index 00000000000000..2201a9a390797c
--- /dev/null
+++ b/Mathlib/LinearAlgebra/Projectivization/PSL/Stabilizer.lean
@@ -0,0 +1,142 @@
+/-
+Copyright (c) 2026 Yunzhou Xie. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Edison Xie
+-/
+
+module
+
+public import Mathlib.LinearAlgebra.Projectivization.Action
+
+/-!
+# Stabilizer of a line in PSL(n, F)
+This file contains key constructions to prove that `PSL(n, F)` is simple via
+showing it has an Iwasawa structure.
+
+## Main definitions
+
+* `Matrix.SpecialLinearGroup.lineStab` : the unipotent radical attached to a subspace `L ⊆ ι → F`
+ defined as the subgroup of `SL ι F` consisting of matrices `A` such that `A - 1`
+ sends every vector into `L`.
+
+* `PSL.iwasawaT` : the candidate family of subgroups for the Iwasawa structure on
+ `PSL ι F` acting on the projective space `ℙ F (ι → F)` from `Matrix.SpecialLinearGroup.lineStab`.
+
+-/
+
+@[expose] public section
+
+variable {F : Type*} [Field F] {ι : Type*} [DecidableEq ι] [Fintype ι]
+
+/-- The "unipotent radical" attached to a subspace `L ⊆ ι → F`: the subgroup of
+`SL ι F` consisting of matrices `A` such that `A - 1` sends every vector into `L`.
+When `L` is one-dimensional this is an abelian subgroup of the stabilizer of `L` in `SL`. -/
+def Matrix.SpecialLinearGroup.lineStab (L : Submodule F (ι → F)) :
+ Subgroup (SpecialLinearGroup ι F) where
+ carrier := {A | ∀ w : ι → F, A • w - w ∈ L}
+ one_mem' := by simp
+ mul_mem' {A B} hA hB := fun w ↦ by
+ simp only [Set.mem_setOf_eq, mul_smul] at hA hB ⊢
+ rw [show A • B • w - w = ((A • (B • w) - A • w) - (B • w - w)) +
+ (B • w - w) + (A • w - w) by abel, ← smul_sub]
+ exact add_mem (add_mem (hA _) (hB w)) (hA w)
+ inv_mem' {A} hA := fun w ↦ by
+ convert neg_mem (hA (A⁻¹ • w)) using 1
+ rw [← mul_smul, mul_inv_cancel, one_smul, neg_sub]
+
+@[simp]
+lemma Matrix.SpecialLinearGroup.mem_lineStab_iff (A : SpecialLinearGroup ι F)
+ (L : Submodule F (ι → F)) : A ∈ lineStab L ↔ ∀ w : ι → F, A • w - w ∈ L :=
+ Iff.rfl
+
+open scoped LinearAlgebra.Projectivization
+
+/-- The candidate family of subgroups for the Iwasawa structure on
+`PSL ι F` acting on the projective space `ℙ F (ι → F)`: the unipotent radical
+attached to the line through `p`. -/
+noncomputable abbrev PSL.iwasawaT (p : ℙ F (ι → F)) :
+ Subgroup (Matrix.ProjectiveSpecialLinearGroup ι F) :=
+ Subgroup.map (QuotientGroup.mk' _)
+ (Matrix.SpecialLinearGroup.lineStab p.submodule)
+
+open scoped Pointwise
+
+lemma PSL.smul_submodule (g : Matrix.SpecialLinearGroup ι F) (p : ℙ F (ι → F)) :
+ (g • p).submodule = g • p.submodule:= by
+ induction p using Projectivization.ind with | _ v hv => ?_
+ simp [Submodule.ext_iff, Submodule.pointwise_smul_def, Submodule.mem_span_singleton, smul_comm]
+
+/-- Equivariance of `lineStab` under conjugation by elements of `SL`. -/
+lemma Matrix.SpecialLinearGroup.lineStab_smul
+ (g : Matrix.SpecialLinearGroup ι F) (L : Submodule F (ι → F)) :
+ Matrix.SpecialLinearGroup.lineStab (g • L) =
+ MulAut.conj g • Matrix.SpecialLinearGroup.lineStab L := by
+ ext A
+ rw [Subgroup.mem_pointwise_smul_iff_inv_smul_mem]
+ simp only [mem_lineStab_iff, Submodule.mem_smul_pointwise_iff_exists, MulAut.smul_def,
+ MulAut.inv_apply, MulAut.conj_symm_apply]
+ refine ⟨fun hA w ↦ ?_, fun hA w ↦ ⟨g⁻¹ • (A • w - w), ?_, by simp⟩⟩
+ · obtain ⟨v, hv, hvw⟩ := hA (g • w)
+ simp_all [eq_comm (a := g • v), sub_eq_iff_eq_add, mul_smul]
+ · simpa [mul_smul, smul_sub] using hA (g⁻¹ • w)
+
+/-- The SL-level equivariance pushed through the quotient: the image in `PSL` of
+the conjugate `MulAut.conj g_SL • H` equals `MulAut.conj (mk g_SL) • (image of H)`. -/
+lemma PSL.iwasawaT_map_conj (g : Matrix.SpecialLinearGroup ι F)
+ (H : Subgroup (Matrix.SpecialLinearGroup ι F)) :
+ Subgroup.map (QuotientGroup.mk' (Subgroup.center (Matrix.SpecialLinearGroup ι F)))
+ (MulAut.conj g • H) =
+ MulAut.conj (QuotientGroup.mk g : Matrix.ProjectiveSpecialLinearGroup ι F) •
+ Subgroup.map (QuotientGroup.mk' (Subgroup.center (Matrix.SpecialLinearGroup ι F))) H := by
+ ext x
+ simp only [Subgroup.mem_map, Subgroup.mem_pointwise_smul_iff_inv_smul_mem,
+ MulAut.smul_def, MulAut.inv_apply, MulAut.conj_symm_apply, QuotientGroup.mk'_apply]
+ exact ⟨fun ⟨a, ha, ha'⟩ ↦ ⟨g⁻¹ * a * g, ha, by simp [ha']⟩,
+ fun ⟨a, ha, hx⟩ ↦ ⟨g * a * g⁻¹, by simp [mul_assoc, ha], by simp [hx, mul_assoc]⟩⟩
+
+private lemma LinearMap.exists_restrict_span_singleton_eq_smul_id
+ {R V : Type*} [CommSemiring R] [AddCommMonoid V] [Module R V]
+ {v : V} {A : V →ₗ[R] V} (hAv : A v ∈ Submodule.span R {v}) :
+ ∃ c : R, A v = c • v ∧ ∃ hcomap : Submodule.span R {v} ≤ (Submodule.span R {v}).comap A,
+ A.restrict hcomap = (c • LinearMap.id : Submodule.span R {v} →ₗ[R] _) := by
+ obtain ⟨c, hc⟩ := Submodule.mem_span_singleton.1 hAv
+ refine ⟨c, hc.symm, fun w hw ↦ ?_, LinearMap.ext fun ⟨w, hw⟩ ↦ ?_⟩
+ <;> obtain ⟨a, rfl⟩ := Submodule.mem_span_singleton.1 hw
+ · simpa [Submodule.mem_comap, map_smul] using Submodule.smul_mem _ _ hAv
+ · simp [Subtype.ext_iff, ← hc, smul_comm a c v]
+
+lemma Matrix.SpecialLinearGroup.lineStab_fix_of_span
+ (v : ι → F) (hv : v ≠ 0)
+ (A : Matrix.SpecialLinearGroup ι F)
+ (hA : A ∈ lineStab (Submodule.span F {v})) :
+ A • v = v := by
+ set L : Submodule F (ι → F) := Submodule.span F {v}
+ obtain ⟨c, hcv, hcomap, hres⟩ :=
+ LinearMap.exists_restrict_span_singleton_eq_smul_id (A := A.toLin'.toLinearMap)
+ (by simpa using! add_mem (hA v) (Submodule.mem_span_singleton_self v))
+ have hQ : L.mapQ L A.toLin'.toLinearMap hcomap = LinearMap.id := LinearMap.ext fun x ↦ by
+ induction x using Submodule.Quotient.induction_on with
+ | _ w => simpa [Submodule.Quotient.eq] using! hA w
+ have hdet := A.toLin'.toLinearMap.det_eq_det_mul_det L hcomap
+ rw [show LinearMap.det A.toLin'.toLinearMap = 1 by simp [toLin'_to_linearMap],
+ hres, hQ, LinearMap.det_smul, finrank_span_singleton hv, pow_one,
+ LinearMap.det_id, LinearMap.det_id, mul_one, mul_one] at hdet
+ exact hcv.trans (hdet ▸ one_smul F v)
+
+/-- The subgroup `lineStab (span F {v})` is commutative when `v ≠ 0`. -/
+lemma Matrix.SpecialLinearGroup.lineStab_isMulCommutative_of_span'
+ (v : ι → F) (hv : v ≠ 0) (A B : SpecialLinearGroup ι F)
+ (hA : A ∈ SpecialLinearGroup.lineStab (Submodule.span F {v}))
+ (hB : B ∈ SpecialLinearGroup.lineStab (Submodule.span F {v})) :
+ A * B = B * A := by
+ refine Subtype.ext <| ext_iff_smul.2 fun w ↦ ?_
+ obtain ⟨α, hα⟩ := Submodule.mem_span_singleton.mp (hA w)
+ obtain ⟨β, hβ⟩ := Submodule.mem_span_singleton.mp (hB w)
+ simp only [coe_mul, mul_smul, ← Matrix.SpecialLinearGroup.smul_def]
+ rw [← sub_add_cancel (A • w) w, ← hα, ← sub_add_cancel (B • w) w,
+ ← hβ, smul_add, smul_add, ← sub_left_inj (a := w), ← add_sub, ← hα, ← add_sub, ← hβ,
+ smul_comm, lineStab_fix_of_span v hv A hA, smul_comm, lineStab_fix_of_span v hv B hB, add_comm]
+
+lemma Matrix.SpecialLinearGroup.lineStab_isMulCommutative_of_span
+ (v : ι → F) (hv : v ≠ 0) : IsMulCommutative (lineStab (Submodule.span F {v})) :=
+ ⟨⟨fun ⟨A, hA⟩ ⟨B, hB⟩ ↦ by simpa using lineStab_isMulCommutative_of_span' v hv A B hA hB⟩⟩
From 9158e203ca676281c73e395d6d295ad2552a3599 Mon Sep 17 00:00:00 2001
From: Thomas Browning <13339017+tb65536@users.noreply.github.com>
Date: Fri, 19 Jun 2026 12:19:45 +0000
Subject: [PATCH 0179/1300] chore(Order/Northcott): use `TendstoCofinite` in
`Northcott.lean` (#40778)
This PR switches `comp_of_finite_fibers` to use the existing typeclass `Filter.TendstoCofinite`.
Co-authored-by: tb65536
---
Mathlib/Order/Northcott.lean | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/Mathlib/Order/Northcott.lean b/Mathlib/Order/Northcott.lean
index db31e98d3efd98..15eb5362b13d27 100644
--- a/Mathlib/Order/Northcott.lean
+++ b/Mathlib/Order/Northcott.lean
@@ -5,7 +5,7 @@ Authors: Thomas Browning
-/
module
-public import Mathlib.Order.Filter.Cofinite
+public import Mathlib.Order.Filter.TendstoCofinite
/-!
# Northcott Functions
@@ -62,10 +62,11 @@ lemma comp_of_bddAbove [Preorder β] [LE γ] [Northcott h] (H : ∀ c, BddAbove
exact (finite_le (h := h) b).subset <| by grind
/-- A composition `h' ∘ h` is Northcott when `h'` is Northcott and the fibers of `h` are finite. -/
-lemma comp_of_finite_fibers [LE γ] [Northcott h'] (H : ∀ b, (h ⁻¹' {b}).Finite) :
+lemma comp_of_finite_fibers [LE γ] [Northcott h'] [Filter.TendstoCofinite h] :
Northcott (h' ∘ h) where
finite_le c := by
- refine Set.Finite.of_finite_fibers h ?_ fun x _ ↦ (H x).inter_of_right _
+ refine Set.Finite.of_finite_fibers h ?_ fun x _ ↦
+ (Filter.TendstoCofinite.finite_preimage_singleton h x).inter_of_right _
exact (finite_le (h := h') c).subset <| by grind
end Northcott
From 555b02bc956f8a17a8dc95eb1131efda57d8b837 Mon Sep 17 00:00:00 2001
From: Mac Malone <9020483+tydeu@users.noreply.github.com>
Date: Fri, 19 Jun 2026 12:19:49 +0000
Subject: [PATCH 0180/1300] chore: prerequisite options for the Lake cache
(#40784)
This PR sets Lake options that are prerequisites for using the Lake cache (i.e., `lake cache`) but are also compatible with the current setup. It does not enable the local Lake artifact cache by default (e.g., `enableArtifactCache := true`), because the local cache is largely redundant with `lake exe cache get`.
---
.github/workflows/lake_cache_shadow.yml | 16 ++++++----------
lake-manifest.json | 2 +-
lakefile.lean | 6 ++++++
3 files changed, 13 insertions(+), 11 deletions(-)
diff --git a/.github/workflows/lake_cache_shadow.yml b/.github/workflows/lake_cache_shadow.yml
index b0b0491fe7f769..50ce19d3b21d59 100644
--- a/.github/workflows/lake_cache_shadow.yml
+++ b/.github/workflows/lake_cache_shadow.yml
@@ -186,12 +186,10 @@ jobs:
from pathlib import Path
p = Path("lakefile.lean")
src = p.read_text()
- marker = ' testDriver := "MathlibTest"'
+ marker = ' restoreAllArtifacts := true'
inject = (
- ' testDriver := "MathlibTest"\n'
- ' fixedToolchain := true\n'
- ' enableArtifactCache := true\n'
- ' restoreAllArtifacts := true'
+ ' restoreAllArtifacts := true\n'
+ ' enableArtifactCache := true'
)
if marker not in src:
raise SystemExit("lakefile.lean shape changed; update the shadow patch in lake_cache_shadow.yml")
@@ -438,12 +436,10 @@ jobs:
from pathlib import Path
p = Path("lakefile.lean")
src = p.read_text()
- marker = ' testDriver := "MathlibTest"'
+ marker = ' restoreAllArtifacts := true'
inject = (
- ' testDriver := "MathlibTest"\n'
- ' fixedToolchain := true\n'
- ' enableArtifactCache := true\n'
- ' restoreAllArtifacts := true'
+ ' restoreAllArtifacts := true\n'
+ ' enableArtifactCache := true'
)
if marker not in src:
raise SystemExit("lakefile.lean shape changed")
diff --git a/lake-manifest.json b/lake-manifest.json
index 83a478e0413ecb..44a71cb13c5519 100644
--- a/lake-manifest.json
+++ b/lake-manifest.json
@@ -83,4 +83,4 @@
"configFile": "lakefile.toml"}],
"name": "mathlib",
"lakeDir": ".lake",
- "fixedToolchain": false}
+ "fixedToolchain": true}
diff --git a/lakefile.lean b/lakefile.lean
index 46071184a166ee..a4808f93fdd2c0 100644
--- a/lakefile.lean
+++ b/lakefile.lean
@@ -50,6 +50,12 @@ package mathlib where
testDriver := "MathlibTest"
lintDriver := "batteries/runLinter"
lintDriverArgs := #["Mathlib"]
+ -- A version of Mathlib only supports the toolchain it is built with.
+ fixedToolchain := true
+ -- Mathlib oleans are built on Linux CI and used across platforms.
+ platformIndependent := true
+ -- Mathlib currently expects artifacts to be in the build directory.
+ restoreAllArtifacts := true
-- These are additional settings which do not affect the lake hash,
-- so they can be enabled in CI and disabled locally or vice versa.
-- Warning: Do not put any options here that actually change the olean files,
From 63c85da758e583e0be796f43c133ae7529486a40 Mon Sep 17 00:00:00 2001
From: Anatole Dedecker
Date: Fri, 19 Jun 2026 13:16:42 +0000
Subject: [PATCH 0181/1300] chore: rename Pi.algHom to AlgHom.pi (#40044)
---
Mathlib/Algebra/Algebra/Pi.lean | 43 +++++++++++++++----
Mathlib/Algebra/Polynomial/AlgebraMap.lean | 6 +--
Mathlib/AlgebraicGeometry/Morphisms/Flat.lean | 2 +-
Mathlib/RingTheory/RingHom/QuasiFinite.lean | 2 +-
Mathlib/RingTheory/Smooth/Pi.lean | 4 +-
.../Spectrum/Maximal/Localization.lean | 2 +-
6 files changed, 42 insertions(+), 17 deletions(-)
diff --git a/Mathlib/Algebra/Algebra/Pi.lean b/Mathlib/Algebra/Algebra/Pi.lean
index 28037ee3dd8ca1..cf9e8606cde360 100644
--- a/Mathlib/Algebra/Algebra/Pi.lean
+++ b/Mathlib/Algebra/Algebra/Pi.lean
@@ -48,15 +48,41 @@ theorem algebraMap_def (a : R) : algebraMap R (Π i, A i) a = fun i ↦ algebraM
theorem algebraMap_apply (a : R) (i : ι) : algebraMap R (Π i, A i) a i = algebraMap R (A i) a :=
rfl
-variable {ι} (R)
+variable {ι}
-/-- A family of algebra homomorphisms `g i : B →ₐ[R] A i` defines a ring homomorphism
-`Pi.algHom g : B →ₐ[R] Π i, A i` given by `Pi.algHom g x i = g i x`. -/
+variable {A} in
+/-- A family of algebra homomorphisms `g i : B →ₐ[R] A i` defines an algebra homomorphism
+`AlgHom.pi g : B →ₐ[R] Π i, A i` given by `AlgHom.pi g x i = g i x`. -/
@[simps!]
-def algHom {B : Type*} [Semiring B] [Algebra R B] (g : ∀ i, B →ₐ[R] A i) : B →ₐ[R] Π i, A i where
+def _root_.AlgHom.pi {B : Type*} [Semiring B] [Algebra R B] (g : Π i, B →ₐ[R] A i) :
+ B →ₐ[R] Π i, A i where
__ := RingHom.pi fun i ↦ (g i).toRingHom
commutes' r := by ext; simp
+variable {A} in
+/-- `AlgHom.pi` commutes with composition. -/
+theorem _root_.AlgHom.pi_comp {B C : Type*} [Semiring B] [Algebra R B] [Semiring C] [Algebra R C]
+ (g : ∀ i, C →ₐ[R] A i) (h : B →ₐ[R] C) :
+ (AlgHom.pi g).comp h = AlgHom.pi (fun i ↦ (g i).comp h) := rfl
+
+variable (R)
+
+/-- Use `AlgHom.pi` instead. -/
+@[deprecated AlgHom.pi (since := "2026-05-30")]
+abbrev algHom {B : Type*} [Semiring B] [Algebra R B] (g : Π i, B →ₐ[R] A i) : B →ₐ[R] Π i, A i :=
+ .pi g
+
+/-- Use `AlgHom.pi_apply` instead. -/
+@[deprecated AlgHom.pi_apply (since := "2026-05-30")]
+theorem algHom_apply {B : Type*} [Semiring B] [Algebra R B]
+ (g : Π i, B →ₐ[R] A i) (x : B) (i : ι) : Pi.algHom R A g x i = g i x :=
+ AlgHom.pi_apply g x i
+
+@[deprecated AlgHom.pi_comp (since := "2026-05-30")]
+theorem algHom_comp {B C : Type*} [Semiring B] [Algebra R B] [Semiring C] [Algebra R C]
+ (g : ∀ i, C →ₐ[R] A i) (h : B →ₐ[R] C) :
+ (algHom R A g).comp h = algHom R A (fun i ↦ (g i).comp h) := rfl
+
/-- `Function.eval` as an `AlgHom`. The name matches `Pi.evalRingHom`, `Pi.evalMonoidHom`,
etc. -/
@[simps]
@@ -68,12 +94,11 @@ def evalAlgHom (i : ι) : (Π i, A i) →ₐ[R] A i :=
lemma coe_evalAlgHom (i : ι) : evalAlgHom R A i = evalRingHom A i := rfl
@[simp]
-theorem algHom_evalAlgHom : algHom R A (evalAlgHom R A) = AlgHom.id R (Π i, A i) := rfl
+theorem _root_.AlgHom.pi_evalAlgHom : AlgHom.pi (evalAlgHom R A) = AlgHom.id R (Π i, A i) :=
+ rfl
-/-- `Pi.algHom` commutes with composition. -/
-theorem algHom_comp {B C : Type*} [Semiring B] [Algebra R B] [Semiring C] [Algebra R C]
- (g : ∀ i, C →ₐ[R] A i) (h : B →ₐ[R] C) :
- (algHom R A g).comp h = algHom R A (fun i ↦ (g i).comp h) := rfl
+@[deprecated (since := "2026-06-03")]
+alias algHom_evalAlgHom := _root_.AlgHom.pi_evalAlgHom
variable (S : ι → Type*) [∀ i, CommSemiring (S i)]
diff --git a/Mathlib/Algebra/Polynomial/AlgebraMap.lean b/Mathlib/Algebra/Polynomial/AlgebraMap.lean
index 04d27fae9bcaaf..72b9002779afbb 100644
--- a/Mathlib/Algebra/Polynomial/AlgebraMap.lean
+++ b/Mathlib/Algebra/Polynomial/AlgebraMap.lean
@@ -456,12 +456,12 @@ variable (x : Π i, A i) (p : R[X])
/-- Polynomial evaluation on an indexed tuple is the indexed product of the evaluations
on the components.
Generalizes `Polynomial.aeval_prod` to indexed products. -/
-theorem aeval_pi (x : Π i, A i) : aeval (R := R) x = Pi.algHom R A (fun i ↦ aeval (x i)) :=
+theorem aeval_pi (x : Π i, A i) : aeval (R := R) x = AlgHom.pi (fun i ↦ aeval (x i)) :=
(funext fun i ↦ aeval_algHom (Pi.evalAlgHom R A i) x) ▸
- (Pi.algHom_comp R A (Pi.evalAlgHom R A) (aeval x))
+ (AlgHom.pi_comp (Pi.evalAlgHom R A) (aeval x))
theorem aeval_pi_apply₂ (j : I) : p.aeval x j = p.aeval (x j) :=
- aeval_pi (R := R) x ▸ Pi.algHom_apply R A (fun i ↦ aeval (x i)) p j
+ aeval_pi (R := R) x ▸ AlgHom.pi_apply (fun i ↦ aeval (x i)) p j
/-- Polynomial evaluation on an indexed tuple is the indexed tuple of the evaluations
on the components.
diff --git a/Mathlib/AlgebraicGeometry/Morphisms/Flat.lean b/Mathlib/AlgebraicGeometry/Morphisms/Flat.lean
index b8e5ffa0b1bd1b..095c53dd0d0a98 100644
--- a/Mathlib/AlgebraicGeometry/Morphisms/Flat.lean
+++ b/Mathlib/AlgebraicGeometry/Morphisms/Flat.lean
@@ -270,7 +270,7 @@ lemma mono_pushoutSection_of_iSup_eq {ι : Type*} [Finite ι] (VX : ι → X.Ope
let ψY : Γ(Y, UY) →+* Π i, Γ(Y, g ⁻¹ᵁ VX i ⊓ iY ⁻¹ᵁ UT) := RingHom.pi fun i ↦
(Y.presheaf.map (homOfLE (by subst hUY hVU; gcongr; exact le_iSup _ _)).op).hom
-- The map `Γ(X, U) ⟶ ∏ᵢ Γ(X, Vᵢ)`
- let ψ : Γ(X, UX) →ₐ[Γ(S, US)] Π i, Γ(X, VX i) := Pi.algHom _ _ fun i ↦
+ let ψ : Γ(X, UX) →ₐ[Γ(S, US)] Π i, Γ(X, VX i) := AlgHom.pi fun i ↦
⟨(X.presheaf.map (homOfLE (hVU ▸ le_iSup VX i)).op).hom, fun r ↦ by
dsimp [RingHom.algebraMap_toAlgebra]
simp only [← CommRingCat.comp_apply, Scheme.Hom.appLE_map]⟩
diff --git a/Mathlib/RingTheory/RingHom/QuasiFinite.lean b/Mathlib/RingTheory/RingHom/QuasiFinite.lean
index da49a14fd38f9d..0d711883d8668b 100644
--- a/Mathlib/RingTheory/RingHom/QuasiFinite.lean
+++ b/Mathlib/RingTheory/RingHom/QuasiFinite.lean
@@ -81,7 +81,7 @@ lemma QuasiFinite.ofLocalizationSpanTarget : OfLocalizationSpanTarget QuasiFinit
let φ (r : s) : P.Fiber S →ₐ[P.ResidueField] P.Fiber (Localization.Away r.1) :=
Algebra.TensorProduct.map (.id _ _) (IsScalarTower.toAlgHom _ _ _)
let f : P.Fiber S →ₐ[P.ResidueField] Π r : s, (P.Fiber (Localization.Away r.1)) :=
- Pi.algHom _ _ φ
+ AlgHom.pi φ
have : IsNoetherian P.ResidueField (Π r : s, (P.Fiber (Localization.Away r.1))) :=
isNoetherian_of_isNoetherianRing_of_finite ..
suffices Function.Injective f from .of_injective f.toLinearMap this
diff --git a/Mathlib/RingTheory/Smooth/Pi.lean b/Mathlib/RingTheory/Smooth/Pi.lean
index 117f16bd59492d..f4799e26092736 100644
--- a/Mathlib/RingTheory/Smooth/Pi.lean
+++ b/Mathlib/RingTheory/Smooth/Pi.lean
@@ -62,7 +62,7 @@ theorem pi_iff [Finite I] :
fun _ ↦ Ideal.Quotient.mk_surjective _
replace he' : ∀ i, Ideal.Quotient.mk J (e i) = g (Pi.single i 1) := congr_fun he'
let iso : B ≃ₐ[R] ∀ i, B ⧸ Ideal.span {1 - e i} :=
- { __ := Pi.algHom _ _ fun i ↦ Ideal.Quotient.mkₐ R _
+ { __ := AlgHom.pi fun i ↦ Ideal.Quotient.mkₐ R _
__ := Equiv.ofBijective _ he.bijective_pi }
let J' := fun i ↦ J.map (Ideal.Quotient.mk (Ideal.span {1 - e i}))
let ι : ∀ i, (B ⧸ J →ₐ[R] (B ⧸ _) ⧸ J' i) := fun i ↦ Ideal.quotientMapₐ _
@@ -87,7 +87,7 @@ theorem pi_iff [Finite I] :
(by rw [← Ideal.map_pow, hJ, Ideal.map_bot]) g'
exact ⟨a, AlgHom.congr_fun ha⟩
choose a ha using this
- use iso.symm.toAlgHom.comp (Pi.algHom _ _ fun i ↦ (a i).comp (Pi.evalAlgHom R A i))
+ use iso.symm.toAlgHom.comp (AlgHom.pi fun i ↦ (a i).comp (Pi.evalAlgHom R A i))
ext x; rw [← AlgHom.toLinearMap_apply, ← AlgHom.toLinearMap_apply]; congr 1
ext i x
simp only [AlgHom.comp_toLinearMap, AlgEquiv.toAlgHom_toLinearMap,
diff --git a/Mathlib/RingTheory/Spectrum/Maximal/Localization.lean b/Mathlib/RingTheory/Spectrum/Maximal/Localization.lean
index 9bd2c7935bdd97..70e090dc94f605 100644
--- a/Mathlib/RingTheory/Spectrum/Maximal/Localization.lean
+++ b/Mathlib/RingTheory/Spectrum/Maximal/Localization.lean
@@ -174,7 +174,7 @@ theorem toPiLocalization_injective : Function.Injective (toPiLocalization R) :=
/-- The projection from the product of localizations at primes to the product of
localizations at maximal ideals. -/
def piLocalizationToMaximal : PiLocalization R →ₐ[R] MaximalSpectrum.PiLocalization R :=
- Pi.algHom _ _ fun I ↦ Pi.evalAlgHom _ _ I.toPrimeSpectrum
+ AlgHom.pi fun I ↦ Pi.evalAlgHom _ _ I.toPrimeSpectrum
open scoped Classical in
theorem piLocalizationToMaximal_surjective : Function.Surjective (piLocalizationToMaximal R) :=
From 01469fac233f7969277e6f6c5e53f4d1bd018e75 Mon Sep 17 00:00:00 2001
From: Hannah Scholz <70071345+scholzhannah@users.noreply.github.com>
Date: Fri, 19 Jun 2026 13:16:45 +0000
Subject: [PATCH 0182/1300] feat(Geometry/Manifold/Notation): add
(d)elaborators for `UniqueMDiffOn` and `UniqueMDiffWithinAt` (#40748)
This PR adds elaborators and delaborators for `UniqueMDiffOn` and `UniqueMDiffWithinAt`. The elaborators currently don't work for finding a model with corners on `TotalSpace`s since they don't provide `baseInfo`. This should hopefully be fixed by #40047.
---
.../Geometry/Manifold/ContMDiffMFDeriv.lean | 13 ++--
Mathlib/Geometry/Manifold/Diffeomorph.lean | 6 +-
Mathlib/Geometry/Manifold/MFDeriv/Atlas.lean | 4 +-
Mathlib/Geometry/Manifold/MFDeriv/Basic.lean | 77 ++++++++++---------
Mathlib/Geometry/Manifold/MFDeriv/FDeriv.lean | 6 +-
.../Manifold/MFDeriv/NormedSpace.lean | 12 +--
.../Manifold/MFDeriv/SpecificFunctions.lean | 32 ++++----
.../Manifold/MFDeriv/UniqueDifferential.lean | 40 +++++-----
Mathlib/Geometry/Manifold/Notation.lean | 39 ++++++++++
.../Manifold/VectorField/LieBracket.lean | 38 ++++-----
.../Manifold/VectorField/Pullback.lean | 34 ++++----
.../DifferentialGeometry/Notation/Basic.lean | 65 ++++++++++++++++
.../Notation/Delaborators.lean | 16 ++++
13 files changed, 251 insertions(+), 131 deletions(-)
diff --git a/Mathlib/Geometry/Manifold/ContMDiffMFDeriv.lean b/Mathlib/Geometry/Manifold/ContMDiffMFDeriv.lean
index d5747cf0dbe93d..fae86ea50365d1 100644
--- a/Mathlib/Geometry/Manifold/ContMDiffMFDeriv.lean
+++ b/Mathlib/Geometry/Manifold/ContMDiffMFDeriv.lean
@@ -69,7 +69,7 @@ protected theorem ContMDiffWithinAt.mfderivWithin {x₀ : N} {f : N → M → M'
{t : Set N} {u : Set M}
(hf : CMDiffAt[t ×ˢ u] n (Function.uncurry f) (x₀, g x₀))
(hg : CMDiffAt[t] m g x₀) (hx₀ : x₀ ∈ t)
- (hu : MapsTo g t u) (hmn : m + 1 ≤ n) (h'u : UniqueMDiffOn I u) :
+ (hu : MapsTo g t u) (hmn : m + 1 ≤ n) (h'u : UniqueMDiff[u]) :
CMDiffAt[t] m (inTangentCoordinates I I' g (fun x ↦ f x (g x))
(fun x ↦ mfderiv[u] (f x) (g x)) x₀) x₀ := by
-- first localize the result to a smaller set, to make sure everything happens in chart domains
@@ -148,8 +148,7 @@ protected theorem ContMDiffWithinAt.mfderivWithin {x₀ : N} {f : N → M → M'
apply nhdsWithin_mono _ ht't
filter_upwards [h2f, h4f, h2g, self_mem_nhdsWithin] with x hx h'x h2 hxt
have h1 : g x ∈ u := hu hxt
- have h3 : UniqueMDiffWithinAt 𝓘(𝕜, E)
- ((extChartAt I (g x₀)).target ∩ (extChartAt I (g x₀)).symm ⁻¹' u)
+ have h3 : UniqueMDiffAt[(extChartAt I (g x₀)).target ∩ (extChartAt I (g x₀)).symm ⁻¹' u]
((extChartAt I (g x₀)) (g x)) := by
apply UniqueDiffWithinAt.uniqueMDiffWithinAt
apply UniqueMDiffOn.uniqueDiffOn_target_inter h'u
@@ -191,7 +190,7 @@ This is a special case of `ContMDiffWithinAt.mfderivWithin` where `f` does not c
parameters and `g = id`.
-/
theorem ContMDiffWithinAt.mfderivWithin_const {x₀ : M} {f : M → M'}
- (hf : CMDiffAt[s] n f x₀) (hmn : m + 1 ≤ n) (hx : x₀ ∈ s) (hs : UniqueMDiffOn I s) :
+ (hf : CMDiffAt[s] n f x₀) (hmn : m + 1 ≤ n) (hx : x₀ ∈ s) (hs : UniqueMDiff[s]) :
CMDiffAt[s] m (inTangentCoordinates I I' id f (mfderiv[s] f) x₀) x₀ := by
have : CMDiffAt[s ×ˢ s] n (fun x : M × M ↦ f x.2) (x₀, x₀) :=
hf.comp (x₀, x₀) contMDiffWithinAt_snd mapsTo_snd_prod
@@ -210,7 +209,7 @@ theorem ContMDiffWithinAt.mfderivWithin_apply {x₀ : N'}
(hf : CMDiffAt[t ×ˢ u] n (Function.uncurry f) (g₁ x₀, g (g₁ x₀)))
(hg : CMDiffAt[t] m g (g₁ x₀)) (hg₁ : CMDiffAt[v] m g₁ x₀)
(hg₂ : CMDiffAt[v] m g₂ x₀) (hmn : m + 1 ≤ n) (h'g₁ : MapsTo g₁ v t)
- (hg₁x₀ : g₁ x₀ ∈ t) (h'g : MapsTo g t u) (hu : UniqueMDiffOn I u) :
+ (hg₁x₀ : g₁ x₀ ∈ t) (h'g : MapsTo g t u) (hu : UniqueMDiff[u]) :
CMDiffAt[v] m (fun x ↦ (inTangentCoordinates I I' g (fun x ↦ f x (g x))
(fun x ↦ mfderiv[u] (f x) (g x)) (g₁ x₀) (g₁ x)) (g₂ x)) x₀ :=
((hf.mfderivWithin hg hg₁x₀ h'g hmn hu).comp_of_eq hg₁ h'g₁ rfl).clm_apply hg₂
@@ -273,7 +272,7 @@ variable [Is : IsManifold I 1 M] [I's : IsManifold I' 1 M']
/-- If a function is `C^n` on a domain with unique derivatives, then its bundled derivative
is `C^m` when `m+1 ≤ n`. -/
theorem ContMDiffOn.contMDiffOn_tangentMapWithin
- (hf : CMDiff[s] n f) (hmn : m + 1 ≤ n) (hs : UniqueMDiffOn I s) :
+ (hf : CMDiff[s] n f) (hmn : m + 1 ≤ n) (hs : UniqueMDiff[s]) :
CMDiff[(π E (TangentSpace I) ⁻¹' s)] m (tangentMap[s] f) := by
intro x₀ hx₀
let s' : Set (TangentBundle I M) := (π E (TangentSpace I) ⁻¹' s)
@@ -298,7 +297,7 @@ theorem ContMDiffOn.contMDiffOn_tangentMapWithin
/-- If a function is `C^n` on a domain with unique derivatives, with `1 ≤ n`, then its bundled
derivative is continuous there. -/
theorem ContMDiffOn.continuousOn_tangentMapWithin (hf : CMDiff[s] n f) (hmn : 1 ≤ n)
- (hs : UniqueMDiffOn I s) :
+ (hs : UniqueMDiff[s]) :
ContinuousOn (tangentMap[s] f) (π E (TangentSpace I) ⁻¹' s) := by
have : CMDiff[π E (TangentSpace I) ⁻¹' s] 0 (tangentMap[s] f) :=
hf.contMDiffOn_tangentMapWithin hmn hs
diff --git a/Mathlib/Geometry/Manifold/Diffeomorph.lean b/Mathlib/Geometry/Manifold/Diffeomorph.lean
index 0e6e6e8fc38027..f99ed00f458dd2 100644
--- a/Mathlib/Geometry/Manifold/Diffeomorph.lean
+++ b/Mathlib/Geometry/Manifold/Diffeomorph.lean
@@ -338,19 +338,19 @@ theorem toOpenPartialHomeomorph_mdifferentiable (h : M ≃ₘ^n⟮I, J⟯ N) (hn
⟨h.mdifferentiableOn _ hn, h.symm.mdifferentiableOn _ hn⟩
theorem uniqueMDiffOn_image_aux (h : M ≃ₘ^n⟮I, J⟯ N) (hn : n ≠ 0) {s : Set M}
- (hs : UniqueMDiffOn I s) : UniqueMDiffOn J (h '' s) := by
+ (hs : UniqueMDiff[s]) : UniqueMDiff[h '' s] := by
convert! hs.uniqueMDiffOn_preimage (h.toOpenPartialHomeomorph_mdifferentiable hn)
simp [h.image_eq_preimage_symm]
@[simp]
theorem uniqueMDiffOn_image (h : M ≃ₘ^n⟮I, J⟯ N) (hn : n ≠ 0) {s : Set M} :
- UniqueMDiffOn J (h '' s) ↔ UniqueMDiffOn I s :=
+ UniqueMDiff[h '' s] ↔ UniqueMDiff[s] :=
⟨fun hs => h.symm_image_image s ▸ h.symm.uniqueMDiffOn_image_aux hn hs,
h.uniqueMDiffOn_image_aux hn⟩
@[simp]
theorem uniqueMDiffOn_preimage (h : M ≃ₘ^n⟮I, J⟯ N) (hn : n ≠ 0) {s : Set N} :
- UniqueMDiffOn I (h ⁻¹' s) ↔ UniqueMDiffOn J s :=
+ UniqueMDiff[h ⁻¹' s] ↔ UniqueMDiff[s] :=
h.symm_image_eq_preimage s ▸ h.symm.uniqueMDiffOn_image hn
@[simp]
diff --git a/Mathlib/Geometry/Manifold/MFDeriv/Atlas.lean b/Mathlib/Geometry/Manifold/MFDeriv/Atlas.lean
index 70a61d9b9c7dba..2b06e0a39ea983 100644
--- a/Mathlib/Geometry/Manifold/MFDeriv/Atlas.lean
+++ b/Mathlib/Geometry/Manifold/MFDeriv/Atlas.lean
@@ -269,7 +269,7 @@ lemma mfderiv_extChartAt_comp_mfderivWithin_extChartAt_symm {x : M}
{y : E} (hy : y ∈ (extChartAt I x).target) :
(mfderiv% (extChartAt I x) ((extChartAt I x).symm y)) ∘L
(mfderiv[range I] (extChartAt I x).symm y) = ContinuousLinearMap.id _ _ := by
- have U : UniqueMDiffWithinAt 𝓘(𝕜, E) (range ↑I) y := by
+ have U : UniqueMDiffAt[range I] y := by
apply I.uniqueMDiffOn
exact extChartAt_target_subset_range x hy
have h'y : (extChartAt I x).symm y ∈ (extChartAt I x).source := (extChartAt I x).map_target hy
@@ -306,7 +306,7 @@ lemma mfderivWithin_extChartAt_symm_comp_mfderiv_extChartAt
have h'y : (extChartAt I x).symm y ∈ (extChartAt I x).source := (extChartAt I x).map_target hy
have h''y : (extChartAt I x).symm y ∈ (chartAt H x).source := by
rwa [← extChartAt_source (I := I)]
- have U' : UniqueMDiffWithinAt I (extChartAt I x).source ((extChartAt I x).symm y) :=
+ have U' : UniqueMDiffAt[(extChartAt I x).source] ((extChartAt I x).symm y) :=
(isOpen_extChartAt_source x).uniqueMDiffWithinAt h'y
have : mfderiv% (extChartAt I x) ((extChartAt I x).symm y)
= mfderiv[(extChartAt I x).source] (extChartAt I x) ((extChartAt I x).symm y) := by
diff --git a/Mathlib/Geometry/Manifold/MFDeriv/Basic.lean b/Mathlib/Geometry/Manifold/MFDeriv/Basic.lean
index b6b72c2c4f437f..c1c0e5ecbf1239 100644
--- a/Mathlib/Geometry/Manifold/MFDeriv/Basic.lean
+++ b/Mathlib/Geometry/Manifold/MFDeriv/Basic.lean
@@ -8,6 +8,7 @@ module
public import Mathlib.Analysis.Calculus.TangentCone.Prod
public import Mathlib.Geometry.Manifold.MFDeriv.Defs
public import Mathlib.Geometry.Manifold.ContMDiff.Defs
+import Mathlib.Geometry.Manifold.Notation
/-!
# Basic properties of the manifold Fréchet derivative
@@ -49,7 +50,7 @@ variable
{M'' : Type*} [TopologicalSpace M''] [ChartedSpace H'' M'']
{f f₁ : M → M'} {x : M} {s t : Set M} {g : M' → M''} {u : Set M'}
-theorem uniqueMDiffWithinAt_univ : UniqueMDiffWithinAt I univ x := by
+theorem uniqueMDiffWithinAt_univ : UniqueMDiffAt[(univ : Set M)] x := by
unfold UniqueMDiffWithinAt
simp only [preimage_univ, univ_inter]
exact I.uniqueDiffOn _ (mem_range_self _)
@@ -57,57 +58,57 @@ theorem uniqueMDiffWithinAt_univ : UniqueMDiffWithinAt I univ x := by
variable {I}
theorem uniqueMDiffWithinAt_iff_inter_range {s : Set M} {x : M} :
- UniqueMDiffWithinAt I s x ↔
+ UniqueMDiffAt[s] x ↔
UniqueDiffWithinAt 𝕜 ((extChartAt I x).symm ⁻¹' s ∩ range I)
((extChartAt I x) x) := Iff.rfl
theorem uniqueMDiffWithinAt_iff {s : Set M} {x : M} :
- UniqueMDiffWithinAt I s x ↔
+ UniqueMDiffAt[s] x ↔
UniqueDiffWithinAt 𝕜 ((extChartAt I x).symm ⁻¹' s ∩ (extChartAt I x).target)
((extChartAt I x) x) := by
apply uniqueDiffWithinAt_congr
rw [nhdsWithin_inter, nhdsWithin_inter, nhdsWithin_extChartAt_target_eq]
-nonrec theorem UniqueMDiffWithinAt.mono_nhds {s t : Set M} {x : M} (hs : UniqueMDiffWithinAt I s x)
- (ht : 𝓝[s] x ≤ 𝓝[t] x) : UniqueMDiffWithinAt I t x :=
+nonrec theorem UniqueMDiffWithinAt.mono_nhds {s t : Set M} {x : M} (hs : UniqueMDiffAt[s] x)
+ (ht : 𝓝[s] x ≤ 𝓝[t] x) : UniqueMDiffAt[t] x :=
hs.mono_nhds <| by simpa only [← map_extChartAt_nhdsWithin] using Filter.map_mono ht
theorem UniqueMDiffWithinAt.mono_of_mem_nhdsWithin {s t : Set M} {x : M}
- (hs : UniqueMDiffWithinAt I s x) (ht : t ∈ 𝓝[s] x) : UniqueMDiffWithinAt I t x :=
+ (hs : UniqueMDiffAt[s] x) (ht : t ∈ 𝓝[s] x) : UniqueMDiffAt[t] x :=
hs.mono_nhds (nhdsWithin_le_iff.2 ht)
-theorem UniqueMDiffWithinAt.mono (h : UniqueMDiffWithinAt I s x) (st : s ⊆ t) :
- UniqueMDiffWithinAt I t x :=
+theorem UniqueMDiffWithinAt.mono (h : UniqueMDiffAt[s] x) (st : s ⊆ t) :
+ UniqueMDiffAt[t] x :=
UniqueDiffWithinAt.mono h <| inter_subset_inter (preimage_mono st) (Subset.refl _)
-theorem UniqueMDiffWithinAt.inter' (hs : UniqueMDiffWithinAt I s x) (ht : t ∈ 𝓝[s] x) :
- UniqueMDiffWithinAt I (s ∩ t) x :=
+theorem UniqueMDiffWithinAt.inter' (hs : UniqueMDiffAt[s] x) (ht : t ∈ 𝓝[s] x) :
+ UniqueMDiffAt[s ∩ t] x :=
hs.mono_of_mem_nhdsWithin (Filter.inter_mem self_mem_nhdsWithin ht)
-theorem UniqueMDiffWithinAt.inter (hs : UniqueMDiffWithinAt I s x) (ht : t ∈ 𝓝 x) :
- UniqueMDiffWithinAt I (s ∩ t) x :=
+theorem UniqueMDiffWithinAt.inter (hs : UniqueMDiffAt[s] x) (ht : t ∈ 𝓝 x) :
+ UniqueMDiffAt[s ∩ t] x :=
hs.inter' (nhdsWithin_le_nhds ht)
-theorem IsOpen.uniqueMDiffWithinAt (hs : IsOpen s) (xs : x ∈ s) : UniqueMDiffWithinAt I s x :=
+theorem IsOpen.uniqueMDiffWithinAt (hs : IsOpen s) (xs : x ∈ s) : UniqueMDiffAt[s] x :=
(uniqueMDiffWithinAt_univ I).mono_of_mem_nhdsWithin <| nhdsWithin_le_nhds <| hs.mem_nhds xs
-theorem UniqueMDiffOn.inter (hs : UniqueMDiffOn I s) (ht : IsOpen t) : UniqueMDiffOn I (s ∩ t) :=
+theorem UniqueMDiffOn.inter (hs : UniqueMDiff[s]) (ht : IsOpen t) : UniqueMDiff[s ∩ t] :=
fun _x hx => UniqueMDiffWithinAt.inter (hs _ hx.1) (ht.mem_nhds hx.2)
-theorem IsOpen.uniqueMDiffOn (hs : IsOpen s) : UniqueMDiffOn I s :=
+theorem IsOpen.uniqueMDiffOn (hs : IsOpen s) : UniqueMDiff[s] :=
fun _x hx => hs.uniqueMDiffWithinAt hx
-theorem uniqueMDiffOn_univ : UniqueMDiffOn I (univ : Set M) :=
+theorem uniqueMDiffOn_univ : UniqueMDiff[(univ : Set M)] :=
isOpen_univ.uniqueMDiffOn
-nonrec theorem UniqueMDiffWithinAt.prod {x : M} {y : M'} {s t} (hs : UniqueMDiffWithinAt I s x)
- (ht : UniqueMDiffWithinAt I' t y) : UniqueMDiffWithinAt (I.prod I') (s ×ˢ t) (x, y) := by
+nonrec theorem UniqueMDiffWithinAt.prod {x : M} {y : M'} {s : Set M} {t : Set M'}
+ (hs : UniqueMDiffAt[s] x) (ht : UniqueMDiffAt[t] y) : UniqueMDiffAt[s ×ˢ t] (x, y) := by
refine (hs.prod ht).mono ?_
rw [ModelWithCorners.range_prod, ← prod_inter_prod]
rfl
-theorem UniqueMDiffOn.prod {s : Set M} {t : Set M'} (hs : UniqueMDiffOn I s)
- (ht : UniqueMDiffOn I' t) : UniqueMDiffOn (I.prod I') (s ×ˢ t) := fun x h ↦
+theorem UniqueMDiffOn.prod {s : Set M} {t : Set M'} (hs : UniqueMDiff[s])
+ (ht : UniqueMDiff[t]) : UniqueMDiff[s ×ˢ t] := fun x h ↦
(hs x.1 h.1).prod (ht x.2 h.2)
theorem MDifferentiableWithinAt.mono (hst : s ⊆ t) (h : MDifferentiableWithinAt I I' f t x) :
@@ -517,12 +518,12 @@ variable {f' f₀' f₁' : TangentSpace I x →L[𝕜] TangentSpace I' (f x)}
set_option backward.isDefEq.respectTransparency false in
/-- `UniqueMDiffWithinAt` achieves its goal: it implies the uniqueness of the derivative. -/
-protected nonrec theorem UniqueMDiffWithinAt.eq (U : UniqueMDiffWithinAt I s x)
+protected nonrec theorem UniqueMDiffWithinAt.eq (U : UniqueMDiffAt[s] x)
(h : HasMFDerivWithinAt I I' f s x f') (h₁ : HasMFDerivWithinAt I I' f s x f₁') : f' = f₁' := by
-- `by apply` because the instances can be found in the term but not in the goal.
apply U.eq h.2 h₁.2
-protected theorem UniqueMDiffOn.eq (U : UniqueMDiffOn I s) (hx : x ∈ s)
+protected theorem UniqueMDiffOn.eq (U : UniqueMDiff[s]) (hx : x ∈ s)
(h : HasMFDerivWithinAt I I' f s x f') (h₁ : HasMFDerivWithinAt I I' f s x f₁') : f' = f₁' :=
UniqueMDiffWithinAt.eq (U _ hx) h h₁
@@ -685,7 +686,7 @@ protected theorem HasMFDerivAt.mfderiv (h : HasMFDerivAt I I' f x f') : mfderiv
(hasMFDerivAt_unique h h.mdifferentiableAt.hasMFDerivAt).symm
protected theorem HasMFDerivWithinAt.mfderivWithin (h : HasMFDerivWithinAt I I' f s x f')
- (hxs : UniqueMDiffWithinAt I s x) : mfderivWithin I I' f s x = f' := by
+ (hxs : UniqueMDiffAt[s] x) : mfderivWithin I I' f s x = f' := by
ext
rw [hxs.eq h h.mdifferentiableWithinAt.hasMFDerivWithinAt]
@@ -698,11 +699,11 @@ theorem HasMFDerivWithinAt.mfderivWithin_eq_zero (h : HasMFDerivWithinAt I I' f
exact h.2
theorem MDifferentiable.mfderivWithin (h : MDifferentiableAt I I' f x)
- (hxs : UniqueMDiffWithinAt I s x) : mfderivWithin I I' f s x = mfderiv I I' f x := by
+ (hxs : UniqueMDiffAt[s] x) : mfderivWithin I I' f s x = mfderiv I I' f x := by
apply HasMFDerivWithinAt.mfderivWithin _ hxs
exact h.hasMFDerivAt.hasMFDerivWithinAt
-theorem mfderivWithin_subset (st : s ⊆ t) (hs : UniqueMDiffWithinAt I s x)
+theorem mfderivWithin_subset (st : s ⊆ t) (hs : UniqueMDiffAt[s] x)
(h : MDifferentiableWithinAt I I' f t x) :
mfderivWithin I I' f s x = mfderivWithin I I' f t x :=
((MDifferentiableWithinAt.hasMFDerivWithinAt h).mono st).mfderivWithin hs
@@ -755,7 +756,7 @@ theorem hasMFDerivWithinAt_sdiff_singleton (y : M) :
@[deprecated (since := "2026-06-03")]
alias hasMFDerivWithinAt_diff_singleton := hasMFDerivWithinAt_sdiff_singleton
-theorem mfderivWithin_eq_mfderiv (hs : UniqueMDiffWithinAt I s x) (h : MDifferentiableAt I I' f x) :
+theorem mfderivWithin_eq_mfderiv (hs : UniqueMDiffAt[s] x) (h : MDifferentiableAt I I' f x) :
mfderivWithin I I' f s x = mfderiv I I' f x := by
rw [← mfderivWithin_univ]
exact mfderivWithin_subset (subset_univ _) hs h.mdifferentiableWithinAt
@@ -839,7 +840,7 @@ theorem HasMFDerivAt.continuousAt (h : HasMFDerivAt I I' f x f') : ContinuousAt
h.1
theorem tangentMapWithin_subset {p : TangentBundle I M} (st : s ⊆ t)
- (hs : UniqueMDiffWithinAt I s p.1) (h : MDifferentiableWithinAt I I' f t p.1) :
+ (hs : UniqueMDiffAt[s] p.1) (h : MDifferentiableWithinAt I I' f t p.1) :
tangentMapWithin I I' f s p = tangentMapWithin I I' f t p := by
simp only [tangentMapWithin, mfld_simps]
rw [mfderivWithin_subset st hs h]
@@ -848,7 +849,7 @@ theorem tangentMapWithin_univ : tangentMapWithin I I' f univ = tangentMap I I' f
ext p : 1
simp only [tangentMapWithin, tangentMap, mfld_simps]
-theorem tangentMapWithin_eq_tangentMap {p : TangentBundle I M} (hs : UniqueMDiffWithinAt I s p.1)
+theorem tangentMapWithin_eq_tangentMap {p : TangentBundle I M} (hs : UniqueMDiffAt[s] p.1)
(h : MDifferentiableAt I I' f p.1) : tangentMapWithin I I' f s p = tangentMap I I' f p := by
rw [← mdifferentiableWithinAt_univ] at h
rw [← tangentMapWithin_univ]
@@ -1054,17 +1055,17 @@ theorem MDifferentiableAt.congr_of_eventuallyEq (h : MDifferentiableAt I I' f x)
(h.hasMFDerivAt.congr_of_eventuallyEq hL).mdifferentiableAt
theorem MDifferentiableWithinAt.mfderivWithin_congr_mono (h : MDifferentiableWithinAt I I' f s x)
- (hs : ∀ x ∈ t, f₁ x = f x) (hx : f₁ x = f x) (hxt : UniqueMDiffWithinAt I t x) (h₁ : t ⊆ s) :
+ (hs : ∀ x ∈ t, f₁ x = f x) (hx : f₁ x = f x) (hxt : UniqueMDiffAt[t] x) (h₁ : t ⊆ s) :
mfderivWithin I I' f₁ t x = mfderivWithin I I' f s x :=
(HasMFDerivWithinAt.congr_mono h.hasMFDerivWithinAt hs hx h₁).mfderivWithin hxt
theorem MDifferentiableWithinAt.mfderivWithin_mono (h : MDifferentiableWithinAt I I' f s x)
- (hxt : UniqueMDiffWithinAt I t x) (h₁ : t ⊆ s) :
+ (hxt : UniqueMDiffAt[t] x) (h₁ : t ⊆ s) :
mfderivWithin I I' f t x = mfderivWithin I I' f s x :=
h.mfderivWithin_congr_mono (fun _ _ ↦ rfl) rfl hxt h₁
theorem MDifferentiableWithinAt.mfderivWithin_mono_of_mem_nhdsWithin
- (h : MDifferentiableWithinAt I I' f s x) (hxt : UniqueMDiffWithinAt I t x) (h₁ : s ∈ 𝓝[t] x) :
+ (h : MDifferentiableWithinAt I I' f s x) (hxt : UniqueMDiffAt[t] x) (h₁ : s ∈ 𝓝[t] x) :
mfderivWithin I I' f t x = mfderivWithin I I' f s x :=
(HasMFDerivWithinAt.mono_of_mem_nhdsWithin h.hasMFDerivWithinAt h₁).mfderivWithin hxt
@@ -1207,14 +1208,14 @@ theorem MDifferentiableAt.comp_mdifferentiableWithinAt_of_eq {y : M'}
subst hy; exact hg.comp_mdifferentiableWithinAt _ hf
theorem mfderivWithin_comp (hg : MDifferentiableWithinAt I' I'' g u (f x))
- (hf : MDifferentiableWithinAt I I' f s x) (h : s ⊆ f ⁻¹' u) (hxs : UniqueMDiffWithinAt I s x) :
+ (hf : MDifferentiableWithinAt I I' f s x) (h : s ⊆ f ⁻¹' u) (hxs : UniqueMDiffAt[s] x) :
mfderivWithin I I'' (g ∘ f) s x =
(mfderivWithin I' I'' g u (f x)).comp (mfderivWithin I I' f s x) := by
apply HasMFDerivWithinAt.mfderivWithin _ hxs
exact HasMFDerivWithinAt.comp x hg.hasMFDerivWithinAt hf.hasMFDerivWithinAt h
theorem mfderivWithin_comp_of_eq {x : M} {y : M'} (hg : MDifferentiableWithinAt I' I'' g u y)
- (hf : MDifferentiableWithinAt I I' f s x) (h : s ⊆ f ⁻¹' u) (hxs : UniqueMDiffWithinAt I s x)
+ (hf : MDifferentiableWithinAt I I' f s x) (h : s ⊆ f ⁻¹' u) (hxs : UniqueMDiffAt[s] x)
(hy : f x = y) :
mfderivWithin I I'' (g ∘ f) s x =
(mfderivWithin I' I'' g u y).comp (mfderivWithin I I' f s x) := by
@@ -1223,7 +1224,7 @@ theorem mfderivWithin_comp_of_eq {x : M} {y : M'} (hg : MDifferentiableWithinAt
theorem mfderivWithin_comp_of_preimage_mem_nhdsWithin
(hg : MDifferentiableWithinAt I' I'' g u (f x))
(hf : MDifferentiableWithinAt I I' f s x) (h : f ⁻¹' u ∈ 𝓝[s] x)
- (hxs : UniqueMDiffWithinAt I s x) :
+ (hxs : UniqueMDiffAt[s] x) :
mfderivWithin I I'' (g ∘ f) s x =
(mfderivWithin I' I'' g u (f x)).comp (mfderivWithin I I' f s x) := by
have A : s ∩ f ⁻¹' u ∈ 𝓝[s] x := Filter.inter_mem self_mem_nhdsWithin h
@@ -1238,20 +1239,20 @@ theorem mfderivWithin_comp_of_preimage_mem_nhdsWithin
theorem mfderivWithin_comp_of_preimage_mem_nhdsWithin_of_eq {y : M'}
(hg : MDifferentiableWithinAt I' I'' g u y)
(hf : MDifferentiableWithinAt I I' f s x) (h : f ⁻¹' u ∈ 𝓝[s] x)
- (hxs : UniqueMDiffWithinAt I s x) (hy : f x = y) :
+ (hxs : UniqueMDiffAt[s] x) (hy : f x = y) :
mfderivWithin I I'' (g ∘ f) s x =
(mfderivWithin I' I'' g u y).comp (mfderivWithin I I' f s x) := by
subst hy; exact mfderivWithin_comp_of_preimage_mem_nhdsWithin _ hg hf h hxs
theorem mfderiv_comp_mfderivWithin (hg : MDifferentiableAt I' I'' g (f x))
- (hf : MDifferentiableWithinAt I I' f s x) (hxs : UniqueMDiffWithinAt I s x) :
+ (hf : MDifferentiableWithinAt I I' f s x) (hxs : UniqueMDiffAt[s] x) :
mfderivWithin I I'' (g ∘ f) s x =
(mfderiv I' I'' g (f x)).comp (mfderivWithin I I' f s x) := by
rw [← mfderivWithin_univ]
exact mfderivWithin_comp _ hg.mdifferentiableWithinAt hf (by simp) hxs
theorem mfderiv_comp_mfderivWithin_of_eq {x : M} {y : M'} (hg : MDifferentiableAt I' I'' g y)
- (hf : MDifferentiableWithinAt I I' f s x) (hxs : UniqueMDiffWithinAt I s x) (hy : f x = y) :
+ (hf : MDifferentiableWithinAt I I' f s x) (hxs : UniqueMDiffAt[s] x) (hy : f x = y) :
mfderivWithin I I'' (g ∘ f) s x =
(mfderiv I' I'' g y).comp (mfderivWithin I I' f s x) := by
subst hy; exact mfderiv_comp_mfderivWithin x hg hf hxs
@@ -1291,7 +1292,7 @@ theorem MDifferentiable.comp (hg : MDifferentiable I' I'' g) (hf : MDifferentiab
theorem tangentMapWithin_comp_at (p : TangentBundle I M)
(hg : MDifferentiableWithinAt I' I'' g u (f p.1)) (hf : MDifferentiableWithinAt I I' f s p.1)
- (h : s ⊆ f ⁻¹' u) (hps : UniqueMDiffWithinAt I s p.1) :
+ (h : s ⊆ f ⁻¹' u) (hps : UniqueMDiffAt[s] p.1) :
tangentMapWithin I I'' (g ∘ f) s p =
tangentMapWithin I' I'' g u (tangentMapWithin I I' f s p) := by
simp only [tangentMapWithin, mfld_simps]
diff --git a/Mathlib/Geometry/Manifold/MFDeriv/FDeriv.lean b/Mathlib/Geometry/Manifold/MFDeriv/FDeriv.lean
index 453ec2e412a4a7..ad87a847c3abef 100644
--- a/Mathlib/Geometry/Manifold/MFDeriv/FDeriv.lean
+++ b/Mathlib/Geometry/Manifold/MFDeriv/FDeriv.lean
@@ -29,19 +29,19 @@ variable {𝕜 : Type*} [NontriviallyNormedField 𝕜] {E : Type*} [NormedAddCom
section MFDerivFDeriv
theorem uniqueMDiffWithinAt_iff_uniqueDiffWithinAt :
- UniqueMDiffWithinAt 𝓘(𝕜, E) s x ↔ UniqueDiffWithinAt 𝕜 s x := by
+ UniqueMDiffAt[s] x ↔ UniqueDiffWithinAt 𝕜 s x := by
simp only [UniqueMDiffWithinAt, mfld_simps]
alias ⟨UniqueMDiffWithinAt.uniqueDiffWithinAt, UniqueDiffWithinAt.uniqueMDiffWithinAt⟩ :=
uniqueMDiffWithinAt_iff_uniqueDiffWithinAt
-theorem uniqueMDiffOn_iff_uniqueDiffOn : UniqueMDiffOn 𝓘(𝕜, E) s ↔ UniqueDiffOn 𝕜 s := by
+theorem uniqueMDiffOn_iff_uniqueDiffOn : UniqueMDiff[s] ↔ UniqueDiffOn 𝕜 s := by
simp [UniqueMDiffOn, UniqueDiffOn, uniqueMDiffWithinAt_iff_uniqueDiffWithinAt]
alias ⟨UniqueMDiffOn.uniqueDiffOn, UniqueDiffOn.uniqueMDiffOn⟩ := uniqueMDiffOn_iff_uniqueDiffOn
theorem ModelWithCorners.uniqueMDiffOn {H : Type*} [TopologicalSpace H]
- (I : ModelWithCorners 𝕜 E H) : UniqueMDiffOn 𝓘(𝕜, E) (Set.range I) :=
+ (I : ModelWithCorners 𝕜 E H) : UniqueMDiff[Set.range I] :=
I.uniqueDiffOn.uniqueMDiffOn
@[simp, mfld_simps]
diff --git a/Mathlib/Geometry/Manifold/MFDeriv/NormedSpace.lean b/Mathlib/Geometry/Manifold/MFDeriv/NormedSpace.lean
index 0cc8f598e3708b..1329b9ab42ef3b 100644
--- a/Mathlib/Geometry/Manifold/MFDeriv/NormedSpace.lean
+++ b/Mathlib/Geometry/Manifold/MFDeriv/NormedSpace.lean
@@ -486,27 +486,27 @@ lemma mvfderivWithin_const (c : F) {x : M} : d[s] (fun _ : M ↦ c) x = 0 := by
@[simp, to_fun mvfderivWithin_fun_add]
lemma mvfderivWithin_add {g g' : M → F} {x : M}
- (hg : MDiffAt[s] g x) (hg' : MDiffAt[s] g' x) (hs : UniqueMDiffWithinAt I s x) :
+ (hg : MDiffAt[s] g x) (hg' : MDiffAt[s] g' x) (hs : UniqueMDiffAt[s] x) :
d[s](g + g') x = d[s]g x + d[s]g' x := by
simp [mvfderivWithin, mfderivWithin_add hg hg' hs]
rfl
@[simp, to_fun mvfderivWithin_fun_sub]
lemma mvfderivWithin_sub {g g' : M → F} {x : M}
- (hg : MDiffAt[s] g x) (hg' : MDiffAt[s] g' x) (hs : UniqueMDiffWithinAt I s x) :
+ (hg : MDiffAt[s] g x) (hg' : MDiffAt[s] g' x) (hs : UniqueMDiffAt[s] x) :
d[s](g - g') x = d[s]g x - d[s]g' x := by
simp [mvfderivWithin, mfderivWithin_sub hg hg' hs]
rfl
@[simp, to_fun mvfderivWithin_fun_neg]
-lemma mvfderivWithin_neg {g : M → F} {x : M} (hs : UniqueMDiffWithinAt I s x) :
+lemma mvfderivWithin_neg {g : M → F} {x : M} (hs : UniqueMDiffAt[s] x) :
d[s](-g) x = -d[s]g x := by
simp [mvfderivWithin, mfderivWithin_neg hs]
rfl
@[simp, to_fun mvfderivWithin_fun_smul]
lemma mvfderivWithin_smul {a : M → 𝕜} (ha : MDiffAt[s] a x) {g : M → F} (hg : MDiffAt[s] g x)
- (hs : UniqueMDiffWithinAt I s x) :
+ (hs : UniqueMDiffAt[s] x) :
d[s](a • g) x =
a x • d[s] g x + (d[s] a x).smulRight (g x) := by
refine HasMFDerivWithinAt.mfderivWithin ⟨ha.1.smul hg.1, ?_⟩ hs
@@ -516,14 +516,14 @@ lemma mvfderivWithin_smul {a : M → 𝕜} (ha : MDiffAt[s] a x) {g : M → F} (
@[simp, to_fun mvfderivWithin_fun_mul]
lemma mvfderivWithin_mul {f g : M → 𝕜} {x : M} (hf : MDiffAt[s] f x) (hg : MDiffAt[s] g x)
- (hs : UniqueMDiffWithinAt I s x) :
+ (hs : UniqueMDiffAt[s] x) :
d[s](f * g) x = f x • d[s]g x + (g x) • (d[s]f x) := by
convert! mvfderivWithin_smul hf hg hs
ext v
simp [mul_comm]
@[simp]
-lemma mvfderivWithin_zero {s : Set M} (hs : UniqueMDiffWithinAt I s x) :
+lemma mvfderivWithin_zero {s : Set M} (hs : UniqueMDiffAt[s] x) :
d[s] (0 : M → F) x = 0 := by
have : d[s] (0 : M → F) x + d[s] (0 : M → F) x = d[s] (0 : M → F) x := by
rw [← mvfderivWithin_add (by exact mdifferentiableWithinAt_const)
diff --git a/Mathlib/Geometry/Manifold/MFDeriv/SpecificFunctions.lean b/Mathlib/Geometry/Manifold/MFDeriv/SpecificFunctions.lean
index 60b917645a0878..1ab6f3612e7c95 100644
--- a/Mathlib/Geometry/Manifold/MFDeriv/SpecificFunctions.lean
+++ b/Mathlib/Geometry/Manifold/MFDeriv/SpecificFunctions.lean
@@ -83,7 +83,7 @@ protected theorem mdifferentiable : MDiff f :=
theorem mfderiv_eq : mfderiv% f x = f :=
f.hasMFDerivAt.mfderiv
-theorem mfderivWithin_eq (hs : UniqueMDiffWithinAt 𝓘(𝕜, E) s x) : mfderiv[s] f x = f :=
+theorem mfderivWithin_eq (hs : UniqueMDiffAt[s] x) : mfderiv[s] f x = f :=
f.hasMFDerivWithinAt.mfderivWithin hs
end ContinuousLinearMap
@@ -113,7 +113,7 @@ protected theorem mdifferentiable : MDiff f :=
theorem mfderiv_eq : mfderiv% f x = (f : E →L[𝕜] E') :=
f.hasMFDerivAt.mfderiv
-theorem mfderivWithin_eq (hs : UniqueMDiffWithinAt 𝓘(𝕜, E) s x) :
+theorem mfderivWithin_eq (hs : UniqueMDiffAt[s] x) :
mfderiv[s] f x = (f : E →L[𝕜] E') :=
f.hasMFDerivWithinAt.mfderivWithin hs
@@ -153,7 +153,7 @@ theorem mdifferentiableOn_id : MDiff[s] (@id M) :=
theorem mfderiv_id : mfderiv% (@id M) x = ContinuousLinearMap.id 𝕜 (TangentSpace% x) :=
(hasMFDerivAt_id x).mfderiv
-theorem mfderivWithin_id (hxs : UniqueMDiffWithinAt I s x) :
+theorem mfderivWithin_id (hxs : UniqueMDiffAt[s] x) :
mfderiv[s] (@id M) x = ContinuousLinearMap.id 𝕜 (TangentSpace% x) := by
rw [MDifferentiable.mfderivWithin mdifferentiableAt_id hxs]
exact mfderiv_id
@@ -162,7 +162,7 @@ set_option backward.isDefEq.respectTransparency false in
@[simp, mfld_simps]
theorem tangentMap_id : tangentMap% (@id M) = id := by ext1 ⟨x, v⟩; simp [tangentMap]
-theorem tangentMapWithin_id {p : TangentBundle I M} (hs : UniqueMDiffWithinAt I s p.proj) :
+theorem tangentMapWithin_id {p : TangentBundle I M} (hs : UniqueMDiffAt[s] p.proj) :
tangentMap[s] (id : M → M) p = p := by
simp only [tangentMapWithin, id]
rw [mfderivWithin_id]
@@ -227,7 +227,7 @@ theorem HasMFDerivWithinAt.prodMk {f : M → M'} {g : M → M''}
⟨hf.1.prodMk hg.1, hf.2.prodMk hg.2⟩
lemma mfderivWithin_prodMk {f : M → M'} {g : M → M''} (hf : MDiffAt[s] f x) (hg : MDiffAt[s] g x)
- (hs : UniqueMDiffWithinAt I s x) :
+ (hs : UniqueMDiffAt[s] x) :
mfderiv[s] (fun x ↦ (f x, g x)) x = (mfderiv[s] f x).prod (mfderiv[s] g x) :=
(hf.hasMFDerivWithinAt.prodMk hg.hasMFDerivWithinAt).mfderivWithin hs
@@ -316,7 +316,7 @@ theorem mfderiv_fst {x : M × M'} :
(hasMFDerivAt_fst x).mfderiv
theorem mfderivWithin_fst {s : Set (M × M')} {x : M × M'}
- (hxs : UniqueMDiffWithinAt (I.prod I') s x) :
+ (hxs : UniqueMDiffAt[s] x) :
mfderiv[s] (@Prod.fst M M') x =
ContinuousLinearMap.fst 𝕜 (TangentSpace% x.1) (TangentSpace% x.2) := by
rw [MDifferentiable.mfderivWithin mdifferentiableAt_fst hxs]; exact mfderiv_fst
@@ -327,7 +327,7 @@ theorem tangentMap_prodFst {p : TangentBundle (I.prod I') (M × M')} :
simp [tangentMap]; rfl
theorem tangentMapWithin_prodFst {s : Set (M × M')} {p : TangentBundle (I.prod I') (M × M')}
- (hs : UniqueMDiffWithinAt (I.prod I') s p.proj) :
+ (hs : UniqueMDiffAt[s] p.proj) :
tangentMap[s] (@Prod.fst M M') p = ⟨p.proj.1, p.2.1⟩ := by
simp only [tangentMapWithin]
rw [mfderivWithin_fst]
@@ -375,7 +375,7 @@ theorem mfderiv_snd {x : M × M'} :
(hasMFDerivAt_snd x).mfderiv
theorem mfderivWithin_snd {s : Set (M × M')} {x : M × M'}
- (hxs : UniqueMDiffWithinAt (I.prod I') s x) :
+ (hxs : UniqueMDiffAt[s] x) :
mfderiv[s] (@Prod.snd M M') x =
ContinuousLinearMap.snd 𝕜 (TangentSpace% x.1) (TangentSpace% x.2) := by
rw [MDifferentiable.mfderivWithin mdifferentiableAt_snd hxs]; exact mfderiv_snd
@@ -516,7 +516,7 @@ lemma HasMFDerivAt.prodMap {p : M × M'} {f : M → N} {g : M' → N'}
-- could be a strict superset of `s`.
lemma mfderivWithin_prodMap {p : M × M'} {t : Set M'} {f : M → N} {g : M' → N'}
(hf : MDiffAt[s] f p.1) (hg : MDiffAt[t] g p.2)
- (hs : UniqueMDiffWithinAt I s p.1) (ht : UniqueMDiffWithinAt I' t p.2) :
+ (hs : UniqueMDiffAt[s] p.1) (ht : UniqueMDiffAt[t] p.2) :
mfderiv[s ×ˢ t] (Prod.map f g) p = (mfderiv[s] f p.1).prodMap (mfderiv[t] g p.2) := by
have hf' : HasMFDerivAt[Prod.fst '' s ×ˢ t] f p.1 (mfderiv[s] f p.1) :=
hf.hasMFDerivWithinAt.mono (by grind)
@@ -539,7 +539,7 @@ theorem tangentMap_prodSnd {p : TangentBundle (I.prod I') (M × M')} :
simp [tangentMap]; rfl
theorem tangentMapWithin_prodSnd {s : Set (M × M')} {p : TangentBundle (I.prod I') (M × M')}
- (hs : UniqueMDiffWithinAt (I.prod I') s p.proj) :
+ (hs : UniqueMDiffAt[s] p.proj) :
tangentMap[s] (@Prod.snd M M') p = ⟨p.proj.2, p.2.2⟩ := by
simp only [tangentMapWithin]
rw [mfderivWithin_snd hs]
@@ -671,7 +671,7 @@ theorem hasMFDerivAt_sumSwap :
cases p <;> simp
@[simp]
-theorem mfderivWithin_sumSwap {s : Set (M ⊕ M')} (hs : UniqueMDiffWithinAt I s p) :
+theorem mfderivWithin_sumSwap {s : Set (M ⊕ M')} (hs : UniqueMDiffAt[s] p) :
mfderiv[s] (@Sum.swap M M') p = ContinuousLinearMap.id 𝕜 (TangentSpace% p) :=
hasMFDerivAt_sumSwap.hasMFDerivWithinAt.mfderivWithin hs
@@ -732,7 +732,7 @@ theorem hasMFDerivAt_inr :
HasMFDerivAt% (@Sum.inr M M') q' (ContinuousLinearMap.id 𝕜 (TangentSpace% p)) := by
simpa [HasMFDerivAt, hasMFDerivWithinAt_univ] using! hasMFDerivWithinAt_inr (t := Set.univ)
-theorem mfderivWithin_sumInl (hU : UniqueMDiffWithinAt I s q) :
+theorem mfderivWithin_sumInl (hU : UniqueMDiffAt[s] q) :
mfderiv[s] (@Sum.inl M M') q = ContinuousLinearMap.id 𝕜 (TangentSpace% p) :=
hasMFDerivWithinAt_inl.mfderivWithin hU
@@ -740,7 +740,7 @@ theorem mfderiv_sumInl :
mfderiv% (@Sum.inl M M') q = ContinuousLinearMap.id 𝕜 (TangentSpace% p) := by
simpa [mfderivWithin_univ] using (mfderivWithin_sumInl (uniqueMDiffWithinAt_univ I))
-theorem mfderivWithin_sumInr {t : Set M'} (hU : UniqueMDiffWithinAt I t q') :
+theorem mfderivWithin_sumInr {t : Set M'} (hU : UniqueMDiffAt[t] q') :
mfderiv[t] (@Sum.inr M M') q' = ContinuousLinearMap.id 𝕜 (TangentSpace% q') :=
hasMFDerivWithinAt_inr.mfderivWithin hU
@@ -794,7 +794,7 @@ theorem mfderiv_add (hf : MDiffAt f z) (hg : MDiffAt g z) :
(hf.hasMFDerivAt.add hg.hasMFDerivAt).mfderiv
theorem mfderivWithin_add (hf : MDiffAt[s] f z) (hg : MDiffAt[s] g z)
- (hs : UniqueMDiffWithinAt I s z) :
+ (hs : UniqueMDiffAt[s] z) :
(mfderiv[s] (f + g) z : TangentSpace% z →L[𝕜] E') =
(by exact mfderiv[s] f z) + (by exact mfderiv[s] g z) :=
(hf.hasMFDerivWithinAt.add hg.hasMFDerivWithinAt).mfderivWithin hs
@@ -881,7 +881,7 @@ theorem mdifferentiableAt_neg : MDiffAt (-f) z ↔ MDiffAt f z :=
theorem MDifferentiable.neg (hf : MDiff f) : MDiff (-f) := fun x ↦ (hf x).neg
set_option backward.isDefEq.respectTransparency false in
-theorem mfderivWithin_neg (hs : UniqueMDiffWithinAt I s x) :
+theorem mfderivWithin_neg (hs : UniqueMDiffAt[s] x) :
mfderiv[s] (-f) x = -mfderiv[s] f x := by
simp_rw [mfderivWithin]
by_cases hf : MDiffAt[s] f x
@@ -914,7 +914,7 @@ theorem MDifferentiable.sub (hf : MDiff f) (hg : MDiff g) : MDiff (f - g) :=
fun x ↦ (hf x).sub (hg x)
theorem mfderivWithin_sub (hf : MDiffAt[s] f z) (hg : MDiffAt[s] g z)
- (hs : UniqueMDiffWithinAt I s z) :
+ (hs : UniqueMDiffAt[s] z) :
(mfderiv[s] (f - g) z : TangentSpace% z →L[𝕜] E') =
(by exact mfderiv[s] f z) - (by exact mfderiv[s] g z) :=
(hf.hasMFDerivWithinAt.sub hg.hasMFDerivWithinAt).mfderivWithin hs
diff --git a/Mathlib/Geometry/Manifold/MFDeriv/UniqueDifferential.lean b/Mathlib/Geometry/Manifold/MFDeriv/UniqueDifferential.lean
index 87483b86150d63..3e0ad20b32c3a2 100644
--- a/Mathlib/Geometry/Manifold/MFDeriv/UniqueDifferential.lean
+++ b/Mathlib/Geometry/Manifold/MFDeriv/UniqueDifferential.lean
@@ -45,9 +45,9 @@ section
/-- If `s` has the unique differential property at `x`, `f` is differentiable within `s` at `x` and
its derivative has dense range, then `f '' s` has the unique differential property at `f x`. -/
-theorem UniqueMDiffWithinAt.image_denseRange (hs : UniqueMDiffWithinAt I s x)
+theorem UniqueMDiffWithinAt.image_denseRange (hs : UniqueMDiffAt[s] x)
{f : M → M'} {f' : E →L[𝕜] E'} (hf : HasMFDerivAt[s] f x f')
- (hd : DenseRange f') : UniqueMDiffWithinAt I' (f '' s) (f x) := by
+ (hd : DenseRange f') : UniqueMDiffAt[f '' s] (f x) := by
/- Rewrite in coordinates, apply `HasFDerivWithinAt.uniqueDiffWithinAt`. -/
have := hs.inter' <| hf.1 (extChartAt_source_mem_nhds (I := I') (f x))
refine (((hf.2.mono ?sub1).uniqueDiffWithinAt this hd).mono ?sub2).congr_pt ?pt
@@ -60,22 +60,22 @@ theorem UniqueMDiffWithinAt.image_denseRange (hs : UniqueMDiffWithinAt I s x)
/-- If `s` has the unique differential property, `f` is differentiable on `s` and its derivative
at every point of `s` has dense range, then `f '' s` has the unique differential property.
This version uses the `HasMFDerivWithinAt` predicate. -/
-theorem UniqueMDiffOn.image_denseRange' (hs : UniqueMDiffOn I s) {f : M → M'}
+theorem UniqueMDiffOn.image_denseRange' (hs : UniqueMDiff[s]) {f : M → M'}
{f' : M → E →L[𝕜] E'} (hf : ∀ x ∈ s, HasMFDerivAt[s] f x (f' x))
(hd : ∀ x ∈ s, DenseRange (f' x)) :
- UniqueMDiffOn I' (f '' s) :=
+ UniqueMDiff[f '' s] :=
forall_mem_image.2 fun x hx ↦ (hs x hx).image_denseRange (hf x hx) (hd x hx)
/-- If `s` has the unique differential property, `f` is differentiable on `s` and its derivative
at every point of `s` has dense range, then `f '' s` has the unique differential property. -/
-theorem UniqueMDiffOn.image_denseRange (hs : UniqueMDiffOn I s) {f : M → M'}
+theorem UniqueMDiffOn.image_denseRange (hs : UniqueMDiff[s]) {f : M → M'}
(hf : MDiff[s] f) (hd : ∀ x ∈ s, DenseRange (mfderiv[s] f x)) :
- UniqueMDiffOn I' (f '' s) :=
+ UniqueMDiff[f '' s] :=
hs.image_denseRange' (fun x hx ↦ (hf x hx).hasMFDerivWithinAt) hd
protected theorem UniqueMDiffWithinAt.preimage_openPartialHomeomorph
- (hs : UniqueMDiffWithinAt I s x) {e : OpenPartialHomeomorph M M'} (he : e.MDifferentiable I I')
- (hx : x ∈ e.source) : UniqueMDiffWithinAt I' (e.target ∩ e.symm ⁻¹' s) (e x) := by
+ (hs : UniqueMDiffAt[s] x) {e : OpenPartialHomeomorph M M'} (he : e.MDifferentiable I I')
+ (hx : x ∈ e.source) : UniqueMDiffAt[e.target ∩ e.symm ⁻¹' s] (e x) := by
rw [← e.image_source_inter_eq', inter_comm]
exact (hs.inter (e.open_source.mem_nhds hx)).image_denseRange
(he.mdifferentiableAt hx).hasMFDerivAt.hasMFDerivWithinAt
@@ -83,16 +83,16 @@ protected theorem UniqueMDiffWithinAt.preimage_openPartialHomeomorph
/-- If a set has the unique differential property, then its image under a local
diffeomorphism also has the unique differential property. -/
-theorem UniqueMDiffOn.uniqueMDiffOn_preimage (hs : UniqueMDiffOn I s)
+theorem UniqueMDiffOn.uniqueMDiffOn_preimage (hs : UniqueMDiff[s])
{e : OpenPartialHomeomorph M M'} (he : e.MDifferentiable I I') :
- UniqueMDiffOn I' (e.target ∩ e.symm ⁻¹' s) := fun _x hx ↦
+ UniqueMDiff[e.target ∩ e.symm ⁻¹' s] := fun _x hx ↦
e.right_inv hx.1 ▸ (hs _ hx.2).preimage_openPartialHomeomorph he (e.map_target hx.1)
variable [IsManifold I 1 M] in
/-- If a set in a manifold has the unique derivative property, then its pullback by any extended
chart, in the vector space, also has the unique derivative property. -/
-theorem UniqueMDiffOn.uniqueMDiffOn_target_inter (hs : UniqueMDiffOn I s) (x : M) :
- UniqueMDiffOn 𝓘(𝕜, E) ((extChartAt I x).target ∩ (extChartAt I x).symm ⁻¹' s) := by
+theorem UniqueMDiffOn.uniqueMDiffOn_target_inter (hs : UniqueMDiff[s]) (x : M) :
+ UniqueMDiff[(extChartAt I x).target ∩ (extChartAt I x).symm ⁻¹' s] := by
-- this is just a reformulation of `UniqueMDiffOn.uniqueMDiffOn_preimage`, using as `e`
-- the local chart at `x`.
rw [← PartialEquiv.image_source_inter_eq', inter_comm, extChartAt_source]
@@ -103,12 +103,12 @@ theorem UniqueMDiffOn.uniqueMDiffOn_target_inter (hs : UniqueMDiffOn I s) (x : M
variable [IsManifold I 1 M] in
/-- If a set in a manifold has the unique derivative property, then its pullback by any extended
chart, in the vector space, also has the unique derivative property. -/
-theorem UniqueMDiffOn.uniqueDiffOn_target_inter (hs : UniqueMDiffOn I s) (x : M) :
+theorem UniqueMDiffOn.uniqueDiffOn_target_inter (hs : UniqueMDiff[s]) (x : M) :
UniqueDiffOn 𝕜 ((extChartAt I x).target ∩ (extChartAt I x).symm ⁻¹' s) :=
(hs.uniqueMDiffOn_target_inter x).uniqueDiffOn
variable [IsManifold I 1 M] in
-theorem UniqueMDiffOn.uniqueDiffWithinAt_range_inter (hs : UniqueMDiffOn I s) (x : M) (y : E)
+theorem UniqueMDiffOn.uniqueDiffWithinAt_range_inter (hs : UniqueMDiff[s]) (x : M) (y : E)
(hy : y ∈ (extChartAt I x).target ∩ (extChartAt I x).symm ⁻¹' s) :
UniqueDiffWithinAt 𝕜 (range I ∩ (extChartAt I x).symm ⁻¹' s) y := by
apply (hs.uniqueDiffOn_target_inter x y hy).mono
@@ -118,11 +118,11 @@ variable [IsManifold I 1 M] in
/-- When considering functions between manifolds, this statement shows up often. It entails
the unique differential of the pullback in extended charts of the set where the function can
be read in the charts. -/
-theorem UniqueMDiffOn.uniqueDiffOn_inter_preimage (hs : UniqueMDiffOn I s) (x : M) (y : M'')
+theorem UniqueMDiffOn.uniqueDiffOn_inter_preimage (hs : UniqueMDiff[s]) (x : M) (y : M'')
{f : M → M''} (hf : ContinuousOn f s) :
UniqueDiffOn 𝕜
((extChartAt I x).target ∩ (extChartAt I x).symm ⁻¹' (s ∩ f ⁻¹' (extChartAt I' y).source)) :=
- haveI : UniqueMDiffOn I (s ∩ f ⁻¹' (extChartAt I' y).source) := by
+ haveI : UniqueMDiff[s ∩ f ⁻¹' (extChartAt I' y).source] := by
intro z hz
apply (hs z hz.1).inter'
apply (hf z hz.1).preimage_mem_nhdsWithin
@@ -138,7 +138,7 @@ variable {F : Type*} [NormedAddCommGroup F] [NormedSpace 𝕜 F] {Z : M → Type
set_option backward.isDefEq.respectTransparency false in
private lemma UniqueMDiffWithinAt.bundle_preimage_aux {p : TotalSpace F Z}
- (hs : UniqueMDiffWithinAt I s p.proj) (h's : s ⊆ (trivializationAt F Z p.proj).baseSet) :
+ (hs : UniqueMDiffAt[s] p.proj) (h's : s ⊆ (trivializationAt F Z p.proj).baseSet) :
UniqueMDiffWithinAt (I.prod 𝓘(𝕜, F)) (π F Z ⁻¹' s) p := by
suffices ((extChartAt I p.proj).symm ⁻¹' s ∩ range I) ×ˢ univ ⊆
(extChartAt (I.prod 𝓘(𝕜, F)) p).symm ⁻¹' (TotalSpace.proj ⁻¹' s) ∩ range (I.prod 𝓘(𝕜, F)) by
@@ -170,7 +170,7 @@ private lemma UniqueMDiffWithinAt.bundle_preimage_aux {p : TotalSpace F Z}
/-- In a fiber bundle, the preimage under the projection of a set with unique differentials
in the base has unique differentials in the bundle. -/
theorem UniqueMDiffWithinAt.bundle_preimage {p : TotalSpace F Z}
- (hs : UniqueMDiffWithinAt I s p.proj) :
+ (hs : UniqueMDiffAt[s] p.proj) :
UniqueMDiffWithinAt (I.prod 𝓘(𝕜, F)) (π F Z ⁻¹' s) p := by
suffices UniqueMDiffWithinAt (I.prod 𝓘(𝕜, F))
(π F Z ⁻¹' (s ∩ (trivializationAt F Z p.proj).baseSet)) p from this.mono (by simp)
@@ -182,13 +182,13 @@ variable (Z)
/-- In a fiber bundle, the preimage under the projection of a set with unique differentials
in the base has unique differentials in the bundle. Version with a point `⟨b, x⟩`. -/
-theorem UniqueMDiffWithinAt.bundle_preimage' {b : M} (hs : UniqueMDiffWithinAt I s b)
+theorem UniqueMDiffWithinAt.bundle_preimage' {b : M} (hs : UniqueMDiffAt[s] b)
(x : Z b) : UniqueMDiffWithinAt (I.prod 𝓘(𝕜, F)) (π F Z ⁻¹' s) ⟨b, x⟩ :=
hs.bundle_preimage (p := ⟨b, x⟩)
/-- In a fiber bundle, the preimage under the projection of a set with unique differentials
in the base has unique differentials in the bundle. -/
-theorem UniqueMDiffOn.bundle_preimage (hs : UniqueMDiffOn I s) :
+theorem UniqueMDiffOn.bundle_preimage (hs : UniqueMDiff[s]) :
UniqueMDiffOn (I.prod 𝓘(𝕜, F)) (π F Z ⁻¹' s) := fun _p hp ↦
(hs _ hp).bundle_preimage
diff --git a/Mathlib/Geometry/Manifold/Notation.lean b/Mathlib/Geometry/Manifold/Notation.lean
index 24e6737558c2c5..c742d0a4e9df21 100644
--- a/Mathlib/Geometry/Manifold/Notation.lean
+++ b/Mathlib/Geometry/Manifold/Notation.lean
@@ -40,6 +40,8 @@ including inference of the model with corners.
| `TangentSpace% x` | `TangentSpace I x` |
| `tangentMap[s] f` | `tangentMapWithin I J f s` |
| `tangentMap% f` | `tangentMap I J f` |
+| `UniqueMDiff[s]` | `UniqueMDiffOn I s` |
+| `UniqueMDiffAt[s] x` | `UniqueMDiffWithinAt I s x` |
In each of these cases, the models with corners are inferred from the domain and codomain of `f`.
The search for models with corners uses the local context and is (almost) only based on expression
@@ -967,6 +969,29 @@ scoped elab:max "tangentMap%" ppSpace f:term:arg : term => do
let (srcI, tgtI) ← findModels ef none
mkAppM ``tangentMap #[srcI, tgtI, ef]
+/-- `UniqueMDiff[s]` elaborates to `UniqueMDiffOn I s`,
+trying to determine `I` from the local context. -/
+scoped elab:max "UniqueMDiff[" s:term "]" : term => do
+ let es ← Term.elabTerm s none
+ let estype : Expr ← inferType es
+ match_expr estype with
+ | Set α =>
+ let I ← findModel α
+ mkAppM ``UniqueMDiffOn #[I, es]
+ | _ => throwError "`{es}` has type `{estype}` which is not of the form `Set α` for some `α`."
+
+/-- `UniqueMDiffAt[s] x` elaborates to `UniqueMDiffWithinAt I s x`
+trying to determine `I` from the local context.
+The argument `x` can be omitted. -/
+scoped elab:max "UniqueMDiffAt[" s:term "]" : term => do
+ let es ← Term.elabTerm s none
+ let estype : Expr ← inferType es
+ match_expr estype with
+ | Set α =>
+ let I ← findModel α
+ mkAppM ``UniqueMDiffWithinAt #[I, es]
+ | _ => throwError "`{es}` has type `{estype}` which is not of the form `Set α` for some `α`."
+
end Manifold
section trace
@@ -1126,6 +1151,20 @@ arguments that can use the `T%` elaborator. -/
let fs ← withNaryArg 20 <| delab
`(MDiffAt[$ss] $fs) >>= annotateGoToSyntaxDef
+/-- Delaborator for `UniqueMDiffOn` using the custom elaborator. -/
+@[app_delab UniqueMDiffOn] meta def delabUniqueMDiffOn : Delab := do
+ whenPPOption getPPNotation do
+ withOverApp 12 do
+ let ss ← withAppArg delab
+ `(UniqueMDiff[$ss]) >>= annotateGoToSyntaxDef
+
+/-- Delaborator for `UniqueMDiffWithinAt` using the custom elaborator. -/
+@[app_delab UniqueMDiffWithinAt] meta def delabUniqueMDiffWithinAt : Delab := do
+ whenPPOption getPPNotation do
+ withOverApp 12 do
+ let ss ← withAppArg delab
+ `(UniqueMDiffAt[$ss]) >>= annotateGoToSyntaxDef
+
-- TODO: add more delaborators (and tests) for
-- ContMDiff, ContMDiffOn, ContMDiffAt, ContMDiffWithinAt, HasMFDerivAt, HasMFDerivWithinAt
diff --git a/Mathlib/Geometry/Manifold/VectorField/LieBracket.lean b/Mathlib/Geometry/Manifold/VectorField/LieBracket.lean
index d107d81b0c2720..40c611771099de 100644
--- a/Mathlib/Geometry/Manifold/VectorField/LieBracket.lean
+++ b/Mathlib/Geometry/Manifold/VectorField/LieBracket.lean
@@ -360,7 +360,7 @@ Product rule for Lie brackets: given two vector fields `V` and `W` on `M` and a
-/
lemma mlieBracketWithin_smul_right {f : M → 𝕜} (hf : MDiffAt[s] f x)
(hW : MDiffAt[s] (fun x ↦ (W x : TangentBundle I M)) x)
- (hs : UniqueMDiffWithinAt I s x) :
+ (hs : UniqueMDiffAt[s] x) :
mlieBracketWithin I V (f • W) s x =
d[s] f x (V x) • (W x) + (f x) • mlieBracketWithin I V W s x := by
simp only [mlieBracketWithin, mpullbackWithin_smul]
@@ -401,7 +401,7 @@ Product rule for Lie brackets: given two vector fields `V` and `W` on `M` and a
-/
lemma mlieBracketWithin_smul_left {f : M → 𝕜} (hf : MDiffAt[s] f x)
(hV : MDiffAt[s] (fun x ↦ (V x : TangentBundle I M)) x)
- (hs : UniqueMDiffWithinAt I s x) :
+ (hs : UniqueMDiffAt[s] x) :
mlieBracketWithin I (f • V) W s x =
- d[s] f x (W x) • (V x) + (f x) • mlieBracketWithin I V W s x := by
rw [mlieBracketWithin_swap, Pi.neg_apply, mlieBracketWithin_smul_right hf hV (V := W) hs,
@@ -420,7 +420,7 @@ lemma mlieBracket_smul_left {f : M → 𝕜} (hf : MDiffAt f x)
exact mlieBracketWithin_smul_left hf hV (uniqueMDiffWithinAt_univ I)
lemma mlieBracketWithin_const_smul_left
- (hV : MDiffAt[s] (T% V) x) (hs : UniqueMDiffWithinAt I s x) :
+ (hV : MDiffAt[s] (T% V) x) (hs : UniqueMDiffAt[s] x) :
mlieBracketWithin I (c • V) W s x = c • mlieBracketWithin I V W s x := by
simpa [mfderivWithin_const, mvfderivWithin] using!
mlieBracketWithin_smul_left (mdifferentiableWithinAt_const (c := c)) (W := W) hV hs
@@ -431,7 +431,7 @@ lemma mlieBracket_const_smul_left (hV : MDiffAt (T% V) x) :
exact mlieBracketWithin_const_smul_left hV (uniqueMDiffWithinAt_univ _)
lemma mlieBracketWithin_const_smul_right
- (hW : MDiffAt[s] (T% W) x) (hs : UniqueMDiffWithinAt I s x) :
+ (hW : MDiffAt[s] (T% W) x) (hs : UniqueMDiffAt[s] x) :
mlieBracketWithin I V (c • W) s x = c • mlieBracketWithin I V W s x := by
simpa [mfderivWithin_const, mvfderivWithin] using!
mlieBracketWithin_smul_right (mdifferentiableWithinAt_const (c := c)) (V := V) hW hs
@@ -443,7 +443,7 @@ lemma mlieBracket_const_smul_right (hW : MDiffAt (T% W) x) :
set_option backward.isDefEq.respectTransparency false in
lemma mlieBracketWithin_add_left
- (hV : MDiffAt[s] (T% V) x) (hV₁ : MDiffAt[s] (T% V₁) x) (hs : UniqueMDiffWithinAt I s x) :
+ (hV : MDiffAt[s] (T% V) x) (hV₁ : MDiffAt[s] (T% V₁) x) (hs : UniqueMDiffAt[s] x) :
mlieBracketWithin I (V + V₁) W s x =
mlieBracketWithin I V W s x + mlieBracketWithin I V₁ W s x := by
simp only [mlieBracketWithin_apply]
@@ -458,7 +458,7 @@ lemma mlieBracket_add_left (hV : MDiffAt (T% V) x) (hV₁ : MDiffAt (T% V₁) x)
exact mlieBracketWithin_add_left hV hV₁ (uniqueMDiffWithinAt_univ _)
lemma mlieBracketWithin_add_right
- (hW : MDiffAt[s] (T% W) x) (hW₁ : MDiffAt[s] (T% W₁) x) (hs : UniqueMDiffWithinAt I s x) :
+ (hW : MDiffAt[s] (T% W) x) (hW₁ : MDiffAt[s] (T% W₁) x) (hs : UniqueMDiffAt[s] x) :
mlieBracketWithin I V (W + W₁) s x =
mlieBracketWithin I V W s x + mlieBracketWithin I V W₁ s x := by
rw [mlieBracketWithin_swap, Pi.neg_apply, mlieBracketWithin_add_left hW hW₁ hs,
@@ -470,7 +470,7 @@ lemma mlieBracket_add_right (hW : MDiffAt (T% W) x) (hW₁ : MDiffAt (T% W₁) x
simp only [← mlieBracketWithin_univ] at hW hW₁ ⊢
exact mlieBracketWithin_add_right hW hW₁ (uniqueMDiffWithinAt_univ _)
-theorem mlieBracketWithin_of_mem_nhdsWithin (st : t ∈ 𝓝[s] x) (hs : UniqueMDiffWithinAt I s x)
+theorem mlieBracketWithin_of_mem_nhdsWithin (st : t ∈ 𝓝[s] x) (hs : UniqueMDiffAt[s] x)
(hV : MDiffAt[t] (T% V) x) (hW : MDiffAt[t] (T% W) x) :
mlieBracketWithin I V W s x = mlieBracketWithin I V W t x := by
simp only [mlieBracketWithin_apply]
@@ -484,12 +484,12 @@ theorem mlieBracketWithin_of_mem_nhdsWithin (st : t ∈ 𝓝[s] x) (hs : UniqueM
· exact hV.differentiableWithinAt_mpullbackWithin_vectorField
· exact hW.differentiableWithinAt_mpullbackWithin_vectorField
-theorem mlieBracketWithin_subset (st : s ⊆ t) (ht : UniqueMDiffWithinAt I s x)
+theorem mlieBracketWithin_subset (st : s ⊆ t) (ht : UniqueMDiffAt[s] x)
(hV : MDiffAt[t] (T% V) x) (hW : MDiffAt[t] (T% W) x) :
mlieBracketWithin I V W s x = mlieBracketWithin I V W t x :=
mlieBracketWithin_of_mem_nhdsWithin (nhdsWithin_mono _ st self_mem_nhdsWithin) ht hV hW
-theorem mlieBracketWithin_eq_mlieBracket (hs : UniqueMDiffWithinAt I s x)
+theorem mlieBracketWithin_eq_mlieBracket (hs : UniqueMDiffAt[s] x)
(hV : MDiffAt (T% V) x) (hW : MDiffAt (T% W) x) :
mlieBracketWithin I V W s x = mlieBracket I V W x := by
simp only [← mlieBracketWithin_univ, ← mdifferentiableWithinAt_univ] at hV hW ⊢
@@ -498,7 +498,7 @@ theorem mlieBracketWithin_eq_mlieBracket (hs : UniqueMDiffWithinAt I s x)
theorem _root_.DifferentiableWithinAt.mlieBracketWithin_congr_mono
(hV : MDiffAt[s] (T% V) x) (hVs : EqOn V₁ V t) (hVx : V₁ x = V x)
(hW : MDiffAt[s] (T% W) x) (hWs : EqOn W₁ W t) (hWx : W₁ x = W x)
- (hxt : UniqueMDiffWithinAt I t x) (h₁ : t ⊆ s) :
+ (hxt : UniqueMDiffAt[t] x) (h₁ : t ⊆ s) :
mlieBracketWithin I V₁ W₁ t x = mlieBracketWithin I V W s x := by
rw [mlieBracketWithin_congr hVs hVx hWs hWx]
exact mlieBracketWithin_subset h₁ hxt hV hW
@@ -516,7 +516,7 @@ in chart domains. -/
private lemma mpullbackWithin_mlieBracketWithin_aux [CompleteSpace E']
{f : M → M'} {V W : Π (x : M'), TangentSpace I' x} {x₀ : M} {s : Set M} {t : Set M'}
(hV : MDiffAt[t] (T% V) (f x₀)) (hW : MDiffAt[t] (T% W) (f x₀))
- (hu : UniqueMDiffOn I s) (hf : CMDiff[s] 2 f) (hx₀ : x₀ ∈ s)
+ (hu : UniqueMDiff[s]) (hf : CMDiff[s] 2 f) (hx₀ : x₀ ∈ s)
(ht : t ⊆ (extChartAt I' (f x₀)).source) (hst : MapsTo f s t)
(hsymm : IsSymmSndFDerivWithinAt 𝕜 ((extChartAt I' (f x₀)) ∘ f ∘ (extChartAt I x₀).symm)
((extChartAt I x₀).symm ⁻¹' s ∩ range I) (extChartAt I x₀ x₀)) :
@@ -578,7 +578,7 @@ private lemma mpullbackWithin_mlieBracketWithin_aux [CompleteSpace E']
filter_upwards [self_mem_nhdsWithin, this] with y hy h'''y
have h'y : f ((extChartAt I x₀).symm y) ∈ (extChartAt I' (f x₀)).source := ht (hst hy.1)
have h''y : f ((extChartAt I x₀).symm y) ∈ (chartAt H' (f x₀)).source := by simpa using h'y
- have huy : UniqueMDiffWithinAt 𝓘(𝕜, E) ((extChartAt I x₀).symm ⁻¹' s ∩ range I) y := by
+ have huy : UniqueMDiffAt[(extChartAt I x₀).symm ⁻¹' s ∩ range I] y := by
apply UniqueDiffWithinAt.uniqueMDiffWithinAt
rw [inter_comm]
apply hu.uniqueDiffWithinAt_range_inter
@@ -652,7 +652,7 @@ diffeomorphisms. -/
lemma mpullbackWithin_mlieBracketWithin_of_isSymmSndFDerivWithinAt
{f : M → M'} {V W : Π (x : M'), TangentSpace I' x} {x₀ : M} {s : Set M} {t : Set M'}
(hV : MDiffAt[t] (T% V) (f x₀)) (hW : MDiffAt[t] (T% W) (f x₀))
- (hu : UniqueMDiffOn I s) (hf : CMDiffAt[s] 2 f x₀) (hx₀ : x₀ ∈ s)
+ (hu : UniqueMDiff[s]) (hf : CMDiffAt[s] 2 f x₀) (hx₀ : x₀ ∈ s)
(hst : f ⁻¹' t ∈ 𝓝[s] x₀)
(hsymm : IsSymmSndFDerivWithinAt 𝕜 ((extChartAt I' (f x₀)) ∘ f ∘ (extChartAt I x₀).symm)
((extChartAt I x₀).symm ⁻¹' s ∩ range I) (extChartAt I x₀ x₀)) :
@@ -741,7 +741,7 @@ becomes easier to check.) -/
lemma mpullbackWithin_mlieBracketWithin'
{f : M → M'} {V W : Π (x : M'), TangentSpace I' x} {x₀ : M} {s u : Set M} {t : Set M'}
(hV : MDiffAt[t] (T% V) (f x₀)) (hW : MDiffAt[t] (T% W) (f x₀))
- (hs : UniqueMDiffOn I s) (hu : UniqueMDiffOn I u)
+ (hs : UniqueMDiff[s]) (hu : UniqueMDiff[u])
(hf : CMDiffAt[u] n f x₀) (hx₀ : x₀ ∈ s) (hn : minSmoothness 𝕜 2 ≤ n)
(hst : f ⁻¹' t ∈ 𝓝[s] x₀) (h'x₀ : x₀ ∈ closure (interior u)) (hsu : s ⊆ u) :
mpullbackWithin I I' f (mlieBracketWithin I' V W t) s x₀ =
@@ -777,7 +777,7 @@ lemma mpullbackWithin_mlieBracketWithin'
lemma mpullbackWithin_mlieBracketWithin
{f : M → M'} {V W : Π (x : M'), TangentSpace I' x} {x₀ : M} {s : Set M} {t : Set M'}
(hV : MDiffAt[t] (T% V) (f x₀)) (hW : MDiffAt[t] (T% W) (f x₀))
- (hu : UniqueMDiffOn I s) (hf : CMDiffAt[s] n f x₀) (hx₀ : x₀ ∈ s)
+ (hu : UniqueMDiff[s]) (hf : CMDiffAt[s] n f x₀) (hx₀ : x₀ ∈ s)
(hn : minSmoothness 𝕜 2 ≤ n)
(hst : f ⁻¹' t ∈ 𝓝[s] x₀) (h'x₀ : x₀ ∈ closure (interior s)) :
mpullbackWithin I I' f (mlieBracketWithin I' V W t) s x₀ =
@@ -788,7 +788,7 @@ lemma mpullbackWithin_mlieBracketWithin
lemma mpullback_mlieBracketWithin
{f : M → M'} {V W : Π (x : M'), TangentSpace I' x} {x₀ : M} {s : Set M} {t : Set M'}
(hV : MDiffAt[t] (T% V) (f x₀)) (hW : MDiffAt[t] (T% W) (f x₀))
- (hu : UniqueMDiffOn I s) (hf : CMDiffAt n f x₀) (hx₀ : x₀ ∈ s)
+ (hu : UniqueMDiff[s]) (hf : CMDiffAt n f x₀) (hx₀ : x₀ ∈ s)
(hn : minSmoothness 𝕜 2 ≤ n) (hst : f ⁻¹' t ∈ 𝓝[s] x₀) :
mpullback I I' f (mlieBracketWithin I' V W t) x₀ =
mlieBracketWithin I (mpullback I I' f V) (mpullback I I' f W) s x₀ := by
@@ -830,7 +830,7 @@ protected lemma _root_.ContMDiffWithinAt.mlieBracketWithin_vectorField
[IsManifold I (n + 1) M] {m : ℕ∞ω}
{U V : Π (x : M), TangentSpace I x} {s : Set M} {x : M}
(hU : CMDiffAt[s] n (T% U) x) (hV : CMDiffAt[s] n (T% V) x)
- (hs : UniqueMDiffOn I s) (hx : x ∈ s) (hmn : minSmoothness 𝕜 (m + 1) ≤ n) :
+ (hs : UniqueMDiff[s]) (hx : x ∈ s) (hmn : minSmoothness 𝕜 (m + 1) ≤ n) :
CMDiffAt[s] m (T% (mlieBracketWithin I U V s)) x := by
/- The statement is not obvious, since at different points the Lie bracket is defined using
different charts. However, since we know that the Lie bracket is invariant under diffeos, we can
@@ -902,7 +902,7 @@ lemma _root_.ContMDiffAt.mlieBracket_vectorField {m n : ℕ∞}
lemma _root_.ContMDiffOn.mlieBracketWithin_vectorField {m n : ℕ∞}
[IsManifold I (n + 1) M] {U V : Π (x : M), TangentSpace I x}
(hU : CMDiff[s] n (T% U)) (hV : CMDiff[s] n (T% V))
- (hs : UniqueMDiffOn I s) (hmn : minSmoothness 𝕜 (m + 1) ≤ n) :
+ (hs : UniqueMDiff[s]) (hmn : minSmoothness 𝕜 (m + 1) ≤ n) :
CMDiff[s] m (T% (mlieBracketWithin I U V s)) :=
fun x hx ↦ (hU x hx).mlieBracketWithin_vectorField (hV x hx) hs hx hmn
@@ -924,7 +924,7 @@ variable [IsManifold I (minSmoothness 𝕜 3) M] [CompleteSpace E]
`[U, [V, W]] = [[U, V], W] + [V, [U, W]]` (also called Jacobi identity). -/
theorem leibniz_identity_mlieBracketWithin_apply
{U V W : Π (x : M), TangentSpace I x} {s : Set M} {x : M}
- (hs : UniqueMDiffOn I s) (h's : x ∈ closure (interior s)) (hx : x ∈ s)
+ (hs : UniqueMDiff[s]) (h's : x ∈ closure (interior s)) (hx : x ∈ s)
(hU : CMDiffAt[s] (minSmoothness 𝕜 2) (T% U) x)
(hV : CMDiffAt[s] (minSmoothness 𝕜 2) (T% V) x)
(hW : CMDiffAt[s] (minSmoothness 𝕜 2) (T% W) x) :
diff --git a/Mathlib/Geometry/Manifold/VectorField/Pullback.lean b/Mathlib/Geometry/Manifold/VectorField/Pullback.lean
index 3510738005c08e..7a44b80b89f725 100644
--- a/Mathlib/Geometry/Manifold/VectorField/Pullback.lean
+++ b/Mathlib/Geometry/Manifold/VectorField/Pullback.lean
@@ -154,7 +154,7 @@ lemma mpullbackWithin_neg :
simp [mpullbackWithin_apply]
set_option backward.isDefEq.respectTransparency false in
-lemma mpullbackWithin_id {V : Π (x : M), TangentSpace I x} (h : UniqueMDiffWithinAt I s x) :
+lemma mpullbackWithin_id {V : Π (x : M), TangentSpace I x} (h : UniqueMDiffAt[s] x) :
mpullbackWithin I I id V s x = V x := by
simp [mpullbackWithin_apply, mfderivWithin_id h]
@@ -222,7 +222,7 @@ set_option backward.isDefEq.respectTransparency false in
lemma mpullbackWithin_comp_of_left
{g : M' → M''} {f : M → M'} {V : Π (x : M''), TangentSpace I'' x} {s : Set M} {t : Set M'}
{x₀ : M} (hf : MDiffAt[s] f x₀) (h : Set.MapsTo f s t)
- (hu : UniqueMDiffWithinAt I s x₀) (hg' : (mfderiv[t] g (f x₀)).IsInvertible) :
+ (hu : UniqueMDiffAt[s] x₀) (hg' : (mfderiv[t] g (f x₀)).IsInvertible) :
mpullbackWithin I I'' (g ∘ f) V s x₀ =
mpullbackWithin I I' f (mpullbackWithin I' I'' g V t) s x₀ := by
simp only [mpullbackWithin]
@@ -235,7 +235,7 @@ set_option backward.isDefEq.respectTransparency false in
lemma mpullbackWithin_comp_of_right
{g : M' → M''} {f : M → M'} {V : Π (x : M''), TangentSpace I'' x} {s : Set M} {t : Set M'}
{x₀ : M} (hg : MDiffAt[t] g (f x₀)) (h : Set.MapsTo f s t)
- (hu : UniqueMDiffWithinAt I s x₀) (hf' : (mfderiv[s] f x₀).IsInvertible) :
+ (hu : UniqueMDiffAt[s] x₀) (hf' : (mfderiv[s] f x₀).IsInvertible) :
mpullbackWithin I I'' (g ∘ f) V s x₀ =
mpullbackWithin I I' f (mpullbackWithin I' I'' g V t) s x₀ := by
simp only [mpullbackWithin]
@@ -260,7 +260,7 @@ variable [IsManifold I 2 M] [IsManifold I' 2 M'] [CompleteSpace E]
differentiable. Version within a set at a point. -/
protected lemma _root_.MDifferentiableWithinAt.mpullbackWithin_vectorField_inter
(hV : MDiffAt[t] (T% V) (f x₀)) (hf : CMDiffAt[s] n f x₀) (hf' : (mfderiv[s] f x₀).IsInvertible)
- (hx₀ : x₀ ∈ s) (hs : UniqueMDiffOn I s) (hmn : 2 ≤ n) :
+ (hx₀ : x₀ ∈ s) (hs : UniqueMDiff[s]) (hmn : 2 ≤ n) :
MDiffAt[s ∩ f ⁻¹' t] (T% (mpullbackWithin I I' f V s)) x₀ := by
/- We want to apply the theorem `MDifferentiableWithinAt.clm_apply_of_inCoordinates`,
stating that applying linear maps to vector fields gives a smooth result when the linear map and
@@ -324,7 +324,7 @@ protected lemma _root_.MDifferentiableWithinAt.mpullbackWithin_vectorField_inter
lemma _root_.MDifferentiableWithinAt.mpullbackWithin_vectorField_inter_of_eq
(hV : MDiffAt[t] (T% V) y₀) (hf : CMDiffAt[s] n f x₀)
(hf' : (mfderiv[s] f x₀).IsInvertible)
- (hx₀ : x₀ ∈ s) (hs : UniqueMDiffOn I s) (hmn : 2 ≤ n) (h : y₀ = f x₀) :
+ (hx₀ : x₀ ∈ s) (hs : UniqueMDiff[s]) (hmn : 2 ≤ n) (h : y₀ = f x₀) :
MDiffAt[s ∩ f ⁻¹' t] (T% (mpullbackWithin I I' f V s)) x₀ := by
subst h
exact hV.mpullbackWithin_vectorField_inter hf hf' hx₀ hs hmn
@@ -334,7 +334,7 @@ differentiable. Version on a set. -/
protected lemma _root_.MDifferentiableOn.mpullbackWithin_vectorField_inter
(hV : MDiff[t] (T% V)) (hf : CMDiff[s] n f)
(hf' : ∀ x ∈ s ∩ f ⁻¹' t, (mfderiv[s] f x).IsInvertible)
- (hs : UniqueMDiffOn I s) (hmn : 2 ≤ n) :
+ (hs : UniqueMDiff[s]) (hmn : 2 ≤ n) :
MDiff[(s ∩ f ⁻¹' t)] (T% (mpullbackWithin I I' f V s)) :=
fun _ hx₀ ↦ MDifferentiableWithinAt.mpullbackWithin_vectorField_inter
(hV _ hx₀.2) (hf _ hx₀.1) (hf' _ hx₀) hx₀.1 hs hmn
@@ -395,7 +395,7 @@ Version within a set at a point. -/
protected lemma _root_.ContMDiffWithinAt.mpullbackWithin_vectorField_inter
(hV : CMDiffAt[t] m (T% V) (f x₀)) (hf : CMDiffAt[s] n f x₀)
(hf' : (mfderiv[s] f x₀).IsInvertible)
- (hx₀ : x₀ ∈ s) (hs : UniqueMDiffOn I s) (hmn : m + 1 ≤ n) :
+ (hx₀ : x₀ ∈ s) (hs : UniqueMDiff[s]) (hmn : m + 1 ≤ n) :
CMDiffAt[s ∩ f ⁻¹' t] m (T% (mpullbackWithin I I' f V s)) x₀ := by
/- We want to apply the theorem `ContMDiffWithinAt.clm_apply_of_inCoordinates`, stating
that applying linear maps to vector fields gives a smooth result when the linear map and the
@@ -454,7 +454,7 @@ protected lemma _root_.ContMDiffWithinAt.mpullbackWithin_vectorField_inter
lemma _root_.ContMDiffWithinAt.mpullbackWithin_vectorField_inter_of_eq
(hV : CMDiffAt[t] m (T% V) y₀) (hf : CMDiffAt[s] n f x₀) (hf' : (mfderiv[s] f x₀).IsInvertible)
- (hx₀ : x₀ ∈ s) (hs : UniqueMDiffOn I s) (hmn : m + 1 ≤ n) (h : f x₀ = y₀) :
+ (hx₀ : x₀ ∈ s) (hs : UniqueMDiff[s]) (hmn : m + 1 ≤ n) (h : f x₀ = y₀) :
CMDiffAt[s ∩ f ⁻¹' t] m (T% (mpullbackWithin I I' f V s)) x₀ := by
subst h
exact ContMDiffWithinAt.mpullbackWithin_vectorField_inter hV hf hf' hx₀ hs hmn
@@ -465,7 +465,7 @@ Version within a set at a point. -/
protected lemma _root_.ContMDiffWithinAt.mpullbackWithin_vectorField_of_mem
(hV : CMDiffAt[t] m (T% V) (f x₀)) (hf : CMDiffAt[s] n f x₀)
(hf' : (mfderiv[s] f x₀).IsInvertible)
- (hx₀ : x₀ ∈ s) (hs : UniqueMDiffOn I s) (hmn : m + 1 ≤ n) (hst : f ⁻¹' t ∈ 𝓝[s] x₀) :
+ (hx₀ : x₀ ∈ s) (hs : UniqueMDiff[s]) (hmn : m + 1 ≤ n) (hst : f ⁻¹' t ∈ 𝓝[s] x₀) :
CMDiffAt[s] m (T% (mpullbackWithin I I' f V s)) x₀ := by
apply (ContMDiffWithinAt.mpullbackWithin_vectorField_inter
hV hf hf' hx₀ hs hmn).mono_of_mem_nhdsWithin
@@ -476,7 +476,7 @@ with `m + 1 ≤ n` is `C^m`.
Version within a set at a point. -/
protected lemma _root_.ContMDiffWithinAt.mpullbackWithin_vectorField_of_mem_of_eq
(hV : CMDiffAt[t] m (T% V) y₀) (hf : CMDiffAt[s] n f x₀) (hf' : (mfderiv[s] f x₀).IsInvertible)
- (hx₀ : x₀ ∈ s) (hs : UniqueMDiffOn I s) (hmn : m + 1 ≤ n) (hst : f ⁻¹' t ∈ 𝓝[s] x₀)
+ (hx₀ : x₀ ∈ s) (hs : UniqueMDiff[s]) (hmn : m + 1 ≤ n) (hst : f ⁻¹' t ∈ 𝓝[s] x₀)
(hy₀ : f x₀ = y₀) :
CMDiffAt[s] m (T% (mpullbackWithin I I' f V s)) x₀ := by
subst hy₀
@@ -488,7 +488,7 @@ Version within a set at a point. -/
protected lemma _root_.ContMDiffWithinAt.mpullbackWithin_vectorField
(hV : CMDiffAt[t] m (T% V) (f x₀)) (hf : CMDiffAt[s] n f x₀)
(hf' : (mfderiv[s] f x₀).IsInvertible)
- (hx₀ : x₀ ∈ s) (hs : UniqueMDiffOn I s) (hmn : m + 1 ≤ n) (hst : MapsTo f s t) :
+ (hx₀ : x₀ ∈ s) (hs : UniqueMDiff[s]) (hmn : m + 1 ≤ n) (hst : MapsTo f s t) :
CMDiffAt[s] m (T% (mpullbackWithin I I' f V s)) x₀ :=
ContMDiffWithinAt.mpullbackWithin_vectorField_of_mem hV hf hf' hx₀ hs hmn
hst.preimage_mem_nhdsWithin
@@ -498,7 +498,7 @@ with `m + 1 ≤ n` is `C^m`.
Version within a set at a point. -/
protected lemma _root_.ContMDiffWithinAt.mpullbackWithin_vectorField_of_eq
(hV : CMDiffAt[t] m (T% V) y₀) (hf : CMDiffAt[s] n f x₀) (hf' : (mfderiv[s] f x₀).IsInvertible)
- (hx₀ : x₀ ∈ s) (hs : UniqueMDiffOn I s) (hmn : m + 1 ≤ n) (hst : MapsTo f s t) (h : f x₀ = y₀) :
+ (hx₀ : x₀ ∈ s) (hs : UniqueMDiff[s]) (hmn : m + 1 ≤ n) (hst : MapsTo f s t) (h : f x₀ = y₀) :
CMDiffAt[s] m (T% (mpullbackWithin I I' f V s)) x₀ := by
subst h
exact ContMDiffWithinAt.mpullbackWithin_vectorField hV hf hf' hx₀ hs hmn hst
@@ -509,7 +509,7 @@ Version within a set at a point, with a set used for the pullback possibly large
protected lemma _root_.ContMDiffWithinAt.mpullbackWithin_vectorField' {u : Set M}
(hV : CMDiffAt[t] m (T% V) (f x₀))
(hf : CMDiffAt[u] n f x₀) (hf' : (mfderiv[u] f x₀).IsInvertible)
- (hx₀ : x₀ ∈ s) (hs : UniqueMDiffOn I s) (hmn : m + 1 ≤ n)
+ (hx₀ : x₀ ∈ s) (hs : UniqueMDiff[s]) (hmn : m + 1 ≤ n)
(hst : f ⁻¹' t ∈ 𝓝[s] x₀) (hu : s ⊆ u) :
CMDiffAt[s] m (T% (mpullbackWithin I I' f V u)) x₀ := by
have hn : 1 ≤ n := le_trans (by simp) hmn
@@ -530,7 +530,7 @@ with `m + 1 ≤ n` is `C^m`.
Version within a set at a point, with a set used for the pullback possibly larger. -/
protected lemma _root_.ContMDiffWithinAt.mpullbackWithin_vectorField_of_eq' {u : Set M}
(hV : CMDiffAt[t] m (T% V) y₀) (hf : CMDiffAt[u] n f x₀) (hf' : (mfderiv[u] f x₀).IsInvertible)
- (hx₀ : x₀ ∈ s) (hs : UniqueMDiffOn I s) (hmn : m + 1 ≤ n) (hst : f ⁻¹' t ∈ 𝓝[s] x₀)
+ (hx₀ : x₀ ∈ s) (hs : UniqueMDiff[s]) (hmn : m + 1 ≤ n) (hst : f ⁻¹' t ∈ 𝓝[s] x₀)
(hu : s ⊆ u) (hy₀ : f x₀ = y₀) :
CMDiffAt[s] m (T% (mpullbackWithin I I' f V u)) x₀ := by
subst hy₀
@@ -542,7 +542,7 @@ Version on a set. -/
protected lemma _root_.ContMDiffOn.mpullbackWithin_vectorField_inter
(hV : CMDiff[t] m (T% V)) (hf : CMDiff[s] n f)
(hf' : ∀ x ∈ s ∩ f ⁻¹' t, (mfderiv[s] f x).IsInvertible)
- (hs : UniqueMDiffOn I s) (hmn : m + 1 ≤ n) :
+ (hs : UniqueMDiff[s]) (hmn : m + 1 ≤ n) :
CMDiff[s ∩ f ⁻¹' t] m (T% (mpullbackWithin I I' f V s)) :=
fun _ hx₀ ↦ ContMDiffWithinAt.mpullbackWithin_vectorField_inter
(hV _ hx₀.2) (hf _ hx₀.1) (hf' _ hx₀) hx₀.1 hs hmn
@@ -616,7 +616,7 @@ protected lemma _root_.ContMDiff.mpullback_vectorField
lemma contMDiffWithinAt_mpullbackWithin_extChartAt_symm
{V : Π (x : M), TangentSpace I x} (hV : CMDiffAt[s] m (T% V) x)
- (hs : UniqueMDiffOn I s) (hx : x ∈ s) (hmn : m + 1 ≤ n) :
+ (hs : UniqueMDiff[s]) (hx : x ∈ s) (hmn : m + 1 ≤ n) :
CMDiffAt[(extChartAt I x).target ∩ (extChartAt I x).symm ⁻¹' s] m
(T% (mpullbackWithin 𝓘(𝕜, E) I (extChartAt I x).symm V (range I))) (extChartAt I x x) :=
ContMDiffWithinAt.mpullbackWithin_vectorField_of_eq' hV
@@ -628,7 +628,7 @@ lemma contMDiffWithinAt_mpullbackWithin_extChartAt_symm
lemma eventually_contMDiffWithinAt_mpullbackWithin_extChartAt_symm
{V : Π (x : M), TangentSpace I x} (hV : CMDiffAt[s] m (T% V) x)
- (hs : UniqueMDiffOn I s) (hx : x ∈ s) (hmn : m + 1 ≤ n) (hm : m ≠ ∞) :
+ (hs : UniqueMDiff[s]) (hx : x ∈ s) (hmn : m + 1 ≤ n) (hm : m ≠ ∞) :
∀ᶠ y in 𝓝[s] x, CMDiffAt[(extChartAt I x).target ∩ (extChartAt I x).symm ⁻¹' s] m
(T% (mpullbackWithin 𝓘(𝕜, E) I (extChartAt I x).symm V (range I))) (extChartAt I x y) := by
have T := nhdsWithin_mono _ (subset_insert _ _)
diff --git a/MathlibTest/DifferentialGeometry/Notation/Basic.lean b/MathlibTest/DifferentialGeometry/Notation/Basic.lean
index 862e96f248d2da..36f1634c00a05f 100644
--- a/MathlibTest/DifferentialGeometry/Notation/Basic.lean
+++ b/MathlibTest/DifferentialGeometry/Notation/Basic.lean
@@ -529,6 +529,71 @@ end
end differentiability
+/-! Tests for the elaborators for `UniqueMDiff{WithinAt,On}`. -/
+section UniqueMDiff
+
+variable {s : Set M} {m : M}
+
+/-- info: UniqueMDiffOn I s : Prop -/
+#guard_msgs in
+#check UniqueMDiff[s]
+
+/-- info: UniqueMDiffOn (modelWithCornersSelf Real Real) (Set.Icc 0 1) : Prop -/
+#guard_msgs in
+#check UniqueMDiff[(Set.Icc 0 1 : Set ℝ)]
+
+/-- error: `Real` has type `Type` which is not of the form `Set α` for some `α`. -/
+#guard_msgs in
+#check UniqueMDiff[ℝ]
+
+/-- info: UniqueMDiffWithinAt I s : M → Prop -/
+#guard_msgs in
+#check UniqueMDiffAt[s]
+
+/-- info: UniqueMDiffWithinAt I s m : Prop -/
+#guard_msgs in
+#check UniqueMDiffAt[s] m
+
+/-- info: UniqueMDiffWithinAt I Set.univ m : Prop -/
+#guard_msgs in
+#check UniqueMDiffAt[(Set.univ : Set M)] m
+
+-- In the future, the elaborators should take the type of `m` into account.
+/--
+error: Could not find a model with corners for `?_`.
+
+Hint: the expected type contains metavariables, maybe you need to provide an implicit argument
+-/
+#guard_msgs in
+set_option pp.mvars.anonymous false in
+#check UniqueMDiffAt[Set.univ] m
+
+variable {s : TopologicalSpace.Opens M}
+
+/-- info: UniqueMDiffOn I s.carrier : Prop -/
+#guard_msgs in
+#check UniqueMDiff[s.carrier]
+
+/-- error: `s` has type `TopologicalSpace.Opens M` which is not of the form `Set α` for some `α`. -/
+#guard_msgs in
+#check UniqueMDiff[s]
+
+/--
+error: Application type mismatch: The argument
+ s
+has type
+ TopologicalSpace.Opens M
+but is expected to have type
+ Set ?_
+in the application
+ UniqueMDiffOn I s
+-/
+#guard_msgs in
+set_option pp.mvars.anonymous false in
+#check UniqueMDiffOn I s
+
+end UniqueMDiff
+
/-! Tests for the custom elaborators for `ContMDiff{WithinAt,At,On}` -/
section smoothness
diff --git a/MathlibTest/DifferentialGeometry/Notation/Delaborators.lean b/MathlibTest/DifferentialGeometry/Notation/Delaborators.lean
index ce05e61ed64c58..f943715dd466e2 100644
--- a/MathlibTest/DifferentialGeometry/Notation/Delaborators.lean
+++ b/MathlibTest/DifferentialGeometry/Notation/Delaborators.lean
@@ -82,6 +82,22 @@ variable
#guard_msgs in
#check TotalSpace.mk (F := E) x (v x)
+/-- info: UniqueMDiff[s] : Prop -/
+#guard_msgs in
+#check UniqueMDiffOn I s
+
+/-- info: UniqueMDiffAt[s] : M → Prop -/
+#guard_msgs in
+#check UniqueMDiffWithinAt I s
+
+/-- info: UniqueMDiffAt[s] x : Prop -/
+#guard_msgs in
+#check UniqueMDiffWithinAt I s x
+
+/-- info: UniqueMDiffAt[s] : M → Prop -/
+#guard_msgs in
+#check UniqueMDiffWithinAt (𝕜 := ℝ) I s
+
section ambiguity
variable {g : E × E → M} in
From 0c7d9c10847cd4a97cf50bd1a2f034acf5e9a9b0 Mon Sep 17 00:00:00 2001
From: Michael Rothgang <10105016+grunweg@users.noreply.github.com>
Date: Fri, 19 Jun 2026 13:51:17 +0000
Subject: [PATCH 0183/1300] chore(Geometry/Manifold/ContMDiff/Defs): use
`variable` more (#40798)
The file uses `x' : M` and `y : M'` throughout: extract this into a variable to declutter statements.
---
Mathlib/Geometry/Manifold/ContMDiff/Defs.lean | 36 +++++++++----------
1 file changed, 16 insertions(+), 20 deletions(-)
diff --git a/Mathlib/Geometry/Manifold/ContMDiff/Defs.lean b/Mathlib/Geometry/Manifold/ContMDiff/Defs.lean
index 4520d963cfe915..1a717ac3747ca5 100644
--- a/Mathlib/Geometry/Manifold/ContMDiff/Defs.lean
+++ b/Mathlib/Geometry/Manifold/ContMDiff/Defs.lean
@@ -69,8 +69,8 @@ variable {𝕜 : Type*} [NontriviallyNormedField 𝕜]
[NormedAddCommGroup E''] [NormedSpace 𝕜 E''] {H'' : Type*} [TopologicalSpace H'']
{I'' : ModelWithCorners 𝕜 E'' H''} {M'' : Type*} [TopologicalSpace M''] [ChartedSpace H'' M'']
-- declare functions, sets, points and smoothness indices
- {e : OpenPartialHomeomorph M H}
- {e' : OpenPartialHomeomorph M' H'} {f f₁ : M → M'} {s s₁ t : Set M} {x : M} {m n : ℕ∞ω}
+ {e : OpenPartialHomeomorph M H} {e' : OpenPartialHomeomorph M' H'}
+ {f f₁ : M → M'} {s s₁ t : Set M} {x x' : M} {y : M'} {m n : ℕ∞ω}
variable (I I') in
/-- Property in the model space of a model with corners of being `C^n` within a set at a point,
@@ -337,14 +337,14 @@ theorem contMDiffWithinAt_iff_source_of_mem_maximalAtlas
rfl
theorem contMDiffWithinAt_iff_source_of_mem_source
- [IsManifold I n M] {x' : M} (hx' : x' ∈ (chartAt H x).source) :
+ [IsManifold I n M] (hx' : x' ∈ (chartAt H x).source) :
ContMDiffWithinAt I I' n f s x' ↔
ContMDiffWithinAt 𝓘(𝕜, E) I' n (f ∘ (extChartAt I x).symm)
((extChartAt I x).symm ⁻¹' s ∩ range I) (extChartAt I x x') :=
contMDiffWithinAt_iff_source_of_mem_maximalAtlas (chart_mem_maximalAtlas x) hx'
theorem contMDiffAt_iff_source_of_mem_source
- [IsManifold I n M] {x' : M} (hx' : x' ∈ (chartAt H x).source) :
+ [IsManifold I n M] (hx' : x' ∈ (chartAt H x).source) :
ContMDiffAt I I' n f x' ↔
ContMDiffWithinAt 𝓘(𝕜, E) I' n (f ∘ (extChartAt I x).symm) (range I) (extChartAt I x x') := by
simp_rw [ContMDiffAt, contMDiffWithinAt_iff_source_of_mem_source hx', preimage_univ, univ_inter]
@@ -362,20 +362,20 @@ theorem contMDiffWithinAt_iff_target_of_mem_maximalAtlas
simp_rw [StructureGroupoid.liftPropWithinAt_self_target, A, A']
simp [ContDiffWithinAtProp, comp_assoc]
-theorem contMDiffWithinAt_iff_target_of_mem_source
- [IsManifold I' n M'] {x : M} {y : M'} (hy : f x ∈ (chartAt H' y).source) :
+theorem contMDiffWithinAt_iff_target_of_mem_source [IsManifold I' n M']
+ (hy : f x ∈ (chartAt H' y).source) :
ContMDiffWithinAt I I' n f s x ↔
ContinuousWithinAt f s x ∧ ContMDiffWithinAt I 𝓘(𝕜, E') n (extChartAt I' y ∘ f) s x :=
contMDiffWithinAt_iff_target_of_mem_maximalAtlas (chart_mem_maximalAtlas _) hy
-theorem contMDiffAt_iff_target_of_mem_source
- [IsManifold I' n M'] {x : M} {y : M'} (hy : f x ∈ (chartAt H' y).source) :
+theorem contMDiffAt_iff_target_of_mem_source [IsManifold I' n M']
+ (hy : f x ∈ (chartAt H' y).source) :
ContMDiffAt I I' n f x ↔
ContinuousAt f x ∧ ContMDiffAt I 𝓘(𝕜, E') n (extChartAt I' y ∘ f) x := by
rw [ContMDiffAt, contMDiffWithinAt_iff_target_of_mem_source hy, continuousWithinAt_univ,
ContMDiffAt]
-theorem contMDiffWithinAt_iff_of_mem_maximalAtlas {x : M} (he : e ∈ maximalAtlas I n M)
+theorem contMDiffWithinAt_iff_of_mem_maximalAtlas (he : e ∈ maximalAtlas I n M)
(he' : e' ∈ maximalAtlas I' n M') (hx : x ∈ e.source) (hy : f x ∈ e'.source) :
ContMDiffWithinAt I I' n f s x ↔
ContinuousWithinAt f s x ∧
@@ -385,8 +385,8 @@ theorem contMDiffWithinAt_iff_of_mem_maximalAtlas {x : M} (he : e ∈ maximalAtl
/-- An alternative formulation of `contMDiffWithinAt_iff_of_mem_maximalAtlas`
if the set `s` lies in `e.source`. -/
-theorem contMDiffWithinAt_iff_image {x : M} (he : e ∈ maximalAtlas I n M)
- (he' : e' ∈ maximalAtlas I' n M')
+theorem contMDiffWithinAt_iff_image
+ (he : e ∈ maximalAtlas I n M) (he' : e' ∈ maximalAtlas I' n M')
(hs : s ⊆ e.source) (hx : x ∈ e.source) (hy : f x ∈ e'.source) :
ContMDiffWithinAt I I' n f s x ↔
ContinuousWithinAt f s x ∧
@@ -399,8 +399,7 @@ theorem contMDiffWithinAt_iff_image {x : M} (he : e ∈ maximalAtlas I n M)
/-- One can reformulate being `C^n` within a set at a point as continuity within this set at this
point, and being `C^n` in any chart containing that point. -/
theorem contMDiffWithinAt_iff_of_mem_source [IsManifold I n M] [IsManifold I' n M']
- {x' : M} {y : M'} (hx : x' ∈ (chartAt H x).source)
- (hy : f x' ∈ (chartAt H' y).source) :
+ (hx : x' ∈ (chartAt H x).source) (hy : f x' ∈ (chartAt H' y).source) :
ContMDiffWithinAt I I' n f s x' ↔
ContinuousWithinAt f s x' ∧
ContDiffWithinAt 𝕜 n (extChartAt I' y ∘ f ∘ (extChartAt I x).symm)
@@ -409,8 +408,7 @@ theorem contMDiffWithinAt_iff_of_mem_source [IsManifold I n M] [IsManifold I' n
(chart_mem_maximalAtlas y) hx hy
theorem contMDiffWithinAt_iff_of_mem_source' [IsManifold I n M] [IsManifold I' n M']
- {x' : M} {y : M'} (hx : x' ∈ (chartAt H x).source)
- (hy : f x' ∈ (chartAt H' y).source) :
+ (hx : x' ∈ (chartAt H x).source) (hy : f x' ∈ (chartAt H' y).source) :
ContMDiffWithinAt I I' n f s x' ↔
ContinuousWithinAt f s x' ∧
ContDiffWithinAt 𝕜 n (extChartAt I' y ∘ f ∘ (extChartAt I x).symm)
@@ -428,8 +426,7 @@ theorem contMDiffWithinAt_iff_of_mem_source' [IsManifold I n M] [IsManifold I' n
exact hc (extChartAt_source_mem_nhds' hy)
theorem contMDiffAt_iff_of_mem_source [IsManifold I n M] [IsManifold I' n M']
- {x' : M} {y : M'} (hx : x' ∈ (chartAt H x).source)
- (hy : f x' ∈ (chartAt H' y).source) :
+ (hx : x' ∈ (chartAt H x).source) (hy : f x' ∈ (chartAt H' y).source) :
ContMDiffAt I I' n f x' ↔
ContinuousAt f x' ∧
ContDiffWithinAt 𝕜 n (extChartAt I' y ∘ f ∘ (extChartAt I x).symm) (range I)
@@ -458,7 +455,7 @@ these charts.
Note: this lemma uses `extChartAt I x '' s` instead of `(extChartAt I x).symm ⁻¹' s` to ensure
that this set lies in `(extChartAt I x).target`. -/
theorem contMDiffOn_iff_of_subset_source [IsManifold I n M] [IsManifold I' n M']
- {x : M} {y : M'} (hs : s ⊆ (chartAt H x).source)
+ (hs : s ⊆ (chartAt H x).source)
(h2s : MapsTo f s (chartAt H' y).source) :
ContMDiffOn I I' n f s ↔
ContinuousOn f s ∧
@@ -472,8 +469,7 @@ these charts.
Note: this lemma uses `extChartAt I x '' s` instead of `(extChartAt I x).symm ⁻¹' s` to ensure
that this set lies in `(extChartAt I x).target`. -/
theorem contMDiffOn_iff_of_subset_source' [IsManifold I n M] [IsManifold I' n M']
- {x : M} {y : M'} (hs : s ⊆ (extChartAt I x).source)
- (h2s : MapsTo f s (extChartAt I' y).source) :
+ (hs : s ⊆ (extChartAt I x).source) (h2s : MapsTo f s (extChartAt I' y).source) :
ContMDiffOn I I' n f s ↔
ContDiffOn 𝕜 n (extChartAt I' y ∘ f ∘ (extChartAt I x).symm) (extChartAt I x '' s) := by
rw [extChartAt_source] at hs h2s
From 10212ec219a35b6f8603f9dd72751931be2a6167 Mon Sep 17 00:00:00 2001
From: Julia Markus Himmel <2065352+TwoFX@users.noreply.github.com>
Date: Fri, 19 Jun 2026 13:51:20 +0000
Subject: [PATCH 0184/1300] chore(CI): allow PR title to start with 'E2'
(#40806)
This PR relaxes the check at `ValidatePRTitle` to allow titles like `feat(ModularForm): E2 is bounded at ImInfty`.
This came up in #40765.
Zulip discussion (reviewers only): [#mathlib reviewers > Check PR title](https://leanprover.zulipchat.com/#narrow/channel/345428-mathlib-reviewers/topic/Check.20PR.20title/with/604860804)
---
Mathlib/Tactic/Linter/ValidatePRTitle.lean | 8 +++++++-
MathlibTest/ValidatePRTitle.lean | 20 ++++++++++++++++++++
2 files changed, 27 insertions(+), 1 deletion(-)
diff --git a/Mathlib/Tactic/Linter/ValidatePRTitle.lean b/Mathlib/Tactic/Linter/ValidatePRTitle.lean
index feb10809e868a7..29db84c14bc984 100644
--- a/Mathlib/Tactic/Linter/ValidatePRTitle.lean
+++ b/Mathlib/Tactic/Linter/ValidatePRTitle.lean
@@ -69,6 +69,12 @@ def prTitle : Parser (String × Option String × String) := do
#guard_msgs in
#eval Parser.run prTitle "chore: test"
+/--
+Check if `word` looks like an abbreviation, like `JSON` or `E2` or `W3C`.
+-/
+def isAbbreviation (word : String.Slice) : Bool :=
+ word.all (fun c => c.isUpper || c.isDigit) && word.chars.length != 1
+
open Mathlib.Linter.TextBased in
/--
Check if `title` matches the mathlib conventions for PR titles
@@ -115,7 +121,7 @@ public def validateTitle (title : String) : Array String := Id.run do
-- Titles should be lower-cased (but we allow abbreviations).
if subject.front.toLower != subject.front then
let firstWord := subject.takeWhile (!·.isWhitespace)
- if !(firstWord.all (·.isUpper)) then
+ if !isAbbreviation firstWord then
errors := errors.push "error: the PR subject should be lowercased"
if subject.endsWith "." then
errors := errors.push "error: the PR title should not end with a full stop"
diff --git a/MathlibTest/ValidatePRTitle.lean b/MathlibTest/ValidatePRTitle.lean
index aacda293a7e90d..b9b01432b3dbbf 100644
--- a/MathlibTest/ValidatePRTitle.lean
+++ b/MathlibTest/ValidatePRTitle.lean
@@ -186,3 +186,23 @@ info: Message: 'error: the PR title contains multiple consecutive spaces; please
-/
#guard_msgs in
#check_title "feat(Mathlib/Algebra.lean): title."
+
+#guard_msgs in
+#check_title "feat(ModularForm): E2 is bounded at ImInfty"
+
+#guard_msgs in
+#check_title "feat(ModuleForm): 2E is bounded at ImInfty"
+
+#guard_msgs in
+#check_title "feat(ModuleForm): 2e is less than 6"
+
+/-- info: Message: 'error: the PR subject should be lowercased' -/
+#guard_msgs in
+#check_title "feat(ModuleForm): W3c"
+
+#guard_msgs in
+#check_title "feat(ModuleForm): W3C"
+
+/-- info: Message: 'error: the PR subject should be lowercased' -/
+#guard_msgs in
+#check_title "feat(ModuleForm): A new lemma"
From 4084fffec05cf6a84e0097e3c3e4083b92e04fa4 Mon Sep 17 00:00:00 2001
From: Whysoserioushah <109107491+Whysoserioushah@users.noreply.github.com>
Date: Fri, 19 Jun 2026 14:33:10 +0000
Subject: [PATCH 0185/1300] feat(Representation/Continuous): show TopRep is a
linear category (#40739)
co-authored-by : @rmhi
---
.../Continuous/Basic.lean | 142 +++++++++++++++++-
.../Continuous/TopRep.lean | 50 ++++++
2 files changed, 189 insertions(+), 3 deletions(-)
diff --git a/Mathlib/RepresentationTheory/Continuous/Basic.lean b/Mathlib/RepresentationTheory/Continuous/Basic.lean
index 506eb63ce991a7..fc6080606d36fd 100644
--- a/Mathlib/RepresentationTheory/Continuous/Basic.lean
+++ b/Mathlib/RepresentationTheory/Continuous/Basic.lean
@@ -66,6 +66,7 @@ namespace ContIntertwiningMap
open ContRepresentation
variable {π₁ : ContRepresentation R G V} {π₂ : ContRepresentation R G W}
+ {π₃ : ContRepresentation R G U}
/-- Any continuous intertwining map is an intertwining map. -/
abbrev toIntertwiningMap (f : π₁ →ⁱL π₂) :
@@ -78,16 +79,25 @@ def id : π₁ →ⁱL π₁ where
__ := ContinuousLinearMap.id R V
isIntertwining' g := by simp
+@[simp]
+lemma toContinuousLinearMap_id :
+ (id : π₁ →ⁱL π₁).toContinuousLinearMap = ContinuousLinearMap.id R V := rfl
+
@[ext]
lemma ext {π₁ : ContRepresentation R G V} {π₂ : ContRepresentation R G W}
{f g : π₁ →ⁱL π₂} (h : f.toContinuousLinearMap = g.toContinuousLinearMap) : f = g := by
cases f; cases g; congr
-lemma toIntertwiningMap_injective {π₁ : ContRepresentation R G V}
+lemma toContinuousLinearMap_injective {π₁ : ContRepresentation R G V}
{π₂ : ContRepresentation R G W} :
Function.Injective fun f : π₁ →ⁱL π₂ ↦ f.toContinuousLinearMap :=
fun _ _ ↦ ext
+lemma toIntertwiningMap_injective {π₁ : ContRepresentation R G V}
+ {π₂ : ContRepresentation R G W} :
+ Function.Injective fun f : π₁ →ⁱL π₂ ↦ f.toIntertwiningMap :=
+ fun _ _ _ ↦ by ext; simp_all
+
lemma toFun_injective {π₁ : ContRepresentation R G V} {π₂ : ContRepresentation R G W} :
Function.Injective fun f : π₁ →ⁱL π₂ ↦ f.toFun := fun f g h ↦ by
ext x; exact congr_fun h x
@@ -97,6 +107,11 @@ instance {π₁ : ContRepresentation R G V} {π₂ : ContRepresentation R G W} :
coe f := f.toFun
coe_injective := toFun_injective
+lemma id_apply (v : V) : (.id : π₁ →ⁱL π₁) v = v := rfl
+
+lemma toContinuousLinearMap_apply (f : π₁ →ⁱL π₂) (v : V) :
+ f.toContinuousLinearMap v = f v := rfl
+
lemma isIntertwining {π₁ : ContRepresentation R G V} {π₂ : ContRepresentation R G W}
(f : π₁ →ⁱL π₂) (g : G) (v : V) : f (π₁ g v) = π₂ g (f v) :=
f.toIntertwiningMap.isIntertwining _ _ g v
@@ -114,6 +129,116 @@ def comp {π₁ : ContRepresentation R G V} {π₂ : ContRepresentation R G W}
__ := f.toContinuousLinearMap.comp g.toContinuousLinearMap
isIntertwining' h := by rw [comp_assoc, g.2, ← comp_assoc, f.2, comp_assoc]
+@[simp]
+lemma toContinuousLinearMap_comp {π₁ : ContRepresentation R G V} {π₂ : ContRepresentation R G W}
+ {π₃ : ContRepresentation R G U} (f : π₂ →ⁱL π₃) (g : π₁ →ⁱL π₂) :
+ (f.comp g).toContinuousLinearMap = f.toContinuousLinearMap.comp g.toContinuousLinearMap := rfl
+
+instance : Add (π₁ →ⁱL π₂) where
+ add f g := ⟨f.toContinuousLinearMap + g.toContinuousLinearMap, by simp [g.2, f.2]⟩
+
+@[simp]
+lemma toContinuousLinearMap_add (f g : π₁ →ⁱL π₂) :
+ (f + g).toContinuousLinearMap = f.toContinuousLinearMap + g.toContinuousLinearMap := rfl
+
+lemma add_apply (f g : π₁ →ⁱL π₂) (v : V) : (f + g) v = f v + g v := rfl
+
+lemma comp_add (f : π₂ →ⁱL π₃) (g h : π₁ →ⁱL π₂) :
+ f.comp (g + h) = f.comp g + f.comp h := by ext; simp
+
+lemma add_comp (f g : π₂ →ⁱL π₃) (h : π₁ →ⁱL π₂) :
+ (f + g).comp h = f.comp h + g.comp h := by ext; simp
+
+instance : One (π₁ →ⁱL π₁) where one := .id
+
+lemma one_def : (1 : π₁ →ⁱL π₁) = .id := rfl
+
+@[simp]
+lemma toContinuousLinearMap_one : (1 : π₁ →ⁱL π₁).toContinuousLinearMap = 1 := rfl
+
+lemma one_apply (v : V) : (1 : π₁ →ⁱL π₁) v = v := rfl
+
+instance : Zero (π₁ →ⁱL π₂) where zero := ⟨0, by simp⟩
+
+@[simp]
+lemma toContinuousLinearMap_zero : (0 : π₁ →ⁱL π₂).toContinuousLinearMap = 0 := rfl
+
+lemma zero_apply (v : V) : (0 : π₁ →ⁱL π₂) v = 0 := rfl
+
+instance : AddZeroClass (π₁ →ⁱL π₂) :=
+ fast_instance% toContinuousLinearMap_injective.addZeroClass _
+ toContinuousLinearMap_zero toContinuousLinearMap_add
+
+instance : AddCommSemigroup (π₁ →ⁱL π₂) :=
+ fast_instance% toContinuousLinearMap_injective.addCommSemigroup _
+ toContinuousLinearMap_add
+
+instance : Neg (π₁ →ⁱL π₂) where
+ neg f := ⟨-f.toContinuousLinearMap, by simp [f.2]⟩
+
+@[simp]
+lemma toContinuousLinearMap_neg (f : π₁ →ⁱL π₂) :
+ (-f).toContinuousLinearMap = -f.toContinuousLinearMap := rfl
+
+lemma neg_apply (f : π₁ →ⁱL π₂) (v : V) : (-f) v = -f v := rfl
+
+instance : Sub (π₁ →ⁱL π₂) where
+ sub f g := ⟨f.toContinuousLinearMap - g.toContinuousLinearMap, by simp [g.2, f.2]⟩
+
+@[simp]
+lemma toContinuousLinearMap_sub (f g : π₁ →ⁱL π₂) :
+ (f - g).toContinuousLinearMap = f.toContinuousLinearMap - g.toContinuousLinearMap := rfl
+
+lemma sub_apply (f g : π₁ →ⁱL π₂) (v : V) : (f - g) v = f v - g v := rfl
+
+instance instSMul {S : Type*} [Monoid S] [DistribMulAction S W] [SMulCommClass R S W]
+ [ContinuousConstSMul S W] [LinearMap.CompatibleSMul W W S R] :
+ SMul S (π₁ →ⁱL π₂) where
+ smul s f := ⟨s • f.toContinuousLinearMap, fun g ↦ by
+ rw [ContinuousLinearMap.smul_comp, f.2, ContinuousLinearMap.comp_smul]⟩
+
+section addcommgroup
+
+variable {S : Type*} [Monoid S] [DistribMulAction S W] [SMulCommClass R S W]
+ [ContinuousConstSMul S W] [LinearMap.CompatibleSMul W W S R]
+
+@[simp]
+lemma toContinuousLinearMap_smul (s : S) (f : π₁ →ⁱL π₂) :
+ (s • f).toContinuousLinearMap = s • f.toContinuousLinearMap := rfl
+
+lemma smul_apply (s : S) (f : π₁ →ⁱL π₂) (v : V) : (s • f) v = s • f v := rfl
+
+lemma smul_comp {S : Type*} [Monoid S] [DistribMulAction S U] [SMulCommClass R S U]
+ [ContinuousConstSMul S U] [LinearMap.CompatibleSMul U U S R]
+ (s : S) (f : π₂ →ⁱL π₃) (g : π₁ →ⁱL π₂) : (s • f).comp g = s • (f.comp g) := by
+ ext; simp
+
+lemma comp_smul {S : Type*} [Monoid S] [DistribMulAction S U] [SMulCommClass R S U]
+ [ContinuousConstSMul S U] [LinearMap.CompatibleSMul U U S R]
+ [DistribMulAction S W] [SMulCommClass R S W] [ContinuousConstSMul S W]
+ [LinearMap.CompatibleSMul W W S R] [LinearMap.CompatibleSMul W U S R]
+ (s : S) (f : π₂ →ⁱL π₃) (g : π₁ →ⁱL π₂) : f.comp (s • g) = s • (f.comp g) := by
+ ext; simp
+
+instance : AddCommGroup (π₁ →ⁱL π₂) :=
+ fast_instance% toContinuousLinearMap_injective.addCommGroup _ toContinuousLinearMap_zero
+ toContinuousLinearMap_add toContinuousLinearMap_neg toContinuousLinearMap_sub
+ (fun _ _ ↦ toContinuousLinearMap_smul _ _) (fun _ _ ↦ toContinuousLinearMap_smul _ _)
+
+instance : DistribMulAction S (π₁ →ⁱL π₂) where
+ one_smul _ := by ext; simp
+ mul_smul _ _ _ := by ext; simp [mul_smul]
+ smul_zero _ := by ext; simp
+ smul_add _ _ _ := by ext; simp [smul_add]
+
+instance instModule {S : Type*} [Ring S] [Module S W] [SMulCommClass R S W]
+ [ContinuousConstSMul S W] [LinearMap.CompatibleSMul W W S R] :
+ Module S (π₁ →ⁱL π₂) where
+ add_smul _ _ _ := by ext; simp [add_smul]
+ zero_smul _ := by ext; simp
+
+end addcommgroup
+
end ContIntertwiningMap
namespace ContRepresentation
@@ -279,6 +404,17 @@ lemma trivial_apply (g : G) (v : V) : trivial R G V g v = v := rfl
def restrict {H : Type*} [Monoid H] (π : ContRepresentation R G V) (φ : H →* G) :
ContRepresentation R H V := .comp π φ
+/-- The submodule of `G`-invariant elements of a continuous representation. -/
+def invariants (π : ContRepresentation R G V) : Submodule R V where
+ carrier := {v | ∀ g, π g v = v}
+ zero_mem' := by simp
+ add_mem' _ _ := by simp_all
+ smul_mem' _ _ hv g := by simp [hv g]
+
+@[simp]
+lemma mem_invariants {π : ContRepresentation R G V} (v : V) :
+ v ∈ π.invariants ↔ ∀ g, π g v = v := Iff.rfl
+
-- TODO : define `IsTopologicalMonoid` and then replace `Homeomorph.mulLeft g⁻¹` with the
-- `ContinuousMap.mulRight g` to make `coind₁` work for monoids.
variable {G H : Type*} [Group G] [TopologicalSpace G] [TopologicalSpace R]
@@ -336,7 +472,7 @@ def coind₁ (π : ContRepresentation R G V) :
/-- The functoriality of `coind₁`. -/
@[simps]
-def coind₁_map (π₁ : ContRepresentation R G V) (π₂ : ContRepresentation R G W) (f : π₁ →ⁱL π₂) :
+def coind₁Map (π₁ : ContRepresentation R G V) (π₂ : ContRepresentation R G W) (f : π₁ →ⁱL π₂) :
coind₁ π₁ →ⁱL coind₁ π₂ where
toFun := (f : ContinuousMap _ _).comp
map_add' _ _ := by ext; simp
@@ -346,7 +482,7 @@ def coind₁_map (π₁ : ContRepresentation R G V) (π₂ : ContRepresentation
/-- The naturality of the transformation from `𝟭 ⟶ coind₁`. -/
@[simps]
-def coind₁_ι (π : ContRepresentation R G V) : π →ⁱL coind₁ π where
+def coind₁ι (π : ContRepresentation R G V) : π →ⁱL coind₁ π where
toFun := .const G
map_add' _ _ := rfl
map_smul' _ _ := rfl
diff --git a/Mathlib/RepresentationTheory/Continuous/TopRep.lean b/Mathlib/RepresentationTheory/Continuous/TopRep.lean
index 1240e47542d0a2..c3fa7ff35657b2 100644
--- a/Mathlib/RepresentationTheory/Continuous/TopRep.lean
+++ b/Mathlib/RepresentationTheory/Continuous/TopRep.lean
@@ -128,6 +128,56 @@ variable {A B} in
lemma hom_comm_apply (f : A ⟶ B) (g : G) (a : A) : f.hom (A.ρ g a) = B.ρ g (f.hom a) := by
simpa using! congr($(f.hom.2 g) a)
+instance : AddCommGroup (A ⟶ B) := ConcreteCategory.homEquiv.addCommGroup
+
+lemma hom_add (f g : A ⟶ B) : (f + g).hom = f.hom + g.hom := rfl
+
+lemma hom_sub (f g : A ⟶ B) : (f - g).hom = f.hom - g.hom := rfl
+
+lemma ofHom_add (f g : ρ →ⁱL σ) : ofHom (f + g) = ofHom f + ofHom g := rfl
+
+lemma ofHom_sub (f g : ρ →ⁱL σ) : ofHom (f - g) = ofHom f - ofHom g := rfl
+
+lemma comp_add' (f : A ⟶ B) (g h : B ⟶ C) : f ≫ (g + h) = f ≫ g + f ≫ h := by
+ ext : 1; simp [hom_add, ContIntertwiningMap.add_comp]
+
+lemma add_comp' (f g : A ⟶ B) (h : B ⟶ C) : (f + g) ≫ h = f ≫ h + g ≫ h := by
+ ext : 1; simp [hom_add, ContIntertwiningMap.comp_add]
+
+instance : Preadditive (TopRep k G) where
+ homGroup := inferInstance
+ add_comp := TopRep.add_comp'
+ comp_add := TopRep.comp_add'
+
+section Linear
+
+variable {k : Type u} {G : Type v} {X Y : Type w} [TopologicalSpace k] [CommRing k]
+ [IsTopologicalRing k] [Monoid G] [AddCommGroup X] [Module k X] [TopologicalSpace X]
+ [IsTopologicalAddGroup X] [ContinuousSMul k X] [AddCommGroup Y] [Module k Y] [TopologicalSpace Y]
+ [IsTopologicalAddGroup Y] [ContinuousSMul k Y] {ρ : ContRepresentation k G X}
+ {σ : ContRepresentation k G Y} {A B C : TopRep k G}
+
+instance : Module k (A ⟶ B) := ConcreteCategory.homEquiv.module k
+
+lemma hom_smul (r : k) (f : A ⟶ B) : (r • f).hom = r • f.hom := rfl
+
+lemma ofHom_smul (r : k) (f : ρ →ⁱL σ) : ofHom (r • f) = r • ofHom f := rfl
+
+variable (A B C) in
+lemma smul_comp' (r : k) (f : A ⟶ B) (g : B ⟶ C) : (r • f) ≫ g = r • (f ≫ g) := by
+ ext; simp [hom_smul, ContIntertwiningMap.comp_smul]
+
+variable (A B C) in
+lemma comp_smul' (f : A ⟶ B) (r : k) (g : B ⟶ C) : f ≫ (r • g) = r • (f ≫ g) := by
+ ext; simp [hom_smul, ContIntertwiningMap.smul_comp]
+
+instance : CategoryTheory.Linear k (TopRep k G) where
+ homModule := inferInstance
+ smul_comp := smul_comp'
+ comp_smul := comp_smul'
+
+end Linear
+
section equivAction
/-- The functor sending a topological representation to the corresponding object in
From e72c92ce6faaaa9e1c19532a0a7b3cefd18006c6 Mon Sep 17 00:00:00 2001
From: Jorge Luis Mayoral <178652443+j-mayoral@users.noreply.github.com>
Date: Fri, 19 Jun 2026 14:56:32 +0000
Subject: [PATCH 0186/1300] feat(FinitelyPresentedGroup): free product of
finitely presented is finitely presented (#40332)
Adds the instance for the Coproduct of FinitelyPresentedGroup:
`[IsFinitelyPresented G] [IsFinitelyPresented H] : IsFinitelyPresented (Coprod G H)`
Co-authored-by: j-mayoral
---
.../GroupTheory/FinitelyPresentedGroup.lean | 30 ++++++++++++++++---
1 file changed, 26 insertions(+), 4 deletions(-)
diff --git a/Mathlib/GroupTheory/FinitelyPresentedGroup.lean b/Mathlib/GroupTheory/FinitelyPresentedGroup.lean
index 81cbeaebab1630..57f0f1f6729ce7 100644
--- a/Mathlib/GroupTheory/FinitelyPresentedGroup.lean
+++ b/Mathlib/GroupTheory/FinitelyPresentedGroup.lean
@@ -8,7 +8,12 @@ module
public import Mathlib.Algebra.Group.Subgroup.Basic
public import Mathlib.Data.Set.Finite.Basic
+public import Mathlib.Data.Finite.Sum
public import Mathlib.GroupTheory.FreeGroup.Basic
+public import Mathlib.GroupTheory.Coprod.Basic
+public import Mathlib.GroupTheory.PresentedGroup
+public import Mathlib.GroupTheory.QuotientGroup.Basic
+public import Mathlib.Logic.Equiv.Fin.Basic
/-!
# Finitely Presented Groups
@@ -93,7 +98,7 @@ namespace Group.IsFinitelyPresented
/-- Finitely presented groups are closed under isomorphism. -/
@[to_additive /-- Finitely presented additive groups are closed under additive isomorphism. -/
]
-theorem equiv (iso : G ≃* H) (h : IsFinitelyPresented G) : IsFinitelyPresented H := by
+theorem equiv (iso : G ≃* H) [h : IsFinitelyPresented G] : IsFinitelyPresented H := by
obtain ⟨n, φ, hφsurj, hNC⟩ := h
refine ⟨n, (iso : G →* H).comp φ, iso.surjective.comp hφsurj, ?_⟩
rwa [φ.ker_mulEquiv_comp iso]
@@ -106,6 +111,12 @@ theorem of_surjective [hG : IsFinitelyPresented G] (f : G →* H)
rw [← MonoidHom.comap_ker]
exact hf_ker.comap hφ_surj hφ_ker
+open QuotientGroup in
+theorem exists_mulEquiv_presentedGroup [hg : IsFinitelyPresented G] :
+ ∃ n : ℕ, ∃ s : Set (FreeGroup (Fin n)), Set.Finite s ∧ Nonempty (G ≃* PresentedGroup s) := by
+ obtain ⟨n, φ, hφ, s, hs, hsφ⟩ := hg
+ exact ⟨n, s, hs, ⟨(quotientKerEquivOfSurjective φ hφ).symm.trans (quotientMulEquivOfEq hsφ.symm)⟩⟩
+
/-- A free group with a finite number of generators is finitely presented. -/
@[to_additive /-- A free additive group with a finite number of generators is finitely presented. -/
]
@@ -115,13 +126,24 @@ instance [Finite α] : IsFinitelyPresented (FreeGroup α) := by
· rw [(FreeGroup.map f).ker_eq_bot (FreeGroup.map_injective hf_inj.injective)]
exact .bot
+instance [Finite α] (s : Set (FreeGroup α)) [Finite s] :
+ IsFinitelyPresented (PresentedGroup s) :=
+ of_surjective (PresentedGroup.mk s) (PresentedGroup.mk_surjective s)
+ ⟨s, ‹_›, (QuotientGroup.ker_mk' (Subgroup.normalClosure s)).symm⟩
+
/-- `Multiplicative ℤ` is finitely presented. -/
instance : IsFinitelyPresented (Multiplicative ℤ) :=
- equiv (FreeGroup.mulEquivIntOfUnique : FreeGroup Unit ≃* Multiplicative ℤ) inferInstance
+ equiv (FreeGroup.mulEquivIntOfUnique : FreeGroup Unit ≃* Multiplicative ℤ)
/-- ℤ is finitely presented -/
instance : AddGroup.IsFinitelyPresented ℤ :=
- AddGroup.IsFinitelyPresented.equiv
- (FreeAddGroup.addEquivIntOfUnique : FreeAddGroup Unit ≃+ ℤ) inferInstance
+ AddGroup.IsFinitelyPresented.equiv (FreeAddGroup.addEquivIntOfUnique : FreeAddGroup Unit ≃+ ℤ)
+
+/-- The free product of finitely presented groups is finitely presented -/
+instance [IsFinitelyPresented G] [IsFinitelyPresented H] :
+ IsFinitelyPresented (Monoid.Coprod G H) := by
+ obtain ⟨_, sG, ⟨_ : Finite sG, ⟨φG⟩⟩⟩ := exists_mulEquiv_presentedGroup (G := G)
+ obtain ⟨_, sH, ⟨_ : Finite sH, ⟨φH⟩⟩⟩ := exists_mulEquiv_presentedGroup (G := H)
+ exact equiv ((PresentedGroup.coprodPresentations sG sH).trans (MulEquiv.coprodCongr φG φH).symm)
end Group.IsFinitelyPresented
From c9ba3100f3ae30fffc9f6d2bbfe033dcd44ce577 Mon Sep 17 00:00:00 2001
From: Thomas Browning <13339017+tb65536@users.noreply.github.com>
Date: Fri, 19 Jun 2026 15:34:35 +0000
Subject: [PATCH 0187/1300] chore(FieldTheory/Galois/IsGaloisGroup): generalize
`IsGaloisGroup.of_ringEquiv` to surjective ring homs (#40805)
`IsGaloisGroup.of_ringEquiv` only uses surjectivity, so I have added the generalization that only assumes surjectivity.
Co-authored-by: tb65536
---
Mathlib/FieldTheory/Galois/IsGaloisGroup.lean | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean b/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean
index f76a7cbae7f149..cdb2d060676346 100644
--- a/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean
+++ b/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean
@@ -97,13 +97,13 @@ theorem IsGaloisGroup.of_algEquiv [hG : IsGaloisGroup G A B] (B' : Type*) [Semir
simp [he, hx'])
exact ⟨a, by rw [← e.commutes, ha, AlgEquiv.apply_symm_apply]⟩⟩
-theorem IsGaloisGroup.of_ringEquiv [hG : IsGaloisGroup G A B] [CommSemiring A'] [Algebra A' B]
- (e : A ≃+* A') (he : ∀ a, algebraMap A' B (e a) = algebraMap A B a) :
- IsGaloisGroup G A' B where
+theorem IsGaloisGroup.of_ringHom_surjective [hG : IsGaloisGroup G A B] [CommSemiring A']
+ [Algebra A' B] (e : A →+* A') (he : ∀ a, algebraMap A' B (e a) = algebraMap A B a)
+ (he' : Function.Surjective e) : IsGaloisGroup G A' B where
faithful := hG.faithful
commutes := ⟨by
intro g a' b
- obtain ⟨a, rfl⟩ : ∃ a, e a = a' := e.surjective a'
+ obtain ⟨a, rfl⟩ : ∃ a, e a = a' := he' a'
rw [Algebra.smul_def, Algebra.smul_def, he, ← Algebra.smul_def, ← Algebra.smul_def]
exact hG.commutes.smul_comm g a b⟩
isInvariant := ⟨by
@@ -111,6 +111,11 @@ theorem IsGaloisGroup.of_ringEquiv [hG : IsGaloisGroup G A B] [CommSemiring A']
obtain ⟨a, ha⟩ := hG.isInvariant.isInvariant b h
exact ⟨e a, by rw [he, ha]⟩⟩
+theorem IsGaloisGroup.of_ringEquiv [hG : IsGaloisGroup G A B] [CommSemiring A'] [Algebra A' B]
+ (e : A ≃+* A') (he : ∀ a, algebraMap A' B (e a) = algebraMap A B a) :
+ IsGaloisGroup G A' B :=
+ .of_ringHom_surjective G A A' B e he e.surjective
+
attribute [instance low] IsGaloisGroup.commutes IsGaloisGroup.isInvariant
variable {C : Type*} [CommSemiring C] [Algebra C B]
From d846133bc96504cc64e86ff8010b310a38d09849 Mon Sep 17 00:00:00 2001
From: Thomas Browning <13339017+tb65536@users.noreply.github.com>
Date: Fri, 19 Jun 2026 15:48:50 +0000
Subject: [PATCH 0188/1300] chore(Topology/Algebra/Category/ProfiniteGrp/*):
to_additivize some declarations (#39973)
This PR to_additivizes some declarations relating to profinite groups. There were a few trickier declarations that I left out of this PR for now.
Co-authored-by: tb65536
---
.../Algebra/Category/ProfiniteGrp/Basic.lean | 5 ++++-
.../Algebra/Category/ProfiniteGrp/Completion.lean | 12 ++++++++++++
.../Algebra/Category/ProfiniteGrp/Limits.lean | 14 ++++++++++++--
3 files changed, 28 insertions(+), 3 deletions(-)
diff --git a/Mathlib/Topology/Algebra/Category/ProfiniteGrp/Basic.lean b/Mathlib/Topology/Algebra/Category/ProfiniteGrp/Basic.lean
index 03526eb4ebbb72..4d7a993258824a 100644
--- a/Mathlib/Topology/Algebra/Category/ProfiniteGrp/Basic.lean
+++ b/Mathlib/Topology/Algebra/Category/ProfiniteGrp/Basic.lean
@@ -263,6 +263,8 @@ def ofContinuousMulEquiv {G : ProfiniteGrp.{u}} {H : Type v} [TopologicalSpace H
/-- Build an isomorphism in the category `ProfiniteGrp` from
a `ContinuousMulEquiv` between `ProfiniteGrp`s. -/
+@[to_additive /-- Build an isomorphism in the category `ProfiniteAddGrp` from
+a `ContinuousAddEquiv` between `ProfiniteAddGrp`s. -/]
def ContinuousMulEquiv.toProfiniteGrpIso {X Y : ProfiniteGrp} (e : X ≃ₜ* Y) : X ≅ Y where
hom := ofHom e
inv := ofHom e.symm
@@ -279,7 +281,7 @@ instance : (forget₂ ProfiniteGrp Profinite).Faithful := {
map_injective := fun {_ _} _ _ h =>
ConcreteCategory.hom_ext _ _ fun x ↦ CategoryTheory.congr_fun h x }
-
+@[to_additive]
instance : (forget₂ ProfiniteGrp Profinite).ReflectsIsomorphisms where
reflects {X Y} f _ := by
let i := asIso ((forget₂ ProfiniteGrp Profinite).map f)
@@ -288,6 +290,7 @@ instance : (forget₂ ProfiniteGrp Profinite).ReflectsIsomorphisms where
map_mul' := map_mul f.hom }
exact (ContinuousMulEquiv.toProfiniteGrpIso e).isIso_hom
+@[to_additive]
instance : (forget ProfiniteGrp.{u}).ReflectsIsomorphisms :=
CategoryTheory.reflectsIsomorphisms_comp (forget₂ ProfiniteGrp Profinite) (forget Profinite)
diff --git a/Mathlib/Topology/Algebra/Category/ProfiniteGrp/Completion.lean b/Mathlib/Topology/Algebra/Category/ProfiniteGrp/Completion.lean
index b148f05cb044f6..d56ec3594c15ff 100644
--- a/Mathlib/Topology/Algebra/Category/ProfiniteGrp/Completion.lean
+++ b/Mathlib/Topology/Algebra/Category/ProfiniteGrp/Completion.lean
@@ -56,6 +56,7 @@ namespace ProfiniteCompletion
variable (G : GrpCat.{u})
/-- The diagram of finite quotients indexed by finite-index normal subgroups of `G`. -/
+@[to_additive /-- The diagram of finite quotients indexed by finite-index normal subgroups. -/]
def finiteGrpDiagram : FiniteIndexNormalSubgroup G ⥤ FiniteGrp.{u} where
obj H := FiniteGrp.of <| G ⧸ H.toSubgroup
map f := FiniteGrp.ofHom <| QuotientGroup.map _ _ (MonoidHom.id _) f.le
@@ -63,16 +64,20 @@ def finiteGrpDiagram : FiniteIndexNormalSubgroup G ⥤ FiniteGrp.{u} where
map_comp f g := by ext ⟨x⟩; rfl
/-- The finite-quotient diagram viewed in `ProfiniteGrp`. -/
+@[to_additive /-- The finite-quotient diagram viewed in `ProfiniteAddGrp`. -/]
def diagram : FiniteIndexNormalSubgroup G ⥤ ProfiniteGrp.{u} :=
finiteGrpDiagram _ ⋙ forget₂ _ _
/-- The profinite completion of `G` as a projective limit. -/
+@[to_additive /-- The profinite completion of `G` as a projective limit. -/]
def completion : ProfiniteGrp.{u} := limit (diagram G)
/-- The canonical map from `G` to its profinite completion, as a function. -/
+@[to_additive /-- The canonical map from `G` to its profinite completion, as a function. -/]
def etaFn (x : G) : completion G := ⟨fun _ => QuotientGroup.mk x, fun _ _ _ => rfl⟩
/-- The canonical morphism from `G` to its profinite completion. -/
+@[to_additive /-- The canonical morphism from `G` to its profinite completion. -/]
def eta : G ⟶ GrpCat.of (completion G) := GrpCat.ofHom {
toFun := etaFn G
map_one' := rfl
@@ -80,6 +85,7 @@ def eta : G ⟶ GrpCat.of (completion G) := GrpCat.ofHom {
}
set_option backward.isDefEq.respectTransparency false in
+@[to_additive]
theorem mono_eta_iff_residuallyFinite : Mono (eta G) ↔ Group.ResiduallyFinite G := by
rw [GrpCat.mono_iff_injective, injective_iff_map_eq_one,
Group.residuallyFinite_iff_forall_finiteIndexNormalSubgroup]
@@ -87,11 +93,13 @@ theorem mono_eta_iff_residuallyFinite : Mono (eta G) ↔ Group.ResiduallyFinite
rw [Subtype.ext_iff, funext_iff]
exact forall_congr' fun H ↦ QuotientGroup.eq_one_iff g
+@[to_additive]
theorem etaFn_injective_iff_residuallyFinite :
Function.Injective (etaFn G) ↔ Group.ResiduallyFinite G :=
(GrpCat.mono_iff_injective (eta G)).symm.trans (mono_eta_iff_residuallyFinite G)
set_option backward.isDefEq.respectTransparency false in
+@[to_additive]
lemma denseRange : DenseRange (etaFn G) := by
apply dense_iff_inter_open.mpr
rintro U ⟨s, hsO, hsv⟩ ⟨⟨spc, hspc⟩, uDefaultSpec⟩
@@ -119,14 +127,17 @@ variable {G}
variable {P : ProfiniteGrp.{u}}
/-- The preimage of an open normal subgroup under a morphism to a profinite group. -/
+@[to_additive /-- The preimage of an open normal subgroup under a morphism to a profinite group. -/]
def preimage (f : G ⟶ GrpCat.of P) (H : OpenNormalSubgroup P) : FiniteIndexNormalSubgroup G :=
H.toFiniteIndexNormalSubgroup.comap f.hom
+@[to_additive]
lemma preimage_le {f : G ⟶ GrpCat.of P} {H K : OpenNormalSubgroup P}
(h : H ≤ K) : preimage f H ≤ preimage f K :=
FiniteIndexNormalSubgroup.comap_mono _ h
/-- The induced map on finite quotients coming from a morphism to `P`. -/
+@[to_additive /-- The induced map on finite quotients coming from a morphism to `P`. -/]
def quotientMap (f : G ⟶ GrpCat.of P) (H : OpenNormalSubgroup P) :
FiniteGrp.of (G ⧸ (preimage f H).toSubgroup) ⟶ FiniteGrp.of (P ⧸ H.toSubgroup) :=
FiniteGrp.ofHom <| QuotientGroup.map _ _ f.hom <| fun _ h => h
@@ -163,6 +174,7 @@ lemma lift_eta (f : G ⟶ GrpCat.of P) : eta G ≫ (forget₂ _ _).map (lift f)
simp only [Category.assoc, Iso.inv_hom_id]
rfl
+@[to_additive]
lemma lift_unique (f g : completion G ⟶ P)
(h : eta G ≫ (forget₂ _ _).map f = eta G ≫ (forget₂ _ _).map g) : f = g := by
ext x
diff --git a/Mathlib/Topology/Algebra/Category/ProfiniteGrp/Limits.lean b/Mathlib/Topology/Algebra/Category/ProfiniteGrp/Limits.lean
index 8c314bc61cabf3..e05a25f8247f72 100644
--- a/Mathlib/Topology/Algebra/Category/ProfiniteGrp/Limits.lean
+++ b/Mathlib/Topology/Algebra/Category/ProfiniteGrp/Limits.lean
@@ -43,6 +43,8 @@ namespace ProfiniteGrp
/-- The functor from `OpenNormalSubgroup P` to `FiniteGrp` sending `U` to `P ⧸ U`,
where `P : ProfiniteGrp`. -/
+@[to_additive /-- The functor from `OpenNormalAddSubgroup P` to `FiniteAddGrp` sending `U` to
+`P ⧸ U`, where `P : ProfiniteAddGrp`. -/]
def toFiniteQuotientFunctor (P : ProfiniteGrp) : OpenNormalSubgroup P ⥤ FiniteGrp where
obj := fun H => FiniteGrp.of (P ⧸ H.toSubgroup)
map := fun fHK => FiniteGrp.ofHom (QuotientGroup.map _ _ (.id _) (leOfHom fHK))
@@ -51,18 +53,22 @@ def toFiniteQuotientFunctor (P : ProfiniteGrp) : OpenNormalSubgroup P ⥤ Finite
_ _ _ (.id _) (.id _) (leOfHom f) (leOfHom g)).symm
/-- The diagram of finite quotients of `P` viewed in `ProfiniteGrp`. -/
-@[simps! obj map]
+@[to_additive (attr := simps! obj map)
+/-- The diagram of finite quotients of `P` viewed in `ProfiniteAddGrp`. -/]
def diagram (P : ProfiniteGrp.{u}) : OpenNormalSubgroup P ⥤ ProfiniteGrp.{u} :=
toFiniteQuotientFunctor P ⋙ forget₂ FiniteGrp ProfiniteGrp
/-- The `MonoidHom` from a profinite group `P` to the projective limit of its quotients by
open normal subgroups ordered by inclusion -/
+@[to_additive /-- The `AddMonoidHom` from a profinite additive group `P` to the projective limit of
+its quotients by open normal subgroups ordered by inclusion -/]
def toLimitFun (P : ProfiniteGrp.{u}) : P →* limit (diagram P) where
toFun p := ⟨fun _ => QuotientGroup.mk p, fun _ ↦ fun _ _ ↦ rfl⟩
map_one' := Subtype.val_inj.mp rfl
map_mul' _ _ := Subtype.val_inj.mp rfl
set_option backward.isDefEq.respectTransparency false in
+@[to_additive]
lemma toLimitFun_continuous (P : ProfiniteGrp.{u}) : Continuous (toLimitFun P) := by
apply continuous_induced_rng.mpr (continuous_pi _)
intro H
@@ -81,6 +87,8 @@ lemma toLimitFun_continuous (P : ProfiniteGrp.{u}) : Continuous (toLimitFun P) :
/-- The morphism in the category of `ProfiniteGrp` from a profinite group `P` to
the projective limit of its quotients by open normal subgroups ordered by inclusion -/
+@[to_additive /-- The morphism in the category of `ProfiniteAddGrp` from a profinite additive group
+`P` to the projective limit of its quotients by open normal subgroups ordered by inclusion -/]
def toLimit (P : ProfiniteGrp.{u}) : P ⟶ limit (diagram P) :=
ofHom { toLimitFun P with
continuous_toFun := toLimitFun_continuous P }
@@ -115,6 +123,7 @@ theorem toLimit_surjective (P : ProfiniteGrp.{u}) : Function.Surjective (toLimit
rw [← Set.range_eq_univ, ← closure_eq_iff_isClosed.mpr this,
Dense.closure_eq (denseRange_toLimit P)]
+@[to_additive]
theorem toLimit_injective (P : ProfiniteGrp.{u}) : Function.Injective (toLimit P) := by
change Function.Injective (toLimit P).hom.toMonoidHom
rw [← MonoidHom.ker_eq_bot_iff, Subgroup.eq_bot_iff_forall]
@@ -144,6 +153,7 @@ noncomputable def isoLimittoFiniteQuotientFunctor (P : ProfiniteGrp.{u}) :
ContinuousMulEquiv.toProfiniteGrpIso (continuousMulEquivLimittoFiniteQuotientFunctor P)
/-- The projection from `P` to the quotient by an open normal subgroup. -/
+@[to_additive /-- The projection from `P` to the quotient by an open normal subgroup. -/]
def proj {P : ProfiniteGrp.{u}} (U : OpenNormalSubgroup P) : P ⟶ (diagram P).obj U :=
ProfiniteGrp.ofHom (Y := (diagram P).obj U) {
toFun := QuotientGroup.mk
@@ -154,7 +164,7 @@ def proj {P : ProfiniteGrp.{u}} (U : OpenNormalSubgroup P) : P ⟶ (diagram P).o
}
/-- The canonical cone over `diagram P` with point `P`. -/
-@[simps]
+@[to_additive (attr := simps) /-- The canonical cone over `diagram P` with point `P`. -/]
def cone (P : ProfiniteGrp.{u}) : Limits.Cone (diagram P) where
pt := P
π := { app := proj }
From 1b82673ebcbcca37455a100743b2c2f56b61bda6 Mon Sep 17 00:00:00 2001
From: Thomas Browning <13339017+tb65536@users.noreply.github.com>
Date: Fri, 19 Jun 2026 15:48:53 +0000
Subject: [PATCH 0189/1300] feat(RingTheory/Ideal/Over): add `Ideal.smul_under`
(#40385)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This PR proves `g • P.under A = (g • P).under A` in the `SMulDistribClass` setting.
Co-authored-by: tb65536
---
Mathlib/RingTheory/Ideal/Over.lean | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/Mathlib/RingTheory/Ideal/Over.lean b/Mathlib/RingTheory/Ideal/Over.lean
index 0965eb9bcd53a5..37a2399ed0d352 100644
--- a/Mathlib/RingTheory/Ideal/Over.lean
+++ b/Mathlib/RingTheory/Ideal/Over.lean
@@ -90,7 +90,7 @@ section Semiring
variable (A : Type*) [CommSemiring A] {B C : Type*} [Semiring B] [Semiring C] [Algebra A B]
[Algebra A C] (P : Ideal B) {Q : Ideal C} (p : Ideal A)
- {G : Type*} [Group G] [MulSemiringAction G B] [SMulCommClass G A B] (g : G)
+ {G : Type*} [Group G] [MulSemiringAction G B] (g : G)
/-- The ideal obtained by pulling back the ideal `P` from `B` to `A`. -/
abbrev under : Ideal A := Ideal.comap (algebraMap A B) P
@@ -103,10 +103,19 @@ instance IsPrime.under [hP : P.IsPrime] : (P.under A).IsPrime :=
hP.comap (algebraMap A B)
@[simp]
-lemma under_smul : (g • P : Ideal B).under A = P.under A := by
+lemma under_smul [SMulCommClass G A B] : (g • P : Ideal B).under A = P.under A := by
ext a
rw [mem_comap, mem_comap, mem_pointwise_smul_iff_inv_smul_mem, smul_algebraMap]
+@[simp]
+theorem smul_under [MulSemiringAction G A] [SMulDistribClass G A B] :
+ g • P.under A = (g • P).under A := by
+ conv_lhs => rw [pointwise_smul_eq_comap, ← comap_coe, under_def, comap_comap]
+ conv_rhs => rw [pointwise_smul_eq_comap, ← comap_coe, under_def, comap_comap]
+ congr
+ ext
+ simp [algebraMap.smul']
+
variable (B) in
theorem under_top : under A (⊤ : Ideal B) = ⊤ := comap_top
@@ -154,7 +163,7 @@ theorem LiesOver.of_eq_map_equiv [P.LiesOver p] {E : Type*} [EquivLike E B C]
exact of_eq_comap p (AlgEquivClass.toAlgEquiv σ : B ≃ₐ[A] C).symm h
variable {p} in
-instance LiesOver.smul [h : P.LiesOver p] : (g • P).LiesOver p :=
+instance LiesOver.smul [SMulCommClass G A B] [h : P.LiesOver p] : (g • P).LiesOver p :=
⟨h.over.trans (under_smul A P g).symm⟩
variable (P) (Q)
From 2e6e057c008759e50eda6b03746d6309d59d7b65 Mon Sep 17 00:00:00 2001
From: Thomas Browning <13339017+tb65536@users.noreply.github.com>
Date: Fri, 19 Jun 2026 15:48:55 +0000
Subject: [PATCH 0190/1300] feat(RingTheory/Ideal/Pointwise): add
`IsScalarTower` instance for pointwise action on ideals (#40386)
This PR adds an `IsScalarTower` instance for pointwise action on ideals.
Co-authored-by: tb65536
---
Mathlib/RingTheory/Ideal/Pointwise.lean | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/Mathlib/RingTheory/Ideal/Pointwise.lean b/Mathlib/RingTheory/Ideal/Pointwise.lean
index 46e853d9691a61..cf3bbdbd200298 100644
--- a/Mathlib/RingTheory/Ideal/Pointwise.lean
+++ b/Mathlib/RingTheory/Ideal/Pointwise.lean
@@ -27,13 +27,13 @@ Where possible, try to keep them in sync.
open Set
-variable {M R : Type*}
+variable {M N R : Type*}
namespace Ideal
section Monoid
-variable [Monoid M] [Semiring R] [MulSemiringAction M R]
+variable [Monoid M] [Monoid N] [Semiring R] [MulSemiringAction M R] [MulSemiringAction N R]
/-- The action on an ideal corresponding to applying the action to every element.
@@ -67,6 +67,13 @@ theorem pointwise_smul_def {a : M} (S : Ideal R) :
a • S = S.map (MulSemiringAction.toRingHom _ _ a) :=
rfl
+instance [SMul M N] [IsScalarTower M N R] : IsScalarTower M N (Ideal R) where
+ smul_assoc x y z := by
+ simp_rw [pointwise_smul_def, map_map]
+ congr
+ ext
+ simp
+
-- note: unlike with `Subring`, `pointwise_smul_toAddSubgroup` wouldn't be true
theorem smul_mem_pointwise_smul (m : M) (r : R) (S : Ideal R) : r ∈ S → m • r ∈ m • S :=
From 4cf5258e7fcaee29f7b3abb025add73bdea63f41 Mon Sep 17 00:00:00 2001
From: Thomas Browning <13339017+tb65536@users.noreply.github.com>
Date: Fri, 19 Jun 2026 15:48:57 +0000
Subject: [PATCH 0191/1300] feat(RingTheory/Ideal/Over): add instances for
`LiesOver` in towers (#40437)
This PR adds instances for `LiesOver` in towers.
Co-authored-by: tb65536
---
Mathlib/RingTheory/Ideal/Over.lean | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Mathlib/RingTheory/Ideal/Over.lean b/Mathlib/RingTheory/Ideal/Over.lean
index 37a2399ed0d352..cc305ab486e261 100644
--- a/Mathlib/RingTheory/Ideal/Over.lean
+++ b/Mathlib/RingTheory/Ideal/Over.lean
@@ -194,6 +194,12 @@ theorem LiesOver.trans [𝔓.LiesOver P] [P.LiesOver p] : 𝔓.LiesOver p where
theorem LiesOver.tower_bot [hp : 𝔓.LiesOver p] [hP : 𝔓.LiesOver P] : P.LiesOver p where
over := by rw [𝔓.over_def p, 𝔓.over_def P, under_under]
+instance [𝔓.LiesOver P] : 𝔓.LiesOver (P.under A) :=
+ .trans 𝔓 P (P.under A)
+
+instance [𝔓.LiesOver P] : P.LiesOver (𝔓.under A) :=
+ .tower_bot 𝔓 P (𝔓.under A)
+
/--
Consider the following commutative diagram of ring maps
```
From c367137fa1e20d856c0d58e0efa86a62c87121fb Mon Sep 17 00:00:00 2001
From: Thomas Browning <13339017+tb65536@users.noreply.github.com>
Date: Fri, 19 Jun 2026 15:49:00 +0000
Subject: [PATCH 0192/1300] refactor(RingTheory/QuasiFinite/Basic): golf proof
of `eq_of_le_of_under_eq` (#40772)
This PR uses `Ideal.isPrime_map_of_isLocalizationAtPrime` and `Ideal.isPrime_map_of_isLocalizationAtPrime` to golf the proof of `eq_of_le_of_under_eq` slightly.
Co-authored-by: tb65536
---
Mathlib/RingTheory/QuasiFinite/Basic.lean | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/Mathlib/RingTheory/QuasiFinite/Basic.lean b/Mathlib/RingTheory/QuasiFinite/Basic.lean
index cff1eb8679363a..08f45dedcf66ea 100644
--- a/Mathlib/RingTheory/QuasiFinite/Basic.lean
+++ b/Mathlib/RingTheory/QuasiFinite/Basic.lean
@@ -412,16 +412,15 @@ omit [Algebra S T] in
lemma QuasiFiniteAt.eq_of_le_of_under_eq {P Q : Ideal S} [P.IsPrime] [Q.IsPrime]
(h₁ : P ≤ Q) (h₂ : P.under R = Q.under R) [QuasiFiniteAt R Q] :
P = Q := by
- have : Disjoint (Q.primeCompl : Set S) P := by simpa [Set.disjoint_iff, Set.ext_iff, not_imp_comm]
- have inst := IsLocalization.isPrime_of_isPrime_disjoint _ (Localization.AtPrime Q) P ‹_› this
+ have := Q.isPrime_map_of_isLocalizationAtPrime h₁ (S := Localization.AtPrime Q)
have H := QuasiFinite.eq_of_le_of_under_eq (R := R)
(Ideal.map (algebraMap S (Localization.AtPrime Q)) P) _
(IsLocalRing.le_maximalIdeal_of_isPrime _) (by
convert! h₂ <;> rw [← Ideal.under_under (B := S)]
- · rw [IsLocalization.under_map_of_isPrime_disjoint Q.primeCompl _ ‹P.IsPrime› this]
+ · rw [Q.under_map_of_isLocalizationAtPrime h₁]
· rw [Localization.AtPrime.under_maximalIdeal])
rw [← Localization.AtPrime.under_maximalIdeal (I := Q), ← H,
- IsLocalization.under_map_of_isPrime_disjoint Q.primeCompl _ ‹P.IsPrime› this]
+ Q.under_map_of_isLocalizationAtPrime h₁]
instance (p : Ideal R) [p.IsPrime] (P : Ideal S) [P.IsPrime] [P.LiesOver p] [QuasiFiniteAt R P]
[Algebra (Localization.AtPrime p) (Localization.AtPrime P)]
From e1f7efcd7be2541b98efd51874fe6498299f6bab Mon Sep 17 00:00:00 2001
From: Thomas Browning <13339017+tb65536@users.noreply.github.com>
Date: Fri, 19 Jun 2026 15:49:02 +0000
Subject: [PATCH 0193/1300] feat(RingTheory/Ideal/Maps): coheight of ideal
under surjective ring homomorphism (#40780)
This PR proves that the coheight of an ideal is preserved when taking the preimage under a surjective ring homomorphism.
Co-authored-by: tb65536
---
Mathlib/RingTheory/Ideal/Maps.lean | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/Mathlib/RingTheory/Ideal/Maps.lean b/Mathlib/RingTheory/Ideal/Maps.lean
index 523c5a0fdc457d..62de906cb2c249 100644
--- a/Mathlib/RingTheory/Ideal/Maps.lean
+++ b/Mathlib/RingTheory/Ideal/Maps.lean
@@ -6,6 +6,7 @@ Authors: Kenny Lau
module
public import Mathlib.Data.DFinsupp.Module
+public import Mathlib.Order.KrullDimension
public import Mathlib.RingTheory.Ideal.Operations
/-!
@@ -392,6 +393,7 @@ theorem comap_le_comap_iff_of_surjective (hf : Function.Surjective f) (I J : Ide
le_comap_of_map_le ((map_comap_of_surjective f hf I).le.trans h)⟩
/-- The map on ideals induced by a surjective map preserves inclusion. -/
+@[simps]
def orderEmbeddingOfSurjective (hf : Function.Surjective f) : Ideal S ↪o Ideal R where
toFun := comap f
inj' _ _ eq := SetLike.ext' (Set.preimage_injective.mpr hf <| SetLike.ext'_iff.mp eq)
@@ -588,6 +590,15 @@ theorem comap_map_of_surjective (hf : Function.Surjective f) (I : Ideal R) :
add_sub_cancel s r⟩)
(sup_le (map_le_iff_le_comap.1 le_rfl) (comap_mono bot_le))
+theorem coheight_comap_of_surjective (hf : Function.Surjective f) (I : Ideal S) :
+ Order.coheight (I.comap f) = Order.coheight I := by
+ let φ := orderEmbeddingOfSurjective f hf
+ refine (Order.coheight_eq_of_strictMono φ φ.strictMono (fun J K h ↦ ⟨K.map f, ?_, ?_⟩) I).symm
+ · rw [← J.map_comap_of_surjective f hf]
+ apply lt_of_le_not_ge (map_mono h.le)
+ simpa [map_le_iff_le_comap, φ] using h.not_ge
+ · exact (K.comap_map_of_surjective f hf).trans (sup_of_le_left ((comap_mono bot_le).trans h.le))
+
/-- Correspondence theorem -/
def relIsoOfSurjective (hf : Function.Surjective f) :
Ideal S ≃o { p : Ideal R // comap f ⊥ ≤ p } where
From 2216b5b1ea909cc6bcd2c3b45516c1ece827135f Mon Sep 17 00:00:00 2001
From: teorth <199308+teorth@users.noreply.github.com>
Date: Fri, 19 Jun 2026 16:19:57 +0000
Subject: [PATCH 0194/1300] feat(NumberTheory/PrimeCounting):
primesBelow/primesLE as filters of Ioo/Ioc (#40654)
Added some minor variants of existing `primesBelow_eq_filter_Ico_one` type API to also handle `Ioo` and `Ioc` type intervals. This will be needed in a subsequent PR establishing Mertens' theorems.
Co-authored-by: Terence Tao
---
Mathlib/NumberTheory/PrimeCounting.lean | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/Mathlib/NumberTheory/PrimeCounting.lean b/Mathlib/NumberTheory/PrimeCounting.lean
index b88f94d8aa0873..094ba7830c3724 100644
--- a/Mathlib/NumberTheory/PrimeCounting.lean
+++ b/Mathlib/NumberTheory/PrimeCounting.lean
@@ -193,6 +193,14 @@ lemma primesLE_eq_filter_Icc_zero (n : ℕ) : primesLE n = filter Nat.Prime (Icc
ext p
simp [primesLE_eq_filter_range]
+lemma primesBelow_eq_filter_Ioo_zero (n : ℕ) : primesBelow n = filter Nat.Prime (Ioo 0 n) := by
+ ext p
+ simp +contextual [primesBelow_eq_filter_range, Nat.Prime.pos]
+
+lemma primesLE_eq_filter_Ioc_zero (n : ℕ) : primesLE n = filter Nat.Prime (Ioc 0 n) := by
+ ext p
+ simp +contextual [primesLE_eq_filter_range, Nat.Prime.pos]
+
lemma primesBelow_eq_filter_Ico_one (n : ℕ) : primesBelow n = filter Nat.Prime (Ico 1 n) := by
ext p
simp +contextual [primesBelow_eq_filter_range, Nat.Prime.one_le]
@@ -201,6 +209,14 @@ lemma primesLE_eq_filter_Icc_one (n : ℕ) : primesLE n = filter Nat.Prime (Icc
ext p
simp +contextual [primesLE_eq_filter_range, Nat.Prime.one_le]
+lemma primesBelow_eq_filter_Ioo_one (n : ℕ) : primesBelow n = filter Nat.Prime (Ioo 1 n) := by
+ ext p
+ simp +contextual [primesBelow_eq_filter_range, Nat.Prime.one_lt]
+
+lemma primesLE_eq_filter_Ioc_one (n : ℕ) : primesLE n = filter Nat.Prime (Ioc 1 n) := by
+ ext p
+ simp +contextual [primesLE_eq_filter_range, Nat.Prime.one_lt]
+
lemma primesBelow_eq_filter_Ico_two (n : ℕ) : primesBelow n = filter Nat.Prime (Ico 2 n) := by
ext p
simp +contextual [primesBelow_eq_filter_range, Nat.Prime.two_le]
From 413a92168ed3397410753fb2cd9373eb0d28824a Mon Sep 17 00:00:00 2001
From: Leonid Ryvkin <24719821+Ljon4ik4@users.noreply.github.com>
Date: Fri, 19 Jun 2026 17:03:37 +0000
Subject: [PATCH 0195/1300] feat: Lie-Rinehart subalgebras introduced (#39850)
This PR introduces subalgebras of Lie-Rinehart algebras. It defines the corresponding structures, introduces some basic api and shows that a Lie-Rinehart subalgebra is again a Lie-Rinehart algebra.
Co-authored-by: Oliver Nash
---
Mathlib.lean | 1 +
Mathlib/Algebra/LieRinehartAlgebra/Defs.lean | 4 +-
.../LieRinehartAlgebra/Subalgebra.lean | 232 ++++++++++++++++++
3 files changed, 235 insertions(+), 2 deletions(-)
create mode 100644 Mathlib/Algebra/LieRinehartAlgebra/Subalgebra.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index f2a1f9bc7ef3f4..b499769e07f489 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -764,6 +764,7 @@ public import Mathlib.Algebra.Lie.Weights.Killing
public import Mathlib.Algebra.Lie.Weights.Linear
public import Mathlib.Algebra.Lie.Weights.RootSystem
public import Mathlib.Algebra.LieRinehartAlgebra.Defs
+public import Mathlib.Algebra.LieRinehartAlgebra.Subalgebra
public import Mathlib.Algebra.LinearRecurrence
public import Mathlib.Algebra.ModEq
public import Mathlib.Algebra.Module.Basic
diff --git a/Mathlib/Algebra/LieRinehartAlgebra/Defs.lean b/Mathlib/Algebra/LieRinehartAlgebra/Defs.lean
index e9150dd43eb190..063ea6e14fe236 100644
--- a/Mathlib/Algebra/LieRinehartAlgebra/Defs.lean
+++ b/Mathlib/Algebra/LieRinehartAlgebra/Defs.lean
@@ -45,8 +45,6 @@ class LieRinehartAlgebra (R A L : Type*) [CommRing A] [LieRing L]
[CommRing R] [Algebra R A] [LieAlgebra R L] : Prop extends
IsScalarTower R A L, LieModule R L A
-namespace LieRinehartAlgebra
-
variable {R A₁ L₁ A₂ L₂ A₃ L₃ : Type*} [CommRing R]
[CommRing A₁] [LieRing L₁] [Module A₁ L₁] [LieRingModule L₁ A₁]
[CommRing A₂] [LieRing L₂] [Module A₂ L₂] [LieRingModule L₂ A₂]
@@ -72,6 +70,8 @@ instance : LieRinehartRing A₁ (Derivation R A₁ A₁) where
/-- The derivations of a commutative Algebra themselves form a LieRinehart-Algebra. -/
instance : LieRinehartAlgebra R A₁ (Derivation R A₁ A₁) where
+namespace LieRinehartAlgebra
+
/-- A morphism of Lie-Rinehart algebras, from `(A₁, L₁)` to `(A₂, L₂)`, consists of a pair of maps
`(σ, F)` where `σ : A₁ → A₂` is a morphism of algebras and `F` is a morphism of Lie algebras, which
respect the module structures.
diff --git a/Mathlib/Algebra/LieRinehartAlgebra/Subalgebra.lean b/Mathlib/Algebra/LieRinehartAlgebra/Subalgebra.lean
new file mode 100644
index 00000000000000..56250b377b7ffd
--- /dev/null
+++ b/Mathlib/Algebra/LieRinehartAlgebra/Subalgebra.lean
@@ -0,0 +1,232 @@
+/-
+Copyright (c) 2026 Leonid Ryvkin. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Leonid Ryvkin
+-/
+
+module
+
+public import Mathlib.Algebra.LieRinehartAlgebra.Defs
+
+/-!
+# Lie-Rinehart subalgebras
+
+This file defines Lie-Rinehart subalgebras of a Lie-Rinehart algebra and provides basic related
+definitions and results.
+
+## Main definitions/ statements:
+
+* `LieRinehartSubalgebra` as an `A`-submodule of `L` stable under the Lie bracket. (This is also
+applicable to Lie-Rinehart rings and more generally any `A`-module with a Lie ring structure).
+
+* A Lie-Rinehart subalgebra of a Lie-Rinehart ring is a Lie-Rinehart ring
+
+* A Lie-Rinehart subalgebra of a Lie-Rinehart algebra is a Lie-Rinehart algebra over the same ring.
+
+-/
+
+public section
+
+open scoped LieRinehartAlgebra
+
+variable (A L : Type*) [CommRing A] [LieRing L] [Module A L]
+
+/-- A Lie-Rinehart subalgebra of a Lie-Rinehart algebra `(R A L)` is an `A`-submodule of `L`, which
+is stable under the Lie bracket. (This can be defined independently of `R` and most
+Lie-Rinehart algebra axioms). -/
+structure LieRinehartSubalgebra extends Submodule A L where
+ lie_mem' {a b} : a ∈ carrier → b ∈ carrier → ⁅a, b⁆ ∈ carrier
+
+instance : Zero (LieRinehartSubalgebra A L) :=
+ ⟨⟨0, fun {x y hx _hy} ↦ by simp [(Submodule.mem_bot A).mp hx]⟩⟩
+
+instance : Inhabited (LieRinehartSubalgebra A L) :=
+ ⟨0⟩
+
+namespace LieRinehartSubalgebra
+
+instance : SetLike (LieRinehartSubalgebra A L) L where
+ coe L' := L'.carrier
+ coe_injective L' L'' h := by
+ rcases L'
+ rcases L''
+ congr
+ exact SetLike.coe_injective h
+
+instance : PartialOrder (LieRinehartSubalgebra A L) := .ofSetLike (LieRinehartSubalgebra A L) L
+
+instance : AddSubgroupClass (LieRinehartSubalgebra A L) L where
+ add_mem := Submodule.add_mem _
+ zero_mem L' := L'.zero_mem'
+ neg_mem {L'} x hx := show -x ∈ L'.toSubmodule from neg_mem hx
+
+instance : SMulMemClass (LieRinehartSubalgebra A L) A L where
+ smul_mem {s} := SMulMemClass.smul_mem (s := s.toSubmodule)
+
+/-- A Lie-Rinehart subalgebra forms a Lie ring. -/
+instance lieRing (L' : LieRinehartSubalgebra A L) : LieRing L' where
+ bracket x y := ⟨⁅x.val, y.val⁆, L'.lie_mem' x.property y.property⟩
+ lie_add x y z := by aesop
+ add_lie x y z := by aesop
+ lie_self x := by aesop
+ leibniz_lie x y z := by aesop
+
+variable {A L}
+variable (L' : LieRinehartSubalgebra A L)
+
+protected theorem zero_mem : (0 : L) ∈ L' :=
+ zero_mem L'
+
+protected theorem add_mem {x y : L} : x ∈ L' → y ∈ L' → (x + y : L) ∈ L' :=
+ add_mem
+
+protected theorem sub_mem {x y : L} : x ∈ L' → y ∈ L' → (x - y : L) ∈ L' :=
+ sub_mem
+
+protected theorem smul_mem (t : A) {x : L} (h : x ∈ L') : t • x ∈ L' :=
+ SMulMemClass.smul_mem _ h
+
+theorem lie_mem {x y : L} (hx : x ∈ L') (hy : y ∈ L') : (⁅x, y⁆ : L) ∈ L' :=
+ L'.lie_mem' hx hy
+
+theorem mem_carrier {x : L} : x ∈ L'.carrier ↔ x ∈ (L' : Set L) :=
+ Iff.rfl
+
+theorem mem_mk_iff (S : Set L) (h₁ h₂ h₃ h₄) {x : L} :
+ x ∈ (⟨⟨⟨⟨S, h₁⟩, h₂⟩, h₃⟩, h₄⟩ : LieRinehartSubalgebra A L) ↔ x ∈ S :=
+ Iff.rfl
+
+@[simp]
+theorem mem_toSubmodule {x : L} : x ∈ L'.toSubmodule ↔ x ∈ L' :=
+ Iff.rfl
+
+@[simp]
+theorem mem_mk_iff' (p : Submodule A L) (h) {x : L} :
+ x ∈ (⟨p, h⟩ : LieRinehartSubalgebra A L) ↔ x ∈ p :=
+ Iff.rfl
+
+theorem mem_coe {x : L} : x ∈ (L' : Set L) ↔ x ∈ L' :=
+ Iff.rfl
+
+@[simp, norm_cast]
+theorem coe_bracket (x y : L') : (↑⁅x, y⁆ : L) = ⁅(↑x : L), ↑y⁆ :=
+ rfl
+
+theorem ext_iff (x y : L') : x = y ↔ (x : L) = y := Subtype.ext_iff
+
+theorem coe_zero_iff_zero (x : L') : (x : L) = 0 ↔ x = 0 := (ext_iff L' x 0).symm
+
+@[ext]
+theorem ext (L₁' L₂' : LieRinehartSubalgebra A L) (h : ∀ x, x ∈ L₁' ↔ x ∈ L₂') : L₁' = L₂' :=
+ SetLike.ext h
+
+theorem ext_iff' (L₁' L₂' : LieRinehartSubalgebra A L) : L₁' = L₂' ↔ ∀ x, x ∈ L₁' ↔ x ∈ L₂' :=
+ SetLike.ext_iff
+
+@[simp]
+theorem mk_coe (S : Set L) (h₁ h₂ h₃ h₄) :
+ ((⟨⟨⟨⟨S, h₁⟩, h₂⟩, h₃⟩, h₄⟩ : LieRinehartSubalgebra A L) : Set L) = S :=
+ rfl
+
+theorem toSubmodule_mk (p : Submodule A L) (h) :
+ ({ p with lie_mem' := h } : LieRinehartSubalgebra A L).toSubmodule = p := rfl
+
+theorem coe_injective : Function.Injective ((↑) : LieRinehartSubalgebra A L → Set L) :=
+ SetLike.coe_injective
+
+@[norm_cast]
+theorem coe_set_eq (L₁' L₂' : LieRinehartSubalgebra A L) : (L₁' : Set L) = L₂' ↔ L₁' = L₂' :=
+ SetLike.coe_set_eq
+
+theorem toSubmodule_injective : Function.Injective (toSubmodule (A := A) (L := L)) := by
+ intro L₁' L₂' h
+ rw [SetLike.ext'_iff] at h
+ rw [← coe_set_eq]
+ exact h
+
+theorem coe_toSubmodule : (L'.toSubmodule : Set L) = L' :=
+ rfl
+
+section LieModule
+
+variable {M : Type*} [AddCommGroup M] [LieRingModule L M]
+
+instance : Bracket L' M where
+ bracket x m := ⁅(x : L), m⁆
+
+@[simp]
+theorem coe_bracket_of_module (x : L') (m : M) : ⁅x, m⁆ = ⁅(x : L), m⁆ :=
+ rfl
+
+instance : IsLieTower L' L M where
+ leibniz_lie x y m := leibniz_lie x.val y m
+
+/-- Given a Lie-Rinehart algebra `L` containing a LieRinehart subalgebra `L' ⊆ L`, together with a
+Lie ring module `M` of `L`, we may regard `M` as a Lie ring module of `L'` by restriction. -/
+instance lieRingModule : LieRingModule L' M where
+ add_lie x y m := add_lie (x : L) y m
+ lie_add x y m := lie_add (x : L) y m
+ leibniz_lie x y m := leibniz_lie x (y : L) m
+
+end LieModule
+
+variable [LieRingModule L A] [LieRinehartRing A L]
+
+/-- A Lie-Rinehart subalgebra of a Lie-Rinehart ring forms a new Lie-Rinehart ring. -/
+instance : LieRinehartRing A L' where
+ lie_smul_eq_mul' a b x := LieRinehartRing.lie_smul_eq_mul a b (x : L)
+ leibniz_mul_right' x a b := LieRinehartRing.leibniz_mul_right (x : L) a b
+ leibniz_smul_right' _ _ _ := by simp [ext_iff]
+
+variable (R : Type*) [CommRing R] [Algebra R A] [LieAlgebra R L] [LieRinehartAlgebra R A L]
+
+/-- A Lie-Rinehart subalgebra of a Lie-Rinehart algebra forms a Lie algebra. -/
+instance lieAlgebra : LieAlgebra R L' where
+ lie_smul := by aesop
+
+/-- Converts a Lie-Rinehart subalgebra to the corresponding Lie subalgebra. -/
+@[expose] def toLieSubalgebra : LieSubalgebra R L where
+ toSubmodule := L'.toSubmodule.restrictScalars R
+ lie_mem' := L'.lie_mem'
+
+theorem toLieSubalgebra_injective : Function.Injective (fun L' =>
+ L'.toLieSubalgebra R : LieRinehartSubalgebra A L → LieSubalgebra R L) := fun L₁' L₂' h ↦ by
+ rw [SetLike.ext'_iff] at h
+ rw [← coe_set_eq]
+ exact h
+
+@[simp]
+theorem toLieSubalgebra_inj (L₁' L₂' : LieRinehartSubalgebra A L) :
+ (L₁'.toLieSubalgebra R) = (L₂'.toLieSubalgebra R) ↔ L₁' = L₂' :=
+ (toLieSubalgebra_injective R).eq_iff
+
+theorem coe_toLieSubalgebra : ((L'.toLieSubalgebra R) : Set L) = L' := rfl
+
+section LieModule
+
+variable {M : Type*} [AddCommGroup M] [LieRingModule L M] [Module R M]
+
+/-- Given a Lie-Rinehart algebra `L` containing a LieRinehart subalgebra `L' ⊆ L`, together with a
+ Lie module `M` of `L`, we may regard `M` as a Lie module of `L'` by restriction. -/
+instance lieModule [LieModule R L M] : LieModule R L' M where
+ smul_lie t x m := by
+ rw [coe_bracket_of_module, Submodule.coe_smul_of_tower, smul_lie, coe_bracket_of_module]
+ lie_smul t x m := by simp only [coe_bracket_of_module, lie_smul]
+
+end LieModule
+
+/-- A Lie-Rinehart subalgebra forms a new Lie-Rinehart algebra. -/
+instance : LieRinehartAlgebra R A L' where
+
+/-- The embedding of a Lie-Rinehart subalgebra into the ambient space as a morphism of
+Lie-Rinehart algebras. -/
+@[expose] def incl : L' →ₗ⁅(AlgHom.id R A)⁆ L where
+ __ := L'.toSubmodule.subtype.restrictScalars R
+ map_lie' {x y} := coe_bracket L' x y
+ map_smul_apply' a x := L'.toSubmodule.subtype.map_smul a x
+ apply_lie' a x := AlgHom.id_apply ⁅x, a⁆
+
+@[simp]
+theorem coe_incl : ⇑(L'.incl R) = ((↑) : L' → L) := rfl
+
+end LieRinehartSubalgebra
From 3685731497f316bb1b3ecbfa8282eca31494b20b Mon Sep 17 00:00:00 2001
From: Michael Rothgang <10105016+grunweg@users.noreply.github.com>
Date: Fri, 19 Jun 2026 18:16:51 +0000
Subject: [PATCH 0196/1300] feat: add
`contMDiffWithinAt_iff_of_mem_maximalAtlas'` (#40810)
Add a version of `contMDiffWithinAt_iff_of_mem_maximalAtlas` specialized of to the source chart being the preferred chart at that point: this way, we can apply it without requiring an `IsManifold` instance to conclude
the extended chart being in the maximal atlas. This allows avoiding an `IsManifold` hypothesis in #28865.
---
Mathlib/Geometry/Manifold/ContMDiff/Defs.lean | 66 ++++++++++++-------
1 file changed, 42 insertions(+), 24 deletions(-)
diff --git a/Mathlib/Geometry/Manifold/ContMDiff/Defs.lean b/Mathlib/Geometry/Manifold/ContMDiff/Defs.lean
index 1a717ac3747ca5..e823fee3758e25 100644
--- a/Mathlib/Geometry/Manifold/ContMDiff/Defs.lean
+++ b/Mathlib/Geometry/Manifold/ContMDiff/Defs.lean
@@ -281,36 +281,37 @@ theorem contMDiffAt_iff_target {x : M} :
ContinuousAt f x ∧ ContMDiffAt I 𝓘(𝕜, E') n (extChartAt I' (f x) ∘ f) x := by
rw [ContMDiffAt, ContMDiffAt, contMDiffWithinAt_iff_target, continuousWithinAt_univ]
+theorem continuousWithinAt_iff_source :
+ ContinuousWithinAt f s x ↔
+ ContinuousWithinAt (f ∘ (extChartAt I x).symm)
+ ((extChartAt I x).symm ⁻¹' s ∩ range I) (extChartAt I x x) := by
+ refine ⟨fun h ↦ ?_, fun h ↦ ?_⟩
+ · apply h.comp_of_eq
+ · exact (continuousAt_extChartAt_symm x).continuousWithinAt
+ · exact (mapsTo_preimage _ _).mono_left inter_subset_left
+ · exact extChartAt_to_inv x
+ · rw [← continuousWithinAt_inter (extChartAt_source_mem_nhds (I := I) x)]
+ have : ContinuousWithinAt ((f ∘ ↑(extChartAt I x).symm) ∘ ↑(extChartAt I x))
+ (s ∩ (extChartAt I x).source) x := by
+ apply h.comp (continuousAt_extChartAt x).continuousWithinAt
+ intro y hy
+ have : (chartAt H x).symm ((chartAt H x) y) = y :=
+ OpenPartialHomeomorph.left_inv _ (by simpa using hy.2)
+ simpa [this] using hy.1
+ apply this.congr
+ · intro y hy
+ have : (chartAt H x).symm ((chartAt H x) y) = y :=
+ OpenPartialHomeomorph.left_inv _ (by simpa using hy.2)
+ simp [this]
+ · simp
+
/-- One can reformulate being `Cⁿ` within a set at a point as being `Cⁿ` in the source space when
composing with the extended chart. -/
theorem contMDiffWithinAt_iff_source :
ContMDiffWithinAt I I' n f s x ↔
ContMDiffWithinAt 𝓘(𝕜, E) I' n (f ∘ (extChartAt I x).symm)
((extChartAt I x).symm ⁻¹' s ∩ range I) (extChartAt I x x) := by
- simp_rw [ContMDiffWithinAt, liftPropWithinAt_iff']
- have : ContinuousWithinAt f s x
- ↔ ContinuousWithinAt (f ∘ ↑(extChartAt I x).symm) (↑(extChartAt I x).symm ⁻¹' s ∩ range ↑I)
- (extChartAt I x x) := by
- refine ⟨fun h ↦ ?_, fun h ↦ ?_⟩
- · apply h.comp_of_eq
- · exact (continuousAt_extChartAt_symm x).continuousWithinAt
- · exact (mapsTo_preimage _ _).mono_left inter_subset_left
- · exact extChartAt_to_inv x
- · rw [← continuousWithinAt_inter (extChartAt_source_mem_nhds (I := I) x)]
- have : ContinuousWithinAt ((f ∘ ↑(extChartAt I x).symm) ∘ ↑(extChartAt I x))
- (s ∩ (extChartAt I x).source) x := by
- apply h.comp (continuousAt_extChartAt x).continuousWithinAt
- intro y hy
- have : (chartAt H x).symm ((chartAt H x) y) = y :=
- OpenPartialHomeomorph.left_inv _ (by simpa using hy.2)
- simpa [this] using hy.1
- apply this.congr
- · intro y hy
- have : (chartAt H x).symm ((chartAt H x) y) = y :=
- OpenPartialHomeomorph.left_inv _ (by simpa using hy.2)
- simp [this]
- · simp
- rw [← this]
+ simp_rw [ContMDiffWithinAt, liftPropWithinAt_iff', ← continuousWithinAt_iff_source]
simp only [ContDiffWithinAtProp, mfld_simps, preimage_comp, comp_assoc]
/-- One can reformulate being `Cⁿ` at a point as being `Cⁿ` in the source space when
@@ -383,6 +384,23 @@ theorem contMDiffWithinAt_iff_of_mem_maximalAtlas (he : e ∈ maximalAtlas I n M
((e.extend I).symm ⁻¹' s ∩ range I) (e.extend I x) :=
(contDiffWithinAt_localInvariantProp n).liftPropWithinAt_indep_chart he hx he' hy
+/-- An alternative version of `contMDiffWithinAt_iff_of_mem_maximalAtlas` which takes a
+chart `e'` in the target in the maximal atlas, but uses the preferred chart on the domain. -/
+theorem contMDiffWithinAt_iff_of_mem_maximalAtlas'
+ (he' : e' ∈ maximalAtlas I' n M') (hy : f x ∈ e'.source) :
+ ContMDiffWithinAt I I' n f s x ↔
+ ContinuousWithinAt f s x ∧
+ ContDiffWithinAt 𝕜 n (e'.extend I' ∘ f ∘ (extChartAt I x).symm)
+ ((extChartAt I x).symm ⁻¹' s ∩ range I) (extChartAt I x x) := by
+ rw [contMDiffWithinAt_iff_source,
+ contMDiffWithinAt_iff_target_of_mem_maximalAtlas he' (by simpa)]
+ apply and_congr continuousWithinAt_iff_source.symm
+ -- TODO: this is `contMDiffWithinAt_iff_contDiffWithinAt` copied,
+ -- which is not put here for import reasons
+ simp +contextual only [ContMDiffWithinAt, liftPropWithinAt_iff',
+ ContDiffWithinAtProp, iff_def, mfld_simps]
+ exact ContDiffWithinAt.continuousWithinAt
+
/-- An alternative formulation of `contMDiffWithinAt_iff_of_mem_maximalAtlas`
if the set `s` lies in `e.source`. -/
theorem contMDiffWithinAt_iff_image
From 1220aa6023f69a3283decf8e204984a6075cd9bd Mon Sep 17 00:00:00 2001
From: Eric Wieser <425260+eric-wieser@users.noreply.github.com>
Date: Fri, 19 Jun 2026 19:19:23 +0000
Subject: [PATCH 0197/1300] refactor: make `{Con,AddCon,RingCon}.congr` match
`Quotient.congr` (#40819)
This adds an explicit equivalence argument, rather than defaulting it to `refl`.
Also adds a missing `symm` lemma for each.
---
Mathlib/GroupTheory/Congruence/Basic.lean | 30 ++++++++-----
Mathlib/RingTheory/Congruence/Hom.lean | 55 ++++++++++++++---------
2 files changed, 52 insertions(+), 33 deletions(-)
diff --git a/Mathlib/GroupTheory/Congruence/Basic.lean b/Mathlib/GroupTheory/Congruence/Basic.lean
index 3700a2bd93fbdf..ef9728e074efa5 100644
--- a/Mathlib/GroupTheory/Congruence/Basic.lean
+++ b/Mathlib/GroupTheory/Congruence/Basic.lean
@@ -63,17 +63,25 @@ def pi {ι : Type*} {f : ι → Type*} [∀ i, Mul (f i)] (C : ∀ i, Con (f i))
{ @piSetoid _ _ fun i => (C i).toSetoid with
mul' := fun h1 h2 i => (C i).mul (h1 i) (h2 i) }
-/-- Makes an isomorphism of quotients by two congruence relations, given that the relations are
-equal. -/
-@[to_additive /-- Makes an additive isomorphism of quotients by two additive congruence relations,
-given that the relations are equal. -/]
-protected def congr {c d : Con M} (h : c = d) : c.Quotient ≃* d.Quotient :=
- { Quotient.congr (Equiv.refl M) <| by apply Con.ext_iff.mp h with
- map_mul' := fun x y => by rcases x with ⟨⟩; rcases y with ⟨⟩; rfl }
+/-- A multiplicative equivalence `e : α ≃* β` generates an equivalence between quotient spaces,
+if it is compatible with the relations. -/
+@[to_additive
+/-- An additive equivalence `e : α ≃+ β` generates an equivalence between quotient spaces,
+if it is compatible with the relations. -/]
+protected def congr {c : Con M} {d : Con N} (e : M ≃* N) (h : c = d.comap e (map_mul e)) :
+ c.Quotient ≃* d.Quotient where
+ __ := Quotient.congr e <| by apply Con.ext_iff.mp h
+ map_mul' := by rintro ⟨x⟩ ⟨y⟩; exact congrArg toQuotient (e.map_mul x y)
@[to_additive (attr := simp)]
-theorem congr_mk {c d : Con M} (h : c = d) (a : M) :
- Con.congr h (a : c.Quotient) = (a : d.Quotient) := rfl
+theorem congr_mk {c : Con M} {d : Con N} (e : M ≃* N) (h : c = d.comap e (map_mul e)) (a : M) :
+ Con.congr e h (a : c.Quotient) = (e a : d.Quotient) := rfl
+
+@[to_additive (attr := simp)]
+theorem congr_symm {c : Con M} {d : Con N} (e : M ≃* N) (h : c = d.comap e (map_mul e)) :
+ (Con.congr e h).symm =
+ Con.congr e.symm (ext <| e.surjective.forall₂.2 <| by simp [h]) :=
+ rfl
@[to_additive]
theorem comap_conGen_equiv {M N : Type*} [Mul M] [Mul N] (f : MulEquiv M N) (rel : N → N → Prop) :
@@ -220,7 +228,7 @@ noncomputable def quotientKerEquivOfSurjective (f : M →* P) (hf : Surjective f
AddCon N -/]
noncomputable def comapQuotientEquivOfSurj (c : Con M) (f : N →* M) (hf : Function.Surjective f) :
(Con.comap f f.map_mul c).Quotient ≃* c.Quotient :=
- (Con.congr Con.comap_eq).trans <| Con.quotientKerEquivOfSurjective (c.mk'.comp f) <|
+ (Con.congr (.refl _) Con.comap_eq).trans <| Con.quotientKerEquivOfSurjective (c.mk'.comp f) <|
Con.mk'_surjective.comp hf
@[to_additive (attr := simp)]
@@ -244,7 +252,7 @@ lemma comapQuotientEquivOfSurj_symm_mk' (c : Con M) (f : N ≃* M) (x : N) :
@[to_additive /-- The second isomorphism theorem for `AddMonoid`s. -/]
noncomputable def comapQuotientEquiv (f : N →* M) :
(comap f f.map_mul c).Quotient ≃* MonoidHom.mrange (c.mk'.comp f) :=
- (Con.congr comap_eq).trans <| quotientKerEquivRange <| c.mk'.comp f
+ (Con.congr (.refl _) comap_eq).trans <| quotientKerEquivRange <| c.mk'.comp f
/-- The **third isomorphism theorem for monoids**. -/
@[to_additive /-- The third isomorphism theorem for `AddMonoid`s. -/]
diff --git a/Mathlib/RingTheory/Congruence/Hom.lean b/Mathlib/RingTheory/Congruence/Hom.lean
index 29f28b9dcabf2a..485dbe2a79ad80 100644
--- a/Mathlib/RingTheory/Congruence/Hom.lean
+++ b/Mathlib/RingTheory/Congruence/Hom.lean
@@ -80,16 +80,21 @@ theorem comap_eq {g : N →+* M} :
c.comap g = ker (c.mk'.comp g) := by
rw [ker_comp, ker_mk'_eq]
-/-- Makes an isomorphism of quotients by two ring congruence
-relations, given that the relations are equal. -/
-protected def congr (h : c = d) :
- c.Quotient ≃+* d.Quotient :=
- { Quotient.congr (Equiv.refl M) <| by apply RingCon.ext_iff.mp h with
- map_add' x y := by rcases x with ⟨⟩; rcases y with ⟨⟩; rfl
- map_mul' x y := by rcases x with ⟨⟩; rcases y with ⟨⟩; rfl }
-
-@[simp] theorem congr_mk (h : c = d) (a : M) :
- RingCon.congr h (a : c.Quotient) = (a : d.Quotient) := rfl
+/-- An isomorphism of rings `e : M ≃+* N` generates an isomorphism between quotient spaces,
+if it is compatible with the relations. -/
+protected def congr {c : RingCon M} {d : RingCon N} (e : M ≃+* N) (h : c = d.comap e) :
+ c.Quotient ≃+* d.Quotient where
+ __ := Quotient.congr e <| by apply RingCon.ext_iff.mp h
+ map_mul' := by rintro ⟨x⟩ ⟨y⟩; exact congrArg toQuotient (e.map_mul x y)
+ map_add' := by rintro ⟨x⟩ ⟨y⟩; exact congrArg toQuotient (e.map_add x y)
+
+@[simp] theorem congr_mk {c : RingCon M} {d : RingCon N} (e : M ≃+* N) (h : c = d.comap e) (a : M) :
+ RingCon.congr e h (a : c.Quotient) = (e a : d.Quotient) := rfl
+
+@[simp] theorem congr_symm {c : RingCon M} {d : RingCon N} (e : M ≃+* N) (h : c = d.comap e) :
+ (RingCon.congr e h).symm =
+ RingCon.congr e.symm (ext <| e.surjective.forall₂.2 <| by simp [h]) :=
+ rfl
/-- Given a function `f`, the smallest ring congruence relation containing the binary
relation on `f`'s image defined by '`x ≈ y` iff the elements of `f⁻¹(x)` are related to
@@ -321,7 +326,7 @@ noncomputable def comapQuotientEquivOfSurj
(c : RingCon M) (f : N →+* M) (hf : Function.Surjective f)
{d : RingCon N} (hcd : d = c.comap f) :
d.Quotient ≃+* c.Quotient :=
- (RingCon.congr (hcd.trans c.comap_eq)).trans
+ (RingCon.congr (.refl _) (hcd.trans c.comap_eq)).trans
<| RingCon.quotientKerEquivOfSurjective (c.mk'.comp f)
(c.mk'_surjective.comp hf)
@@ -348,7 +353,7 @@ noncomputable def comapQuotientEquivOfSurj
noncomputable def comapQuotientEquivRangeS (f : N →+* M)
{d : RingCon N} (hcd : d = comap c f) :
d.Quotient ≃+* RingHom.rangeS (c.mk'.comp f) :=
- (RingCon.congr (hcd.trans comap_eq)).trans <| quotientKerEquivRangeS <| c.mk'.comp f
+ (RingCon.congr (.refl _) (hcd.trans comap_eq)).trans <| quotientKerEquivRangeS <| c.mk'.comp f
@[simp] theorem comapQuotientEquivRangeS_mk (f : N →+* M)
{d : RingCon N} (hcd : d = comap c f) (x : N) :
@@ -449,15 +454,21 @@ variable {R : Type*} [CommSemiring R]
variable {c d : RingCon M} {f : M →ₐ[R] P}
variable (R) in
-/-- Makes an algebra isomorphism of quotients by two ring congruence
-relations, given that the relations are equal. -/
-protected def congrₐ {c d : RingCon M} (h : c = d) :
- c.Quotient ≃ₐ[R] d.Quotient :=
- { RingCon.congr h with
- commutes' _ := rfl }
-
-theorem congrₐ_mk {c d : RingCon M} (h : c = d) (a : M) :
- RingCon.congrₐ R h (a : c.Quotient) = (a : d.Quotient) :=
+/-- An isomorphism of algebras `e : M ≃ₐ[R] N` generates an isomorphism between quotient spaces,
+if it is compatible with the relations. -/
+protected def congrₐ {c : RingCon M} {d : RingCon N} (e : M ≃ₐ[R] N) (h : c = d.comap e) :
+ c.Quotient ≃ₐ[R] d.Quotient where
+ __ := RingCon.congr e h
+ commutes' r := by simp [← coe_algebraMap]
+
+@[simp]
+theorem congrₐ_mk {c : RingCon M} {d : RingCon N} (e : M ≃ₐ[R] N) (h : c = d.comap e) (a : M) :
+ RingCon.congrₐ R e h (a : c.Quotient) = (e a : d.Quotient) :=
+ rfl
+
+@[simp] theorem congrₐ_symm {c : RingCon M} {d : RingCon N} (e : M ≃ₐ[R] N) (h : c = d.comap e) :
+ (RingCon.congrₐ R e h).symm =
+ RingCon.congrₐ R e.symm (ext <| e.surjective.forall₂.2 <| by simp [h]) :=
rfl
theorem range_mkₐ : AlgHom.range (mkₐ R c) = ⊤ :=
@@ -570,7 +581,7 @@ theorem quotientKerEquivRangeₐ_comp_mkₐ (φ : M →ₐ[R] N) :
/-- The **second isomorphism theorem for algebras**. -/
noncomputable def comapQuotientEquivRangeₐ (f : N →ₐ[R] M) {d : RingCon N} (h : d = comap c f) :
d.Quotient ≃ₐ[R] AlgHom.range ((c.mkₐ _).comp f) :=
- (RingCon.congrₐ R (h.trans comap_eq)).trans <| quotientKerEquivRangeₐ ((c.mkₐ _).comp f)
+ (RingCon.congrₐ R .refl (h.trans comap_eq)).trans <| quotientKerEquivRangeₐ ((c.mkₐ _).comp f)
theorem comapQuotientEquivRangeₐ_mk (f : N →ₐ[R] M) {d : RingCon N} (h : d = comap c f) (x : N) :
c.comapQuotientEquivRangeₐ f h x = ⟨f x, AlgHom.mem_range_self _ x⟩ :=
From 5392b8aa32a2d5f6d24ab5c9e76ea4fd1c593538 Mon Sep 17 00:00:00 2001
From: Eric Wieser <425260+eric-wieser@users.noreply.github.com>
Date: Fri, 19 Jun 2026 19:54:38 +0000
Subject: [PATCH 0198/1300] chore: move RingQuot results (#40823)
This is prework for swapping `FreeProduct` to use `RingCon` instead of `RingQuot`; at which point this would allow the import to be removed from that file entirely.
I renamed `R` to `S` in the move, to match the ambient variables in the destination.
I haven't attempted to golf the proofs since my intent is to ultimately deprecate the entire destination file.
---
Mathlib/Algebra/RingQuot.lean | 34 +++++++++++++++++-
Mathlib/LinearAlgebra/FreeProduct/Basic.lean | 37 --------------------
2 files changed, 33 insertions(+), 38 deletions(-)
diff --git a/Mathlib/Algebra/RingQuot.lean b/Mathlib/Algebra/RingQuot.lean
index 6bb7fc25be788b..b5a17e7e40604c 100644
--- a/Mathlib/Algebra/RingQuot.lean
+++ b/Mathlib/Algebra/RingQuot.lean
@@ -5,7 +5,7 @@ Authors: Kim Morrison
-/
module
-public import Mathlib.Algebra.Algebra.Hom
+public import Mathlib.Algebra.Algebra.Equiv
public import Mathlib.RingTheory.Congruence.Basic
public import Mathlib.RingTheory.Ideal.Quotient.Defs
public import Mathlib.RingTheory.Ideal.Span
@@ -546,6 +546,38 @@ theorem eq_liftAlgHom_comp_mkAlgHom {s : A → A → Prop} (f : RingQuot s →
f = liftAlgHom S ⟨f.comp (mkAlgHom S s), fun _ _ h ↦ congr_arg f (mkAlgHom_rel S h)⟩ :=
liftAlgHom_unique S (f.comp (mkAlgHom S s)) (fun _ _ h ↦ congr_arg (⇑f) (mkAlgHom_rel S h)) f rfl
+open scoped Function -- required for scoped `on` notation
+
+variable {S}
+
+/-- If two `S`-algebras are `S`-equivalent and their quotients by a relation `rel` are defined,
+then their quotients are also `S`-equivalent.
+
+(Special case of the third isomorphism theorem.) -/
+def algEquivQuotAlgEquiv (f : A ≃ₐ[S] B) (rel : A → A → Prop) :
+ RingQuot rel ≃ₐ[S] RingQuot (rel on f.symm) :=
+ AlgEquiv.ofAlgHom
+ (RingQuot.liftAlgHom S (s := rel)
+ ⟨AlgHom.comp (RingQuot.mkAlgHom S (rel on f.symm)) f,
+ fun x y h_rel ↦ by
+ apply RingQuot.mkAlgHom_rel
+ simpa [Function.onFun]⟩)
+ ((RingQuot.liftAlgHom S (s := rel on f.symm)
+ ⟨AlgHom.comp (RingQuot.mkAlgHom S rel) f.symm,
+ fun x y h ↦ by apply RingQuot.mkAlgHom_rel; simpa⟩))
+ (by ext b; simp) (by ext a; simp)
+
+/-- If two (semi)rings are equivalent and their quotients by a relation `rel` are defined,
+then their quotients are also equivalent.
+
+(Special case of `algEquivQuotAlgEquiv` when `S = ℕ`, which in turn is a special
+case of the third isomorphism theorem.) -/
+def equivQuotEquiv (f : A ≃+* B) (rel : A → A → Prop) :
+ RingQuot rel ≃+* RingQuot (rel on f.symm) :=
+ let f_alg : A ≃ₐ[ℕ] B :=
+ AlgEquiv.ofRingEquiv (f := f) (fun n ↦ by simp)
+ algEquivQuotAlgEquiv f_alg rel |>.toRingEquiv
+
end Algebra
end RingQuot
diff --git a/Mathlib/LinearAlgebra/FreeProduct/Basic.lean b/Mathlib/LinearAlgebra/FreeProduct/Basic.lean
index 48423cfb9ad65b..af6700f7588803 100644
--- a/Mathlib/LinearAlgebra/FreeProduct/Basic.lean
+++ b/Mathlib/LinearAlgebra/FreeProduct/Basic.lean
@@ -65,43 +65,6 @@ theorem induction_lon {R : Type*} [Semiring R] {ι : Type*} [DecidableEq ι]
end DirectSum
-namespace RingQuot
-universe uS uA uB
-
-open scoped Function -- required for scoped `on` notation
-
-/-- If two `R`-algebras are `R`-equivalent and their quotients by a relation `rel` are defined,
-then their quotients are also `R`-equivalent.
-
-(Special case of the third isomorphism theorem.) -/
-def algEquivQuotAlgEquiv
- {R : Type u} [CommSemiring R] {A B : Type v} [Semiring A] [Semiring B]
- [Algebra R A] [Algebra R B] (f : A ≃ₐ[R] B) (rel : A → A → Prop) :
- RingQuot rel ≃ₐ[R] RingQuot (rel on f.symm) :=
- AlgEquiv.ofAlgHom
- (RingQuot.liftAlgHom R (s := rel)
- ⟨AlgHom.comp (RingQuot.mkAlgHom R (rel on f.symm)) f,
- fun x y h_rel ↦ by
- apply RingQuot.mkAlgHom_rel
- simpa [Function.onFun]⟩)
- ((RingQuot.liftAlgHom R (s := rel on f.symm)
- ⟨AlgHom.comp (RingQuot.mkAlgHom R rel) f.symm,
- fun x y h ↦ by apply RingQuot.mkAlgHom_rel; simpa⟩))
- (by ext b; simp) (by ext a; simp)
-
-/-- If two (semi)rings are equivalent and their quotients by a relation `rel` are defined,
-then their quotients are also equivalent.
-
-(Special case of `algEquiv_quot_algEquiv` when `R = ℕ`, which in turn is a special
-case of the third isomorphism theorem.) -/
-def equivQuotEquiv {A B : Type v} [Semiring A] [Semiring B] (f : A ≃+* B) (rel : A → A → Prop) :
- RingQuot rel ≃+* RingQuot (rel on f.symm) :=
- let f_alg : A ≃ₐ[ℕ] B :=
- AlgEquiv.ofRingEquiv (f := f) (fun n ↦ by simp)
- algEquivQuotAlgEquiv f_alg rel |>.toRingEquiv
-
-end RingQuot
-
open TensorAlgebra DirectSum TensorPower
variable {I : Type u} [DecidableEq I] {i : I} -- The type of the indexing set
From 7ba890ea1765d6395f6236529f9e4e8cae006bb0 Mon Sep 17 00:00:00 2001
From: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com>
Date: Fri, 19 Jun 2026 20:08:12 +0000
Subject: [PATCH 0199/1300] feat: add `Homeomorph.chartedSpace` (#39107)
Mathlib already has `IsLocalHomeomorph.chartedSpace`, which we can re-use.
#40202 shows that this charted space is a `C^n` manifold whenever the initial manifold is one.
---
Mathlib/Geometry/Manifold/ChartedSpace.lean | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Mathlib/Geometry/Manifold/ChartedSpace.lean b/Mathlib/Geometry/Manifold/ChartedSpace.lean
index c5f1f48b6ac267..59a70521d47941 100644
--- a/Mathlib/Geometry/Manifold/ChartedSpace.lean
+++ b/Mathlib/Geometry/Manifold/ChartedSpace.lean
@@ -591,6 +591,12 @@ def IsLocalHomeomorph.chartedSpace
ChartedSpace H M' :=
hf.chartedSpaceOfRightInverse hf'.hasRightInverse.choose_spec
+/-- Given a homeomorphism `f : M ≃ₜ M'`, endow `M'` with a `ChartedSpace` structure by pushing
+forward the `ChartedSpace` structure from `M`. -/
+@[implicit_reducible]
+def Homeomorph.chartedSpace (f : M ≃ₜ M') : ChartedSpace H M' :=
+ f.isLocalHomeomorph.chartedSpace f.surjective
+
end IsLocalHomeomorph
end Constructions
From 005f0aa67b6922eb1a8f5209fd0e707aeb867945 Mon Sep 17 00:00:00 2001
From: Eric Wieser <425260+eric-wieser@users.noreply.github.com>
Date: Fri, 19 Jun 2026 20:08:14 +0000
Subject: [PATCH 0200/1300] chore: remove an unused import (#40825)
---
Mathlib/RingTheory/Congruence/Hom.lean | 1 -
1 file changed, 1 deletion(-)
diff --git a/Mathlib/RingTheory/Congruence/Hom.lean b/Mathlib/RingTheory/Congruence/Hom.lean
index 485dbe2a79ad80..5e7a93606ddf80 100644
--- a/Mathlib/RingTheory/Congruence/Hom.lean
+++ b/Mathlib/RingTheory/Congruence/Hom.lean
@@ -12,7 +12,6 @@ public import Mathlib.Algebra.Group.Hom.Defs
public import Mathlib.RingTheory.Congruence.Basic
public import Mathlib.Algebra.Ring.Subsemiring.Basic
public import Mathlib.Algebra.Ring.Subring.Basic
-public import Mathlib.Algebra.RingQuot
/-!
# Congruence relations and ring homomorphisms
From d3d49a109bea04e0f3b9bf33b43d2ad80ec33c09 Mon Sep 17 00:00:00 2001
From: Jireh Loreaux
Date: Sat, 20 Jun 2026 03:11:37 +0000
Subject: [PATCH 0201/1300] feat: conditions for commuting with a unitary
element (#40516)
---
Mathlib/Algebra/Group/Submonoid/Basic.lean | 4 ++++
Mathlib/Algebra/Star/Unitary.lean | 17 +++++++++++++++++
2 files changed, 21 insertions(+)
diff --git a/Mathlib/Algebra/Group/Submonoid/Basic.lean b/Mathlib/Algebra/Group/Submonoid/Basic.lean
index 133c0073fadcfa..e080248b0791ed 100644
--- a/Mathlib/Algebra/Group/Submonoid/Basic.lean
+++ b/Mathlib/Algebra/Group/Submonoid/Basic.lean
@@ -361,6 +361,10 @@ theorem IsUnit.mem_submonoid_iff {M : Type*} [Monoid M] (a : M) :
end IsUnit
+@[simp] lemma Submonoid.commute_coe_coe {S M : Type*} [Mul M] [SetLike S M]
+ [MulMemClass S M] {s : S} {x y : s} : Commute (x : M) (y : M) ↔ Commute x y := by
+ simp [commute_iff_eq, Subtype.ext_iff]
+
namespace MonoidHom
open Submonoid
diff --git a/Mathlib/Algebra/Star/Unitary.lean b/Mathlib/Algebra/Star/Unitary.lean
index c9164a65c0508e..3cf41580a98415 100644
--- a/Mathlib/Algebra/Star/Unitary.lean
+++ b/Mathlib/Algebra/Star/Unitary.lean
@@ -189,6 +189,23 @@ instance coe_isStarNormal (u : unitary R) : IsStarNormal (u : R) where
lemma _root_.isStarNormal_of_mem_unitary {u : R} (hu : u ∈ unitary R) : IsStarNormal u :=
coe_isStarNormal ⟨u, hu⟩
+lemma commute_self_star (u : unitary R) : Commute u (star u) := by simp [commute_iff_eq]
+lemma commute_star_self (u : unitary R) : Commute (star u) u := by simp [commute_iff_eq]
+
+lemma _root_.commute_unitary_star_self {u : R} (hu : u ∈ unitary R) : Commute (star u) u :=
+ isStarNormal_of_mem_unitary hu |>.star_comm_self
+
+lemma _root_.commute_unitary_self_star {u : R} (hu : u ∈ unitary R) : Commute u (star u) :=
+ commute_unitary_star_self hu |>.symm
+
+lemma _root_.commute_unitary_iff_star_left_conjugate {x u : R} (hu : u ∈ unitary R) :
+ Commute u x ↔ star u * x * u = x := by
+ simpa using! (Unitary.toUnits ⟨u, hu⟩).commute_iff_inv_mul_cancel
+
+lemma _root_.commute_unitary_iff_star_right_conjugate {x u : R} (hu : u ∈ unitary R) :
+ Commute u x ↔ u * x * star u = x := by
+ simpa using! (Unitary.toUnits ⟨u, hu⟩).commute_iff_mul_inv_cancel
+
end Monoid
end Unitary
From 6eb4a1ce07e071ac0f8365e6999a3135b45d8ae4 Mon Sep 17 00:00:00 2001
From: Jireh Loreaux
Date: Sat, 20 Jun 2026 03:53:49 +0000
Subject: [PATCH 0202/1300] =?UTF-8?q?feat:=20in=20a=20dense=20order,=20`?=
=?UTF-8?q?=F0=9D=93=9D[<]=20a`=20has=20`fun=20x=20=E2=86=A6=20Ico=20x=20a?=
=?UTF-8?q?`=20as=20a=20basis=20(#40523)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Mathlib/Topology/Order/LeftRightNhds.lean | 24 +++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/Mathlib/Topology/Order/LeftRightNhds.lean b/Mathlib/Topology/Order/LeftRightNhds.lean
index 5925877c50128b..6d518605ac27d8 100644
--- a/Mathlib/Topology/Order/LeftRightNhds.lean
+++ b/Mathlib/Topology/Order/LeftRightNhds.lean
@@ -78,6 +78,18 @@ theorem nhdsGT_basis_of_exists_gt {a : α} (h : ∃ b, a < b) : (𝓝[>] a).HasB
lemma nhdsGT_basis [NoMaxOrder α] (a : α) : (𝓝[>] a).HasBasis (a < ·) (Ioo a) :=
nhdsGT_basis_of_exists_gt <| exists_gt a
+lemma nhdsGT_basis_Ioc_of_exists_gt [DenselyOrdered α] {a : α} (h : ∃ b, a < b) :
+ (𝓝[>] a).HasBasis (fun x ↦ a < x) (Ioc a) :=
+ nhdsGT_basis_of_exists_gt h |>.to_hasBasis'
+ (fun _ hac ↦
+ have ⟨b, hab, hbc⟩ := exists_between hac
+ ⟨b, hab, Ioc_subset_Ioo_right hbc⟩)
+ fun _ hac ↦ mem_of_superset ((nhdsGT_basis_of_exists_gt h).mem_of_mem hac) Ioo_subset_Ioc_self
+
+lemma nhdsGT_basis_Ioc [DenselyOrdered α] [NoMaxOrder α] (a : α) :
+ (𝓝[>] a).HasBasis (fun x ↦ a < x) (Ioc a) :=
+ nhdsGT_basis_Ioc_of_exists_gt <| exists_gt a
+
theorem nhdsGT_eq_bot_iff {a : α} : 𝓝[>] a = ⊥ ↔ IsTop a ∨ ∃ b, a ⋖ b := by
by_cases ha : IsTop a
· simp [ha, ha.isMax.Ioi_eq]
@@ -210,6 +222,18 @@ theorem nhdsLT_basis_of_exists_lt {a : α} (h : ∃ b, b < a) : (𝓝[<] a).HasB
theorem nhdsLT_basis [NoMinOrder α] (a : α) : (𝓝[<] a).HasBasis (· < a) (Ioo · a) :=
nhdsLT_basis_of_exists_lt <| exists_lt a
+lemma nhdsLT_basis_Ico_of_exists_lt [DenselyOrdered α] {a : α} (h : ∃ b, b < a) :
+ (𝓝[<] a).HasBasis (· < a) (Ico · a) :=
+ nhdsLT_basis_of_exists_lt h |>.to_hasBasis'
+ (fun _ hac ↦
+ have ⟨b, hab, hbc⟩ := exists_between hac
+ ⟨b, hbc, Ico_subset_Ioo_left hab⟩)
+ fun _ hac ↦ mem_of_superset ((nhdsLT_basis_of_exists_lt h).mem_of_mem hac) Ioo_subset_Ico_self
+
+lemma nhdsLT_basis_Ico [DenselyOrdered α] [NoMinOrder α] (a : α) :
+ (𝓝[<] a).HasBasis (· < a) (Ico · a) :=
+ nhdsLT_basis_Ico_of_exists_lt <| exists_lt a
+
theorem nhdsLT_eq_bot_iff {a : α} : 𝓝[<] a = ⊥ ↔ IsBot a ∨ ∃ b, b ⋖ a := by
convert! (config := { preTransparency := .default })
nhdsGT_eq_bot_iff (a := OrderDual.toDual a) using 4
From 887d94632e78e6e41701cf57c3e7206c6c2b1e5b Mon Sep 17 00:00:00 2001
From: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com>
Date: Sat, 20 Jun 2026 05:51:14 +0000
Subject: [PATCH 0203/1300] doc(1000-yaml): we don't have Cauchy's integral
theorem (#40335)
The [referenced declaration](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Analysis/Complex/CauchyIntegral.html#Complex.circleIntegral_div_sub_of_differentiable_on_off_countable) is [Cauchy's integral formula](https://en.wikipedia.org/wiki/Cauchy%27s_integral_formula), not [Cauchy's integral theorem](https://en.wikipedia.org/wiki/Cauchy%27s_integral_theorem).
We do have [`DiffContOnCl.circleIntegral_eq_zero`](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Analysis/Complex/CauchyIntegral.html#DiffContOnCl.circleIntegral_eq_zero) for circle contours.
---
docs/1000.yaml | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/docs/1000.yaml b/docs/1000.yaml
index 55f54757b9fe15..a500e287af5204 100644
--- a/docs/1000.yaml
+++ b/docs/1000.yaml
@@ -909,9 +909,8 @@ Q830513:
Q834025:
title: Cauchy integral theorem
- decl: Complex.circleIntegral_div_sub_of_differentiable_on_off_countable
- authors: Yury Kudryashov
- date: 2021
+ comment: We have `DiffContOnCl.circleIntegral_eq_zero` for circle contours
+ url: https://leanprover-community.github.io/mathlib4_docs/Mathlib/Analysis/Complex/CauchyIntegral.html#DiffContOnCl.circleIntegral_eq_zero
Q834211:
title: Wallace–Bolyai–Gerwien theorem
From 8690e4fcb159b28ef4b3afd5c68404c9bc0a8992 Mon Sep 17 00:00:00 2001
From: "Yi.Yuan"
Date: Sat, 20 Jun 2026 08:01:11 +0000
Subject: [PATCH 0204/1300] chore(RingTheory/Polynomial/Basic): squeeze
terminal `simp`s (#40833)
As recommended in the style guide. Extracted from #40793.
---
Mathlib/RingTheory/Polynomial/Basic.lean | 13 ++-----------
1 file changed, 2 insertions(+), 11 deletions(-)
diff --git a/Mathlib/RingTheory/Polynomial/Basic.lean b/Mathlib/RingTheory/Polynomial/Basic.lean
index c186c48bb63ee7..efa4f23011e935 100644
--- a/Mathlib/RingTheory/Polynomial/Basic.lean
+++ b/Mathlib/RingTheory/Polynomial/Basic.lean
@@ -763,17 +763,8 @@ theorem prime_rename_iff (s : Set σ) {p : MvPolynomial s R} :
(renameEquiv R <| (Equiv.sumComm (↥sᶜ) s).trans <| Equiv.Set.sumCompl s)
have : (rename (↑)).toRingHom = eqv.toAlgHom.toRingHom.comp C := by
apply ringHom_ext
- · intro
- simp only [eqv, AlgHom.toRingHom_eq_coe, RingHom.coe_coe, rename_C,
- AlgEquiv.toAlgHom_toRingHom, RingHom.coe_comp, AlgEquiv.coe_trans,
- Function.comp_apply, MvPolynomial.sumAlgEquiv_symm_apply, iterToSum_C_C,
- renameEquiv_apply, Equiv.coe_trans, Equiv.sumComm_apply]
- · intro
- simp only [eqv, AlgHom.toRingHom_eq_coe, RingHom.coe_coe, rename_X,
- AlgEquiv.toAlgHom_toRingHom, RingHom.coe_comp, AlgEquiv.coe_trans,
- Function.comp_apply, MvPolynomial.sumAlgEquiv_symm_apply, iterToSum_C_X,
- renameEquiv_apply, Equiv.coe_trans, Equiv.sumComm_apply, Sum.swap_inr,
- Equiv.Set.sumCompl_apply_inl]
+ · simp [eqv]
+ · simp [eqv]
apply_fun (· p) at this
simp only [AlgHom.toRingHom_eq_coe, RingHom.coe_coe, AlgEquiv.toAlgHom_toRingHom,
RingHom.coe_comp, Function.comp_apply] at this
From fbfd7f57b6e24d35f345edee2d888a3e6b5f4cc4 Mon Sep 17 00:00:00 2001
From: Thomas Browning <13339017+tb65536@users.noreply.github.com>
Date: Sat, 20 Jun 2026 09:31:52 +0000
Subject: [PATCH 0205/1300] refactor(RingTheory/Localization/FractionRing):
remove bottom ring and field from `IsFractionRing.mulSemiringAction` (#40804)
This PR removes the bottom ring and field from `IsFractionRing.mulSemiringAction` since they are unnecessary.
Co-authored-by: tb65536
---
Mathlib/Algebra/Ring/Action/Group.lean | 22 ++++++++++--
Mathlib/Algebra/Star/UnitaryStarAlgAut.lean | 2 +-
Mathlib/FieldTheory/Galois/IsGaloisGroup.lean | 35 +++++++-----------
.../RamificationInertia/Galois.lean | 6 ++--
.../RingTheory/Localization/FractionRing.lean | 36 +++++++++++++++----
5 files changed, 67 insertions(+), 34 deletions(-)
diff --git a/Mathlib/Algebra/Ring/Action/Group.lean b/Mathlib/Algebra/Ring/Action/Group.lean
index d5dce7f17fa431..91e4cd7961ba25 100644
--- a/Mathlib/Algebra/Ring/Action/Group.lean
+++ b/Mathlib/Algebra/Ring/Action/Group.lean
@@ -7,6 +7,7 @@ module
public import Mathlib.Algebra.GroupWithZero.Action.Basic
public import Mathlib.Algebra.Ring.Action.Basic
+public import Mathlib.Algebra.Ring.Aut
public import Mathlib.Algebra.Ring.Equiv
/-!
@@ -25,7 +26,24 @@ variable (R : Type*) [Semiring R]
/-- Each element of the group defines a semiring isomorphism. -/
@[simps!]
-def MulSemiringAction.toRingEquiv [MulSemiringAction G R] (x : G) : R ≃+* R :=
- { DistribMulAction.toAddEquiv R x, MulSemiringAction.toRingHom G R x with }
+def MulSemiringAction.toRingEquiv [MulSemiringAction G R] : G →* (R ≃+* R) where
+ toFun x := { DistribMulAction.toAddEquiv R x, MulSemiringAction.toRingHom G R x with }
+ map_one' := by ext; simp
+ map_mul' x y := by ext; simp [mul_smul]
+
+@[deprecated (since := "2026-06-19")] alias MulSemiringAction.toRingEquiv_apply :=
+MulSemiringAction.toRingEquiv_apply_apply
+
+@[deprecated (since := "2026-06-19")] alias MulSemiringAction.toRingEquiv_symm_apply :=
+MulSemiringAction.toRingEquiv_apply_symm_apply
+
+instance : MulSemiringAction (R ≃+* R) R where
+ smul := (· ·)
+ mul_smul _ _ _ := rfl
+ one_smul _ := rfl
+ smul_zero := map_zero
+ smul_one := map_one
+ smul_add := map_add
+ smul_mul := map_mul
end Semiring
diff --git a/Mathlib/Algebra/Star/UnitaryStarAlgAut.lean b/Mathlib/Algebra/Star/UnitaryStarAlgAut.lean
index 25d846324e92f6..6dfa47f00a244d 100644
--- a/Mathlib/Algebra/Star/UnitaryStarAlgAut.lean
+++ b/Mathlib/Algebra/Star/UnitaryStarAlgAut.lean
@@ -38,7 +38,7 @@ def conjStarAlgAut : unitary R →* (R ≃⋆ₐ[S] R) where
dsimp [ConjAct.units_smul_def]
simp [mul_assoc, ← Unitary.star_eq_inv] }
map_one' := by ext; simp
- map_mul' g h := by ext; simp [mul_smul]
+ map_mul' g h := by ext; simp
@[simp] theorem conjStarAlgAut_apply (u : unitary R) (x : R) :
conjStarAlgAut S R u x = u * x * (star u : R) := rfl
diff --git a/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean b/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean
index cdb2d060676346..ccc1a84e609c41 100644
--- a/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean
+++ b/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean
@@ -239,21 +239,17 @@ theorem IsGaloisGroup.iff_isFractionRing [Finite G] [IsIntegrallyClosed A] :
@[deprecated (since := "2026-04-20")] alias FractionRing.mulSemiringAction_of_isGaloisGroup :=
IsFractionRing.mulSemiringAction
-attribute [local instance] FractionRing.liftAlgebra in
/--
If `G` is finite and `IsGaloisGroup G A B` with `A` and `B` domains, then `G` is also
a Galois group for `FractionRing B / FractionRing A` for the action defined by
`IsFractionRing.mulSemiringAction`.
-/
-theorem IsGaloisGroup.toFractionRing [IsDomain A] [IsDomain B] [IsTorsionFree A B] [Finite G]
- [IsGaloisGroup G A B] :
- letI := IsFractionRing.mulSemiringAction G A B (FractionRing A) (FractionRing B)
+instance IsGaloisGroup.toFractionRing [IsDomain A] [IsDomain B] [IsTorsionFree A B] [Finite G]
+ [IsGaloisGroup G A B] [Algebra (FractionRing A) (FractionRing B)]
+ [IsScalarTower A (FractionRing A) (FractionRing B)] :
+ letI := IsFractionRing.mulSemiringAction G B (FractionRing B)
IsGaloisGroup G (FractionRing A) (FractionRing B) := by
- let := IsFractionRing.mulSemiringAction G A B (FractionRing A) (FractionRing B)
- have : SMulDistribClass G B (FractionRing B) := ⟨fun g b x ↦ by
- rw [Algebra.smul_def', Algebra.smul_def', smul_mul']
- congr
- exact IsFractionRing.fieldEquivOfAlgEquiv_algebraMap (FractionRing A) _ _ _ b⟩
+ let := IsFractionRing.mulSemiringAction G B (FractionRing B)
apply IsGaloisGroup.to_isFractionRing G A B _ _
open NumberField
@@ -314,7 +310,7 @@ protected theorem finite (R B : Type*) [CommRing R] [CommRing B] [Algebra R B] [
[IsDomain B] [MulSemiringAction G B] [IsGaloisGroup G R B] : Finite G := by
let A : Subring B := (algebraMap R B).range
let := FractionRing.liftAlgebra A (FractionRing B)
- let := IsFractionRing.mulSemiringAction G A B (FractionRing A) (FractionRing B)
+ let := IsFractionRing.mulSemiringAction G B (FractionRing B)
let : Algebra R A := (algebraMap R B).rangeRestrict.toAlgebra
have : IsScalarTower R A B := IsScalarTower.of_algebraMap_eq' rfl
have : Module.Finite A B := Module.Finite.of_restrictScalars_finite R A B
@@ -331,9 +327,9 @@ theorem card_eq_finrank' (A B : Type*) [CommRing A] [CommRing B] [Algebra A B] [
Nat.card G = Module.finrank A B := by
have := IsDomain.of_faithfulSMul A B
let := FractionRing.liftAlgebra A (FractionRing B)
- let := IsFractionRing.mulSemiringAction G A B (FractionRing A) (FractionRing B)
+ let := IsFractionRing.mulSemiringAction G B (FractionRing B)
have : Algebra.IsIntegral A B := IsGaloisGroup.isInvariant.isIntegral A B G
- rw [(IsGaloisGroup.toFractionRing G A B).card_eq_finrank,
+ rw [IsGaloisGroup.card_eq_finrank G (FractionRing A) (FractionRing B),
Algebra.IsAlgebraic.finrank_of_isFractionRing A (FractionRing A) B (FractionRing B)]
/-- If `G` is a finite Galois group for `L/K`, then `G` is isomorphic to `Gal(L/K)`. -/
@@ -374,10 +370,8 @@ noncomputable def mulEquivCongr [Finite G] [Finite G'] (A B : Type*) [CommRing A
haveI : IsDomain A := (FaithfulSMul.algebraMap_injective A B).isDomain
letI K := FractionRing A
letI L := FractionRing B
- letI : MulSemiringAction G L := IsFractionRing.mulSemiringAction G A B K L
- letI : MulSemiringAction G' L := IsFractionRing.mulSemiringAction G' A B K L
- haveI : IsGaloisGroup G K L := IsGaloisGroup.toFractionRing G A B
- haveI : IsGaloisGroup G' K L := IsGaloisGroup.toFractionRing G' A B
+ letI : MulSemiringAction G L := IsFractionRing.mulSemiringAction G B L
+ letI : MulSemiringAction G' L := IsFractionRing.mulSemiringAction G' B L
mulEquivCongr' G G' K L
attribute [local instance] FractionRing.liftAlgebra in
@@ -389,10 +383,8 @@ theorem mulEquivCongr_apply_smul [Finite G] [Finite G'] (A B : Type*) [CommRing
haveI : IsDomain A := (FaithfulSMul.algebraMap_injective A B).isDomain
letI K := FractionRing A
letI L := FractionRing B
- letI : MulSemiringAction G L := IsFractionRing.mulSemiringAction G A B K L
- letI : MulSemiringAction G' L := IsFractionRing.mulSemiringAction G' A B K L
- haveI : IsGaloisGroup G K L := IsGaloisGroup.toFractionRing G A B
- haveI : IsGaloisGroup G' K L := IsGaloisGroup.toFractionRing G' A B
+ letI : MulSemiringAction G L := IsFractionRing.mulSemiringAction G B L
+ letI : MulSemiringAction G' L := IsFractionRing.mulSemiringAction G' B L
apply FaithfulSMul.algebraMap_injective B L
rw [algebraMap.smul', algebraMap.smul']
exact mulEquivCongr'_apply_smul G G' K L g _
@@ -576,8 +568,7 @@ theorem fixingSubgroup_range_algebraMap [Finite G] (A B C : Type*) (H : Subgroup
have : IsDomain A := (FaithfulSMul.algebraMap_injective A C).isDomain
let K := FractionRing A
let L := FractionRing C
- let : MulSemiringAction G L := IsFractionRing.mulSemiringAction G A C K L
- have : IsGaloisGroup G K L := IsGaloisGroup.toFractionRing G A C
+ let : MulSemiringAction G L := IsFractionRing.mulSemiringAction G C L
have : IsGaloisGroup H (FractionRing B) L := IsGaloisGroup.toFractionRing H B C
rw [← fixingSubgroup_range_algebraMap' G K L H (FractionRing B)]
ext g
diff --git a/Mathlib/NumberTheory/RamificationInertia/Galois.lean b/Mathlib/NumberTheory/RamificationInertia/Galois.lean
index b0f32f53494438..06d4f5a5d2f7c0 100644
--- a/Mathlib/NumberTheory/RamificationInertia/Galois.lean
+++ b/Mathlib/NumberTheory/RamificationInertia/Galois.lean
@@ -244,9 +244,9 @@ variable {A B : Type*} [CommRing A] [IsDomain A] [CommRing B] [IsDomain B]
include G GAC GBC in
theorem ncard_primesOver_mul_ncard_primesOver :
(p.primesOver B).ncard * (P.primesOver C).ncard = (p.primesOver C).ncard := by
- let := IsFractionRing.mulSemiringAction G A B (FractionRing A) (FractionRing B)
- let := IsFractionRing.mulSemiringAction GAC A C (FractionRing A) (FractionRing C)
- let := IsFractionRing.mulSemiringAction GBC B C (FractionRing B) (FractionRing C)
+ let := IsFractionRing.mulSemiringAction G B (FractionRing B)
+ let := IsFractionRing.mulSemiringAction GAC C (FractionRing C)
+ let := IsFractionRing.mulSemiringAction GBC C (FractionRing C)
have : p.ramificationIdxIn C * p.inertiaDegIn C ≠ 0 :=
mul_ne_zero (ramificationIdxIn_ne_zero GAC) (inertiaDegIn_ne_zero GAC)
rw [← Nat.mul_left_inj this, ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn p C GAC]
diff --git a/Mathlib/RingTheory/Localization/FractionRing.lean b/Mathlib/RingTheory/Localization/FractionRing.lean
index 00659e147c0d83..54d432f40eba71 100644
--- a/Mathlib/RingTheory/Localization/FractionRing.lean
+++ b/Mathlib/RingTheory/Localization/FractionRing.lean
@@ -437,6 +437,10 @@ lemma ringEquivOfRingEquiv_algebraMap
(a : A) : ringEquivOfRingEquiv h (algebraMap A K a) = algebraMap B L (h a) := by
simp
+@[simp]
+lemma ringEquivOfRingEquiv_refl :
+ ringEquivOfRingEquiv (.refl A) = .refl K := by ext; simp
+
@[simp]
lemma ringEquivOfRingEquiv_symm :
(ringEquivOfRingEquiv h : K ≃+* L).symm = ringEquivOfRingEquiv h.symm := rfl
@@ -449,6 +453,26 @@ theorem ringEquivOfRingEquiv_comp {C : Type*} (M : Type*) [CommRing C]
ext a
simp [IsLocalization.map_map]
+variable (A K)
+
+/-- A ring automorphism of a ring induces an ring automorphism of its fraction field.
+
+This is a bundled version of `ringEquivOfRingEquiv`. -/
+noncomputable def ringEquivOfRingEquivHom : (A ≃+* A) →* (K ≃+* K) where
+ toFun := ringEquivOfRingEquiv
+ map_one' := ringEquivOfRingEquiv_refl
+ map_mul' f g := ringEquivOfRingEquiv_comp K K K g f
+
+@[simp]
+lemma ringEquivOfRingEquivHom_apply (f : A ≃+* A) :
+ ringEquivOfRingEquivHom A K f = ringEquivOfRingEquiv f :=
+ rfl
+
+lemma ringEquivOfRingEquivHom_injective : Function.Injective (ringEquivOfRingEquivHom A K) := by
+ intro f g h
+ ext b
+ simpa using RingEquiv.ext_iff.mp h (algebraMap A K b)
+
end ringEquivOfRingEquiv
section semilinearEquivOfRingEquiv
@@ -624,21 +648,21 @@ variable (G A B K L : Type*) [Group G] [CommRing A] [CommRing B] [MulSemiringAct
/-- Given a `MulSemiringAction G B`, extend the action of `G` on `B` to a `MulSemiringAction G L`
on the fraction field `L` of `B`. -/
@[implicit_reducible]
-noncomputable def mulSemiringAction [SMulCommClass G A B] :
+noncomputable def mulSemiringAction :
MulSemiringAction G L :=
MulSemiringAction.compHom L
- ((fieldEquivOfAlgEquivHom K L).comp (MulSemiringAction.toAlgAut G A B))
+ ((ringEquivOfRingEquivHom B L).comp (MulSemiringAction.toRingEquiv G B))
/-- The action of `G` on the fraction field `L` of `B` given by `IsFractionRing.mulSemiringAction`
is compatible with the embedding `B ⊆ L`. -/
-instance smulDistribClass [SMulCommClass G A B] :
- letI := mulSemiringAction G A B K L
+instance smulDistribClass :
+ letI := mulSemiringAction G B L
SMulDistribClass G B L :=
- let := mulSemiringAction G A B K L
+ let := mulSemiringAction G B L
⟨fun g b x ↦ by
rw [Algebra.smul_def', Algebra.smul_def', smul_mul']
congr
- apply fieldEquivOfAlgEquiv_algebraMap⟩
+ apply ringEquivOfRingEquiv_algebraMap⟩
variable [MulSemiringAction G L] [SMulDistribClass G B L]
From 843d7890de006fe2cb1d2974f5bafcf1b0e3fad8 Mon Sep 17 00:00:00 2001
From: "mathlib-splicebot[bot]"
<261196803+mathlib-splicebot[bot]@users.noreply.github.com>
Date: Sat, 20 Jun 2026 13:53:39 +0000
Subject: [PATCH 0206/1300] chore(Algebra/Group/Subgroup/Basic): automated
extraction from #38864 (#40842)
This PR was automatically created from PR #38864 by @xroblot via a [review comment](https://github.com/leanprover-community/mathlib4/pull/38864#discussion_r3446463992) by @tb65536.
Co-authored-by: xroblot <46200072+xroblot@users.noreply.github.com>
---
Mathlib/Algebra/Group/Subgroup/Basic.lean | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/Mathlib/Algebra/Group/Subgroup/Basic.lean b/Mathlib/Algebra/Group/Subgroup/Basic.lean
index 8d12154ce3c9d3..c5a07c6d1114d7 100644
--- a/Mathlib/Algebra/Group/Subgroup/Basic.lean
+++ b/Mathlib/Algebra/Group/Subgroup/Basic.lean
@@ -939,6 +939,15 @@ theorem Normal.of_map_subtype {K : Subgroup G} {L : Subgroup K}
(n : (Subgroup.map K.subtype L).Normal) : L.Normal :=
n.of_map_injective K.subtype_injective
+theorem normal_comap_iff_of_surjective {f : G →* N} (hf : Function.Surjective f) {H : Subgroup N} :
+ (H.comap f).Normal ↔ H.Normal := by
+ rw [← normalizer_eq_top_iff, ← comap_normalizer_eq_of_surjective H hf, ← comap_top f,
+ (comap_injective hf).eq_iff, normalizer_eq_top_iff]
+
+theorem _root_.MulEquiv.normal_map_iff {f : G ≃* G'} {H : Subgroup G} :
+ (H.map (f : G →* G')).Normal ↔ H.Normal := by
+ rw [map_equiv_eq_comap_symm, normal_comap_iff_of_surjective f.symm.surjective]
+
section SubgroupNormal
@[to_additive]
From 2dfe37a6fa59521018b61dc988495a84dd47dd30 Mon Sep 17 00:00:00 2001
From: "mathlib-splicebot[bot]"
<261196803+mathlib-splicebot[bot]@users.noreply.github.com>
Date: Sat, 20 Jun 2026 15:15:36 +0000
Subject: [PATCH 0207/1300] chore(FieldTheory/IntermediateField/Basic):
automated extraction from #38864 (#40844)
This PR was automatically created from PR #38864 by @xroblot via a [review comment](https://github.com/leanprover-community/mathlib4/pull/38864#discussion_r3446477153) by @tb65536.
Co-authored-by: xroblot <46200072+xroblot@users.noreply.github.com>
Co-authored-by: Thomas Browning
---
.../FieldTheory/IntermediateField/Basic.lean | 20 ++++++++++---------
1 file changed, 11 insertions(+), 9 deletions(-)
diff --git a/Mathlib/FieldTheory/IntermediateField/Basic.lean b/Mathlib/FieldTheory/IntermediateField/Basic.lean
index ca54cf81abab2a..3e4e49af1025a5 100644
--- a/Mathlib/FieldTheory/IntermediateField/Basic.lean
+++ b/Mathlib/FieldTheory/IntermediateField/Basic.lean
@@ -541,24 +541,26 @@ theorem coe_fieldRange : ↑f.fieldRange = Set.range f :=
theorem fieldRange_toSubfield : f.fieldRange.toSubfield = (f : L →+* L').fieldRange :=
rfl
-variable {f}
-
+variable {f} in
@[simp]
theorem mem_fieldRange {y : L'} : y ∈ f.fieldRange ↔ ∃ x, f x = y :=
Iff.rfl
-/-- An algebra homomorphism between fields restricts to an algebra equivalence onto its range. -/
+/-- The isomorphism from `L` to the field range of the `AlgHom` `f`, sending `x` to `f x`. -/
+@[simps! apply_coe]
noncomputable def equivFieldRange : L ≃ₐ[K] f.fieldRange :=
- AlgEquiv.ofBijective
- (f.codRestrict f.range fun x ↦ mem_fieldRange.mpr ⟨x, rfl⟩)
- ⟨fun _ _ h ↦ f.injective (congr_arg Subtype.val h),
- fun ⟨_, hy⟩ ↦ (mem_fieldRange.mp hy).imp fun _ hx => Subtype.ext hx⟩
+ .ofBijective f.rangeRestrict ⟨f.rangeRestrict.injective, fun ⟨_, ⟨x, hx⟩⟩ ↦ ⟨x, Subtype.ext hx⟩⟩
-@[simp]
-theorem equivFieldRange_apply (x : L) : f.equivFieldRange x = f x := rfl
+@[deprecated (since := "2026-06-20")] alias equivFieldRange_apply := equivFieldRange_apply_coe
end AlgHom
+variable (K L L') in
+@[simp]
+theorem IsScalarTower.toAlgHom_fieldRange [Algebra L L'] [IsScalarTower K L L'] :
+ (IsScalarTower.toAlgHom K L L').fieldRange = Set.range (algebraMap L L') := by
+ ext; simp
+
namespace IntermediateField
/-- The embedding from an intermediate field of `L / K` to `L`. -/
From 0bee4471e48fc38bb650f9547e743222d9c2b11d Mon Sep 17 00:00:00 2001
From: Thomas Browning <13339017+tb65536@users.noreply.github.com>
Date: Sat, 20 Jun 2026 15:40:22 +0000
Subject: [PATCH 0208/1300] refactor(FieldTheory/Galois/IsGaloisGroup):
generalize `mulEquivAlgEquiv` to domains (#40822)
This PR generalizes `mulEquivAlgEquiv` to domains. This allows us to remove `mulEquivCongr'` (a field version of `mulEquivCongr`).
Co-authored-by: tb65536
---
Mathlib/FieldTheory/Galois/IsGaloisGroup.lean | 80 +++++++------------
Mathlib/Logic/Function/Basic.lean | 4 +
2 files changed, 34 insertions(+), 50 deletions(-)
diff --git a/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean b/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean
index ccc1a84e609c41..1495a47a590cae 100644
--- a/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean
+++ b/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean
@@ -307,7 +307,7 @@ theorem finiteDimensional [Finite G] [IsGaloisGroup G K L] : FiniteDimensional K
FiniteDimensional.of_finrank_pos (card_eq_finrank G K L ▸ Nat.card_pos)
protected theorem finite (R B : Type*) [CommRing R] [CommRing B] [Algebra R B] [Module.Finite R B]
- [IsDomain B] [MulSemiringAction G B] [IsGaloisGroup G R B] : Finite G := by
+ [IsDomain B] [MulSemiringAction G B] [IsGaloisGroup G R B] : Finite G := by
let A : Subring B := (algebraMap R B).range
let := FractionRing.liftAlgebra A (FractionRing B)
let := IsFractionRing.mulSemiringAction G B (FractionRing B)
@@ -319,12 +319,16 @@ protected theorem finite (R B : Type*) [CommRing R] [CommRing B] [Algebra R B] [
rw [card_eq_finrank G (FractionRing A) (FractionRing B)]
exact Module.finrank_pos.ne'
+section IsDomain
+
+variable (A B : Type*) [CommRing A] [CommRing B] [IsDomain B] [Algebra A B] [FaithfulSMul A B]
+ [MulSemiringAction G B] [MulSemiringAction G' B] [IsGaloisGroup G A B] [IsGaloisGroup G' A B]
+ [Finite G] [Finite G']
+
/-- The cardinality of a Galois group of `B/A` equals the rank of `B` as an `A`-module.
-See `IsGaloisGroup.card_eq_finrank` a field-theoretic version that does not assume finiteness. -/
-theorem card_eq_finrank' (A B : Type*) [CommRing A] [CommRing B] [Algebra A B] [Finite G]
- [IsDomain B] [FaithfulSMul A B] [MulSemiringAction G B] [IsGaloisGroup G A B] :
- Nat.card G = Module.finrank A B := by
+See `IsGaloisGroup.card_eq_finrank`, a field-theoretic version that does not assume finiteness. -/
+theorem card_eq_finrank' : Nat.card G = Module.finrank A B := by
have := IsDomain.of_faithfulSMul A B
let := FractionRing.liftAlgebra A (FractionRing B)
let := IsFractionRing.mulSemiringAction G B (FractionRing B)
@@ -332,70 +336,46 @@ theorem card_eq_finrank' (A B : Type*) [CommRing A] [CommRing B] [Algebra A B] [
rw [IsGaloisGroup.card_eq_finrank G (FractionRing A) (FractionRing B),
Algebra.IsAlgebraic.finrank_of_isFractionRing A (FractionRing A) B (FractionRing B)]
-/-- If `G` is a finite Galois group for `L/K`, then `G` is isomorphic to `Gal(L/K)`. -/
-@[simps!] noncomputable def mulEquivAlgEquiv [IsGaloisGroup G K L] [Finite G] : G ≃* Gal(L/K) :=
- MulEquiv.ofBijective (MulSemiringAction.toAlgAut G K L) (by
+attribute [local instance] FractionRing.liftAlgebra in
+/-- If `G` is a finite Galois group for `B/A`, then `G` is isomorphic to `Gal(B/A)`. -/
+@[simps!] noncomputable def mulEquivAlgEquiv : G ≃* Gal(B/A) :=
+ MulEquiv.ofBijective (MulSemiringAction.toAlgAut G A B) (by
+ have := IsDomain.of_faithfulSMul A B
+ letI K := FractionRing A
+ letI L := FractionRing B
+ letI := IsFractionRing.mulSemiringAction G B L
have := isGalois G K L
have := finiteDimensional G K L
+ refine .of_comp_left ?_ (IsFractionRing.fieldEquivOfAlgEquivHom_injective A B K L)
rw [Nat.bijective_iff_injective_and_card, card_eq_finrank G K L,
IsGalois.card_aut_eq_finrank K L]
exact ⟨fun _ _ ↦ (faithful K).eq_of_smul_eq_smul ∘ DFunLike.ext_iff.mp, rfl⟩)
@[simp]
-theorem map_mulEquivAlgEquiv_fixingSubgroup
- [IsGaloisGroup G K L] [Finite G] (F : IntermediateField K L) :
+theorem map_mulEquivAlgEquiv_fixingSubgroup [IsGaloisGroup G K L] (F : IntermediateField K L) :
(fixingSubgroup G (F : Set L)).map (mulEquivAlgEquiv G K L) = F.fixingSubgroup := by
ext g
obtain ⟨g, rfl⟩ := (mulEquivAlgEquiv G K L).surjective g
simp [mem_fixingSubgroup_iff]
-/-- If `G` and `G'` are finite Galois groups for `L/K`, then `G` is isomorphic to `G'`.
-See `mulEquivCongr` for a more general version. -/
-noncomputable def mulEquivCongr' [IsGaloisGroup G K L] [Finite G]
- [IsGaloisGroup G' K L] [Finite G'] : G ≃* G' :=
- (mulEquivAlgEquiv G K L).trans (mulEquivAlgEquiv G' K L).symm
+/-- If `G` and `G'` are finite Galois groups for `B/A`, then `G` is isomorphic to `G'`. -/
+noncomputable def mulEquivCongr : G ≃* G' :=
+ (mulEquivAlgEquiv G A B).trans (mulEquivAlgEquiv G' A B).symm
@[simp]
-theorem mulEquivCongr'_apply_smul [IsGaloisGroup G K L] [Finite G] [IsGaloisGroup G' K L]
- [Finite G'] (g : G) (x : L) : mulEquivCongr' G G' K L g • x = g • x :=
- AlgEquiv.ext_iff.mp ((mulEquivAlgEquiv G' K L).apply_symm_apply (mulEquivAlgEquiv G K L g)) x
+theorem mulEquivCongr_apply_smul (g : G) (x : B) : mulEquivCongr G G' A B g • x = g • x :=
+ AlgEquiv.ext_iff.mp ((mulEquivAlgEquiv G' A B).apply_symm_apply (mulEquivAlgEquiv G A B g)) x
-attribute [local instance] FractionRing.liftAlgebra in
-/-- If `G` and `G'` are finite Galois groups for `B/A` with `B` a domain, then `G` is
-isomorphic to `G'`. -/
-noncomputable def mulEquivCongr [Finite G] [Finite G'] (A B : Type*) [CommRing A]
- [CommRing B] [IsDomain B] [Algebra A B] [FaithfulSMul A B] [MulSemiringAction G B]
- [MulSemiringAction G' B] [IsGaloisGroup G A B] [IsGaloisGroup G' A B] :
- G ≃* G' :=
- haveI : IsDomain A := (FaithfulSMul.algebraMap_injective A B).isDomain
- letI K := FractionRing A
- letI L := FractionRing B
- letI : MulSemiringAction G L := IsFractionRing.mulSemiringAction G B L
- letI : MulSemiringAction G' L := IsFractionRing.mulSemiringAction G' B L
- mulEquivCongr' G G' K L
-
-attribute [local instance] FractionRing.liftAlgebra in
@[simp]
-theorem mulEquivCongr_apply_smul [Finite G] [Finite G'] (A B : Type*) [CommRing A]
- [CommRing B] [IsDomain B] [Algebra A B] [FaithfulSMul A B] [MulSemiringAction G B]
- [MulSemiringAction G' B] [IsGaloisGroup G A B] [IsGaloisGroup G' A B] (g : G) (x : B) :
- mulEquivCongr G G' A B g • x = g • x := by
- haveI : IsDomain A := (FaithfulSMul.algebraMap_injective A B).isDomain
- letI K := FractionRing A
- letI L := FractionRing B
- letI : MulSemiringAction G L := IsFractionRing.mulSemiringAction G B L
- letI : MulSemiringAction G' L := IsFractionRing.mulSemiringAction G' B L
- apply FaithfulSMul.algebraMap_injective B L
- rw [algebraMap.smul', algebraMap.smul']
- exact mulEquivCongr'_apply_smul G G' K L g _
-
-@[simp]
-theorem mulEquivCongr_symm_apply_smul [Finite G] [Finite G'] (A B : Type*) [CommRing A]
- [CommRing B] [IsDomain B] [Algebra A B] [FaithfulSMul A B] [MulSemiringAction G B]
- [MulSemiringAction G' B] [IsGaloisGroup G A B] [IsGaloisGroup G' A B] (g : G') (x : B) :
+theorem mulEquivCongr_symm_apply_smul (g : G') (x : B) :
(mulEquivCongr G G' A B).symm g • x = g • x := by
rw [← mulEquivCongr_apply_smul G G' A B, MulEquiv.apply_symm_apply]
+@[deprecated (since := "2026-06-19")] alias mulEquivCongr' := mulEquivCongr
+@[deprecated (since := "2026-06-19")] alias mulEquivCongr'_apply_smul := mulEquivCongr_apply_smul
+
+end IsDomain
+
variable (H H' : Subgroup G) (F F' : IntermediateField K L)
instance (R S : Type*) [CommRing R] [CommRing S] [Algebra R S]
diff --git a/Mathlib/Logic/Function/Basic.lean b/Mathlib/Logic/Function/Basic.lean
index 6ab2a0cf0c7f63..d2d3f67f4f10da 100644
--- a/Mathlib/Logic/Function/Basic.lean
+++ b/Mathlib/Logic/Function/Basic.lean
@@ -352,6 +352,10 @@ theorem Bijective.of_comp_iff' {f : α → β} (hf : Bijective f) (g : γ → α
Function.Bijective (f ∘ g) ↔ Function.Bijective g :=
and_congr (Injective.of_comp_iff hf.injective _) (Surjective.of_comp_iff' hf _)
+theorem Bijective.of_comp_left {f : α → β} {g : γ → α} (hfg : Function.Bijective (f ∘ g))
+ (hf : Function.Injective f) : Function.Bijective g :=
+ ⟨hfg.1.of_comp, hfg.2.of_comp_left hf⟩
+
/-- If `f : α → α → β` is surjective, then every endofunction on `β` has a fixed point.
This is an instance of Lawvere's fixed-point theorem applied to the category of types
and functions. It is the diagonal argument underlying `cantor_surjective` and
From d5331d6fd7302656853f1c2cd8cbb994504e74d9 Mon Sep 17 00:00:00 2001
From: "Yi.Yuan"
Date: Sat, 20 Jun 2026 17:37:45 +0000
Subject: [PATCH 0209/1300] chore(RingTheory/Spectrum/Prime/FreeLocus):
unsqueeze terminal `simp`s (#40838)
As recommended in the style guide. Extracted from #40793.
---
Mathlib/RingTheory/Spectrum/Prime/FreeLocus.lean | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/Mathlib/RingTheory/Spectrum/Prime/FreeLocus.lean b/Mathlib/RingTheory/Spectrum/Prime/FreeLocus.lean
index 2ae9cab02f13e7..50ad1fe06dcaa0 100644
--- a/Mathlib/RingTheory/Spectrum/Prime/FreeLocus.lean
+++ b/Mathlib/RingTheory/Spectrum/Prime/FreeLocus.lean
@@ -68,11 +68,7 @@ lemma mem_freeLocus_of_isLocalization (p : PrimeSpectrum R)
intro r x
obtain ⟨r, s, rfl⟩ := IsLocalization.exists_mk'_eq p.asIdeal.primeCompl r
apply ((Module.End.isUnit_iff _).mp (IsLocalizedModule.map_units f s)).1
- simp only [e, AddHom.toFun_eq_coe, LinearMap.coe_toAddHom, LinearEquiv.coe_coe,
- algebraMap_end_apply,
- AlgEquiv.toRingEquiv_toRingHom, RingHom.coe_coe, IsLocalization.algEquiv_apply,
- IsLocalization.map_id_mk']
- simp only [← map_smul, ← smul_assoc, IsLocalization.smul_mk'_self, algebraMap_smul]
+ simp [e, ← map_smul, ← smul_assoc]
attribute [local instance] RingHomInvPair.of_ringEquiv in
lemma mem_freeLocus_iff_tensor (p : PrimeSpectrum R)
From 55ad1c56adc88131d141e9f625f9a9b4b28ce941 Mon Sep 17 00:00:00 2001
From: Michael Rothgang <10105016+grunweg@users.noreply.github.com>
Date: Sat, 20 Jun 2026 17:52:00 +0000
Subject: [PATCH 0210/1300] feat: add Homeomorph.pointReflection (#40637)
This is `Equiv.pointReflection` as a homeomorphism.
The motivating use case is for a homeomorphism on the real line in #29077.
While at it, also upgrade `AffineEquiv.pointReflection` to a `ContinuousAffineEquiv`.
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
---
.../AffineSpace/AffineEquiv.lean | 3 ++-
.../Algebra/ContinuousAffineEquiv.lean | 25 +++++++++++++++++++
Mathlib/Topology/Algebra/Group/Torsor.lean | 19 ++++++++++++++
3 files changed, 46 insertions(+), 1 deletion(-)
diff --git a/Mathlib/LinearAlgebra/AffineSpace/AffineEquiv.lean b/Mathlib/LinearAlgebra/AffineSpace/AffineEquiv.lean
index 05e9a6328155cd..75f205c9b55110 100644
--- a/Mathlib/LinearAlgebra/AffineSpace/AffineEquiv.lean
+++ b/Mathlib/LinearAlgebra/AffineSpace/AffineEquiv.lean
@@ -509,7 +509,8 @@ variable {P₁}
open Function
-/-- Point reflection in `x` as a permutation. -/
+/-- The affine equivalence given by reflection about the point `x`.
+This is `Equiv.pointReflection` as an `AffineEquiv`. -/
def pointReflection (x : P₁) : P₁ ≃ᵃ[k] P₁ :=
(constVSub k x).trans (vaddConst k x)
diff --git a/Mathlib/Topology/Algebra/ContinuousAffineEquiv.lean b/Mathlib/Topology/Algebra/ContinuousAffineEquiv.lean
index 46df6b9ac4ba7a..3605d581af24e8 100644
--- a/Mathlib/Topology/Algebra/ContinuousAffineEquiv.lean
+++ b/Mathlib/Topology/Algebra/ContinuousAffineEquiv.lean
@@ -309,6 +309,31 @@ end ReflSymmTrans
section
+variable (k)
+variable [TopologicalSpace V₁] [IsTopologicalAddTorsor P₁]
+
+/-- The affine homeomorphism given by reflection about the point `x`.
+This is `Equiv.pointReflection` as a `ContinuousAffineEquiv`. -/
+@[simps toAffineEquiv]
+def pointReflection (x : P₁) : P₁ ≃ᴬ[k] P₁ where
+ toAffineEquiv := AffineEquiv.pointReflection k x
+ continuous_toFun := by dsimp [Equiv.pointReflection]; fun_prop
+ continuous_invFun := by
+ let : ContinuousNeg V₁ :=
+ IsTopologicalAddTorsor.to_isTopologicalAddGroup (V := V₁) (P := P₁) |>.toContinuousNeg
+ dsimp [Equiv.pointReflection]; fun_prop
+
+theorem pointReflection_apply (x y : P₁) : pointReflection k x y = (x -ᵥ y) +ᵥ x :=
+ rfl
+
+@[simp]
+theorem pointReflection_symm (x : P₁) : (pointReflection k x).symm = pointReflection k x :=
+ toAffineEquiv_injective <| AffineEquiv.pointReflection_symm k x
+
+end
+
+section
+
variable {E F : Type*} [AddCommGroup E] [Module k E] [TopologicalSpace E]
[AddCommGroup F] [Module k F] [TopologicalSpace F]
diff --git a/Mathlib/Topology/Algebra/Group/Torsor.lean b/Mathlib/Topology/Algebra/Group/Torsor.lean
index cb5470f71e0f73..e06ff5bde26c9c 100644
--- a/Mathlib/Topology/Algebra/Group/Torsor.lean
+++ b/Mathlib/Topology/Algebra/Group/Torsor.lean
@@ -98,6 +98,25 @@ theorem IsTopologicalTorsor.to_isTopologicalGroup : IsTopologicalGroup V where
def Homeomorph.smulConst (p : P) : V ≃ₜ P where
__ := Equiv.smulConst p
+/-- The map `p' ↦ p /ₛ p'` as a homeomorphism: `Equiv.constSDiv` as a homeomorphism -/
+@[to_additive (attr := simps!)
+/-- The map `p' ↦ p -ᵥ p'` as a homeomorphism: `Equiv.constVSub` as a homeomorphism -/]
+def Homeomorph.constSDiv (p : P) : P ≃ₜ V where
+ toEquiv := Equiv.constSDiv p
+ continuous_invFun := by
+ have := IsTopologicalTorsor.to_isTopologicalGroup V P
+ fun_prop
+
+/-- `Equiv.pointReflection` as a homeomorphism -/
+def Homeomorph.pointReflection {V P : Type*} [AddGroup V] [TopologicalSpace V] [AddTorsor V P]
+ [TopologicalSpace P] [IsTopologicalAddTorsor P] (p : P) : P ≃ₜ P :=
+ (Homeomorph.constVSub p).trans (Homeomorph.vaddConst p)
+
+@[simp]
+lemma Homeomorph.coe_pointReflection {V P : Type*} [AddGroup V] [TopologicalSpace V] [AddTorsor V P]
+ [TopologicalSpace P] [IsTopologicalAddTorsor P] (p : P) :
+ (Homeomorph.pointReflection p : P → P) = Equiv.pointReflection p := rfl
+
end Torsor
section Group
From 8d1ee6c7d56bcad90d248365305abcecf1cf5729 Mon Sep 17 00:00:00 2001
From: Lua Viana Reis <4229115+lua-vr@users.noreply.github.com>
Date: Sat, 20 Jun 2026 17:52:02 +0000
Subject: [PATCH 0211/1300] chore(Integral/Layercake): fix LaTeX syntax in the
module doc-string (#40839)
The LaTeX formulas were not rendering correctly in the web docs due to missing escapes for the brackets. Besides that, I also added some standard spacing in the formulas for readability.
---
Mathlib/MeasureTheory/Integral/Layercake.lean | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/Mathlib/MeasureTheory/Integral/Layercake.lean b/Mathlib/MeasureTheory/Integral/Layercake.lean
index 89d87338d77988..3a5b7c938a8f4f 100644
--- a/Mathlib/MeasureTheory/Integral/Layercake.lean
+++ b/Mathlib/MeasureTheory/Integral/Layercake.lean
@@ -27,7 +27,7 @@ The essence of the (mathematical) proof is Fubini's theorem.
We also give the most common application of the layer cake formula -
a representation of the integral of a nonnegative function f:
-$$∫ f(ω) ∂μ(ω) = ∫ μ {ω | f(ω) ≥ t} dt$$
+$$∫ f(ω) \,∂μ(ω) = ∫ μ \{ω \mid f(ω) ≥ t\} \,dt$$
Variants of the formulas with measures of sets of the form `{ω | f(ω) > t}` instead of
`{ω | f(ω) ≥ t}` are also included.
@@ -41,12 +41,12 @@ Variants of the formulas with measures of sets of the form `{ω | f(ω) > t}` in
* `MeasureTheory.lintegral_eq_lintegral_meas_le` and
`MeasureTheory.lintegral_eq_lintegral_meas_lt`:
The most common special cases of the layer cake formulas, stating that for a nonnegative
- function f we have $∫ f(ω) ∂μ(ω) = ∫ μ {ω | f(ω) ≥ t} dt$ and
- $∫ f(ω) ∂μ(ω) = ∫ μ {ω | f(ω) > t} dt$, respectively.
+ function f we have $∫ f(ω) \,∂μ(ω) = ∫ μ \{ω \mid f(ω) ≥ t\} \,dt$ and
+ $∫ f(ω) \,∂μ(ω) = ∫ μ \{ω \mid f(ω) > t\} \,dt$, respectively.
* `Integrable.integral_eq_integral_meas_lt`:
A Bochner integral version of the most common special case of the layer cake formulas, stating
that for an integrable and a.e.-nonnegative function f we have
- $∫ f(ω) ∂μ(ω) = ∫ μ {ω | f(ω) > t} dt$.
+ $∫ f(ω) \,∂μ(ω) = ∫ μ \{ω \mid f(ω) > t\} \,dt$.
## See also
From 09b373db6e247a35cfa5e44578c09a20e7c97271 Mon Sep 17 00:00:00 2001
From: "mathlib-nolints[bot]"
<258989889+mathlib-nolints[bot]@users.noreply.github.com>
Date: Sun, 21 Jun 2026 00:48:00 +0000
Subject: [PATCH 0212/1300] chore(scripts): update nolints.json (#40856)
I am happy to remove some nolints for you!
---
scripts/nolints.json | 3 ---
1 file changed, 3 deletions(-)
diff --git a/scripts/nolints.json b/scripts/nolints.json
index f46adf7bf9cee7..c2e26b90be252a 100644
--- a/scripts/nolints.json
+++ b/scripts/nolints.json
@@ -103,8 +103,6 @@
"CompactlySupportedContinuousMap.pullback_addMonoidHom"],
["defsWithUnderscore", "CompactlySupportedContinuousMap.pullback_monoidHom"],
["defsWithUnderscore", "ContDiffMapSupportedIn.of_support_subset"],
- ["defsWithUnderscore", "ContRepresentation.coind₁_map"],
- ["defsWithUnderscore", "ContRepresentation.coind₁_ι"],
["defsWithUnderscore",
"ContinuousLinearMap.antilipschitzConstant_of_injective_of_isClosed_range"],
["defsWithUnderscore",
@@ -638,7 +636,6 @@
["docBlame", "RingQuot.toQuot"],
["docBlame", "Shrink.rec"],
["docBlame", "SlashAction.map"],
- ["docBlame", "StarAlgEquiv.restrictScalars"],
["docBlame", "StarAlgHom.restrictScalars"],
["docBlame", "StateT.callCC"],
["docBlame", "StateT.mkLabel"],
From f85645b51d74ad187fdc751c05070829540f3ee1 Mon Sep 17 00:00:00 2001
From: Jack McCarthy <37917934+Deicyde@users.noreply.github.com>
Date: Sun, 21 Jun 2026 05:43:02 +0000
Subject: [PATCH 0213/1300] doc: add wikidata attributes (#40747)
This PR adds a batch of 25 `@[wikidata]` attributes.
Claude helped generate the list of crossrefs (by scanning Wikidata + Mathlib). Comments are generated by [crossref-report](https://github.com/jcommelin/mathlib-crossref-report) and Wikilean.
See https://wikilean.jackmccarthy.org/review?pr=40747 for reviewer UI.
---
Mathlib/Algebra/Module/Projective.lean | 1 +
Mathlib/Algebra/Ring/Periodic.lean | 2 +-
Mathlib/Analysis/InnerProductSpace/Defs.lean | 1 +
Mathlib/Analysis/InnerProductSpace/Laplacian.lean | 1 +
Mathlib/Combinatorics/SimpleGraph/Basic.lean | 1 +
.../AffineSpace/AffineSubspace/Basic.lean | 1 +
Mathlib/Probability/CentralLimitTheorem.lean | 1 +
Mathlib/Topology/UniformSpace/Defs.lean | 1 +
WikifunctionsScratch_probe.lean | 10 ++++++++++
9 files changed, 18 insertions(+), 1 deletion(-)
create mode 100644 WikifunctionsScratch_probe.lean
diff --git a/Mathlib/Algebra/Module/Projective.lean b/Mathlib/Algebra/Module/Projective.lean
index 5d944d77a74cbc..c6bd0bb04be016 100644
--- a/Mathlib/Algebra/Module/Projective.lean
+++ b/Mathlib/Algebra/Module/Projective.lean
@@ -67,6 +67,7 @@ from the free `R`-module on `P` to `P` splits. -/
/-- An R-module is projective if it is a direct summand of a free module, or equivalently
if maps from the module lift along surjections. There are several other equivalent
definitions. -/
+@[wikidata Q942423]
class Module.Projective (R : Type*) [Semiring R] (P : Type*) [AddCommMonoid P] [Module R P] :
Prop where
out : ∃ s : P →ₗ[R] P →₀ R, Function.LeftInverse (Finsupp.linearCombination R id) s
diff --git a/Mathlib/Algebra/Ring/Periodic.lean b/Mathlib/Algebra/Ring/Periodic.lean
index 9b86104f1dc820..bb0a228b59f6d7 100644
--- a/Mathlib/Algebra/Ring/Periodic.lean
+++ b/Mathlib/Algebra/Ring/Periodic.lean
@@ -41,7 +41,7 @@ namespace Function
/-- A function `f` is said to be `Periodic` with period `c` if for all `x`, `f (x + c) = f x`. -/
-@[simp]
+@[simp, wikidata Q184743]
def Periodic [Add α] (f : α → β) (c : α) : Prop :=
∀ x : α, f (x + c) = f x
diff --git a/Mathlib/Analysis/InnerProductSpace/Defs.lean b/Mathlib/Analysis/InnerProductSpace/Defs.lean
index cc34ea1e8dda94..2bc916e9ff9f00 100644
--- a/Mathlib/Analysis/InnerProductSpace/Defs.lean
+++ b/Mathlib/Analysis/InnerProductSpace/Defs.lean
@@ -103,6 +103,7 @@ Note that `NormedSpace` does not assume that `‖x‖=0` implies `x=0` (it is ra
To construct a seminorm from an inner product, see `PreInnerProductSpace.ofCore`.
-/
+@[wikidata Q214159]
class InnerProductSpace (𝕜 : Type*) (E : Type*) [RCLike 𝕜] [SeminormedAddCommGroup E] extends
NormedSpace 𝕜 E, Inner 𝕜 E where
/-- The inner product induces the norm. -/
diff --git a/Mathlib/Analysis/InnerProductSpace/Laplacian.lean b/Mathlib/Analysis/InnerProductSpace/Laplacian.lean
index b75c2c3ef2661b..2041f4bc929c6d 100644
--- a/Mathlib/Analysis/InnerProductSpace/Laplacian.lean
+++ b/Mathlib/Analysis/InnerProductSpace/Laplacian.lean
@@ -128,6 +128,7 @@ variable (f s) in
Laplacian for functions on real inner product spaces, with respect to a set `s`. Use `open
InnerProductSpace` to access the notation `Δ[s]` for `InnerProductSpace.LaplacianWithin`.
-/
+@[wikidata Q203484]
noncomputable def laplacianWithin : E → F :=
fun x ↦ tensorIteratedFDerivWithinTwo ℝ f s x (InnerProductSpace.canonicalCovariantTensor E)
diff --git a/Mathlib/Combinatorics/SimpleGraph/Basic.lean b/Mathlib/Combinatorics/SimpleGraph/Basic.lean
index d1b86b494d8c74..e5a45faaf3d37a 100644
--- a/Mathlib/Combinatorics/SimpleGraph/Basic.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/Basic.lean
@@ -316,6 +316,7 @@ instance completeAtomicBooleanAlgebra : CompleteAtomicBooleanAlgebra (SimpleGrap
iInf_iSup_eq f := by ext; simp [Classical.skolem]
/-- The complete graph on a type `V` is the simple graph with all pairs of distinct vertices. -/
+@[wikidata Q45715]
abbrev completeGraph (V : Type u) : SimpleGraph V := ⊤
/-- The graph with no edges on a given vertex type `V`. -/
diff --git a/Mathlib/LinearAlgebra/AffineSpace/AffineSubspace/Basic.lean b/Mathlib/LinearAlgebra/AffineSpace/AffineSubspace/Basic.lean
index 5874d92b08e4bd..bec793d3828b41 100644
--- a/Mathlib/LinearAlgebra/AffineSpace/AffineSubspace/Basic.lean
+++ b/Mathlib/LinearAlgebra/AffineSpace/AffineSubspace/Basic.lean
@@ -820,6 +820,7 @@ variable [AffineSpace V P]
/-- Two affine subspaces are parallel if one is related to the other by adding the same vector
to all points. -/
+@[wikidata Q53875]
def Parallel (s₁ s₂ : AffineSubspace k P) : Prop :=
∃ v : V, s₂ = s₁.map (constVAdd k P v)
diff --git a/Mathlib/Probability/CentralLimitTheorem.lean b/Mathlib/Probability/CentralLimitTheorem.lean
index 312e75db6e0570..1e1676806ae8f7 100644
--- a/Mathlib/Probability/CentralLimitTheorem.lean
+++ b/Mathlib/Probability/CentralLimitTheorem.lean
@@ -120,6 +120,7 @@ private theorem tendstoInDistribution_inv_sqrt_mul_var_mul_sum_sub
independent, identically distributed with mean `μ` and variance `v`, and a random variable
`Y : Ω' → ℝ` following `gaussianReal 0 v`, the sequence
`n ↦ (√n)⁻¹ * (∑ k ∈ Finset.range n, X k ω - n * μ)` converges to `Y` in distribution. -/
+@[wikidata Q190391]
theorem tendstoInDistribution_inv_sqrt_mul_sum_sub
(hY : HasLaw Y (gaussianReal 0 Var[X 0; P].toNNReal) P')
(hX : MemLp (X 0) 2 P) (hindep : iIndepFun X P)
diff --git a/Mathlib/Topology/UniformSpace/Defs.lean b/Mathlib/Topology/UniformSpace/Defs.lean
index f23cfccc9011c6..b2078d7073c3d9 100644
--- a/Mathlib/Topology/UniformSpace/Defs.lean
+++ b/Mathlib/Topology/UniformSpace/Defs.lean
@@ -189,6 +189,7 @@ theorem UniformSpace.Core.nhds_toTopologicalSpace {α : Type u} (u : Core α) (x
A metric space has a natural uniformity, and a uniform space has a natural topology.
A topological group also has a natural uniformity, even when it is not metrizable. -/
+@[wikidata Q652446]
class UniformSpace (α : Type u) extends TopologicalSpace α where
/-- The uniformity filter. -/
protected uniformity : Filter (α × α)
diff --git a/WikifunctionsScratch_probe.lean b/WikifunctionsScratch_probe.lean
new file mode 100644
index 00000000000000..13124263b5a9c6
--- /dev/null
+++ b/WikifunctionsScratch_probe.lean
@@ -0,0 +1,10 @@
+import Mathlib.Data.Nat.GCD.Basic
+import Mathlib.Tactic
+open Nat
+#check @Nat.gcd_rec
+#check @Nat.Coprime
+#check @Nat.coprime_iff_gcd_eq_one
+#check @Nat.gcd_zero_left
+#check @Nat.gcd_zero_right
+#check @Nat.gcd_comm
+#print Nat.Coprime
From e10f52c0007c0ae339eadb810170c739a99f4c58 Mon Sep 17 00:00:00 2001
From: Xavier Roblot <46200072+xroblot@users.noreply.github.com>
Date: Sun, 21 Jun 2026 08:49:04 +0000
Subject: [PATCH 0214/1300] feat(NumberTheory/GaussSum): results on Gauss sums
of trivial characters (#40730)
Add the special values of the Gauss sum when one or both of the characters is trivial:
* `gaussSum_one_one`: the Gauss sum of the two trivial characters equals the number of units of `R`;
* `gaussSum_one_right`: the Gauss sum of a nontrivial multiplicative character and the trivial additive character vanishes;
* `gaussSum_one_left`: the Gauss sum of the trivial multiplicative character and a nontrivial additive character, over a finite field, equals `-1`.
:robot: This PR was extracted from the [SKW project](https://github.com/xroblot/SKW) by Claude.
---
Mathlib/NumberTheory/GaussSum.lean | 43 ++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/Mathlib/NumberTheory/GaussSum.lean b/Mathlib/NumberTheory/GaussSum.lean
index dec2c041cf88a3..9092406fa0325b 100644
--- a/Mathlib/NumberTheory/GaussSum.lean
+++ b/Mathlib/NumberTheory/GaussSum.lean
@@ -95,6 +95,49 @@ lemma star_gaussSum_eq (χ : MulChar R ℂ) (ψ : AddChar R ℂ) :
end GaussSumDef
+/-!
+### Gauss sums of trivial characters
+-/
+
+section GaussSumTrivial
+
+variable {R R' : Type*} [CommRing R] [Fintype R] [CommRing R']
+
+/-- The Gauss sum of the two trivial characters is the cardinality of the unit group of `R`. -/
+@[simp]
+theorem gaussSum_one_one : gaussSum (1 : MulChar R R') (1 : AddChar R R') = Nat.card Rˣ := by
+ classical
+ simp [gaussSum, MulChar.sum_one_eq_card_units]
+
+/-- The Gauss sum of a nontrivial multiplicative character and the trivial additive character
+vanishes. -/
+theorem gaussSum_one_right [IsDomain R'] {χ : MulChar R R'} (hχ : χ ≠ 1) :
+ gaussSum χ (1 : AddChar R R') = 0 := by
+ simpa [gaussSum] using MulChar.sum_eq_zero_of_ne_one hχ
+
+end GaussSumTrivial
+
+section GaussSumTrivialField
+
+variable {R R' : Type*} [Field R] [Fintype R] [CommRing R'] [IsDomain R']
+
+/-- The Gauss sum of the trivial multiplicative character and a nontrivial additive character,
+over a finite field, is `-1`. -/
+theorem gaussSum_one_left {ψ : AddChar R R'} (hψ : ψ ≠ 1) :
+ gaussSum (1 : MulChar R R') ψ = -1 := by
+ classical
+ simp only [gaussSum, ← add_eq_zero_iff_eq_neg]
+ calc ∑ a, (1 : MulChar R R') a * ψ a + 1
+ _ = ∑ a ∈ {0}ᶜ, (1 : MulChar R R') a * ψ a + 1 := by
+ simp [← ({0} : Finset R).sum_compl_add_sum]
+ _ = ∑ a ∈ {0}ᶜ, ψ a + ψ 0 := by
+ congr! <;> aesop (add simp MulChar.one_apply)
+ _ = 0 := by
+ rw [← AddChar.sum_eq_zero_of_ne_one hψ, ← Finset.sum_compl_add_sum (s := {0})]
+ simp
+
+end GaussSumTrivialField
+
/-!
### The product of two Gauss sums
-/
From 403c5d3f621d47843aed5a9c6f8a3ede7237bf1a Mon Sep 17 00:00:00 2001
From: "Filippo A. E. Nuccio" <65080144+faenuccio@users.noreply.github.com>
Date: Sun, 21 Jun 2026 09:11:17 +0000
Subject: [PATCH 0215/1300] fix: remove extra file related to wikidata (#40859)
Co-authored-by: faenuccio
---
WikifunctionsScratch_probe.lean | 10 ----------
1 file changed, 10 deletions(-)
delete mode 100644 WikifunctionsScratch_probe.lean
diff --git a/WikifunctionsScratch_probe.lean b/WikifunctionsScratch_probe.lean
deleted file mode 100644
index 13124263b5a9c6..00000000000000
--- a/WikifunctionsScratch_probe.lean
+++ /dev/null
@@ -1,10 +0,0 @@
-import Mathlib.Data.Nat.GCD.Basic
-import Mathlib.Tactic
-open Nat
-#check @Nat.gcd_rec
-#check @Nat.Coprime
-#check @Nat.coprime_iff_gcd_eq_one
-#check @Nat.gcd_zero_left
-#check @Nat.gcd_zero_right
-#check @Nat.gcd_comm
-#print Nat.Coprime
From e5836dbb05fccdfa24ab3396d46f1f49b1f816ed Mon Sep 17 00:00:00 2001
From: Hannah Scholz <70071345+scholzhannah@users.noreply.github.com>
Date: Sun, 21 Jun 2026 09:53:45 +0000
Subject: [PATCH 0216/1300] feat: add `fun_prop` attribute to
`OpenPartialHomeomorph.continuousOn` and friends (#40462)
---
Mathlib/Topology/OpenPartialHomeomorph/Defs.lean | 2 ++
Mathlib/Topology/PartialHomeomorph/Defs.lean | 2 ++
2 files changed, 4 insertions(+)
diff --git a/Mathlib/Topology/OpenPartialHomeomorph/Defs.lean b/Mathlib/Topology/OpenPartialHomeomorph/Defs.lean
index a8f90bd94cf6df..c9563195b3dc36 100644
--- a/Mathlib/Topology/OpenPartialHomeomorph/Defs.lean
+++ b/Mathlib/Topology/OpenPartialHomeomorph/Defs.lean
@@ -86,9 +86,11 @@ def Simps.symm_apply (e : OpenPartialHomeomorph X Y) : Y → X := e.symm
initialize_simps_projections OpenPartialHomeomorph (toFun → apply, invFun → symm_apply)
+@[fun_prop]
protected theorem continuousOn : ContinuousOn e e.source :=
e.continuousOn_toFun
+@[fun_prop]
theorem continuousOn_symm : ContinuousOn e.symm e.target :=
e.continuousOn_invFun
diff --git a/Mathlib/Topology/PartialHomeomorph/Defs.lean b/Mathlib/Topology/PartialHomeomorph/Defs.lean
index ab78f5be70780c..3cd827e68cc315 100644
--- a/Mathlib/Topology/PartialHomeomorph/Defs.lean
+++ b/Mathlib/Topology/PartialHomeomorph/Defs.lean
@@ -86,9 +86,11 @@ def Simps.symm_apply (e : PartialHomeomorph X Y) : Y → X := e.symm
initialize_simps_projections PartialHomeomorph (toFun → apply, invFun → symm_apply)
+@[fun_prop]
protected theorem continuousOn : ContinuousOn e e.source :=
e.continuousOn_toFun
+@[fun_prop]
theorem continuousOn_symm : ContinuousOn e.symm e.target :=
e.continuousOn_invFun
From 29af5245bafea7d69fdca69591450f60b916ed71 Mon Sep 17 00:00:00 2001
From: Michael Rothgang <10105016+grunweg@users.noreply.github.com>
Date: Sun, 21 Jun 2026 13:42:19 +0000
Subject: [PATCH 0217/1300] chore: adaptation for batteries#1864 and
batteries#1866 (#40821)
- With the `checkUniv` linter being removed, we delete all corresponding `nolint` entries.
We move any comments to the corresponding `checkUniv` core linter suppression.
- With the `simpVarHead` linter being removed, we delete all corresponding `nolint` entries.
Co-authored-by: mathlib-nightly-testing[bot]
---
Mathlib/Algebra/Category/FGModuleCat/Basic.lean | 6 ------
Mathlib/Algebra/Category/Grp/Basic.lean | 6 ++----
Mathlib/Algebra/Module/Presentation/Basic.lean | 2 --
Mathlib/CategoryTheory/Bicategory/Basic.lean | 1 -
Mathlib/CategoryTheory/Category/Cat.lean | 1 -
Mathlib/CategoryTheory/Category/Quiv.lean | 1 -
Mathlib/CategoryTheory/Category/ReflQuiv.lean | 1 -
.../FiberedCategory/BasedCategory.lean | 1 -
.../CategoryTheory/FiberedCategory/HasFibers.lean | 1 -
Mathlib/CategoryTheory/Groupoid/Grpd/Basic.lean | 1 -
Mathlib/CategoryTheory/Limits/Chosen/End.lean | 4 ++--
Mathlib/CategoryTheory/Limits/Creates.lean | 4 ++--
Mathlib/CategoryTheory/Limits/Preserves/Basic.lean | 8 ++++----
.../CategoryTheory/Limits/Preserves/Filtered.lean | 8 ++++----
.../CategoryTheory/Limits/Shapes/Multiequalizer.lean | 4 ----
Mathlib/CategoryTheory/MorphismProperty/Comma.lean | 2 +-
Mathlib/Data/PFunctor/Univariate/Basic.lean | 5 +++--
Mathlib/Geometry/RingedSpace/Basic.lean | 2 +-
Mathlib/Geometry/RingedSpace/LocallyRingedSpace.lean | 2 +-
Mathlib/ModelTheory/Basic.lean | 1 -
Mathlib/RingTheory/Extension/Presentation/Basic.lean | 1 -
.../Extension/Presentation/Submersive.lean | 2 --
Mathlib/SetTheory/Ordinal/Univ.lean | 4 ++--
Mathlib/SetTheory/ZFC/PSet.lean | 1 -
Mathlib/Topology/Category/CompHausLike/Limits.lean | 12 +++++-------
lake-manifest.json | 2 +-
26 files changed, 28 insertions(+), 55 deletions(-)
diff --git a/Mathlib/Algebra/Category/FGModuleCat/Basic.lean b/Mathlib/Algebra/Category/FGModuleCat/Basic.lean
index bd7a124b616bc7..eefbde73cc87b9 100644
--- a/Mathlib/Algebra/Category/FGModuleCat/Basic.lean
+++ b/Mathlib/Algebra/Category/FGModuleCat/Basic.lean
@@ -99,12 +99,6 @@ abbrev of (V : Type v) [AddCommGroup V] [Module R V] [Module.Finite R V] : FGMod
lemma of_carrier (V : Type v) [AddCommGroup V] [Module R V] [Module.Finite R V] :
of R V = V := rfl
-/-
-The reduction done by `simpVarHead` is stronger than the one actually used by `simp`,
-so we get a false positive here
--/
-attribute [nolint simpVarHead] of_carrier
-
variable {R} in
/-- Lift a linear map between finitely generated modules to `FGModuleCat R`. -/
abbrev ofHom {V W : Type v} [AddCommGroup V] [Module R V] [Module.Finite R V]
diff --git a/Mathlib/Algebra/Category/Grp/Basic.lean b/Mathlib/Algebra/Category/Grp/Basic.lean
index bd4be06cb2de90..58e59d68d21006 100644
--- a/Mathlib/Algebra/Category/Grp/Basic.lean
+++ b/Mathlib/Algebra/Category/Grp/Basic.lean
@@ -602,22 +602,20 @@ instance CommGrpCat.forget_reflects_isos : (forget CommGrpCat.{u}).ReflectsIsomo
-- this variant is then renamed with an `Aux` suffix
set_option linter.checkUnivs false in
/-- An alias for `GrpCat.{max u v}`, to deal around unification issues. -/
-@[to_additive (attr := nolint checkUnivs) GrpMaxAux
+@[to_additive GrpMaxAux
/-- An alias for `AddGrpCat.{max u v}`, to deal around unification issues. -/]
abbrev GrpMax.{u1, u2} := GrpCat.{max u1 u2}
set_option linter.checkUnivs false in
/-- An alias for `AddGrpCat.{max u v}`, to deal around unification issues. -/
-@[nolint checkUnivs]
abbrev AddGrpMax.{u1, u2} := AddGrpCat.{max u1 u2}
set_option linter.checkUnivs false in
/-- An alias for `CommGrpCat.{max u v}`, to deal around unification issues. -/
-@[to_additive (attr := nolint checkUnivs) AddCommGrpMaxAux
+@[to_additive AddCommGrpMaxAux
/-- An alias for `AddCommGrpCat.{max u v}`, to deal around unification issues. -/]
abbrev CommGrpMax.{u1, u2} := CommGrpCat.{max u1 u2}
set_option linter.checkUnivs false in
/-- An alias for `AddCommGrpCat.{max u v}`, to deal around unification issues. -/
-@[nolint checkUnivs]
abbrev AddCommGrpMax.{u1, u2} := AddCommGrpCat.{max u1 u2}
diff --git a/Mathlib/Algebra/Module/Presentation/Basic.lean b/Mathlib/Algebra/Module/Presentation/Basic.lean
index 47775e86dd5dff..4f372e7e6ee899 100644
--- a/Mathlib/Algebra/Module/Presentation/Basic.lean
+++ b/Mathlib/Algebra/Module/Presentation/Basic.lean
@@ -52,7 +52,6 @@ set_option linter.checkUnivs false in
/-- Given a ring `A`, this structure involves a family of elements (indexed by a type `R`)
in a free module `G →₀ A`. This allows to define an `A`-module by generators and relations,
see `Relations.Quotient`. -/
-@[nolint checkUnivs]
structure Relations where
/-- the index type for generators -/
G : Type w₀
@@ -492,7 +491,6 @@ variable (M : Type v) [AddCommGroup M] [Module A M]
set_option linter.checkUnivs false in
/-- Given an `A`-module `M`, a term in this type is a presentation by `M` by
generators and relations. -/
-@[nolint checkUnivs]
structure Presentation extends Relations.{w₀, w₁} A,
toRelations.Solution M, toSolution.IsPresentation where
diff --git a/Mathlib/CategoryTheory/Bicategory/Basic.lean b/Mathlib/CategoryTheory/Bicategory/Basic.lean
index a715b9ac6890cd..99c3d8903325ad 100644
--- a/Mathlib/CategoryTheory/Bicategory/Basic.lean
+++ b/Mathlib/CategoryTheory/Bicategory/Basic.lean
@@ -58,7 +58,6 @@ These associators and unitors satisfy the pentagon and triangle equations.
See https://ncatlab.org/nlab/show/bicategory.
-/
-@[nolint checkUnivs]
class Bicategory (B : Type u) extends CategoryStruct.{v} B where
/-- The category structure on the collection of 1-morphisms -/
homCategory : ∀ a b : B, Category.{w} (a ⟶ b) := by infer_instance
diff --git a/Mathlib/CategoryTheory/Category/Cat.lean b/Mathlib/CategoryTheory/Category/Cat.lean
index b179e05ca015f0..d2b24eb2228105 100644
--- a/Mathlib/CategoryTheory/Category/Cat.lean
+++ b/Mathlib/CategoryTheory/Category/Cat.lean
@@ -33,7 +33,6 @@ open Bicategory Functor
-- intended to be used with explicit universe parameters
set_option linter.checkUnivs false in
/-- Category of categories. -/
-@[nolint checkUnivs]
def Cat :=
Bundled Category.{v, u}
diff --git a/Mathlib/CategoryTheory/Category/Quiv.lean b/Mathlib/CategoryTheory/Category/Quiv.lean
index 60b2bf23c8709f..0cdcf0680ed8c8 100644
--- a/Mathlib/CategoryTheory/Category/Quiv.lean
+++ b/Mathlib/CategoryTheory/Category/Quiv.lean
@@ -24,7 +24,6 @@ namespace CategoryTheory
-- intended to be used with explicit universe parameters
set_option linter.checkUnivs false in
/-- Category of quivers. -/
-@[nolint checkUnivs]
def Quiv :=
Bundled Quiver.{v, u}
diff --git a/Mathlib/CategoryTheory/Category/ReflQuiv.lean b/Mathlib/CategoryTheory/Category/ReflQuiv.lean
index f1dc344cb96886..649de3f73a0344 100644
--- a/Mathlib/CategoryTheory/Category/ReflQuiv.lean
+++ b/Mathlib/CategoryTheory/Category/ReflQuiv.lean
@@ -23,7 +23,6 @@ universe v u v₁ v₂ u₁ u₂
set_option linter.checkUnivs false in
/-- Category of refl quivers. -/
-@[nolint checkUnivs]
def ReflQuiv :=
Bundled ReflQuiver.{v, u}
diff --git a/Mathlib/CategoryTheory/FiberedCategory/BasedCategory.lean b/Mathlib/CategoryTheory/FiberedCategory/BasedCategory.lean
index 02f09d2a413c2a..0f0df6df1fc83e 100644
--- a/Mathlib/CategoryTheory/FiberedCategory/BasedCategory.lean
+++ b/Mathlib/CategoryTheory/FiberedCategory/BasedCategory.lean
@@ -38,7 +38,6 @@ variable {𝒮 : Type u₁} [Category.{v₁} 𝒮]
set_option linter.checkUnivs false in
/-- A based category over `𝒮` is a category `𝒳` together with a functor `p : 𝒳 ⥤ 𝒮`. -/
-@[nolint checkUnivs]
structure BasedCategory (𝒮 : Type u₁) [Category.{v₁} 𝒮] where
/-- The type of objects in a `BasedCategory` -/
obj : Type u₂
diff --git a/Mathlib/CategoryTheory/FiberedCategory/HasFibers.lean b/Mathlib/CategoryTheory/FiberedCategory/HasFibers.lean
index 7dc53e2dabba98..18d798ebab7cd3 100644
--- a/Mathlib/CategoryTheory/FiberedCategory/HasFibers.lean
+++ b/Mathlib/CategoryTheory/FiberedCategory/HasFibers.lean
@@ -63,7 +63,6 @@ set_option linter.checkUnivs false in
collection of categories `Fib S` for every `S : 𝒮` (the fiber categories), each equipped with a
functors `ι : Fib S ⥤ 𝒳` which map constantly to `S` on the base such that the induced functor
`Fib S ⥤ Fiber p S` is an equivalence. -/
-@[nolint checkUnivs]
class HasFibers (p : 𝒳 ⥤ 𝒮) where
/-- The type of objects of the category `Fib S` for each `S`. -/
Fib (S : 𝒮) : Type u₃
diff --git a/Mathlib/CategoryTheory/Groupoid/Grpd/Basic.lean b/Mathlib/CategoryTheory/Groupoid/Grpd/Basic.lean
index 9860d8bd1379b6..fcfc708418d719 100644
--- a/Mathlib/CategoryTheory/Groupoid/Grpd/Basic.lean
+++ b/Mathlib/CategoryTheory/Groupoid/Grpd/Basic.lean
@@ -35,7 +35,6 @@ namespace CategoryTheory
-- intended to be used with explicit universe parameters
set_option linter.checkUnivs false in
/-- Category of groupoids -/
-@[nolint checkUnivs]
def Grpd :=
Bundled Groupoid.{v, u}
diff --git a/Mathlib/CategoryTheory/Limits/Chosen/End.lean b/Mathlib/CategoryTheory/Limits/Chosen/End.lean
index 8bacfbd3374248..6e01480689f9b9 100644
--- a/Mathlib/CategoryTheory/Limits/Chosen/End.lean
+++ b/Mathlib/CategoryTheory/Limits/Chosen/End.lean
@@ -32,7 +32,7 @@ class ChosenCoendsOfShape (J : Type*) [Category* J] (C : Type*) [Category* C] wh
set_option linter.checkUnivs false in
/-- The data of chosen coends in `C`. -/
-@[nolint checkUnivs, pp_with_univ]
+@[pp_with_univ]
abbrev ChosenCoends (C : Type*) [Category* C] :=
∀ {J : Type u} [Category.{v} J], ChosenCoendsOfShape J C
@@ -107,7 +107,7 @@ class ChosenEndsOfShape (J : Type*) [Category* J] (C : Type*) [Category* C] wher
set_option linter.checkUnivs false in
/-- The data of chosen ends in `C`. -/
-@[nolint checkUnivs, pp_with_univ]
+@[pp_with_univ]
abbrev ChosenEnds (C : Type*) [Category* C] :=
∀ {J : Type u} [Category.{v} J], ChosenEndsOfShape J C
diff --git a/Mathlib/CategoryTheory/Limits/Creates.lean b/Mathlib/CategoryTheory/Limits/Creates.lean
index 0608f6fb77867b..553b495bb93180 100644
--- a/Mathlib/CategoryTheory/Limits/Creates.lean
+++ b/Mathlib/CategoryTheory/Limits/Creates.lean
@@ -87,7 +87,7 @@ set_option linter.checkUnivs false in
-- https://github.com/leanprover/lean4/pull/12423, the shape universes in
-- `CreatesLimitsOfSize` and `CreatesColimitsOfSize` would default to universe output parameters.
-- See Note [universe output parameters and typeclass caching].
-@[univ_out_params, nolint checkUnivs, pp_with_univ]
+@[univ_out_params, pp_with_univ]
class CreatesLimitsOfSize (F : C ⥤ D) where
CreatesLimitsOfShape : ∀ {J : Type w} [Category.{w'} J], CreatesLimitsOfShape J F := by
infer_instance
@@ -117,7 +117,7 @@ class CreatesColimitsOfShape (J : Type w) [Category.{w'} J] (F : C ⥤ D) where
-- This should be used with explicit universe variables.
set_option linter.checkUnivs false in
/-- `F` creates colimits if it creates colimits of shape `J` for any small `J`. -/
-@[univ_out_params, nolint checkUnivs, pp_with_univ]
+@[univ_out_params, pp_with_univ]
class CreatesColimitsOfSize (F : C ⥤ D) where
CreatesColimitsOfShape : ∀ {J : Type w} [Category.{w'} J], CreatesColimitsOfShape J F := by
infer_instance
diff --git a/Mathlib/CategoryTheory/Limits/Preserves/Basic.lean b/Mathlib/CategoryTheory/Limits/Preserves/Basic.lean
index 1967dc99751bd3..d9bbd2bac9030c 100644
--- a/Mathlib/CategoryTheory/Limits/Preserves/Basic.lean
+++ b/Mathlib/CategoryTheory/Limits/Preserves/Basic.lean
@@ -80,7 +80,7 @@ diagram `J ⥤ C` to limit cones, where `J : Type u` with `[Category.{v} J]`. -/
-- `PreservesLimitsOfSize`, `PreservesColimitsOfSize`, `ReflectsLimitsOfSize`, and
-- `ReflectsColimitsOfSize` would default to universe output parameters.
-- See Note [universe output parameters and typeclass caching].
-@[univ_out_params, nolint checkUnivs, pp_with_univ]
+@[univ_out_params, pp_with_univ]
class PreservesLimitsOfSize (F : C ⥤ D) : Prop where
preservesLimitsOfShape : ∀ {J : Type w} [Category.{w'} J], PreservesLimitsOfShape J F := by
infer_instance
@@ -93,7 +93,7 @@ abbrev PreservesLimits (F : C ⥤ D) :=
-- This should be used with explicit universe variables.
/-- `PreservesColimitsOfSize.{v u} F` means that `F` sends all colimit cocones over any
diagram `J ⥤ C` to colimit cocones, where `J : Type u` with `[Category.{v} J]`. -/
-@[univ_out_params, nolint checkUnivs, pp_with_univ]
+@[univ_out_params, pp_with_univ]
class PreservesColimitsOfSize (F : C ⥤ D) : Prop where
preservesColimitsOfShape : ∀ {J : Type w} [Category.{w'} J], PreservesColimitsOfShape J F := by
infer_instance
@@ -415,7 +415,7 @@ whenever the image of a cone over some `K : J ⥤ C` under `F` is a limit cone i
the cone was already a limit cone in `C`.
Note that we do not assume a priori that `D` actually has any limits.
-/
-@[univ_out_params, nolint checkUnivs, pp_with_univ]
+@[univ_out_params, pp_with_univ]
class ReflectsLimitsOfSize (F : C ⥤ D) : Prop where
reflectsLimitsOfShape : ∀ {J : Type w} [Category.{w'} J], ReflectsLimitsOfShape J F := by
infer_instance
@@ -434,7 +434,7 @@ whenever the image of a cocone over some `K : J ⥤ C` under `F` is a colimit co
the cocone was already a colimit cocone in `C`.
Note that we do not assume a priori that `D` actually has any colimits.
-/
-@[univ_out_params, nolint checkUnivs, pp_with_univ]
+@[univ_out_params, pp_with_univ]
class ReflectsColimitsOfSize (F : C ⥤ D) : Prop where
reflectsColimitsOfShape : ∀ {J : Type w} [Category.{w'} J], ReflectsColimitsOfShape J F := by
infer_instance
diff --git a/Mathlib/CategoryTheory/Limits/Preserves/Filtered.lean b/Mathlib/CategoryTheory/Limits/Preserves/Filtered.lean
index 693fdffbdb2041..ba98dbaaf5d1d6 100644
--- a/Mathlib/CategoryTheory/Limits/Preserves/Filtered.lean
+++ b/Mathlib/CategoryTheory/Limits/Preserves/Filtered.lean
@@ -48,7 +48,7 @@ filtered diagram `J ⥤ C` to colimit cocones, where `J : Type w` with `[Categor
-- `PreservesFilteredColimitsOfSize`, `ReflectsFilteredColimitsOfSize`,
-- `PreservesCofilteredLimitsOfSize`, and `ReflectsCofilteredLimitsOfSize` would default to
-- universe output parameters. See Note [universe output parameters and typeclass caching].
-@[univ_out_params, nolint checkUnivs, pp_with_univ]
+@[univ_out_params, pp_with_univ]
class PreservesFilteredColimitsOfSize (F : C ⥤ D) : Prop where
preserves_filtered_colimits :
∀ (J : Type w) [Category.{w'} J] [IsFiltered J], PreservesColimitsOfShape J F
@@ -104,7 +104,7 @@ section Reflects
-- This should be used with explicit universe variables.
/-- `ReflectsFilteredColimitsOfSize.{w', w} F` means that whenever the image of a filtered cocone
under `F` is a colimit cocone, the original cocone was already a colimit. -/
-@[univ_out_params, nolint checkUnivs, pp_with_univ]
+@[univ_out_params, pp_with_univ]
class ReflectsFilteredColimitsOfSize (F : C ⥤ D) : Prop where
reflects_filtered_colimits :
∀ (J : Type w) [Category.{w'} J] [IsFiltered J], ReflectsColimitsOfShape J F
@@ -164,7 +164,7 @@ section Preserves
-- This should be used with explicit universe variables.
/-- `PreservesCofilteredLimitsOfSize.{w', w} F` means that `F` sends all limit cones over any
cofiltered diagram `J ⥤ C` to limit cones, where `J : Type w` with `[Category.{w'} J]`. -/
-@[univ_out_params, nolint checkUnivs, pp_with_univ]
+@[univ_out_params, pp_with_univ]
class PreservesCofilteredLimitsOfSize (F : C ⥤ D) : Prop where
preserves_cofiltered_limits :
∀ (J : Type w) [Category.{w'} J] [IsCofiltered J], PreservesLimitsOfShape J F
@@ -220,7 +220,7 @@ section Reflects
-- This should be used with explicit universe variables.
/-- `ReflectsCofilteredLimitsOfSize.{w', w} F` means that whenever the image of a cofiltered cone
under `F` is a limit cone, the original cone was already a limit. -/
-@[univ_out_params, nolint checkUnivs, pp_with_univ]
+@[univ_out_params, pp_with_univ]
class ReflectsCofilteredLimitsOfSize (F : C ⥤ D) : Prop where
reflects_cofiltered_limits :
∀ (J : Type w) [Category.{w'} J] [IsCofiltered J], ReflectsLimitsOfShape J F
diff --git a/Mathlib/CategoryTheory/Limits/Shapes/Multiequalizer.lean b/Mathlib/CategoryTheory/Limits/Shapes/Multiequalizer.lean
index 7a154e987b310e..aa78bf468b47b3 100644
--- a/Mathlib/CategoryTheory/Limits/Shapes/Multiequalizer.lean
+++ b/Mathlib/CategoryTheory/Limits/Shapes/Multiequalizer.lean
@@ -38,7 +38,6 @@ universe t w w' v u
set_option linter.checkUnivs false in
/-- The shape of a multiequalizer diagram. It involves two types `L` and `R`,
and two maps `R → L`. -/
-@[nolint checkUnivs]
structure MulticospanShape where
/-- the left type -/
L : Type w
@@ -62,7 +61,6 @@ def MulticospanShape.prod (ι : Type w) : MulticospanShape where
set_option linter.checkUnivs false in
/-- The shape of a multicoequalizer diagram. It involves two types `L` and `R`,
and two maps `L → R`. -/
-@[nolint checkUnivs]
structure MultispanShape where
/-- the left type -/
L : Type w
@@ -300,7 +298,6 @@ def arrowEquiv :
end WalkingMultispan
/-- This is a structure encapsulating the data necessary to define a `Multicospan`. -/
-@[nolint checkUnivs]
structure MulticospanIndex (J : MulticospanShape.{w, w'})
(C : Type u) [Category.{v} C] where
/-- Left map, from `J.L` to `C` -/
@@ -313,7 +310,6 @@ structure MulticospanIndex (J : MulticospanShape.{w, w'})
snd : ∀ b, left (J.snd b) ⟶ right b
/-- This is a structure encapsulating the data necessary to define a `Multispan`. -/
-@[nolint checkUnivs]
structure MultispanIndex (J : MultispanShape.{w, w'})
(C : Type u) [Category.{v} C] where
/-- Left map, from `J.L` to `C` -/
diff --git a/Mathlib/CategoryTheory/MorphismProperty/Comma.lean b/Mathlib/CategoryTheory/MorphismProperty/Comma.lean
index 8a0d834d4922e1..b942cab0ba82c2 100644
--- a/Mathlib/CategoryTheory/MorphismProperty/Comma.lean
+++ b/Mathlib/CategoryTheory/MorphismProperty/Comma.lean
@@ -168,7 +168,7 @@ structure Hom (X Y : P.Comma L R Q W) extends CommaMorphism X.toComma Y.toComma
abbrev Hom.hom {X Y : P.Comma L R Q W} (f : Comma.Hom X Y) : X.toComma ⟶ Y.toComma :=
f.toCommaMorphism
-@[simp, nolint simpVarHead]
+@[simp]
lemma Hom.hom_mk {X Y : P.Comma L R Q W} (f : CommaMorphism X.toComma Y.toComma) (hf) (hg) :
Comma.Hom.hom ⟨f, hf, hg⟩ = f := rfl
diff --git a/Mathlib/Data/PFunctor/Univariate/Basic.lean b/Mathlib/Data/PFunctor/Univariate/Basic.lean
index 69a65f27239bf0..a7c35efe92bc64 100644
--- a/Mathlib/Data/PFunctor/Univariate/Basic.lean
+++ b/Mathlib/Data/PFunctor/Univariate/Basic.lean
@@ -18,6 +18,8 @@ This file defines polynomial functors and the W-type construction as a polynomia
universe u v uA uB uA₁ uB₁ uA₂ uB₂ v₁ v₂ v₃
+-- Note: `set_option linter.checkUnivs` should not apply here,
+-- we really do want two separate universe levels
set_option linter.checkUnivs false in
/-- A polynomial functor `P` is given by a type `A` and a family `B` of types over `A`. `P` maps
any type `α` to a new type `P α`, which is defined as the sigma type `Σ x, P.B x → α`.
@@ -26,8 +28,7 @@ An element of `P α` is a pair `⟨a, f⟩`, where `a` is an element of a type `
`f : B a → α`. Think of `a` as the shape of the object and `f` as an index to the relevant
elements of `α`.
-/
--- Note: `nolint checkUnivs` should not apply here, we really do want two separate universe levels
-@[pp_with_univ, nolint checkUnivs]
+@[pp_with_univ]
structure PFunctor where
/-- The head type -/
A : Type uA
diff --git a/Mathlib/Geometry/RingedSpace/Basic.lean b/Mathlib/Geometry/RingedSpace/Basic.lean
index 4a615fbba24138..9e998ab3534122 100644
--- a/Mathlib/Geometry/RingedSpace/Basic.lean
+++ b/Mathlib/Geometry/RingedSpace/Basic.lean
@@ -38,9 +38,9 @@ open TopCat.Presheaf
namespace AlgebraicGeometry
+-- The universes appear together in the type, but separately in the value.
set_option linter.checkUnivs false in
/-- The type of Ringed spaces, as an abbreviation for `SheafedSpace CommRingCat`. -/
-@[nolint checkUnivs] -- The universes appear together in the type, but separately in the value.
abbrev RingedSpace : Type max (u + 1) (v + 1) :=
SheafedSpace.{v + 1, v, u} CommRingCat.{v}
diff --git a/Mathlib/Geometry/RingedSpace/LocallyRingedSpace.lean b/Mathlib/Geometry/RingedSpace/LocallyRingedSpace.lean
index be2bf1ffbab2ff..9cde91f2d687b5 100644
--- a/Mathlib/Geometry/RingedSpace/LocallyRingedSpace.lean
+++ b/Mathlib/Geometry/RingedSpace/LocallyRingedSpace.lean
@@ -84,7 +84,7 @@ structure Hom (X Y : LocallyRingedSpace.{u}) : Type _
abbrev Hom.toShHom {X Y : LocallyRingedSpace.{u}} (f : X.Hom Y) :
X.toSheafedSpace ⟶ Y.toSheafedSpace := InducedCategory.homMk f.1
-@[simp, nolint simpVarHead]
+@[simp]
lemma Hom.toShHom_mk {X Y : LocallyRingedSpace.{u}}
(f : X.toPresheafedSpace.Hom Y.toPresheafedSpace) (hf) :
Hom.toShHom ⟨f, hf⟩ = InducedCategory.homMk f := rfl
diff --git a/Mathlib/ModelTheory/Basic.lean b/Mathlib/ModelTheory/Basic.lean
index bab71eb42a7dd3..c1a889021df466 100644
--- a/Mathlib/ModelTheory/Basic.lean
+++ b/Mathlib/ModelTheory/Basic.lean
@@ -55,7 +55,6 @@ namespace FirstOrder
set_option linter.checkUnivs false in
/-- A first-order language consists of a type of functions of every natural-number arity and a
type of relations of every natural-number arity. -/
-@[nolint checkUnivs]
structure Language where
/-- For every arity, a `Type u` of functions of that arity -/
Functions : ℕ → Type u
diff --git a/Mathlib/RingTheory/Extension/Presentation/Basic.lean b/Mathlib/RingTheory/Extension/Presentation/Basic.lean
index 20b88f8700f9c0..b501c0723b700f 100644
--- a/Mathlib/RingTheory/Extension/Presentation/Basic.lean
+++ b/Mathlib/RingTheory/Extension/Presentation/Basic.lean
@@ -55,7 +55,6 @@ A presentation of an `R`-algebra `S` is a family of
generators with `σ → MvPolynomial ι R`: The assignment of
each relation to a polynomial in the generators.
-/
-@[nolint checkUnivs]
structure Algebra.Presentation extends Algebra.Generators R S ι where
/-- The assignment of each relation to a polynomial in the generators. -/
relation : σ → toGenerators.Ring
diff --git a/Mathlib/RingTheory/Extension/Presentation/Submersive.lean b/Mathlib/RingTheory/Extension/Presentation/Submersive.lean
index 08b03267550fd6..956fcb7d611e89 100644
--- a/Mathlib/RingTheory/Extension/Presentation/Submersive.lean
+++ b/Mathlib/RingTheory/Extension/Presentation/Submersive.lean
@@ -64,7 +64,6 @@ with relations equipped with an injective `map : relations → vars`.
This map determines how the differential of `P` is constructed. See
`PreSubmersivePresentation.differential` for details.
-/
-@[nolint checkUnivs]
structure PreSubmersivePresentation extends Algebra.Presentation R S ι σ where
/-- A map from the relations type to the variables type. Used to compute the differential. -/
map : σ → ι
@@ -495,7 +494,6 @@ variable [Finite σ]
A `PreSubmersivePresentation` is submersive if its Jacobian is a unit in `S`
and the presentation is finite.
-/
-@[nolint checkUnivs]
structure SubmersivePresentation extends PreSubmersivePresentation.{t, w} R S ι σ where
jacobian_isUnit : IsUnit toPreSubmersivePresentation.jacobian
diff --git a/Mathlib/SetTheory/Ordinal/Univ.lean b/Mathlib/SetTheory/Ordinal/Univ.lean
index 7abc9881f863c0..5b302b22694094 100644
--- a/Mathlib/SetTheory/Ordinal/Univ.lean
+++ b/Mathlib/SetTheory/Ordinal/Univ.lean
@@ -33,7 +33,7 @@ open Ordinal in
-- intended to be used with explicit universe parameters
/-- The ordinal `univ.{u, v}` is the order type of `Ordinal.{u}` or `Cardinal.{u}`, as an element of
`Ordinal.{v}` (when `u < v`). -/
-@[pp_with_univ, nolint checkUnivs]
+@[pp_with_univ]
def Ordinal.univ : Ordinal.{max (u + 1) v} :=
lift.{v, u + 1} (typeLT Ordinal)
@@ -42,7 +42,7 @@ open Cardinal in
-- intended to be used with explicit universe parameters
/-- The cardinal `univ.{u, v}` is the cardinality of `Ordinal.{u}` or `Cardinal.{u}`, as an element
of `Cardinal.{v}` (when `u < v`). -/
-@[pp_with_univ, nolint checkUnivs]
+@[pp_with_univ]
def Cardinal.univ : Cardinal.{max (u + 1) v} :=
lift.{v, u + 1} #Ordinal
diff --git a/Mathlib/SetTheory/ZFC/PSet.lean b/Mathlib/SetTheory/ZFC/PSet.lean
index ca273f551fb0fe..1e6516a0bbdc19 100644
--- a/Mathlib/SetTheory/ZFC/PSet.lean
+++ b/Mathlib/SetTheory/ZFC/PSet.lean
@@ -443,7 +443,6 @@ protected def Lift : PSet.{u} → PSet.{max u v}
-- intended to be used with explicit universe parameters
set_option linter.checkUnivs false in
/-- Embedding of one universe in another -/
-@[nolint checkUnivs]
def embed : PSet.{max (u + 1) v} :=
⟨ULift.{v, u + 1} PSet, fun ⟨x⟩ => PSet.Lift.{u, max (u + 1) v} x⟩
diff --git a/Mathlib/Topology/Category/CompHausLike/Limits.lean b/Mathlib/Topology/Category/CompHausLike/Limits.lean
index a608a17cabb86d..eedc1febd64392 100644
--- a/Mathlib/Topology/Category/CompHausLike/Limits.lean
+++ b/Mathlib/Topology/Category/CompHausLike/Limits.lean
@@ -120,6 +120,11 @@ lemma finiteCoproduct.ι_desc_apply {B : CompHausLike P} {π : (a : α) → X a
instance : HasCoproduct X where
exists_colimit := ⟨finiteCoproduct.cofan X, finiteCoproduct.isColimit X⟩
+/-
+This linter complains that the universes `u` and `w` only occur together, but `w` appears by itself
+in the indexing type of the coproduct. In almost all cases, `w` will be either `0` or `u`, but we
+want to allow both possibilities.
+-/
set_option linter.checkUnivs false in
variable (P) in
/--
@@ -129,13 +134,6 @@ property `P`.
class HasExplicitFiniteCoproducts : Prop where
hasProp {α : Type w} [Finite α] (X : α → CompHausLike.{max u w} P) : HasExplicitFiniteCoproduct X
-/-
-This linter complains that the universes `u` and `w` only occur together, but `w` appears by itself
-in the indexing type of the coproduct. In almost all cases, `w` will be either `0` or `u`, but we
-want to allow both possibilities.
--/
-attribute [nolint checkUnivs] HasExplicitFiniteCoproducts
-
attribute [instance] HasExplicitFiniteCoproducts.hasProp
instance [HasExplicitFiniteCoproducts.{w} P] (α : Type w) [Finite α] :
diff --git a/lake-manifest.json b/lake-manifest.json
index 44a71cb13c5519..4e97d39e9ad8eb 100644
--- a/lake-manifest.json
+++ b/lake-manifest.json
@@ -65,7 +65,7 @@
"type": "git",
"subDir": null,
"scope": "leanprover-community",
- "rev": "125807f43a86b5d58892b7ea6972eec0d6c164d2",
+ "rev": "e535e4feb0aa360e59e7adf4837b91ffbfb8c943",
"name": "batteries",
"manifestFile": "lake-manifest.json",
"inputRev": "main",
From 712103e03a61d29eafce1b530fa83ad6696ed9e9 Mon Sep 17 00:00:00 2001
From: "mathlib-update-dependencies[bot]"
<258990618+mathlib-update-dependencies[bot]@users.noreply.github.com>
Date: Sun, 21 Jun 2026 17:29:57 +0000
Subject: [PATCH 0218/1300] chore: update Mathlib dependencies 2026-06-21
(#40849)
This PR updates the Mathlib dependencies.
---
lake-manifest.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lake-manifest.json b/lake-manifest.json
index 4e97d39e9ad8eb..07ac27b85df079 100644
--- a/lake-manifest.json
+++ b/lake-manifest.json
@@ -65,7 +65,7 @@
"type": "git",
"subDir": null,
"scope": "leanprover-community",
- "rev": "e535e4feb0aa360e59e7adf4837b91ffbfb8c943",
+ "rev": "7d1b02eb63b526dff04cb990cf05b06b38ccbd3f",
"name": "batteries",
"manifestFile": "lake-manifest.json",
"inputRev": "main",
From 617410cab1195bc6c05516bbf29b44d921845fe8 Mon Sep 17 00:00:00 2001
From: "Yi.Yuan"
Date: Sun, 21 Jun 2026 18:23:57 +0000
Subject: [PATCH 0219/1300] chore(Algebra/simp): remove `@[simp]` tag in
`AlgEquiv.coe_ringEquiv` since it can be proven by simp (#40834)
And deprecate `AlgEquiv.coe_ringEquiv'` as a duplicate of `AlgEquiv.coe_ringEquiv`.
---
Mathlib/Algebra/Algebra/Equiv.lean | 7 ++-----
Mathlib/RingTheory/AdjoinRoot.lean | 2 +-
scripts/nolints_prime_decls.txt | 1 -
3 files changed, 3 insertions(+), 7 deletions(-)
diff --git a/Mathlib/Algebra/Algebra/Equiv.lean b/Mathlib/Algebra/Algebra/Equiv.lean
index bfa2ba100c82b6..d3a116e40011be 100644
--- a/Mathlib/Algebra/Algebra/Equiv.lean
+++ b/Mathlib/Algebra/Algebra/Equiv.lean
@@ -160,12 +160,9 @@ theorem toRingEquiv_eq_coe : e.toRingEquiv = e :=
lemma toRingEquiv_toRingHom : ((e : A₁ ≃+* A₂) : A₁ →+* A₂) = e :=
rfl
-@[simp]
-theorem coe_ringEquiv : ((e : A₁ ≃+* A₂) : A₁ → A₂) = e :=
- rfl
+theorem coe_ringEquiv : ((e : A₁ ≃+* A₂) : A₁ → A₂) = e := by simp
-theorem coe_ringEquiv' : (e.toRingEquiv : A₁ → A₂) = e :=
- rfl
+@[deprecated (since := "2026-06-21")] alias coe_ringEquiv' := coe_ringEquiv
theorem coe_ringEquiv_injective : Function.Injective ((↑) : (A₁ ≃ₐ[R] A₂) → A₁ ≃+* A₂) :=
fun _ _ h => ext <| RingEquiv.congr_fun h
diff --git a/Mathlib/RingTheory/AdjoinRoot.lean b/Mathlib/RingTheory/AdjoinRoot.lean
index 46e60c23c827e6..42bca0ed2495bc 100644
--- a/Mathlib/RingTheory/AdjoinRoot.lean
+++ b/Mathlib/RingTheory/AdjoinRoot.lean
@@ -1014,7 +1014,7 @@ theorem quotientEquivQuotientMinpolyMap_apply_mk (pb : PowerBasis R S) (I : Idea
(Ideal.span ({(minpoly R pb.gen).map (Ideal.Quotient.mk I)} : Set (Polynomial (R ⧸ I))))
(g.map (Ideal.Quotient.mk I)) := by
rw [PowerBasis.quotientEquivQuotientMinpolyMap, AlgEquiv.trans_apply, AlgEquiv.ofRingEquiv_apply,
- quotientEquiv_mk, AlgEquiv.coe_ringEquiv', AdjoinRoot.equiv'_symm_apply, PowerBasis.lift_aeval,
+ quotientEquiv_mk, AlgEquiv.coe_ringEquiv, AdjoinRoot.equiv'_symm_apply, PowerBasis.lift_aeval,
AdjoinRoot.aeval_eq, AdjoinRoot.quotEquivQuotMap_apply_mk]
-- This lemma should have the simp tag but this causes a lint issue.
diff --git a/scripts/nolints_prime_decls.txt b/scripts/nolints_prime_decls.txt
index f5c9c276fd3d88..98c8ef29d15948 100644
--- a/scripts/nolints_prime_decls.txt
+++ b/scripts/nolints_prime_decls.txt
@@ -136,7 +136,6 @@ Algebra.TensorProduct.natCast_def'
Algebra.toMatrix_lmul'
AlgEquiv.apply_smulCommClass'
AlgEquiv.coe_restrictScalars'
-AlgEquiv.coe_ringEquiv'
AlgEquiv.mk_coe'
AlgHom.coe_mk'
AlgHom.coe_restrictScalars'
From 9c27dca6e4fa0831af9fae465fea2ad429876790 Mon Sep 17 00:00:00 2001
From: Felix Pernegger <188575194+felixpernegger@users.noreply.github.com>
Date: Sun, 21 Jun 2026 18:23:58 +0000
Subject: [PATCH 0220/1300] refactor(Topology): rename ```LocPathConnected```
(#40868)
Rename ```LocPathConnected``` to ```LocallyPathConnected``` per discussion at https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Rename.20.60.60.60LocPathConnectedSpace.60.60.60.3F/with/582824006
Co-authored-by: Batixx
---
Mathlib.lean | 2 +-
.../CStarAlgebra/Unitary/Connected.lean | 5 +-
Mathlib/Analysis/Complex/BranchLogRoot.lean | 6 +-
.../Complex/UpperHalfPlane/Topology.lean | 2 +-
Mathlib/Geometry/Manifold/ChartedSpace.lean | 8 +-
Mathlib/Geometry/Manifold/Instances/Real.lean | 8 +-
.../Algebra/Module/LocallyConvex.lean | 13 +-
.../Compactness/DeltaGeneratedSpace.lean | 4 +-
...nnected.lean => LocallyPathConnected.lean} | 136 ++++++++++++------
.../Topology/Connected/PathComponentOne.lean | 6 +-
Mathlib/Topology/Homotopy/Lifting.lean | 10 +-
.../Homotopy/LocallyContractible.lean | 6 +-
12 files changed, 132 insertions(+), 74 deletions(-)
rename Mathlib/Topology/Connected/{LocPathConnected.lean => LocallyPathConnected.lean} (69%)
diff --git a/Mathlib.lean b/Mathlib.lean
index b499769e07f489..92e3cf81853411 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -7779,8 +7779,8 @@ public import Mathlib.Topology.Compactness.SigmaCompact
public import Mathlib.Topology.Connected.Basic
public import Mathlib.Topology.Connected.CardComponents
public import Mathlib.Topology.Connected.Clopen
-public import Mathlib.Topology.Connected.LocPathConnected
public import Mathlib.Topology.Connected.LocallyConnected
+public import Mathlib.Topology.Connected.LocallyPathConnected
public import Mathlib.Topology.Connected.PathComponentOne
public import Mathlib.Topology.Connected.PathConnected
public import Mathlib.Topology.Connected.Separation
diff --git a/Mathlib/Analysis/CStarAlgebra/Unitary/Connected.lean b/Mathlib/Analysis/CStarAlgebra/Unitary/Connected.lean
index ab6c20e1b8b863..ac8d947d77b276 100644
--- a/Mathlib/Analysis/CStarAlgebra/Unitary/Connected.lean
+++ b/Mathlib/Analysis/CStarAlgebra/Unitary/Connected.lean
@@ -47,7 +47,8 @@ products of exponential unitaries.
`expUnitary x` for a selfadjoint element `x`.
+ `Unitary.isPathConnected_ball`: any ball of radius `δ < 2` in the unitary group of a unital
C⋆-algebra is path connected.
-+ `Unitary.instLocPathConnectedSpace`: the unitary group of a C⋆-algebra is locally path connected.
++ `Unitary.instLocallyPathConnectedSpace`: the unitary group of a C⋆-algebra is
+ locally path connected.
+ `Unitary.mem_pathComponentOne_iff`: The path component of the identity in the unitary group of a
C⋆-algebra is the set of unitaries that can be expressed as a product of exponentials of
selfadjoint elements.
@@ -326,7 +327,7 @@ lemma Unitary.isPathConnected_ball (u : unitary A) (δ : ℝ) (hδ₀ : 0 < δ)
norm_expUnitary_smul_argSelfAdjoint_sub_one_le u t.2 (hu.trans hδ₂) |>.trans_lt hu
/-- The unitary group in a C⋆-algebra is locally path connected. -/
-instance Unitary.instLocPathConnectedSpace : LocPathConnectedSpace (unitary A) :=
+instance Unitary.instLocallyPathConnectedSpace : LocallyPathConnectedSpace (unitary A) :=
.of_bases (fun _ ↦ nhds_basis_uniformity <| uniformity_basis_dist_lt zero_lt_two) <| by
simpa using! isPathConnected_ball
diff --git a/Mathlib/Analysis/Complex/BranchLogRoot.lean b/Mathlib/Analysis/Complex/BranchLogRoot.lean
index 2a00d8bd917320..ffcf8ac4c78979 100644
--- a/Mathlib/Analysis/Complex/BranchLogRoot.lean
+++ b/Mathlib/Analysis/Complex/BranchLogRoot.lean
@@ -5,7 +5,7 @@ Authors: Yury Kudryashov
-/
module
-public import Mathlib.Topology.Connected.LocPathConnected
+public import Mathlib.Topology.Connected.LocallyPathConnected
public import Mathlib.Analysis.Complex.Basic
public import Mathlib.AlgebraicTopology.FundamentalGroupoid.SimplyConnected
public import Mathlib.Analysis.Complex.Exponential
@@ -27,7 +27,7 @@ open Set
namespace Complex
-variable {X : Type*} [TopologicalSpace X] [LocPathConnectedSpace X] {U : Set X}
+variable {X : Type*} [TopologicalSpace X] [LocallyPathConnectedSpace X] {U : Set X}
/-- If `g : X → ℂ` defined on a locally path connected space
is continuous on an open simply connected set `U` and `0 ∉ g '' U`,
@@ -39,7 +39,7 @@ theorem exists_continuousOn_eqOn_exp_comp (hUc : IsSimplyConnected U) (hUo : IsO
∃ f : X → ℂ, ContinuousOn f U ∧ EqOn (exp ∘ f) g U := by
classical
have := hUc.simplyConnectedSpace
- have := hUo.locPathConnectedSpace
+ have := hUo.locallyPathConnectedSpace
rcases hUc.nonempty with ⟨x₀, hx₀U⟩
have hx₀ : g x₀ ≠ 0 := ne_of_mem_of_not_mem (mem_image_of_mem g hx₀U) hU₀
lift x₀ to U using hx₀U
diff --git a/Mathlib/Analysis/Complex/UpperHalfPlane/Topology.lean b/Mathlib/Analysis/Complex/UpperHalfPlane/Topology.lean
index 5962cb0757487a..fa7163e6fe288f 100644
--- a/Mathlib/Analysis/Complex/UpperHalfPlane/Topology.lean
+++ b/Mathlib/Analysis/Complex/UpperHalfPlane/Topology.lean
@@ -69,7 +69,7 @@ instance : ContractibleSpace ℍ := by
rw [isEmbedding_coe.toHomeomorph.trans (.setCongr range_coe) |>.contractibleSpace_iff]
exact (convex_halfSpace_im_gt 0).contractibleSpace ⟨I, one_pos.trans_eq I_im.symm⟩
-instance : LocPathConnectedSpace ℍ := isOpenEmbedding_coe.locPathConnectedSpace
+instance : LocallyPathConnectedSpace ℍ := isOpenEmbedding_coe.locallyPathConnectedSpace
instance : NoncompactSpace ℍ where
noncompact_univ h := by
diff --git a/Mathlib/Geometry/Manifold/ChartedSpace.lean b/Mathlib/Geometry/Manifold/ChartedSpace.lean
index 59a70521d47941..778649d7c467bc 100644
--- a/Mathlib/Geometry/Manifold/ChartedSpace.lean
+++ b/Mathlib/Geometry/Manifold/ChartedSpace.lean
@@ -6,7 +6,7 @@ Authors: Sébastien Gouëzel
module
public import Mathlib.Geometry.Manifold.StructureGroupoid
-public import Mathlib.Topology.Connected.LocPathConnected
+public import Mathlib.Topology.Connected.LocallyPathConnected
public import Mathlib.Topology.IsLocalHomeomorph
public import Mathlib.Topology.OpenPartialHomeomorph.Constructions
@@ -265,7 +265,8 @@ theorem ChartedSpace.locallyConnectedSpace [LocallyConnectedSpace H] : LocallyCo
/-- If a topological space `M` admits an atlas with locally path-connected charts,
then `M` itself is locally path-connected. -/
-theorem ChartedSpace.locPathConnectedSpace [LocPathConnectedSpace H] : LocPathConnectedSpace M := by
+theorem ChartedSpace.locallyPathConnectedSpace [LocallyPathConnectedSpace H] :
+ LocallyPathConnectedSpace M := by
refine ⟨fun x ↦ ⟨fun s ↦ ⟨fun hs ↦ ?_, fun ⟨u, hu⟩ ↦ Filter.mem_of_superset hu.1.1 hu.2⟩⟩⟩
let e := chartAt H x
let t := s ∩ e.source
@@ -280,6 +281,9 @@ theorem ChartedSpace.locPathConnectedSpace [LocPathConnectedSpace H] : LocPathCo
· exact (image_mono pathComponentIn_subset).trans
(PartialEquiv.symm_image_image_of_subset_source _ inter_subset_right).subset
+@[deprecated (since := "2026-06-21")]
+alias ChartedSpace.locPathConnectedSpace := ChartedSpace.locallyPathConnectedSpace
+
/-- If `M` is modelled on `H'` and `H'` is itself modelled on `H`, then we can consider `M` as being
modelled on `H`. -/
@[implicit_reducible]
diff --git a/Mathlib/Geometry/Manifold/Instances/Real.lean b/Mathlib/Geometry/Manifold/Instances/Real.lean
index 5e11d5245691c4..0ac71a8e87b7f7 100644
--- a/Mathlib/Geometry/Manifold/Instances/Real.lean
+++ b/Mathlib/Geometry/Manifold/Instances/Real.lean
@@ -108,11 +108,11 @@ instance EuclideanHalfSpace.pathConnectedSpace [NeZero n] :
instance EuclideanQuadrant.pathConnectedSpace : PathConnectedSpace (EuclideanQuadrant n) :=
isPathConnected_iff_pathConnectedSpace.mp <| convex.isPathConnected ⟨0, by simp⟩
-instance [NeZero n] : LocPathConnectedSpace (EuclideanHalfSpace n) :=
- EuclideanHalfSpace.convex.locPathConnectedSpace
+instance [NeZero n] : LocallyPathConnectedSpace (EuclideanHalfSpace n) :=
+ EuclideanHalfSpace.convex.locallyPathConnectedSpace
-instance : LocPathConnectedSpace (EuclideanQuadrant n) :=
- EuclideanQuadrant.convex.locPathConnectedSpace
+instance : LocallyPathConnectedSpace (EuclideanQuadrant n) :=
+ EuclideanQuadrant.convex.locallyPathConnectedSpace
theorem range_euclideanHalfSpace (n : ℕ) [NeZero n] :
range (Subtype.val : EuclideanHalfSpace n → _) = { y | 0 ≤ y 0 } :=
diff --git a/Mathlib/Topology/Algebra/Module/LocallyConvex.lean b/Mathlib/Topology/Algebra/Module/LocallyConvex.lean
index a626fcaee69bcb..c252b2838872f4 100644
--- a/Mathlib/Topology/Algebra/Module/LocallyConvex.lean
+++ b/Mathlib/Topology/Algebra/Module/LocallyConvex.lean
@@ -6,7 +6,7 @@ Authors: Anatole Dedecker
module
public import Mathlib.Analysis.Convex.Topology
-public import Mathlib.Topology.Connected.LocPathConnected
+public import Mathlib.Topology.Connected.LocallyPathConnected
public import Mathlib.Analysis.Convex.PathConnected
/-!
@@ -100,14 +100,14 @@ theorem locallyConvexSpace_iff_exists_convex_subset_zero :
(locallyConvexSpace_iff_zero 𝕜 E).trans hasBasis_self
-- see Note [lower instance priority]
-instance (priority := 100) LocallyConvexSpace.toLocPathConnectedSpace [Module ℝ E]
- [ContinuousSMul ℝ E] [LocallyConvexSpace ℝ E] : LocPathConnectedSpace E :=
+instance (priority := 100) LocallyConvexSpace.toLocallyPathConnectedSpace [Module ℝ E]
+ [ContinuousSMul ℝ E] [LocallyConvexSpace ℝ E] : LocallyPathConnectedSpace E :=
.of_bases (fun x ↦ convex_basis (𝕜 := ℝ) x)
fun _ _ hs ↦ hs.2.isPathConnected <| nonempty_of_mem <| mem_of_mem_nhds hs.1
/-- Convex subsets of locally convex spaces are locally path-connected. -/
-theorem Convex.locPathConnectedSpace [Module ℝ E] [ContinuousSMul ℝ E] [LocallyConvexSpace ℝ E]
- {S : Set E} (hS : Convex ℝ S) : LocPathConnectedSpace S := by
+theorem Convex.locallyPathConnectedSpace [Module ℝ E] [ContinuousSMul ℝ E] [LocallyConvexSpace ℝ E]
+ {S : Set E} (hS : Convex ℝ S) : LocallyPathConnectedSpace S := by
refine ⟨fun x ↦ ⟨fun s ↦ ⟨fun hs ↦ ?_, fun ⟨t, ht⟩ ↦ mem_of_superset ht.1.1 ht.2⟩⟩⟩
let ⟨t, ht⟩ := (mem_nhds_subtype S x s).mp hs
let ⟨t', ht'⟩ := (LocallyConvexSpace.convex_basis (𝕜 := ℝ) x.1).mem_iff.mp ht.1
@@ -116,6 +116,9 @@ theorem Convex.locPathConnectedSpace [Module ℝ E] [ContinuousSMul ℝ E] [Loca
· refine Subtype.preimage_coe_self_inter _ _ ▸ IsPathConnected.preimage_coe ?_ inter_subset_left
exact (hS.inter ht'.1.2).isPathConnected ⟨x, x.2, mem_of_mem_nhds ht'.1.1⟩
+@[deprecated (since := "2026-06-21")]
+alias Convex.locPathConnectedSpace := Convex.locallyPathConnectedSpace
+
end Module
section LinearOrderedField
diff --git a/Mathlib/Topology/Compactness/DeltaGeneratedSpace.lean b/Mathlib/Topology/Compactness/DeltaGeneratedSpace.lean
index d06465c472ce81..abcccc287c9198 100644
--- a/Mathlib/Topology/Compactness/DeltaGeneratedSpace.lean
+++ b/Mathlib/Topology/Compactness/DeltaGeneratedSpace.lean
@@ -49,10 +49,10 @@ abbrev of : Type _ := WithGeneratedByTopology (fun n ↦ Fin n → ℝ) Y
/-- Delta-generated spaces are locally path-connected. -/
instance [DeltaGeneratedSpace X] :
- LocPathConnectedSpace X := by
+ LocallyPathConnectedSpace X := by
rw [← IsGeneratedBy.generatedBy_eq (X := fun n ↦ Fin n → ℝ) (Y := X),
generatedBy_eq_coinduced]
- exact LocPathConnectedSpace.coinduced _
+ exact LocallyPathConnectedSpace.coinduced _
/-- Delta-generated spaces are sequential. -/
instance [DeltaGeneratedSpace X] : SequentialSpace X := by
diff --git a/Mathlib/Topology/Connected/LocPathConnected.lean b/Mathlib/Topology/Connected/LocallyPathConnected.lean
similarity index 69%
rename from Mathlib/Topology/Connected/LocPathConnected.lean
rename to Mathlib/Topology/Connected/LocallyPathConnected.lean
index 4f43cadb842f43..d26494a7700b2a 100644
--- a/Mathlib/Topology/Connected/LocPathConnected.lean
+++ b/Mathlib/Topology/Connected/LocallyPathConnected.lean
@@ -11,7 +11,7 @@ public import Mathlib.Topology.AlexandrovDiscrete
/-!
# Locally path-connected spaces
-This file defines `LocPathConnectedSpace X`, a predicate class asserting that `X` is locally
+This file defines `LocallyPathConnectedSpace X`, a predicate class asserting that `X` is locally
path-connected, in that each point has a basis of path-connected neighborhoods.
## Main results
@@ -23,11 +23,11 @@ path-connected, in that each point has a basis of path-connected neighborhoods.
* `pathConnectedSpace_iff_connectedSpace`: locally path-connected spaces are path-connected iff they
are connected.
* `instLocallyConnectedSpace`: locally path-connected spaces are also locally connected.
-* `IsOpen.locPathConnectedSpace`: open subsets of locally path-connected spaces are
+* `IsOpen.locallyPathConnectedSpace`: open subsets of locally path-connected spaces are
locally path-connected.
-* `LocPathConnectedSpace.coinduced` / `Quotient.locPathConnectedSpace`: quotients of locally
+* `LocallyPathConnectedSpace.coinduced` / `Quotient.locallyPathConnectedSpace`: quotients of locally
path-connected spaces are locally path-connected.
-* `Sum.locPathConnectedSpace` / `Sigma.locPathConnectedSpace`: disjoint unions of locally
+* `Sum.locallyPathConnectedSpace` / `Sigma.locallyPathConnectedSpace`: disjoint unions of locally
path-connected spaces are locally path-connected.
Abstractly, this also shows that locally path-connected spaces form a coreflective subcategory of
@@ -35,7 +35,7 @@ the category of topological spaces, although we do not prove that in this form h
## Implementation notes
-In the definition of `LocPathConnectedSpace X` we require neighbourhoods in the basis to be
+In the definition of `LocallyPathConnectedSpace X` we require neighbourhoods in the basis to be
path-connected, but not necessarily open; that they can also be required to be open is shown as
a theorem in `isOpen_isPathConnected_basis`.
-/
@@ -48,26 +48,34 @@ open Topology Filter unitInterval Set Function
variable {X Y : Type*} [TopologicalSpace X] [TopologicalSpace Y] {x y z : X} {ι : Type*} {F : Set X}
-section LocPathConnectedSpace
+section LocallyPathConnectedSpace
/-- A topological space is locally path connected if, at every point, path connected
neighborhoods form a neighborhood basis. -/
-class LocPathConnectedSpace (X : Type*) [TopologicalSpace X] : Prop where
+class LocallyPathConnectedSpace (X : Type*) [TopologicalSpace X] : Prop where
/-- Each neighborhood filter has a basis of path-connected neighborhoods. -/
path_connected_basis : ∀ x : X, (𝓝 x).HasBasis (fun s : Set X => s ∈ 𝓝 x ∧ IsPathConnected s) id
-export LocPathConnectedSpace (path_connected_basis)
+@[deprecated (since := "2026-06-21")] alias LocPathConnectedSpace := LocallyPathConnectedSpace
+@[deprecated (since := "2026-06-21")]
+alias LocPathConnectedSpace.path_connected_basis :=
+ LocallyPathConnectedSpace.path_connected_basis
-theorem LocPathConnectedSpace.of_bases {p : X → ι → Prop} {s : X → ι → Set X}
+export LocallyPathConnectedSpace (path_connected_basis)
+
+theorem LocallyPathConnectedSpace.of_bases {p : X → ι → Prop} {s : X → ι → Set X}
(h : ∀ x, (𝓝 x).HasBasis (p x) (s x)) (h' : ∀ x i, p x i → IsPathConnected (s x i)) :
- LocPathConnectedSpace X where
+ LocallyPathConnectedSpace X where
path_connected_basis x := by
rw [hasBasis_self]
intro t ht
rcases (h x).mem_iff.mp ht with ⟨i, hpi, hi⟩
exact ⟨s x i, (h x).mem_of_mem hpi, h' x i hpi, hi⟩
-variable [LocPathConnectedSpace X]
+@[deprecated (since := "2026-06-21")]
+alias LocPathConnectedSpace.of_bases := LocallyPathConnectedSpace.of_bases
+
+variable [LocallyPathConnectedSpace X]
protected theorem IsOpen.pathComponentIn (hF : IsOpen F) (x : X) :
IsOpen (pathComponentIn F x) := by
@@ -99,11 +107,14 @@ lemma pathComponentIn_mem_nhds (hF : F ∈ 𝓝 x) : pathComponentIn F x ∈
exact mem_nhds_iff.mpr ⟨pathComponentIn u x, pathComponentIn_mono huF,
hu.pathComponentIn x, mem_pathComponentIn_self hxu⟩
-theorem PathConnectedSpace.of_locPathConnectedSpace [ConnectedSpace X] : PathConnectedSpace X :=
+theorem PathConnectedSpace.of_locallyPathConnectedSpace [ConnectedSpace X] : PathConnectedSpace X :=
⟨inferInstance, by simp [← mem_pathComponent_iff, IsClopen.pathComponent _ |>.eq_univ]⟩
+@[deprecated (since := "2026-06-21")]
+alias PathConnectedSpace.of_locPathConnectedSpace := PathConnectedSpace.of_locallyPathConnectedSpace
+
theorem pathConnectedSpace_iff_connectedSpace : PathConnectedSpace X ↔ ConnectedSpace X :=
- ⟨fun _ ↦ inferInstance, fun _ ↦ .of_locPathConnectedSpace⟩
+ ⟨fun _ ↦ inferInstance, fun _ ↦ .of_locallyPathConnectedSpace⟩
theorem pathComponent_eq_connectedComponent (x : X) : pathComponent x = connectedComponent x :=
(pathComponent_subset_component x).antisymm <|
@@ -149,21 +160,28 @@ theorem isOpen_isPathConnected_basis (x : X) :
exact ⟨pathComponentIn u x, ⟨hu.pathComponentIn _, ⟨mem_pathComponentIn_self hxu,
isPathConnected_pathComponentIn hxu⟩⟩, pathComponentIn_subset.trans hus⟩
-theorem Topology.IsOpenEmbedding.locPathConnectedSpace {e : Y → X} (he : IsOpenEmbedding e) :
- LocPathConnectedSpace Y :=
+theorem Topology.IsOpenEmbedding.locallyPathConnectedSpace {e : Y → X} (he : IsOpenEmbedding e) :
+ LocallyPathConnectedSpace Y :=
have (y : Y) :
(𝓝 y).HasBasis (fun s ↦ s ∈ 𝓝 (e y) ∧ IsPathConnected s ∧ s ⊆ range e) (e ⁻¹' ·) :=
he.basis_nhds <| pathConnected_subset_basis he.isOpen_range (mem_range_self _)
.of_bases this fun x s ⟨_, hs, hse⟩ ↦ by
rwa [he.isPathConnected_iff, image_preimage_eq_of_subset hse]
-theorem IsOpen.locPathConnectedSpace {U : Set X} (h : IsOpen U) : LocPathConnectedSpace U :=
- h.isOpenEmbedding_subtypeVal.locPathConnectedSpace
+@[deprecated (since := "2026-06-21")]
+alias Topology.IsOpenEmbedding.locPathConnectedSpace :=
+ Topology.IsOpenEmbedding.locallyPathConnectedSpace
+
+theorem IsOpen.locallyPathConnectedSpace {U : Set X} (h : IsOpen U) : LocallyPathConnectedSpace U :=
+ h.isOpenEmbedding_subtypeVal.locallyPathConnectedSpace
+
+@[deprecated (since := "2026-06-21")]
+alias IsOpen.locPathConnectedSpace := IsOpen.locallyPathConnectedSpace
theorem IsOpen.isConnected_iff_isPathConnected {U : Set X} (U_op : IsOpen U) :
IsConnected U ↔ IsPathConnected U := by
rw [isConnected_iff_connectedSpace, isPathConnected_iff_pathConnectedSpace]
- haveI := U_op.locPathConnectedSpace
+ haveI := U_op.locallyPathConnectedSpace
exact pathConnectedSpace_iff_connectedSpace.symm
/-- Locally path-connected spaces are locally connected. -/
@@ -174,30 +192,38 @@ instance : LocallyConnectedSpace X := by
exact ⟨u, ⟨hu, hxu, hu'.isConnected⟩, hus⟩
/-- A space is locally path-connected iff all path components of open subsets are open. -/
-lemma locPathConnectedSpace_iff_isOpen_pathComponentIn {X : Type*} [TopologicalSpace X] :
- LocPathConnectedSpace X ↔ ∀ (x : X) (u : Set X), IsOpen u → IsOpen (pathComponentIn u x) :=
+lemma locallyPathConnectedSpace_iff_isOpen_pathComponentIn {X : Type*} [TopologicalSpace X] :
+ LocallyPathConnectedSpace X ↔ ∀ (x : X) (u : Set X), IsOpen u → IsOpen (pathComponentIn u x) :=
⟨fun _ _ _ hu ↦ hu.pathComponentIn _, fun h ↦ ⟨fun x ↦ ⟨fun s ↦ by
refine ⟨fun hs ↦ ?_, fun ⟨_, ht⟩ ↦ Filter.mem_of_superset ht.1.1 ht.2⟩
let ⟨u, hu⟩ := mem_nhds_iff.mp hs
exact ⟨pathComponentIn u x, ⟨(h x u hu.2.1).mem_nhds (mem_pathComponentIn_self hu.2.2),
isPathConnected_pathComponentIn hu.2.2⟩, pathComponentIn_subset.trans hu.1⟩⟩⟩⟩
+@[deprecated (since := "2026-06-21")]
+alias locPathConnectedSpace_iff_isOpen_pathComponentIn :=
+ locallyPathConnectedSpace_iff_isOpen_pathComponentIn
+
/-- A space is locally path-connected iff all path components of open subsets are neighbourhoods. -/
-lemma locPathConnectedSpace_iff_pathComponentIn_mem_nhds {X : Type*} [TopologicalSpace X] :
- LocPathConnectedSpace X ↔
+lemma locallyPathConnectedSpace_iff_pathComponentIn_mem_nhds {X : Type*} [TopologicalSpace X] :
+ LocallyPathConnectedSpace X ↔
∀ x : X, ∀ u : Set X, IsOpen u → x ∈ u → pathComponentIn u x ∈ nhds x := by
- rw [locPathConnectedSpace_iff_isOpen_pathComponentIn]
+ rw [locallyPathConnectedSpace_iff_isOpen_pathComponentIn]
simp_rw [forall_comm (β := Set X), ← imp_forall_iff]
refine forall_congr' fun u ↦ imp_congr_right fun _ ↦ ?_
exact ⟨fun h x hxu ↦ (h x).mem_nhds (mem_pathComponentIn_self hxu),
fun h x ↦ isOpen_iff_mem_nhds.mpr fun y hy ↦
pathComponentIn_congr hy ▸ h y <| pathComponentIn_subset hy⟩
+@[deprecated (since := "2026-06-21")]
+alias locPathConnectedSpace_iff_pathComponentIn_mem_nhds :=
+ locallyPathConnectedSpace_iff_pathComponentIn_mem_nhds
+
/-- Any topology coinduced by a locally path-connected topology is locally path-connected. -/
-lemma LocPathConnectedSpace.coinduced {Y : Type*} (f : X → Y) :
- @LocPathConnectedSpace Y (.coinduced f ‹_›) := by
+lemma LocallyPathConnectedSpace.coinduced {Y : Type*} (f : X → Y) :
+ @LocallyPathConnectedSpace Y (.coinduced f ‹_›) := by
let _ := TopologicalSpace.coinduced f ‹_›; have hf : Continuous f := continuous_coinduced_rng
- refine locPathConnectedSpace_iff_isOpen_pathComponentIn.mpr fun y u hu ↦
+ refine locallyPathConnectedSpace_iff_isOpen_pathComponentIn.mpr fun y u hu ↦
isOpen_coinduced.mpr <| isOpen_iff_mem_nhds.mpr fun x hx ↦ ?_
have hx' := preimage_mono pathComponentIn_subset hx
refine mem_nhds_iff.mpr ⟨pathComponentIn (f ⁻¹' u) x, ?_,
@@ -207,22 +233,37 @@ lemma LocPathConnectedSpace.coinduced {Y : Type*} (f : X → Y) :
⟨x, mem_pathComponentIn_self hx', rfl⟩ <|
(image_mono pathComponentIn_subset).trans <| u.image_preimage_subset f
+@[deprecated (since := "2026-06-21")]
+alias LocPathConnectedSpace.coinduced := LocallyPathConnectedSpace.coinduced
+
/-- Quotients of locally path-connected spaces are locally path-connected. -/
-lemma Topology.IsQuotientMap.locPathConnectedSpace {f : X → Y} (h : IsQuotientMap f) :
- LocPathConnectedSpace Y :=
- h.isCoinducing.eq_coinduced ▸ LocPathConnectedSpace.coinduced f
+lemma Topology.IsQuotientMap.locallyPathConnectedSpace {f : X → Y} (h : IsQuotientMap f) :
+ LocallyPathConnectedSpace Y :=
+ h.isCoinducing.eq_coinduced ▸ LocallyPathConnectedSpace.coinduced f
+
+@[deprecated (since := "2026-06-21")]
+alias Topology.IsQuotientMap.locPathConnectedSpace :=
+ Topology.IsQuotientMap.locallyPathConnectedSpace
/-- Quotients of locally path-connected spaces are locally path-connected. -/
-instance Quot.locPathConnectedSpace {r : X → X → Prop} : LocPathConnectedSpace (Quot r) :=
- isQuotientMap_quot_mk.locPathConnectedSpace
+instance Quot.locallyPathConnectedSpace {r : X → X → Prop} : LocallyPathConnectedSpace (Quot r) :=
+ isQuotientMap_quot_mk.locallyPathConnectedSpace
+
+@[deprecated (since := "2026-06-21")]
+alias Quot.locPathConnectedSpace := Quot.locallyPathConnectedSpace
/-- Quotients of locally path-connected spaces are locally path-connected. -/
-instance Quotient.locPathConnectedSpace {s : Setoid X} : LocPathConnectedSpace (Quotient s) :=
- isQuotientMap_quotient_mk'.locPathConnectedSpace
+instance Quotient.locallyPathConnectedSpace {s : Setoid X} :
+ LocallyPathConnectedSpace (Quotient s) :=
+ isQuotientMap_quotient_mk'.locallyPathConnectedSpace
+
+@[deprecated (since := "2026-06-21")]
+alias Quotient.locPathConnectedSpace := Quotient.locallyPathConnectedSpace
/-- Disjoint unions of locally path-connected spaces are locally path-connected. -/
-instance Sum.locPathConnectedSpace [LocPathConnectedSpace Y] : LocPathConnectedSpace (X ⊕ Y) := by
- rw [locPathConnectedSpace_iff_pathComponentIn_mem_nhds]; intro x u hu hxu; rw [mem_nhds_iff]
+instance Sum.locallyPathConnectedSpace [LocallyPathConnectedSpace Y] :
+ LocallyPathConnectedSpace (X ⊕ Y) := by
+ rw [locallyPathConnectedSpace_iff_pathComponentIn_mem_nhds]; intro x u hu hxu; rw [mem_nhds_iff]
obtain x | y := x
· refine ⟨Sum.inl '' pathComponentIn (Sum.inl ⁻¹' u) x, ?_, ?_, ?_⟩
· apply IsPathConnected.subset_pathComponentIn
@@ -239,11 +280,14 @@ instance Sum.locPathConnectedSpace [LocPathConnectedSpace Y] : LocPathConnectedS
· exact isOpenMap_inr _ <| (hu.preimage continuous_inr).pathComponentIn _
· exact ⟨y, mem_pathComponentIn_self hxu, rfl⟩
+@[deprecated (since := "2026-06-21")]
+alias Sum.locPathConnectedSpace := Sum.locallyPathConnectedSpace
+
/-- Disjoint unions of locally path-connected spaces are locally path-connected. -/
-instance Sigma.locPathConnectedSpace {X : ι → Type*}
- [(i : ι) → TopologicalSpace (X i)] [(i : ι) → LocPathConnectedSpace (X i)] :
- LocPathConnectedSpace ((i : ι) × X i) := by
- rw [locPathConnectedSpace_iff_pathComponentIn_mem_nhds]; intro x u hu hxu; rw [mem_nhds_iff]
+instance Sigma.locallyPathConnectedSpace {X : ι → Type*}
+ [(i : ι) → TopologicalSpace (X i)] [(i : ι) → LocallyPathConnectedSpace (X i)] :
+ LocallyPathConnectedSpace ((i : ι) × X i) := by
+ rw [locallyPathConnectedSpace_iff_pathComponentIn_mem_nhds]; intro x u hu hxu; rw [mem_nhds_iff]
refine ⟨(Sigma.mk x.1) '' pathComponentIn ((Sigma.mk x.1) ⁻¹' u) x.2, ?_, ?_, ?_⟩
· apply IsPathConnected.subset_pathComponentIn
· exact (isPathConnected_pathComponentIn (by exact hxu)).image continuous_sigmaMk
@@ -252,9 +296,12 @@ instance Sigma.locPathConnectedSpace {X : ι → Type*}
· exact isOpenMap_sigmaMk _ <| (hu.preimage continuous_sigmaMk).pathComponentIn _
· exact ⟨x.2, mem_pathComponentIn_self hxu, rfl⟩
-instance AlexandrovDiscrete.locPathConnectedSpace [AlexandrovDiscrete X] :
- LocPathConnectedSpace X := by
- apply LocPathConnectedSpace.of_bases nhds_basis_nhdsKer_singleton
+@[deprecated (since := "2026-06-21")]
+alias Sigma.locPathConnectedSpace := Sigma.locallyPathConnectedSpace
+
+instance AlexandrovDiscrete.locallyPathConnectedSpace [AlexandrovDiscrete X] :
+ LocallyPathConnectedSpace X := by
+ apply LocallyPathConnectedSpace.of_bases nhds_basis_nhdsKer_singleton
simp only [forall_const, IsPathConnected, mem_nhdsKer_singleton]
intro x
exists x, specializes_rfl
@@ -262,6 +309,9 @@ instance AlexandrovDiscrete.locPathConnectedSpace [AlexandrovDiscrete X] :
symm
apply hy.joinedIn <;> rewrite [mem_nhdsKer_singleton] <;> [assumption; rfl]
+@[deprecated (since := "2026-06-21")]
+alias AlexandrovDiscrete.locPathConnectedSpace := AlexandrovDiscrete.locallyPathConnectedSpace
+
/-- If a space is locally path-connected, the topology of its path components is discrete. -/
instance : DiscreteTopology <| ZerothHomotopy X := by
refine discreteTopology_iff_isOpen_singleton.mpr fun c ↦ ?_
@@ -273,4 +323,4 @@ instance : DiscreteTopology <| ZerothHomotopy X := by
instance [CompactSpace X] : Finite <| ZerothHomotopy X :=
finite_of_compact_of_discrete
-end LocPathConnectedSpace
+end LocallyPathConnectedSpace
diff --git a/Mathlib/Topology/Connected/PathComponentOne.lean b/Mathlib/Topology/Connected/PathComponentOne.lean
index c992c88d39b2aa..862803a9493bf8 100644
--- a/Mathlib/Topology/Connected/PathComponentOne.lean
+++ b/Mathlib/Topology/Connected/PathComponentOne.lean
@@ -6,7 +6,7 @@ Authors: Jireh Loreaux
module
public import Mathlib.Topology.Algebra.OpenSubgroup
-public import Mathlib.Topology.Connected.LocPathConnected
+public import Mathlib.Topology.Connected.LocallyPathConnected
/-! # The path component of the identity in a locally path connected topological group
@@ -27,7 +27,7 @@ as an open normal subgroup. It is, in fact, clopen. -/
/-- The path component of the identity in a locally path connected additive topological group,
as an open normal additive subgroup. It is, in fact, clopen. -/]
def OpenNormalSubgroup.pathComponentOne [Group G]
- [IsTopologicalGroup G] [LocPathConnectedSpace G] :
+ [IsTopologicalGroup G] [LocallyPathConnectedSpace G] :
OpenNormalSubgroup G where
toSubgroup := .pathComponentOne G
isOpen' := .pathComponent 1
@@ -36,7 +36,7 @@ def OpenNormalSubgroup.pathComponentOne [Group G]
namespace OpenNormalSubgroup
@[to_additive]
-instance [Group G] [IsTopologicalGroup G] [LocPathConnectedSpace G] :
+instance [Group G] [IsTopologicalGroup G] [LocallyPathConnectedSpace G] :
IsClosed (OpenNormalSubgroup.pathComponentOne G : Set G) :=
.pathComponent 1
diff --git a/Mathlib/Topology/Homotopy/Lifting.lean b/Mathlib/Topology/Homotopy/Lifting.lean
index a98ee8d80d3032..7c5fb225dd713b 100644
--- a/Mathlib/Topology/Homotopy/Lifting.lean
+++ b/Mathlib/Topology/Homotopy/Lifting.lean
@@ -7,7 +7,7 @@ module
public import Mathlib.AlgebraicTopology.FundamentalGroupoid.FundamentalGroup
public import Mathlib.AlgebraicTopology.FundamentalGroupoid.SimplyConnected
-public import Mathlib.Topology.Connected.LocPathConnected
+public import Mathlib.Topology.Connected.LocallyPathConnected
public import Mathlib.Topology.Covering.Quotient
public import Mathlib.Topology.Homotopy.Path
public import Mathlib.Topology.UnitInterval
@@ -168,7 +168,7 @@ open PathConnectedSpace (somePath) in
path `f ∘ γ` in `X` lifts to `E` with endpoint only dependent on the endpoint of `γ` and
independent of the path chosen. In this theorem, we require that a specific point `a₀ : A` is
lifted to a specific point `e₀ : E` over `a₀`. -/
-theorem existsUnique_continuousMap_lifts [PathConnectedSpace A] [LocPathConnectedSpace A]
+theorem existsUnique_continuousMap_lifts [PathConnectedSpace A] [LocallyPathConnectedSpace A]
(f : C(A, X)) (a₀ : A) (e₀ : E) (he : p e₀ = f a₀)
(ex : ∀ γ : C(I, A), γ 0 = a₀ → ∃ Γ : C(I, E), Γ 0 = e₀ ∧ p ∘ Γ = f.comp γ)
(uniq : ∀ γ γ' : C(I, A), ∀ Γ Γ' : C(I, E), γ 0 = a₀ → γ' 0 = a₀ → Γ 0 = e₀ → Γ' 0 = e₀ →
@@ -466,7 +466,7 @@ alias injective_path_homotopic_mapFn := injective_path_homotopic_map
/-- A continuous map `f` from a simply-connected, locally path-connected space `A` to another
space `X` lifts uniquely through a covering map `p : E → X`, after specifying any lift
`e₀ : E` of any point `a₀ : A`. -/
-theorem existsUnique_continuousMap_lifts [SimplyConnectedSpace A] [LocPathConnectedSpace A]
+theorem existsUnique_continuousMap_lifts [SimplyConnectedSpace A] [LocallyPathConnectedSpace A]
(f : C(A, X)) (a₀ : A) (e₀ : E) (he : p e₀ = f a₀) :
∃! F : C(A, E), F a₀ = e₀ ∧ p ∘ F = f := by
refine cov.isLocalHomeomorph.existsUnique_continuousMap_lifts f a₀ e₀ he (fun γ γ_0 ↦ ?_)
@@ -488,7 +488,7 @@ open FundamentalGroup Path.Homotopic.Quotient in
if `f⁎ π₁(A, a₀) ⊆ p⁎ π₁(E, e₀)`. Proposition 1.33 of [hatcher02], known as
the lifting criterion. -/
theorem existsUnique_continuousMap_lifts_of_range_le
- [PathConnectedSpace A] [LocPathConnectedSpace A]
+ [PathConnectedSpace A] [LocallyPathConnectedSpace A]
{f : C(A, X)} {a₀ : A} {e₀ : E} (he : p e₀ = f a₀)
(le : (map f a₀).range ≤ (mapOfEq ⟨p, cov.continuous⟩ he).range) :
∃! F : C(A, E), F a₀ = e₀ ∧ p ∘ F = f := by
@@ -525,7 +525,7 @@ Given a point `a₀` in the domain of `f` and a lift `e₀` of `f a₀` along `p
there exists a unique lift `F` of `f` along `p` such that `F a₀ = e₀`.
-/
theorem IsCoveringMapOn.existsUnique_continuousMap_lifts [SimplyConnectedSpace A]
- [LocPathConnectedSpace A] {s : Set X} (cov : IsCoveringMapOn p s) (f : C(A, X)) {a₀ : A}
+ [LocallyPathConnectedSpace A] {s : Set X} (cov : IsCoveringMapOn p s) (f : C(A, X)) {a₀ : A}
{e₀ : E} (he : p e₀ = f a₀) (hs : ∀ a, f a ∈ s) :
∃! F : C(A, E), F a₀ = e₀ ∧ p ∘ F = f := by
obtain ⟨f, rfl⟩ : ∃ f' : C(A, s), f = .comp ⟨Subtype.val, by fun_prop⟩ f' :=
diff --git a/Mathlib/Topology/Homotopy/LocallyContractible.lean b/Mathlib/Topology/Homotopy/LocallyContractible.lean
index 746ef1a8a3ea24..1f2349bda7e995 100644
--- a/Mathlib/Topology/Homotopy/LocallyContractible.lean
+++ b/Mathlib/Topology/Homotopy/LocallyContractible.lean
@@ -7,7 +7,7 @@ module
public import Mathlib.Topology.Homotopy.Contractible
public import Mathlib.Topology.Homotopy.Basic
-public import Mathlib.Topology.Connected.LocPathConnected
+public import Mathlib.Topology.Connected.LocallyPathConnected
public import Mathlib.Topology.Homeomorph.Lemmas
/-!
@@ -24,7 +24,7 @@ This file defines `LocallyContractibleSpace` and `StronglyLocallyContractibleSpa
## Main results
* `StronglyLocallyContractibleSpace.locallyContractible`: SLC implies classical LC
-* `instLocPathConnectedSpace`: strongly locally contractible spaces are locally path-connected
+* `instLocallyPathConnectedSpace`: strongly locally contractible spaces are locally path-connected
* `StronglyLocallyContractibleSpace.of_bases`: a helper to construct strongly locally contractible
spaces from a neighborhood basis
* `contractible_subset_basis`: basis of contractible neighborhoods contained in an open set
@@ -122,7 +122,7 @@ theorem contractible_subset_basis {U : Set X} (h : IsOpen U) (hx : x ∈ U) :
(contractible_basis x).hasBasis_self_subset (IsOpen.mem_nhds h hx)
/-- Strongly locally contractible spaces are locally path-connected. -/
-instance (priority := 100) instLocPathConnectedSpace : LocPathConnectedSpace X where
+instance (priority := 100) instLocallyPathConnectedSpace : LocallyPathConnectedSpace X where
path_connected_basis x := by
refine contractible_basis x |>.to_hasBasis'
(fun s ⟨hs, hs'⟩ ↦ ⟨s, ⟨hs, ?_⟩, le_rfl⟩) (fun s hs ↦ hs.1)
From 01a1a1f08b533c13dc48e1b5d2b9b05a2bf56c76 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hagb=20=28Junyu=20Guo=20=E9=83=AD=E4=BF=8A=E4=BD=99=29?=
Date: Sun, 21 Jun 2026 19:15:34 +0000
Subject: [PATCH 0221/1300] chore(Order/Preorder/Finite): use `@[to_dual]` on
lemmas about `{Min,Max}imal{For,}` (#39547)
Proofs of other theorems (e.g. in #39427) with dual theorems dependent on these lemmas would benefit from their duality.
---
Mathlib/Order/Preorder/Finite.lean | 36 +++++++++---------------------
1 file changed, 11 insertions(+), 25 deletions(-)
diff --git a/Mathlib/Order/Preorder/Finite.lean b/Mathlib/Order/Preorder/Finite.lean
index 24e16fe9ffe9e6..c64ac316b4bd62 100644
--- a/Mathlib/Order/Preorder/Finite.lean
+++ b/Mathlib/Order/Preorder/Finite.lean
@@ -23,6 +23,7 @@ namespace Finset
section IsTrans
variable [LE α] [IsTrans α LE.le] {s : Finset α} {a : α}
+@[to_dual]
lemma exists_maximalFor (f : ι → α) (s : Finset ι) (hs : s.Nonempty) :
∃ i, MaximalFor (· ∈ s) f i := by
induction hs using Finset.Nonempty.cons_induction with
@@ -35,26 +36,21 @@ lemma exists_maximalFor (f : ι → α) (s : Finset ι) (hs : s.Nonempty) :
exact fun k hk hik ↦ _root_.trans (hj.2 hk <| _root_.trans hji hik) hji
· exact ⟨j, mem_cons_of_mem hj.1, by simpa [hji] using hj.2⟩
-lemma exists_minimalFor (f : ι → α) (s : Finset ι) (hs : s.Nonempty) :
- ∃ i, MinimalFor (· ∈ s) f i := exists_maximalFor (α := αᵒᵈ) f s hs
-
+@[to_dual]
lemma exists_maximal (hs : s.Nonempty) : ∃ i, Maximal (· ∈ s) i := s.exists_maximalFor id hs
-lemma exists_minimal (hs : s.Nonempty) : ∃ i, Minimal (· ∈ s) i := s.exists_minimalFor id hs
end IsTrans
section Preorder
variable [Preorder α] {s : Finset α} {a : α}
+@[to_dual]
lemma exists_le_maximal (s : Finset α) (ha : a ∈ s) : ∃ b, a ≤ b ∧ Maximal (· ∈ s) b := by
classical
obtain ⟨b, hb, hab, hbmin⟩ : ∃ b ∈ s, a ≤ b ∧ _ := by
simpa [Maximal, and_assoc] using {x ∈ s | a ≤ x}.exists_maximal ⟨a, mem_filter.2 ⟨ha, le_rfl⟩⟩
exact ⟨b, hab, hb, fun c hc hbc ↦ hbmin hc (hab.trans hbc) hbc⟩
-lemma exists_le_minimal (s : Finset α) (ha : a ∈ s) : ∃ b ≤ a, Minimal (· ∈ s) b :=
- exists_le_maximal (α := αᵒᵈ) s ha
-
end Preorder
end Finset
@@ -62,42 +58,33 @@ namespace Set
section IsTrans
variable [LE α] [IsTrans α LE.le] {s : Set α} {a : α}
+@[to_dual]
lemma Finite.exists_maximalFor (f : ι → α) (s : Set ι) (h : s.Finite) (hs : s.Nonempty) :
∃ i, MaximalFor (· ∈ s) f i := by
lift s to Finset ι using h; exact s.exists_maximalFor f hs
-lemma Finite.exists_minimalFor (f : ι → α) (s : Set ι) (h : s.Finite) (hs : s.Nonempty) :
- ∃ i, MinimalFor (· ∈ s) f i := Finite.exists_maximalFor (α := αᵒᵈ) f s h hs
-
+@[to_dual]
lemma Finite.exists_maximal (h : s.Finite) (hs : s.Nonempty) : ∃ i, Maximal (· ∈ s) i :=
h.exists_maximalFor id _ hs
-lemma Finite.exists_minimal (h : s.Finite) (hs : s.Nonempty) : ∃ i, Minimal (· ∈ s) i :=
- h.exists_minimalFor id _ hs
-
/-- A version of `Finite.exists_maximalFor` with the (weaker) hypothesis that the image of `s`
is finite rather than `s` itself. -/
+@[to_dual /- A version of `Finite.exists_minimalFor` with the (weaker) hypothesis that the image of
+`s` is finite rather than `s` itself.-/]
lemma Finite.exists_maximalFor' (f : ι → α) (s : Set ι) (h : (f '' s).Finite) (hs : s.Nonempty) :
∃ i, MaximalFor (· ∈ s) f i := by
obtain ⟨_, ⟨a, ha, rfl⟩, hmax⟩ := Finite.exists_maximalFor id (f '' s) h (hs.image f)
exact ⟨a, ha, fun a' ha' hf ↦ hmax (mem_image_of_mem f ha') hf⟩
-/-- A version of `Finite.exists_minimalFor` with the (weaker) hypothesis that the image of `s`
-is finite rather than `s` itself. -/
-lemma Finite.exists_minimalFor' (f : ι → α) (s : Set ι) (h : (f '' s).Finite) (hs : s.Nonempty) :
- ∃ i, MinimalFor (· ∈ s) f i := h.exists_maximalFor' (α := αᵒᵈ) f s hs
-
end IsTrans
section Preorder
variable [Preorder α] {s : Set α} {a : α}
+@[to_dual]
lemma Finite.exists_le_maximal (hs : s.Finite) (ha : a ∈ s) : ∃ b, a ≤ b ∧ Maximal (· ∈ s) b := by
lift s to Finset α using hs; exact s.exists_le_maximal ha
-lemma Finite.exists_le_minimal (hs : s.Finite) (ha : a ∈ s) : ∃ b, b ≤ a ∧ Minimal (· ∈ s) b := by
- lift s to Finset α using hs; exact s.exists_le_minimal ha
-
variable [Nonempty α]
lemma infinite_of_forall_exists_gt (h : ∀ a, ∃ b ∈ s, a < b) : s.Infinite := by
@@ -107,6 +94,7 @@ lemma infinite_of_forall_exists_gt (h : ∀ a, ∃ b ∈ s, a < b) : s.Infinite
exact infinite_of_injective_forall_mem
(strictMono_nat_of_lt_succ fun n => (h _).choose_spec.2).injective hf
+@[to_dual existing infinite_of_forall_exists_gt]
lemma infinite_of_forall_exists_lt (h : ∀ a, ∃ b ∈ s, b < a) : s.Infinite :=
infinite_of_forall_exists_gt (α := αᵒᵈ) h
@@ -115,8 +103,8 @@ end Preorder
section PartialOrder
variable (α) [PartialOrder α]
+@[to_dual]
lemma finite_isTop : {a : α | IsTop a}.Finite := (subsingleton_isTop α).finite
-lemma finite_isBot : {a : α | IsBot a}.Finite := (subsingleton_isBot α).finite
end PartialOrder
@@ -152,10 +140,8 @@ end Set
section Preorder
variable [Preorder α] [Finite α] {p : α → Prop} {a : α}
+@[to_dual]
lemma Finite.exists_le_maximal (h : p a) : ∃ b, a ≤ b ∧ Maximal p b :=
{x | p x}.toFinite.exists_le_maximal h
-lemma Finite.exists_le_minimal (h : p a) : ∃ b ≤ a, Minimal p b :=
- {x | p x}.toFinite.exists_le_minimal h
-
end Preorder
From 7516f8d0247fb957ef0918ef5a5f8b717e9d5eb1 Mon Sep 17 00:00:00 2001
From: Sebastien Gouezel <10818434+sgouezel@users.noreply.github.com>
Date: Sun, 21 Jun 2026 19:56:22 +0000
Subject: [PATCH 0222/1300] chore: fix duplicated lemma (#40870)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Reported on Zulip at [#mathlib4 > Redundant copy of `intervalIntegrable_const` @ 💬](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Redundant.20copy.20of.20.60intervalIntegrable_const.60/near/605394633)
Co-authored-by: sgouezel
---
Mathlib/Analysis/SpecialFunctions/Integrability/Basic.lean | 3 ---
Mathlib/MeasureTheory/Integral/IntervalIntegral/Basic.lean | 5 ++---
2 files changed, 2 insertions(+), 6 deletions(-)
diff --git a/Mathlib/Analysis/SpecialFunctions/Integrability/Basic.lean b/Mathlib/Analysis/SpecialFunctions/Integrability/Basic.lean
index 0dc992510e3b42..e77c76e334d3e1 100644
--- a/Mathlib/Analysis/SpecialFunctions/Integrability/Basic.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Integrability/Basic.lean
@@ -187,9 +187,6 @@ theorem integrableOn_Ioo_cpow_iff {s : ℂ} {t : ℝ} (ht : 0 < t) :
theorem intervalIntegrable_id : IntervalIntegrable (fun x => x) μ a b :=
continuous_id.intervalIntegrable a b
-theorem intervalIntegrable_const : IntervalIntegrable (fun _ => c) μ a b :=
- continuous_const.intervalIntegrable a b
-
theorem intervalIntegrable_one_div (h : ∀ x : ℝ, x ∈ [[a, b]] → f x ≠ 0)
(hf : ContinuousOn f [[a, b]]) : IntervalIntegrable (fun x => 1 / f x) μ a b :=
(continuousOn_const.div hf h).intervalIntegrable
diff --git a/Mathlib/MeasureTheory/Integral/IntervalIntegral/Basic.lean b/Mathlib/MeasureTheory/Integral/IntervalIntegral/Basic.lean
index 94ce88fa5001e0..f0b1203c751e17 100644
--- a/Mathlib/MeasureTheory/Integral/IntervalIntegral/Basic.lean
+++ b/Mathlib/MeasureTheory/Integral/IntervalIntegral/Basic.lean
@@ -171,10 +171,9 @@ theorem intervalIntegrable_const_iff {c : ε} (hc : ‖c‖ₑ ≠ ⊤ := by fin
simp [intervalIntegrable_iff, integrableOn_const_iff hc]
@[simp]
-theorem intervalIntegrable_const [IsLocallyFiniteMeasure μ]
- {c : E} (hc : ‖c‖ₑ ≠ ⊤ := by finiteness) :
+theorem intervalIntegrable_const [IsLocallyFiniteMeasure μ] {c : E} :
IntervalIntegrable (fun _ => c) μ a b :=
- intervalIntegrable_const_iff hc |>.2 <| Or.inr measure_Ioc_lt_top
+ intervalIntegrable_const_iff (by simp) |>.2 <| Or.inr measure_Ioc_lt_top
protected theorem IntervalIntegrable.zero : IntervalIntegrable (0 : ℝ → E) μ a b :=
(intervalIntegrable_const_iff <| by finiteness).mpr <| .inl rfl
From 7a1a9e0449fdf4adcb4e44e4be9552565228dc19 Mon Sep 17 00:00:00 2001
From: Jovan Gerbscheid <56355248+JovanGerb@users.noreply.github.com>
Date: Sun, 21 Jun 2026 21:14:44 +0000
Subject: [PATCH 0223/1300] perf(Algebra/Algebra/Equiv): mark `coe_ringEquiv`
with `defeq` again (#40873)
This PR reverts the proof of `coe_ringEquiv` back to `:= rfl` after #40834, which apparently is necessary for performance reasons.
---
Mathlib/Algebra/Algebra/Equiv.lean | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Mathlib/Algebra/Algebra/Equiv.lean b/Mathlib/Algebra/Algebra/Equiv.lean
index d3a116e40011be..5fad57bfc7b9b8 100644
--- a/Mathlib/Algebra/Algebra/Equiv.lean
+++ b/Mathlib/Algebra/Algebra/Equiv.lean
@@ -160,7 +160,7 @@ theorem toRingEquiv_eq_coe : e.toRingEquiv = e :=
lemma toRingEquiv_toRingHom : ((e : A₁ ≃+* A₂) : A₁ →+* A₂) = e :=
rfl
-theorem coe_ringEquiv : ((e : A₁ ≃+* A₂) : A₁ → A₂) = e := by simp
+theorem coe_ringEquiv : ((e : A₁ ≃+* A₂) : A₁ → A₂) = e := rfl
@[deprecated (since := "2026-06-21")] alias coe_ringEquiv' := coe_ringEquiv
From 8b068a6bd8a1c28826b167583a7379367052fa13 Mon Sep 17 00:00:00 2001
From: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com>
Date: Sun, 21 Jun 2026 21:54:04 +0000
Subject: [PATCH 0224/1300] refactor(Order/Basic): move `Pi` & `Prop` orders to
a new file (#40658)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Move the `LE` instances of `Pi` & `Prop` from `Order/Basic.lean` to a new `Order/Defs/Prop.lean`.
This lets `Logic/Relation.lean` import them so that it can use `≤` between relations.
---
Mathlib.lean | 1 +
Mathlib/Order/Basic.lean | 18 +-----------------
Mathlib/Order/Defs/Prop.lean | 32 ++++++++++++++++++++++++++++++++
3 files changed, 34 insertions(+), 17 deletions(-)
create mode 100644 Mathlib/Order/Defs/Prop.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 92e3cf81853411..49a4130a24ce7c 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -5979,6 +5979,7 @@ public import Mathlib.Order.CountableSupClosed
public import Mathlib.Order.Cover
public import Mathlib.Order.Defs.LinearOrder
public import Mathlib.Order.Defs.PartialOrder
+public import Mathlib.Order.Defs.Prop
public import Mathlib.Order.Defs.Unbundled
public import Mathlib.Order.DirSupClosed
public import Mathlib.Order.Directed
diff --git a/Mathlib/Order/Basic.lean b/Mathlib/Order/Basic.lean
index d03d6ac2f44b9d..24ba1c2d27cb80 100644
--- a/Mathlib/Order/Basic.lean
+++ b/Mathlib/Order/Basic.lean
@@ -7,6 +7,7 @@ module
public import Mathlib.Data.Subtype
public import Mathlib.Order.Defs.LinearOrder
+public import Mathlib.Order.Defs.Prop
public import Mathlib.Order.Notation
public import Mathlib.Tactic.Spread
public import Mathlib.Tactic.Convert
@@ -549,15 +550,6 @@ instance Ne.instIsEquiv_compl : IsEquiv α (· ≠ ·)ᶜ := by
/-! ### Order instances on the function space -/
-
-instance Pi.hasLe [∀ i, LE (π i)] :
- LE (∀ i, π i) where le x y := ∀ i, x i ≤ y i
-
-@[to_dual self]
-theorem Pi.le_def [∀ i, LE (π i)] {x y : ∀ i, π i} :
- x ≤ y ↔ ∀ i, x i ≤ y i :=
- Iff.rfl
-
instance Pi.preorder [∀ i, Preorder (π i)] : Preorder (∀ i, π i) where
__ := (inferInstance : LE (∀ i, π i))
le_refl := fun a i ↦ le_refl (a i)
@@ -1098,14 +1090,6 @@ end PUnit
section «Prop»
-/-- Propositions form a complete Boolean algebra, where the `≤` relation is given by implication. -/
-instance Prop.le : LE Prop :=
- ⟨(· → ·)⟩
-
-@[simp]
-theorem le_Prop_eq : ((· ≤ ·) : Prop → Prop → Prop) = (· → ·) :=
- rfl
-
theorem subrelation_iff_le {r s : α → α → Prop} : Subrelation r s ↔ r ≤ s :=
Iff.rfl
diff --git a/Mathlib/Order/Defs/Prop.lean b/Mathlib/Order/Defs/Prop.lean
new file mode 100644
index 00000000000000..2491ab8634ff70
--- /dev/null
+++ b/Mathlib/Order/Defs/Prop.lean
@@ -0,0 +1,32 @@
+/-
+Copyright (c) 2016 Johannes Hölzl. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Johannes Hölzl, Yury Kudryashov
+-/
+module
+
+import Mathlib.Tactic.ToDual
+
+/-!
+# Order definitions for propositions
+
+This file defines orders on `Pi` and `Prop`.
+-/
+
+public section
+
+instance Pi.hasLe {ι : Type*} {π : ι → Type*} [∀ i, LE (π i)] : LE (∀ i, π i) where
+ le x y := ∀ i, x i ≤ y i
+
+@[to_dual self]
+theorem Pi.le_def {ι : Type*} {π : ι → Type*} [∀ i, LE (π i)] {x y : ∀ i, π i} :
+ x ≤ y ↔ ∀ i, x i ≤ y i :=
+ .rfl
+
+/-- Propositions form a complete Boolean algebra, where the `≤` relation is given by implication. -/
+instance Prop.le : LE Prop :=
+ ⟨(· → ·)⟩
+
+@[simp]
+theorem le_Prop_eq : ((· ≤ ·) : Prop → Prop → Prop) = (· → ·) :=
+ rfl
From 2ddd05d857dc69b284736cba674786a219737907 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Violeta=20Hern=C3=A1ndez=20Palacios?=
Date: Sun, 21 Jun 2026 22:46:15 +0000
Subject: [PATCH 0225/1300] =?UTF-8?q?feat:=20monotone=20function=20`Cardin?=
=?UTF-8?q?al=20=E2=86=92=20=CE=B1`=20is=20eventually=20constant=20(#37344?=
=?UTF-8?q?)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Mathlib.lean | 1 +
Mathlib/Logic/Small/Basic.lean | 2 +
Mathlib/Order/Preorder/Chain.lean | 17 +++++
.../SetTheory/Cardinal/EventuallyConst.lean | 74 +++++++++++++++++++
4 files changed, 94 insertions(+)
create mode 100644 Mathlib/SetTheory/Cardinal/EventuallyConst.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 49a4130a24ce7c..bd78be55936b6e 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -7106,6 +7106,7 @@ public import Mathlib.SetTheory.Cardinal.Defs
public import Mathlib.SetTheory.Cardinal.Divisibility
public import Mathlib.SetTheory.Cardinal.ENat
public import Mathlib.SetTheory.Cardinal.Embedding
+public import Mathlib.SetTheory.Cardinal.EventuallyConst
public import Mathlib.SetTheory.Cardinal.Finite
public import Mathlib.SetTheory.Cardinal.Finsupp
public import Mathlib.SetTheory.Cardinal.Free
diff --git a/Mathlib/Logic/Small/Basic.lean b/Mathlib/Logic/Small/Basic.lean
index eb7769fbd3a1cc..2f947ce5c2af91 100644
--- a/Mathlib/Logic/Small/Basic.lean
+++ b/Mathlib/Logic/Small/Basic.lean
@@ -75,3 +75,5 @@ instance small_quot {α : Type u} [Small.{v} α] (r : α → α → Prop) : Smal
instance small_quotient {α : Type u} [Small.{v} α] (s : Setoid α) : Small.{v} (Quotient s) :=
small_of_surjective Quotient.mk_surjective
+
+instance small_orderDual {α : Type*} [h : Small.{v} α] : Small.{v} αᵒᵈ := h
diff --git a/Mathlib/Order/Preorder/Chain.lean b/Mathlib/Order/Preorder/Chain.lean
index 62045ef0de9174..ef314eaee043c2 100644
--- a/Mathlib/Order/Preorder/Chain.lean
+++ b/Mathlib/Order/Preorder/Chain.lean
@@ -119,6 +119,14 @@ theorem Monotone.isChain_range [LinearOrder α] [Preorder β] {f : α → β} (h
rw [← image_univ]
exact hf.isChain_image (isChain_of_trichotomous _)
+lemma Antitone.isChain_image [Preorder α] [Preorder β] {s : Set α} {f : α → β}
+ (hf : Antitone f) (hs : IsChain (· ≤ ·) s) : IsChain (· ≤ ·) (f '' s) :=
+ hf.dual_left.isChain_image hs.symm
+
+theorem Antitone.isChain_range [LinearOrder α] [Preorder β] {f : α → β} (hf : Antitone f) :
+ IsChain (· ≤ ·) (range f) :=
+ hf.dual_left.isChain_range
+
theorem IsChain.lt_of_le [PartialOrder α] {s : Set α} (h : IsChain (· ≤ ·) s) :
IsChain (· < ·) s := fun _a ha _b hb hne ↦
(h ha hb hne).imp hne.lt_of_le hne.lt_of_le'
@@ -222,6 +230,15 @@ theorem IsChain.exists3 (hchain : IsChain r s) [IsTrans α r] {a b c} (mem1 : a
end Total
+/-- A chain in a partial order is a linear order. -/
+@[implicit_reducible]
+def IsChain.linearOrder [PartialOrder α] [DecidableLE α] {s : Set α} (hs : IsChain (· ≤ ·) s) :
+ LinearOrder s where
+ le_total := by
+ rintro ⟨a, ha⟩ ⟨b, hb⟩
+ exact hs.total ha hb
+ toDecidableLE x y := inferInstanceAs (Decidable (x.1 ≤ y.1))
+
lemma IsChain.le_of_not_gt [Preorder α] (hs : IsChain (· ≤ ·) s)
{x y : α} (hx : x ∈ s) (hy : y ∈ s) (h : ¬ x < y) : y ≤ x := by
cases hs.total hx hy with
diff --git a/Mathlib/SetTheory/Cardinal/EventuallyConst.lean b/Mathlib/SetTheory/Cardinal/EventuallyConst.lean
new file mode 100644
index 00000000000000..acdfa09f5982e9
--- /dev/null
+++ b/Mathlib/SetTheory/Cardinal/EventuallyConst.lean
@@ -0,0 +1,74 @@
+/-
+Copyright (c) 2026 Violeta Hernández Palacios. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Violeta Hernández Palacios
+-/
+module
+
+public import Mathlib.Order.Filter.EventuallyConst
+public import Mathlib.SetTheory.Cardinal.Aleph
+
+/-!
+# Eventually constant monotone functions
+
+This file proves variations of the following theorem: if `α` is a linear order and `β` is a partial
+order with `#β < cof α`, then any monotone function `f : α → β` must be eventually constant. In
+particular, this applies for functions from `Cardinal.{u}` or `Ordinal.{u}` into a `Small.{u}` type.
+-/
+
+public section
+
+universe u v
+
+variable {α : Type u} {β : Type v} [LinearOrder α] [PartialOrder β]
+
+open Cardinal Filter Order Set
+
+namespace Filter.EventuallyConst
+variable {f : α → β}
+
+theorem of_not_isCofinal_rangeSplitting [Nonempty α] (hf : Monotone f)
+ (hf' : ¬ IsCofinal (range (rangeSplitting f))) : atTop.EventuallyConst f := by
+ rw [eventuallyConst_atTop]
+ obtain ⟨i, hi⟩ := not_isCofinal_iff.1 hf'
+ refine ⟨i, fun j hij ↦ (hf hij).antisymm' <| (hf (hi _ ⟨⟨f j, j, rfl⟩, rfl⟩).le).trans' ?_⟩
+ rw [apply_rangeSplitting f]
+
+theorem of_monotone_of_lt_cof (hf : Monotone f) (hα : lift.{u} #β < lift.{v} (cof α)) :
+ atTop.EventuallyConst f := by
+ have : Nonempty α := by by_contra!; simp at hα
+ refine .of_not_isCofinal_rangeSplitting hf ?_
+ contrapose! hα
+ classical let := hf.isChain_range.linearOrder
+ rw [← lift_cof_congr_of_strictMono (rangeSplitting_strictMono hf) hα, lift_le]
+ exact (cof_le_cardinalMk _).trans (mk_set_le _)
+
+theorem of_antitone_of_lt_cof (hf : Antitone f) (hα : lift.{u} #β < lift.{v} (cof α)) :
+ atTop.EventuallyConst f :=
+ .of_monotone_of_lt_cof (β := βᵒᵈ) hf.dual_right hα
+
+end Filter.EventuallyConst
+
+namespace Cardinal
+variable {f : Cardinal.{v} → β} [Small.{v} β]
+
+theorem eventuallyConst_of_monotone (hf : Monotone f) : atTop.EventuallyConst f := by
+ refine .of_monotone_of_lt_cof hf ?_
+ simpa [← small_iff_lift_mk_lt_univ]
+
+theorem eventuallyConst_of_antitone (hf : Antitone f) : atTop.EventuallyConst f :=
+ eventuallyConst_of_monotone (β := βᵒᵈ) hf
+
+end Cardinal
+
+namespace Ordinal
+variable {f : Ordinal.{v} → β} [Small.{v} β]
+
+theorem eventuallyConst_of_monotone (hf : Monotone f) : atTop.EventuallyConst f := by
+ refine .of_monotone_of_lt_cof hf ?_
+ simpa [← small_iff_lift_mk_lt_univ]
+
+theorem eventuallyConst_of_antitone (hf : Antitone f) : atTop.EventuallyConst f :=
+ eventuallyConst_of_monotone (β := βᵒᵈ) hf
+
+end Ordinal
From 914b89d98d8c61ccbfd8546e1617027bde93e6a2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Violeta=20Hern=C3=A1ndez=20Palacios?=
Date: Sun, 21 Jun 2026 22:46:17 +0000
Subject: [PATCH 0226/1300] feat: stationary sets (#39727)
We define stationary sets as sets intersecting all club sets, and prove basic theorems about them.
---
Mathlib/Order/Cofinal.lean | 5 +
.../SetTheory/Cardinal/Cofinality/Club.lean | 171 +++++++++++++++---
2 files changed, 154 insertions(+), 22 deletions(-)
diff --git a/Mathlib/Order/Cofinal.lean b/Mathlib/Order/Cofinal.lean
index ce94954545c4d6..0eddfbd7bccbca 100644
--- a/Mathlib/Order/Cofinal.lean
+++ b/Mathlib/Order/Cofinal.lean
@@ -52,6 +52,11 @@ theorem IsCofinal.mono {s t : Set α} (h : s ⊆ t) (hs : IsCofinal s) : IsCofin
obtain ⟨b, hb, hb'⟩ := hs a
exact ⟨b, h hb, hb'⟩
+theorem IsCofinal.nonempty [Nonempty α] {s : Set α} (h : IsCofinal s) : s.Nonempty := by
+ inhabit α
+ obtain ⟨x, hx, _⟩ := h default
+ exact ⟨x, hx⟩
+
end LE
section Preorder
diff --git a/Mathlib/SetTheory/Cardinal/Cofinality/Club.lean b/Mathlib/SetTheory/Cardinal/Cofinality/Club.lean
index f772f2c6ae1fb6..852a93cbe709b4 100644
--- a/Mathlib/SetTheory/Cardinal/Cofinality/Club.lean
+++ b/Mathlib/SetTheory/Cardinal/Cofinality/Club.lean
@@ -10,11 +10,13 @@ public import Mathlib.Order.IsNormal
public import Mathlib.SetTheory.Cardinal.Cofinality.Basic
/-!
-# Club sets
+# Club sets and stationary sets
-A subset of a well-ordered type `α` is called a club set when it is closed in the order topology and
-cofinal. If `α` has no maximum, then an equivalent condition is that `α` is closed and unbounded;
-hence the name.
+A subset of a well-ordered type `α` is called a **club set** when it is closed in the order topology
+and cofinal. If `α` has no maximum, then an equivalent condition is that `α` is closed and
+unbounded; hence the name.
+
+A **stationary set** is a set which intersects all club sets.
## Implementation notes
@@ -27,7 +29,9 @@ public section
universe u v
-open Cardinal Order
+open Cardinal Order Set
+
+variable {α : Type v} {s t : Set α} {x : α} [LinearOrder α]
/-- A club set is closed under suprema and cofinal. -/
structure IsClub {α : Type*} [LinearOrder α] (s : Set α) where
@@ -40,8 +44,6 @@ structure IsClub {α : Type*} [LinearOrder α] (s : Set α) where
namespace IsClub
-variable {α : Type v} {s t : Set α} {x : α} [LinearOrder α]
-
@[simp]
theorem of_isEmpty [IsEmpty α] {s : Set α} : IsClub s :=
⟨.of_isEmpty, .of_isEmpty⟩
@@ -50,6 +52,12 @@ theorem of_isEmpty [IsEmpty α] {s : Set α} : IsClub s :=
protected theorem univ : IsClub (α := α) .univ :=
⟨.univ, .univ⟩
+protected theorem nonempty [Nonempty α] (hs : IsClub s) : s.Nonempty :=
+ hs.isCofinal.nonempty
+
+theorem _root_.isClub_empty_iff : IsClub (α := α) ∅ ↔ IsEmpty α :=
+ ⟨fun h ↦ isCofinal_empty_iff.1 h.isCofinal, fun _ ↦ .of_isEmpty⟩
+
protected theorem union (hs : IsClub s) (ht : IsClub t) : IsClub (s ∪ t) :=
⟨hs.dirSupClosed.union ht.dirSupClosed, hs.isCofinal.mono Set.subset_union_left⟩
@@ -63,12 +71,12 @@ theorem csSup_mem {α} [ConditionallyCompleteLinearOrder α] {s t : Set α}
theorem sInter_of_orderTop {s : Set (Set α)} [OrderTop α] (hs : ∀ x ∈ s, IsClub x) :
IsClub (⋂₀ s) := by
refine ⟨.sInter fun x hx ↦ (hs x hx).dirSupClosed, ?_⟩
- rw [isCofinal_iff_top_mem, Set.mem_sInter]
+ rw [isCofinal_iff_top_mem, mem_sInter]
exact fun x hx ↦ (hs x hx).isCofinal.top_mem
theorem iInter_of_orderTop {ι : Type*} {f : ι → Set α} [OrderTop α] (hs : ∀ i, IsClub (f i)) :
IsClub (⋂ i, f i) := by
- rw [← Set.sInter_range]
+ rw [← sInter_range]
exact .sInter_of_orderTop (by simpa)
theorem sInter_of_cof_le_one {s : Set (Set α)} (hα : cof α ≤ 1) (hs : ∀ x ∈ s, IsClub x) :
@@ -80,11 +88,10 @@ theorem sInter_of_cof_le_one {s : Set (Set α)} (hα : cof α ≤ 1) (hs : ∀ x
theorem iInter_of_cof_le_one {ι : Type*} {f : ι → Set α} (hα : cof α ≤ 1) (hs : ∀ i, IsClub (f i)) :
IsClub (⋂ i, f i) := by
- rw [← Set.sInter_range]
+ rw [← sInter_range]
exact .sInter_of_cof_le_one hα (by simpa)
section WellFoundedLT
-
variable [WellFoundedLT α]
attribute [local instance]
@@ -102,7 +109,7 @@ protected theorem sInter {s : Set (Set α)} (hα : cof α ≠ ℵ₀) (hsα : #s
refine .of_not_isCofinal fun hg ↦ (cof_le hg).not_gt (hα.trans_le' ?_)
simpa using mk_range_le_lift (f := g)
refine ⟨_, fun t ht ↦ ?_, le_csSup hg ⟨0, rfl⟩⟩
- apply (hs t ht).isLUB_mem (t := .range fun n ↦ f ⟨t, ht⟩ (g n)) _ (Set.range_nonempty _)
+ apply (hs t ht).isLUB_mem (t := .range fun n ↦ f ⟨t, ht⟩ (g n)) _ (range_nonempty _)
· refine ⟨?_, fun b hb ↦ csSup_le' ?_⟩ <;> rintro _ ⟨n, rfl⟩
· apply (le_csSup (.of_not_isCofinal _) _).trans (le_csSup hg ⟨n + 1, rfl⟩)
· exact fun hg' ↦ (cof_le hg').not_gt (mk_range_le.trans_lt hsα)
@@ -113,19 +120,27 @@ protected theorem sInter {s : Set (Set α)} (hα : cof α ≠ ℵ₀) (hsα : #s
protected theorem iInter {ι : Type u} {f : ι → Set α} (hα : cof α ≠ ℵ₀)
(hι : Cardinal.lift.{v} #ι < Cardinal.lift.{u} (cof α)) (hf : ∀ i, IsClub (f i)) :
IsClub (⋂ i, f i) := by
- rw [← Set.sInter_range]
+ rw [← sInter_range]
refine IsClub.sInter hα ?_ (by simpa)
rw [← Cardinal.lift_lt]
exact mk_range_le_lift.trans_lt hι
-protected theorem inter {s t : Set α} (hα : cof α ≠ ℵ₀) (hs : IsClub s) (ht : IsClub t) :
- IsClub (s ∩ t) := by
- rw [← Set.sInter_pair]
- have H : ∀ x ∈ ({s, t} : Set _), IsClub x := by simpa [hs]
- obtain hα | hα' := hα.lt_or_gt
- · rw [cof_lt_aleph0_iff] at hα
- exact .sInter_of_cof_le_one hα H
- · exact .sInter hα (hα'.trans_le' <| by simp) H
+theorem sInter_of_countable {s : Set (Set α)} (hα : cof α ≠ ℵ₀) (hsα : s.Countable)
+ (hs : ∀ x ∈ s, IsClub x) : IsClub (⋂₀ s) := by
+ obtain hα | hα := hα.lt_or_gt
+ · apply IsClub.sInter_of_cof_le_one _ hs
+ rwa [← cof_lt_aleph0_iff]
+ · apply IsClub.sInter hα.ne' (hα.trans_le' _) hs
+ rwa [le_aleph0_iff_set_countable]
+
+theorem iInter_of_countable {ι : Sort*} {f : ι → Set α} [Countable ι] (hα : cof α ≠ ℵ₀)
+ (hf : ∀ i, IsClub (f i)) : IsClub (⋂ i, f i) := by
+ rw [← sInter_range]
+ apply IsClub.sInter_of_countable hα (countable_range f)
+ simpa
+
+protected theorem inter (hα : cof α ≠ ℵ₀) (hs : IsClub s) (ht : IsClub t) : IsClub (s ∩ t) := by
+ simpa [hs, ht] using IsClub.sInter_of_countable (s := {s, t}) hα
theorem _root_.Order.IsNormal.isClub_range {f : α → α} (hf : IsNormal f) : IsClub (.range f) :=
⟨hf.dirSupClosed_range, fun x ↦ ⟨_, ⟨x, rfl⟩, hf.strictMono.le_apply⟩⟩
@@ -134,7 +149,7 @@ theorem _root_.Order.IsNormal.isClub_fixedPoints {f : α → α} (hα : cof α
IsClub f.fixedPoints := by
cases isEmpty_or_nonempty α; · simp
refine ⟨fun s hs hs₀ _ a ha ↦ (hf.map_isLUB ha hs₀).unique ?_, fun a ↦ ?_⟩
- · rwa [Set.image_congr hs, Set.image_id']
+ · rwa [image_congr hs, image_id']
· cases topOrderOrNoTopOrder α with
| inl => use ⊤; simpa using! hf.strictMono.id_le ⊤
| inr h =>
@@ -147,3 +162,115 @@ theorem _root_.Order.IsNormal.isClub_fixedPoints {f : α → α} (hα : cof α
end WellFoundedLT
end IsClub
+
+/-! ### Stationary sets -/
+
+/-- A set is called stationary when it intersects all club sets. -/
+@[expose]
+def IsStationary (s : Set α) : Prop :=
+ ∀ ⦃t⦄, IsClub t → (s ∩ t).Nonempty
+
+theorem not_isStationary_iff : ¬ IsStationary s ↔ ∃ t, IsClub t ∧ Disjoint s t := by
+ simp [IsStationary, disjoint_iff, not_nonempty_iff_eq_empty]
+
+@[gcongr]
+theorem IsStationary.mono (hs : IsStationary s) (h : s ⊆ t) : IsStationary t :=
+ fun _u hu ↦ (hs hu).mono (inter_subset_inter_left _ h)
+
+theorem IsStationary.nonempty (hs : IsStationary s) : s.Nonempty := by
+ simpa using hs .univ
+
+theorem isStationary_univ_iff : IsStationary (.univ (α := α)) ↔ Nonempty α := by
+ simp [IsStationary, ← not_imp_not (b := IsClub _), not_nonempty_iff_eq_empty,
+ isClub_empty_iff]
+
+@[simp]
+protected theorem IsStationary.univ [Nonempty α] : IsStationary (.univ (α := α)) :=
+ isStationary_univ_iff.2 ‹_›
+
+@[simp]
+theorem not_isStationary_empty : ¬ IsStationary (∅ : Set α) := by
+ intro h
+ simpa using h .univ
+
+@[simp]
+theorem not_isStationary_of_isEmpty [IsEmpty α] : ¬ IsStationary s :=
+ s.eq_empty_of_isEmpty ▸ not_isStationary_empty
+
+theorem IsStationary.of_not_isCofinal_compl (hs : ¬ IsCofinal sᶜ) : IsStationary s := by
+ intro t ht
+ obtain ⟨a, ha⟩ := not_isCofinal_iff.1 hs
+ obtain ⟨b, hb, hb'⟩ := ht.isCofinal a
+ refine ⟨b, ?_, hb⟩
+ contrapose! ha
+ exact ⟨b, ha, hb'⟩
+
+theorem isStationary_sUnion_iff_of_cof_le_one {s : Set (Set α)} (hα : cof α ≤ 1) :
+ IsStationary (⋃₀ s) ↔ ∃ x ∈ s, IsStationary x where
+ mp h := by
+ contrapose! h
+ simp_rw [not_isStationary_iff] at h ⊢
+ choose f hf hxf using h
+ refine ⟨⋂ x : s, f _ x.2, ?_, ?_⟩
+ · apply IsClub.iInter_of_cof_le_one hα
+ simpa
+ · rw [disjoint_sUnion_left]
+ exact fun x hx ↦ (hxf _ hx).mono_right (iInter_subset _ ⟨x, hx⟩)
+ mpr := fun ⟨x, hxs, hx⟩ ↦ hx.mono (subset_sUnion_of_mem hxs)
+
+theorem isStationary_iUnion_iff_of_cof_le_one {ι : Sort*} {f : ι → Set α} (hα : cof α ≤ 1) :
+ IsStationary (⋃ i, f i) ↔ ∃ i, IsStationary (f i) := by
+ rw [← sUnion_range, isStationary_sUnion_iff_of_cof_le_one hα]
+ simp
+
+theorem isStationary_sUnion_iff_of_orderTop [OrderTop α] {s : Set (Set α)} :
+ IsStationary (⋃₀ s) ↔ ∃ x ∈ s, IsStationary x :=
+ isStationary_sUnion_iff_of_cof_le_one (by simp)
+
+theorem isStationary_iUnion_iff_of_orderTop [OrderTop α] {ι : Sort*} {f : ι → Set α} :
+ IsStationary (⋃ i, f i) ↔ ∃ i, IsStationary (f i) :=
+ isStationary_iUnion_iff_of_cof_le_one (by simp)
+
+section WellFoundedLT
+variable [WellFoundedLT α]
+
+theorem IsClub.isStationary [Nonempty α] (hα : cof α ≠ ℵ₀) (hs : IsClub s) : IsStationary s :=
+ fun _ ht ↦ (hs.inter hα ht).nonempty
+
+theorem isStationary_sUnion_iff {s : Set (Set α)} (hα : cof α ≠ ℵ₀) (hsα : #s < cof α) :
+ IsStationary (⋃₀ s) ↔ ∃ x ∈ s, IsStationary x where
+ mp h := by
+ contrapose! h
+ simp_rw [not_isStationary_iff] at h ⊢
+ choose f hf hxf using h
+ refine ⟨⋂ x : s, f _ x.2, ?_, ?_⟩
+ · apply IsClub.iInter hα <;> simpa
+ · rw [disjoint_sUnion_left]
+ exact fun x hx ↦ (hxf _ hx).mono_right (iInter_subset _ ⟨x, hx⟩)
+ mpr := fun ⟨x, hxs, hx⟩ ↦ hx.mono (subset_sUnion_of_mem hxs)
+
+theorem isStationary_iUnion_iff {ι : Type u} {f : ι → Set α} (hα : cof α ≠ ℵ₀)
+ (hι : lift.{v} #ι < lift.{u} (cof α)) : IsStationary (⋃ i, f i) ↔ ∃ i, IsStationary (f i) := by
+ rw [← sUnion_range, isStationary_sUnion_iff hα]
+ · simp
+ · rw [← Cardinal.lift_lt]
+ exact mk_range_le_lift.trans_lt hι
+
+theorem isStationary_sUnion_iff_of_countable {s : Set (Set α)} (hα : cof α ≠ ℵ₀)
+ (hsα : s.Countable) : IsStationary (⋃₀ s) ↔ ∃ x ∈ s, IsStationary x := by
+ obtain hα | hα := hα.lt_or_gt
+ · apply isStationary_sUnion_iff_of_cof_le_one
+ rwa [← cof_lt_aleph0_iff]
+ · apply isStationary_sUnion_iff hα.ne' (hα.trans_le' _)
+ rwa [le_aleph0_iff_set_countable]
+
+theorem isStationary_iUnion_iff_of_countable {ι : Sort*} {f : ι → Set α} [Countable ι]
+ (hα : cof α ≠ ℵ₀) : IsStationary (⋃ i, f i) ↔ ∃ i, IsStationary (f i) := by
+ rw [← sUnion_range, isStationary_sUnion_iff_of_countable hα (countable_range f)]
+ simp
+
+theorem isStationary_union_iff (hα : cof α ≠ ℵ₀) :
+ IsStationary (s ∪ t) ↔ IsStationary s ∨ IsStationary t := by
+ simpa using isStationary_sUnion_iff_of_countable (s := {s, t}) hα
+
+end WellFoundedLT
From f0e649cb15423f0afdb2a69a698d0330199518a9 Mon Sep 17 00:00:00 2001
From: "mathlib-nolints[bot]"
<258989889+mathlib-nolints[bot]@users.noreply.github.com>
Date: Mon, 22 Jun 2026 01:32:46 +0000
Subject: [PATCH 0227/1300] chore: remove unnecessary set_option lines (#40881)
I removed 12 unnecessary `set_option` line(s) across 3 file(s).
---
Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean | 1 -
Mathlib/LinearAlgebra/Semisimple.lean | 9 ---------
.../FieldTheory/IsAlgClosed/AlgebraicClosure.lean | 2 --
3 files changed, 12 deletions(-)
diff --git a/Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean b/Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean
index 105ba3a560eaad..69443cf0fe5b99 100644
--- a/Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean
+++ b/Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean
@@ -1095,7 +1095,6 @@ lemma Scheme.exists_isOpenCover_and_isAffine_of_finite [IsCofiltered I]
simp
· rw [← hVU, ← Hom.comp_preimage, c.w]
-set_option backward.isDefEq.respectTransparency false in
open TopologicalSpace in
include hc in
/-- Suppose `{ Xᵢ }` is an inverse system of qcqs schemes with affine transition maps.
diff --git a/Mathlib/LinearAlgebra/Semisimple.lean b/Mathlib/LinearAlgebra/Semisimple.lean
index b1866db02932fd..6109d9ea4dda88 100644
--- a/Mathlib/LinearAlgebra/Semisimple.lean
+++ b/Mathlib/LinearAlgebra/Semisimple.lean
@@ -55,7 +55,6 @@ section CommRing
variable (f : End R M)
-set_option backward.isDefEq.respectTransparency false in
/-- A linear endomorphism of an `R`-module `M` is called *semisimple* if the induced `R[X]`-module
structure on `M` is semisimple. This is equivalent to saying that every `f`-invariant `R`-submodule
of `M` has an `f`-invariant complement: see `Module.End.isSemisimple_iff`. -/
@@ -68,7 +67,6 @@ def IsFinitelySemisimple : Prop :=
variable {f}
-set_option backward.isDefEq.respectTransparency false in
/-- A linear endomorphism is semisimple if every invariant submodule has in invariant complement.
See also `Module.End.isSemisimple_iff`. -/
@@ -82,7 +80,6 @@ lemma isSemisimple_iff :
f.IsSemisimple ↔ ∀ p ∈ invtSubmodule f, ∃ q ∈ invtSubmodule f, IsCompl p q := by
simp [isSemisimple_iff']
-set_option backward.isDefEq.respectTransparency false in
lemma isSemisimple_restrict_iff (p) (hp : p ∈ invtSubmodule f) :
IsSemisimple (LinearMap.restrict f hp) ↔
∀ q ∈ f.invtSubmodule, q ≤ p → ∃ r ≤ p, r ∈ f.invtSubmodule ∧ Disjoint q r ∧ q ⊔ r = p := by
@@ -122,7 +119,6 @@ lemma isSemisimple_id [IsSemisimpleModule R M] : IsSemisimple (LinearMap.id : Mo
@[simp] lemma isSemisimple_neg : (-f).IsSemisimple ↔ f.IsSemisimple := by
simp [isSemisimple_iff, mem_invtSubmodule]
-set_option backward.isDefEq.respectTransparency false in
variable (f) in
protected lemma _root_.LinearEquiv.isSemisimple_iff {M₂ : Type*} [AddCommGroup M₂] [Module R M₂]
(g : End R M₂) (e : M ≃ₗ[R] M₂) (he : e ∘ₗ f = g ∘ₗ e) :
@@ -132,7 +128,6 @@ protected lemma _root_.LinearEquiv.isSemisimple_iff {M₂ : Type*} [AddCommGroup
simp_rw [IsSemisimple, isSemisimpleModule_iff,
(Submodule.orderIsoMapComap e).complementedLattice_iff]
-set_option backward.isDefEq.respectTransparency false in
lemma eq_zero_of_isNilpotent_isSemisimple (hn : IsNilpotent f) (hs : f.IsSemisimple) : f = 0 := by
have ⟨n, h0⟩ := hn
rw [← aeval_X (R := R) f]; rw [← aeval_X_pow (R := R) f] at h0
@@ -170,7 +165,6 @@ lemma isSemisimple_sub_algebraMap_iff {μ : R} :
refine fun p ↦ ⟨fun h x hx ↦ ?_, fun h x hx ↦ p.sub_mem (h hx) (p.smul_mem μ hx)⟩
simpa using p.add_mem (h hx) (p.smul_mem μ hx)
-set_option backward.isDefEq.respectTransparency false in
lemma IsSemisimple.restrict {p : Submodule R M} (hp : p ∈ f.invtSubmodule) (hf : f.IsSemisimple) :
IsSemisimple (f.restrict hp) := by
rw [IsSemisimple] at hf ⊢
@@ -224,7 +218,6 @@ lemma IsSemisimple_smul (t : K) (h : f.IsSemisimple) :
wlog ht : t ≠ 0; · simp [not_not.mp ht]
rwa [IsSemisimple_smul_iff ht]
-set_option backward.isDefEq.respectTransparency false in
theorem isSemisimple_of_squarefree_aeval_eq_zero {p : K[X]}
(hp : Squarefree p) (hpf : aeval f p = 0) : f.IsSemisimple := by
rw [← RingHom.mem_ker, ← AEval.annihilator_eq_ker_aeval (M := M), mem_annihilator,
@@ -249,13 +242,11 @@ open Algebra
variable (hf : f.IsSemisimple)
include hf
-set_option backward.isDefEq.respectTransparency false in
/-- The minimal polynomial of a semisimple endomorphism is square free -/
theorem IsSemisimple.minpoly_squarefree : Squarefree (minpoly K f) :=
IsRadical.squarefree (minpoly.ne_zero <| IsIntegral.isIntegral _) <| by
rw [isRadical_iff_span_singleton, span_minpoly_eq_annihilator]; exact hf.annihilator_isRadical
-set_option backward.isDefEq.respectTransparency false in
protected theorem IsSemisimple.aeval (p : K[X]) : (aeval f p).IsSemisimple :=
let R := K[X] ⧸ Ideal.span {minpoly K f}
have : Module.Finite K R :=
diff --git a/MathlibTest/InstanceDiamonds/FieldTheory/IsAlgClosed/AlgebraicClosure.lean b/MathlibTest/InstanceDiamonds/FieldTheory/IsAlgClosed/AlgebraicClosure.lean
index c38a1903bf7bc7..f6ea1a241f847d 100644
--- a/MathlibTest/InstanceDiamonds/FieldTheory/IsAlgClosed/AlgebraicClosure.lean
+++ b/MathlibTest/InstanceDiamonds/FieldTheory/IsAlgClosed/AlgebraicClosure.lean
@@ -3,12 +3,10 @@ import Mathlib.FieldTheory.IsAlgClosed.AlgebraicClosure
variable {k : Type*} [Field k]
-set_option backward.isDefEq.respectTransparency false in
example : (AddCommMonoid.toNatModule : Module ℕ (AlgebraicClosure k)) =
@Algebra.toModule _ _ _ _ (AlgebraicClosure.instAlgebra k) := by
with_reducible_and_instances rfl
-set_option backward.isDefEq.respectTransparency false in
example : (AddCommGroup.toIntModule _ : Module ℤ (AlgebraicClosure k)) =
@Algebra.toModule _ _ _ _ (AlgebraicClosure.instAlgebra k) := by
with_reducible_and_instances rfl
From 69aaaa313f441d9ce4fe99f374f20820116a8c92 Mon Sep 17 00:00:00 2001
From: Weiyi Wang
Date: Mon, 22 Jun 2026 03:41:57 +0000
Subject: [PATCH 0228/1300] chore: remove stray Subtype.eq_iff declaration
(#40867)
The theorem Subtype.eq_iff has been removed from core in v4.32.0-rc1 as it is a duplicate of Subtype.ext_iff. The `ext` tag here re-introduces a new public Subtype.eq_iff in Mathlib. We can remove this as theorems can just use Subtype.ext_iff from core.
---
Mathlib/Topology/ContinuousMap/Algebra.lean | 3 ---
1 file changed, 3 deletions(-)
diff --git a/Mathlib/Topology/ContinuousMap/Algebra.lean b/Mathlib/Topology/ContinuousMap/Algebra.lean
index ecac820768283a..926994ae9b3157 100644
--- a/Mathlib/Topology/ContinuousMap/Algebra.lean
+++ b/Mathlib/Topology/ContinuousMap/Algebra.lean
@@ -492,9 +492,6 @@ end ContinuousMap
end RingStructure
-set_option linter.deprecated false in
-attribute [local ext] Subtype.eq
-
section ModuleStructure
/-!
From 8052a7142fd20f502171e269bd39a0987ddda93a Mon Sep 17 00:00:00 2001
From: Moritz Doll <21366319+mcdoll@users.noreply.github.com>
Date: Mon, 22 Jun 2026 05:44:19 +0000
Subject: [PATCH 0229/1300] chore(Dynamics): general clean-up (#40259)
We do a bunch of clean-up:
- move the type declarations to the top of the file
- remove `[ContinuousAdd]` assumption from `Flow`
- use sections to split between `AddMonoid` and `AddGroup` assumption (was done with opening and closing the namespace), also add new sections `AddZero` and `SubtractionCommMonoid`
- move definitions and theorem into the correct section
- add `@[simp]` lemmas for fully applied defs
- use `fun_prop`
- minor clean-up (whitespaces, unnecessary coercion arrows, etc)
---
Mathlib/Dynamics/Flow.lean | 89 +++++++++++++++++++++-----------
Mathlib/Dynamics/OmegaLimit.lean | 4 +-
2 files changed, 60 insertions(+), 33 deletions(-)
diff --git a/Mathlib/Dynamics/Flow.lean b/Mathlib/Dynamics/Flow.lean
index bad8543dece3ee..87698e7d8c728b 100644
--- a/Mathlib/Dynamics/Flow.lean
+++ b/Mathlib/Dynamics/Flow.lean
@@ -34,13 +34,13 @@ flow onto an invariant subset, and the time-reversal of a flow by a group.
open Set Function Filter
+variable {τ α : Type*}
+
/-!
### Invariant sets
-/
section Invariant
-variable {τ : Type*} {α : Type*}
-
/-- A set `s ⊆ α` is invariant under `ϕ : τ → α → α` if `ϕ t s ⊆ s` for all `t` in `τ`. -/
def IsInvariant (ϕ : τ → α → α) (s : Set α) : Prop :=
∀ t, MapsTo (ϕ t) s s
@@ -76,21 +76,23 @@ end Invariant
### Flows
-/
+variable (τ α) in
/-- A flow on a topological space `α` by an additive topological
monoid `τ` is a continuous monoid action of `τ` on `α`. -/
-structure Flow (τ : Type*) [TopologicalSpace τ] [AddMonoid τ] [ContinuousAdd τ] (α : Type*)
- [TopologicalSpace α] where
+structure Flow [TopologicalSpace τ] [TopologicalSpace α] [AddZero τ] where
/-- The map `τ → α → α` underlying a flow of `τ` on `α`. -/
toFun : τ → α → α
cont' : Continuous (uncurry toFun)
map_add' : ∀ t₁ t₂ x, toFun (t₁ + t₂) x = toFun t₁ (toFun t₂ x)
map_zero' : ∀ x, toFun 0 x = x
-
namespace Flow
-variable {τ : Type*} [AddMonoid τ] [TopologicalSpace τ] [ContinuousAdd τ]
- {α : Type*} [TopologicalSpace α] (ϕ : Flow τ α)
+variable [TopologicalSpace τ] [TopologicalSpace α]
+
+section AddZero
+
+variable [AddZero τ] (ϕ : Flow τ α)
instance : CoeFun (Flow τ α) fun _ => τ → α → α := ⟨Flow.toFun⟩
@@ -122,6 +124,10 @@ protected theorem continuous {β : Type*} [TopologicalSpace β] {t : β → τ}
alias _root_.Continuous.flow := Flow.continuous
+@[continuity, fun_prop]
+theorem continuous_toFun (t : τ) : Continuous (ϕ.toFun t) := by
+ fun_prop
+
theorem map_add (t₁ t₂ : τ) (x : α) : ϕ (t₁ + t₂) x = ϕ t₁ (ϕ t₂ x) := ϕ.map_add' _ _ _
@[simp]
@@ -132,13 +138,17 @@ theorem map_zero_apply (x : α) : ϕ 0 x = x := ϕ.map_zero' x
/-- Iterations of a continuous function from a topological space `α`
to itself defines a semiflow by `ℕ` on `α`. -/
def fromIter {g : α → α} (h : Continuous g) : Flow ℕ α where
- toFun n x := g^[n] x
- cont' := continuous_prod_of_discrete_left.mpr (Continuous.iterate h)
+ toFun n := g^[n]
+ cont' := continuous_prod_of_discrete_left.mpr h.iterate
map_add' := iterate_add_apply _
map_zero' _x := rfl
+@[simp]
+theorem fromIter_apply {g : α → α} (h : Continuous g) (n : ℕ) (x : α) :
+ fromIter h n x = g^[n] x := rfl
+
/-- Restriction of a flow onto an invariant set. -/
-def restrict {s : Set α} (h : IsInvariant ϕ s) : Flow τ (↥s) where
+def restrict {s : Set α} (h : IsInvariant ϕ s) : Flow τ s where
toFun t := (h t).restrict _ _ _
cont' := Continuous.subtype_mk (by fun_prop) _
map_add' _ _ _ := Subtype.ext (map_add _ _ _ _)
@@ -148,18 +158,23 @@ def restrict {s : Set α} (h : IsInvariant ϕ s) : Flow τ (↥s) where
theorem coe_restrict_apply {s : Set α} (h : IsInvariant ϕ s) (t : τ) (x : s) :
restrict ϕ h t x = ϕ t x := rfl
-set_option linter.style.whitespace false in -- manual alignment is not recognised
+end AddZero
+
+section AddMonoid
+
+variable [AddMonoid τ] (ϕ : Flow τ α)
+
/-- Convert a flow to an additive monoid action. -/
@[implicit_reducible]
def toAddAction : AddAction τ α where
- vadd := ϕ
- add_vadd := ϕ.map_add'
+ vadd := ϕ
+ add_vadd := ϕ.map_add'
zero_vadd := ϕ.map_zero'
/-- Restrict a flow by `τ` to a flow by an additive submonoid of `τ`. -/
def restrictAddSubmonoid (S : AddSubmonoid τ) : Flow S α where
toFun t x := ϕ t x
- cont' := ϕ.continuous (continuous_subtype_val.comp continuous_fst) continuous_snd
+ cont' := by fun_prop
map_add' t₁ t₂ x := ϕ.map_add' t₁ t₂ x
map_zero' := ϕ.map_zero'
@@ -249,12 +264,24 @@ theorem IsFactorOf.trans (h₁ : IsFactorOf ϕ ψ) (h₂ : IsFactorOf ψ χ) : I
/-- Every flow is a factor of itself. -/
theorem IsFactorOf.self : IsFactorOf ϕ ϕ := ⟨id, (isSemiconjugacy_id_iff_eq ϕ ϕ).mpr rfl⟩
-end Flow
+end AddMonoid
-namespace Flow
+section AddGroup
-variable {τ : Type*} [AddCommGroup τ] [TopologicalSpace τ] [IsTopologicalAddGroup τ]
- {α : Type*} [TopologicalSpace α] (ϕ : Flow τ α)
+variable [AddGroup τ] (ϕ : Flow τ α)
+
+/-- The map `ϕ t` as a homeomorphism. -/
+def toHomeomorph (t : τ) : (α ≃ₜ α) where
+ toFun := ϕ t
+ invFun := ϕ (-t)
+ left_inv x := by simp [← map_add]
+ right_inv x := by simp [← map_add]
+
+@[simp]
+theorem toHomeomorph_apply (t : τ) (x : α) : ϕ.toHomeomorph t x = ϕ t x := rfl
+
+@[simp]
+theorem toHomeomorph_symm_apply (t : τ) (x : α) : (ϕ.toHomeomorph t).symm x = ϕ (-t) x := rfl
theorem isInvariant_iff_image_eq (s : Set α) : IsInvariant ϕ s ↔ ∀ t, ϕ t '' s = s :=
(isInvariant_iff_image _ _).trans
@@ -262,26 +289,26 @@ theorem isInvariant_iff_image_eq (s : Set α) : IsInvariant ϕ s ↔ ∀ t, ϕ t
(fun h t => Subset.antisymm (h t) fun _ hx => ⟨_, h (-t) ⟨_, hx, rfl⟩, by simp [← map_add]⟩)
fun h t => by rw [h t])
+theorem image_eq_preimage_symm (t : τ) (s : Set α) : ϕ t '' s = ϕ (-t) ⁻¹' s :=
+ (ϕ.toHomeomorph t).toEquiv.image_eq_preimage_symm s
+
+end AddGroup
+
+section SubtractionCommMonoid
+
+variable [SubtractionCommMonoid τ] [ContinuousNeg τ] (ϕ : Flow τ α)
+
/-- The time-reversal of a flow `ϕ` by a (commutative, additive) group
is defined `ϕ.reverse t x = ϕ (-t) x`. -/
def reverse : Flow τ α where
toFun t := ϕ (-t)
- cont' := ϕ.continuous continuous_fst.neg continuous_snd
+ cont' := by fun_prop
map_add' _ _ _ := by rw [neg_add, map_add]
map_zero' _ := by rw [neg_zero, map_zero_apply]
-@[continuity, fun_prop]
-theorem continuous_toFun (t : τ) : Continuous (ϕ.toFun t) := by
- fun_prop
-
-/-- The map `ϕ t` as a homeomorphism. -/
-def toHomeomorph (t : τ) : (α ≃ₜ α) where
- toFun := ϕ t
- invFun := ϕ (-t)
- left_inv x := by rw [← map_add, neg_add_cancel, map_zero_apply]
- right_inv x := by rw [← map_add, add_neg_cancel, map_zero_apply]
+@[simp]
+theorem reverse_apply (t : τ) (x : α) : ϕ.reverse t x = ϕ (-t) x := rfl
-theorem image_eq_preimage_symm (t : τ) (s : Set α) : ϕ t '' s = ϕ (-t) ⁻¹' s :=
- (ϕ.toHomeomorph t).toEquiv.image_eq_preimage_symm s
+end SubtractionCommMonoid
end Flow
diff --git a/Mathlib/Dynamics/OmegaLimit.lean b/Mathlib/Dynamics/OmegaLimit.lean
index 2054de1b5f9e50..870d7fcb88d7fb 100644
--- a/Mathlib/Dynamics/OmegaLimit.lean
+++ b/Mathlib/Dynamics/OmegaLimit.lean
@@ -306,7 +306,7 @@ end omegaLimit
-/
namespace Flow
-variable {τ : Type*} [TopologicalSpace τ] [AddMonoid τ] [ContinuousAdd τ] {α : Type*}
+variable {τ : Type*} [TopologicalSpace τ] [AddMonoid τ] {α : Type*}
[TopologicalSpace α] (f : Filter τ) (ϕ : Flow τ α) (s : Set α)
open omegaLimit
@@ -329,7 +329,7 @@ end Flow
-/
namespace Flow
-variable {τ : Type*} [TopologicalSpace τ] [AddCommGroup τ] [IsTopologicalAddGroup τ] {α : Type*}
+variable {τ : Type*} [TopologicalSpace τ] [AddCommGroup τ] {α : Type*}
[TopologicalSpace α] (f : Filter τ) (ϕ : Flow τ α) (s : Set α)
open omegaLimit
From bdf26e4015b22b2e2080ca93d810fc65b69d6f90 Mon Sep 17 00:00:00 2001
From: Moritz Doll <21366319+mcdoll@users.noreply.github.com>
Date: Mon, 22 Jun 2026 06:02:41 +0000
Subject: [PATCH 0230/1300] feat(Topology/Algebra): use `Is*Apply` for
`ContinuousMultilinearMap` (#40463)
We replace manual `foo_apply` lemmas by using `IsFooApply` classes.
For each `foo` in `zero`, `one`, `add`, `sub`, `neg`, smul` we do the following:
- add an instance `IsFooApply`
- deprecate `coe_foo` and `foo_apply`
---
Mathlib/Analysis/Analytic/Binomial.lean | 2 +-
Mathlib/Analysis/Analytic/CPolynomialDef.lean | 19 +++----
Mathlib/Analysis/Analytic/ChangeOrigin.lean | 2 +-
Mathlib/Analysis/Analytic/Composition.lean | 21 ++++----
Mathlib/Analysis/Analytic/Inverse.lean | 3 +-
Mathlib/Analysis/Analytic/IsolatedZeros.lean | 2 +-
Mathlib/Analysis/Analytic/IteratedFDeriv.lean | 8 ++-
.../Analysis/Calculus/AbsolutelyMonotone.lean | 3 +-
.../Calculus/ContDiff/FaaDiBruno.lean | 1 -
.../Calculus/ContDiff/Operations.lean | 6 +--
.../Analysis/Calculus/FDeriv/Analytic.lean | 13 ++---
.../Calculus/IteratedDeriv/Lemmas.lean | 7 +--
.../Fourier/FourierTransformDeriv.lean | 20 ++++----
.../Normed/Module/Multilinear/Basic.lean | 9 ++--
.../SpecialFunctions/Exponential.lean | 4 +-
.../TaylorExpansion.lean | 4 +-
.../Algebra/Module/Multilinear/Basic.lean | 51 +++++++++----------
17 files changed, 79 insertions(+), 96 deletions(-)
diff --git a/Mathlib/Analysis/Analytic/Binomial.lean b/Mathlib/Analysis/Analytic/Binomial.lean
index e86e83ba680519..d323470efb0c04 100644
--- a/Mathlib/Analysis/Analytic/Binomial.lean
+++ b/Mathlib/Analysis/Analytic/Binomial.lean
@@ -186,7 +186,7 @@ theorem one_div_sub_pow_hasFPowerSeriesOnBall_zero (a : ℕ) {z : ℂ} (hz : z
simp only [one_div, FunLike.coe_smul, H, Function.comp_def] at this
convert (this.const_smul (c := (z ^ (a + 1))⁻¹)).congr ?_
· ext n
- simp only [FormalMultilinearSeries.smul_apply, ContinuousMultilinearMap.smul_apply,
+ simp only [FormalMultilinearSeries.smul_apply, smul_apply,
FormalMultilinearSeries.compContinuousLinearMap_apply]
simp [add_assoc, pow_add _ _ (a + 1), mul_assoc]
· intro w hw
diff --git a/Mathlib/Analysis/Analytic/CPolynomialDef.lean b/Mathlib/Analysis/Analytic/CPolynomialDef.lean
index 59c66e2d8114ec..080fd7c388245b 100644
--- a/Mathlib/Analysis/Analytic/CPolynomialDef.lean
+++ b/Mathlib/Analysis/Analytic/CPolynomialDef.lean
@@ -215,7 +215,7 @@ theorem HasFiniteFPowerSeriesOnBall.eq_partialSum
fun y hy m hm ↦ (hf.hasSum hy).unique (hasSum_sum_of_ne_finset_zero
(f := fun m => p m (fun _ => y)) (s := Finset.range m)
(fun N hN => by simp only [Finset.mem_range, not_lt] at hN
- rw [hf.finite _ (le_trans hm hN), ContinuousMultilinearMap.zero_apply]))
+ rw [hf.finite _ (le_trans hm hN), zero_apply]))
/-- Variant of the previous result with the variable expressed as `y` instead of `x + y`. -/
theorem HasFiniteFPowerSeriesOnBall.eq_partialSum'
@@ -244,7 +244,7 @@ theorem HasFiniteFPowerSeriesOnBall.bound_zero_of_eq_zero (hf : ∀ y ∈ Metric
· intro y hy
rw [hf (x + y)]
· convert! hasSum_zero
- rw [hp, ContinuousMultilinearMap.zero_apply]
+ rw [hp, zero_apply]
· rwa [Metric.mem_eball, edist_eq_enorm_sub, add_comm, add_sub_cancel_right,
← edist_zero_right, ← Metric.mem_eball]
@@ -362,7 +362,7 @@ lemma changeOriginSeries_sum_eq_partialSum_of_finite (p : FormalMultilinearSerie
intro m hm
rw [Finset.mem_range, not_lt] at hm
rw [p.changeOriginSeries_finite_of_finite hn k (by rw [add_comm]; exact Nat.le_add_of_sub_le hm),
- ContinuousMultilinearMap.zero_apply]
+ _root_.zero_apply]
/-- If `p` is a formal multilinear series such that `p m = 0` for `n ≤ m`, then
`p.changeOrigin x k = 0` for `n ≤ k`. -/
@@ -373,8 +373,7 @@ lemma changeOrigin_finite_of_finite (p : FormalMultilinearSeries 𝕜 E F) {n :
apply Finset.sum_eq_zero
intro m hm
rw [Finset.mem_range] at hm
- rw [p.changeOriginSeries_finite_of_finite hn k (le_add_of_le_left hk),
- ContinuousMultilinearMap.zero_apply]
+ rw [p.changeOriginSeries_finite_of_finite hn k (le_add_of_le_left hk), _root_.zero_apply]
theorem hasFiniteFPowerSeriesOnBall_changeOrigin (p : FormalMultilinearSeries 𝕜 E F) {n : ℕ}
(k : ℕ) (hn : ∀ (m : ℕ), n + k ≤ m → p m = 0) :
@@ -395,20 +394,18 @@ theorem changeOrigin_eval_of_finite (p : FormalMultilinearSeries 𝕜 E F) {n :
· refine fun s ↦ Not.imp_symm fun hs ↦ ?_
simp only [preimage_setOf_eq, changeOriginIndexEquiv_apply_fst, mem_setOf, not_lt] at hs
dsimp only [f]
- rw [changeOriginSeriesTerm_bound p hn _ _ _ hs, ContinuousMultilinearMap.zero_apply,
- ContinuousMultilinearMap.zero_apply]
+ rw [changeOriginSeriesTerm_bound p hn _ _ _ hs, _root_.zero_apply, _root_.zero_apply]
have hfkl k l : HasSum (f ⟨k, l, ·⟩) (changeOriginSeries p k l (fun _ ↦ x) fun _ ↦ y) := by
- simp_rw [changeOriginSeries, ContinuousMultilinearMap.sum_apply]; apply hasSum_fintype
+ simp_rw [changeOriginSeries, sum_apply]; apply hasSum_fintype
have hfk k : HasSum (f ⟨k, ·⟩) (changeOrigin p x k fun _ ↦ y) := by
have (m) (hm : m ∉ Finset.range n) : changeOriginSeries p k m (fun _ ↦ x) = 0 := by
rw [Finset.mem_range, not_lt] at hm
- rw [changeOriginSeries_finite_of_finite _ hn _ (le_add_of_le_right hm),
- ContinuousMultilinearMap.zero_apply]
+ rw [changeOriginSeries_finite_of_finite _ hn _ (le_add_of_le_right hm), _root_.zero_apply]
rw [changeOrigin, FormalMultilinearSeries.sum,
ContinuousMultilinearMap.tsum_eval (summable_of_ne_finset_zero this)]
refine (summable_of_ne_finset_zero (s := Finset.range n) fun m hm ↦ ?_).hasSum.sigma_of_hasSum
(hfkl k) (summable_of_hasFiniteSupport <| finsupp.preimage sigma_mk_injective.injOn)
- rw [this m hm, ContinuousMultilinearMap.zero_apply]
+ rw [this m hm, _root_.zero_apply]
have hf : HasSum f ((p.changeOrigin x).sum y) :=
((p.changeOrigin x).hasSum_of_finite (fun _ ↦ changeOrigin_finite_of_finite p hn) _)
|>.sigma_of_hasSum hfk (summable_of_hasFiniteSupport finsupp)
diff --git a/Mathlib/Analysis/Analytic/ChangeOrigin.lean b/Mathlib/Analysis/Analytic/ChangeOrigin.lean
index 40a765dab824cf..3dbdbdc6ca2e91 100644
--- a/Mathlib/Analysis/Analytic/ChangeOrigin.lean
+++ b/Mathlib/Analysis/Analytic/ChangeOrigin.lean
@@ -279,7 +279,7 @@ theorem changeOrigin_eval (h : (‖x‖₊ + ‖y‖₊ : ℝ≥0∞) < p.radius
have := (p.hasFPowerSeriesOnBall_changeOrigin k h.pos).hasSum x_mem_ball
rw [zero_add] at this
refine HasSum.sigma_of_hasSum this (fun l => ?_) ?_
- · simp only [changeOriginSeries, ContinuousMultilinearMap.sum_apply]
+ · simp only [changeOriginSeries, sum_apply]
apply hasSum_fintype
· refine .of_nnnorm_bounded
(p.changeOriginSeries_summable_aux₂ (mem_eball_zero_iff.1 x_mem_ball) k)
diff --git a/Mathlib/Analysis/Analytic/Composition.lean b/Mathlib/Analysis/Analytic/Composition.lean
index a1ec939506e5f5..a2d67c703fbb11 100644
--- a/Mathlib/Analysis/Analytic/Composition.lean
+++ b/Mathlib/Analysis/Analytic/Composition.lean
@@ -277,7 +277,7 @@ theorem removeZero_comp_of_pos (q : FormalMultilinearSeries 𝕜 F G)
q.removeZero.comp p n = q.comp p n := by
ext v
simp only [FormalMultilinearSeries.comp, compAlongComposition,
- ContinuousMultilinearMap.compAlongComposition_apply, ContinuousMultilinearMap.sum_apply]
+ ContinuousMultilinearMap.compAlongComposition_apply, sum_apply]
refine Finset.sum_congr rfl fun c _hc => ?_
rw [removeZero_of_pos _ (c.length_pos_of_pos hn)]
@@ -395,10 +395,10 @@ theorem comp_id (p : FormalMultilinearSeries 𝕜 E F) (x : E) : p.comp (id 𝕜
let j : Fin b.length := ⟨i.val, b.blocks_length ▸ i.prop⟩
have A : 1 < b.blocksFun j := by convert! lt_k
ext v
- rw [compAlongComposition_apply, ContinuousMultilinearMap.zero_apply]
+ rw [compAlongComposition_apply, _root_.zero_apply]
apply ContinuousMultilinearMap.map_coord_zero _ j
dsimp [applyComposition]
- rw [id_apply_of_one_lt _ _ _ A, ContinuousMultilinearMap.zero_apply]
+ rw [id_apply_of_one_lt _ _ _ A, _root_.zero_apply]
· simp
@[simp]
@@ -427,8 +427,8 @@ theorem id_comp (p : FormalMultilinearSeries 𝕜 E F) (v0 : Fin 0 → E) :
have : 0 < b.length := Composition.length_pos_of_pos b n_pos
lia
ext v
- rw [compAlongComposition_apply, id_apply_of_one_lt _ _ _ A,
- ContinuousMultilinearMap.zero_apply, ContinuousMultilinearMap.zero_apply]
+ rw [compAlongComposition_apply, id_apply_of_one_lt _ _ _ A, _root_.zero_apply,
+ _root_.zero_apply]
· simp
/-- Variant of `id_comp` in which the zero coefficient is given by an equality hypothesis instead
@@ -813,8 +813,7 @@ theorem HasFPowerSeriesWithinAt.comp {g : F → G} {f : E → F} {q : FormalMult
have E : HasSum (fun n => (q.comp p) n fun _j => y) (g (f (x + y))) := by
apply D.sigma
intro n
- simp only [compAlongComposition_apply, FormalMultilinearSeries.comp,
- ContinuousMultilinearMap.sum_apply]
+ simp only [compAlongComposition_apply, FormalMultilinearSeries.comp, sum_apply]
exact hasSum_fintype _
rw [Function.comp_apply]
exact E
@@ -910,7 +909,7 @@ theorem HasFiniteFPowerSeriesAt.comp {m n : ℕ} {g : F → G} {f : E → F}
apply Finset.sum_eq_zero
rintro c -
ext v
- simp only [compAlongComposition_apply, ContinuousMultilinearMap.zero_apply]
+ simp only [compAlongComposition_apply, _root_.zero_apply]
rcases le_or_gt m c.length with hc | hc
· simp [hg.finite _ hc]
obtain ⟨j, hj⟩ : ∃ j, n ≤ c.blocksFun j := by
@@ -1256,9 +1255,9 @@ theorem comp_assoc (r : FormalMultilinearSeries 𝕜 G H) (q : FormalMultilinear
r c.1.length fun i : Fin c.1.length =>
q (c.2 i).length (applyComposition p (c.2 i) (v ∘ c.1.embedding i))
suffices ∑ c, f c = ∑ c, g c by
- simpa +unfoldPartialApp only [FormalMultilinearSeries.comp,
- ContinuousMultilinearMap.sum_apply, compAlongComposition_apply, Finset.sum_sigma',
- applyComposition, ContinuousMultilinearMap.map_sum]
+ simpa +unfoldPartialApp only [FormalMultilinearSeries.comp, sum_apply,
+ compAlongComposition_apply, Finset.sum_sigma', applyComposition,
+ ContinuousMultilinearMap.map_sum]
/- Now, we use `Composition.sigmaEquivSigmaPi n` to change
variables in the second sum, and check that we get exactly the same sums. -/
rw [← (sigmaEquivSigmaPi n).sum_comp]
diff --git a/Mathlib/Analysis/Analytic/Inverse.lean b/Mathlib/Analysis/Analytic/Inverse.lean
index b9b860c5086d23..5a1ae3790ef0d7 100644
--- a/Mathlib/Analysis/Analytic/Inverse.lean
+++ b/Mathlib/Analysis/Analytic/Inverse.lean
@@ -133,8 +133,7 @@ theorem leftInv_comp (p : FormalMultilinearSeries 𝕜 E F) (i : E ≃L[𝕜] F)
(p.leftInv i x (n + 2) fun j : Fin (n + 2) => p 1 fun _ => v j) =
-∑ c ∈ {c : Composition (n + 2) | c.length < n + 2}.toFinset,
(p.leftInv i x c.length) (p.applyComposition c v) := by
- simp only [leftInv, ContinuousMultilinearMap.neg_apply, neg_inj,
- ContinuousMultilinearMap.sum_apply]
+ simp only [leftInv, _root_.neg_apply, neg_inj, _root_.sum_apply]
convert!
(sum_toFinset_eq_subtype (fun c : Composition (n + 2) => c.length < n + 2)
(fun c : Composition (n + 2) =>
diff --git a/Mathlib/Analysis/Analytic/IsolatedZeros.lean b/Mathlib/Analysis/Analytic/IsolatedZeros.lean
index 1fcc5670191e2d..9bad403947a195 100644
--- a/Mathlib/Analysis/Analytic/IsolatedZeros.lean
+++ b/Mathlib/Analysis/Analytic/IsolatedZeros.lean
@@ -103,7 +103,7 @@ theorem eq_pow_order_mul_iterate_dslope (hp : HasFPowerSeriesAt f p z₀) (z :
f z = (z - z₀) ^ p.order • (swap dslope z₀)^[p.order] f z := by
refine (pow_sub_smul_iterate_dslope_of_zero _ (fun k hk ↦ ?_) z).symm
rw [← (has_fpower_series_iterate_dslope_fslope k hp).coeff_zero 1, ← coeff, coeff_iterate_fslope,
- zero_add, coeff, p.apply_eq_zero_of_lt_order hk, ContinuousMultilinearMap.zero_apply]
+ zero_add, coeff, p.apply_eq_zero_of_lt_order hk, _root_.zero_apply]
theorem locally_ne_zero (hp : HasFPowerSeriesAt f p z₀) (h : p ≠ 0) : ∀ᶠ z in 𝓝[≠] z₀, f z ≠ 0 := by
rw [eventually_nhdsWithin_iff]
diff --git a/Mathlib/Analysis/Analytic/IteratedFDeriv.lean b/Mathlib/Analysis/Analytic/IteratedFDeriv.lean
index 2019cd650302ed..2d519981981421 100644
--- a/Mathlib/Analysis/Analytic/IteratedFDeriv.lean
+++ b/Mathlib/Analysis/Analytic/IteratedFDeriv.lean
@@ -99,8 +99,7 @@ lemma FormalMultilinearSeries.iteratedFDerivSeries_eq_zero {k n : ℕ}
ContinuousLinearMap.compFormalMultilinearSeries_apply,
ContinuousLinearMap.compContinuousMultilinearMap_coe, ContinuousLinearEquiv.coe_coe,
LinearIsometryEquiv.coe_toContinuousLinearEquiv, Function.comp_apply,
- continuousMultilinearCurryLeftEquiv_symm_apply, ContinuousMultilinearMap.zero_apply,
- _root_.zero_apply,
+ continuousMultilinearCurryLeftEquiv_symm_apply, _root_.zero_apply,
derivSeries_eq_zero _ (ih (p.congr_zero (Nat.succ_add_eq_add_succ _ _).symm h))]
/-- If the `n`-th term in a power series is zero, then the `n`-th derivative of the corresponding
@@ -111,8 +110,7 @@ lemma HasFPowerSeriesWithinOnBall.iteratedFDerivWithin_eq_zero
iteratedFDerivWithin 𝕜 n f s x = 0 := by
have : iteratedFDerivWithin 𝕜 n f s x = p.iteratedFDerivSeries n 0 (fun _ ↦ 0) :=
((h.iteratedFDerivWithin h' n hu hx).coeff_zero _).symm
- rw [this, p.iteratedFDerivSeries_eq_zero (p.congr_zero (Nat.zero_add n).symm hn),
- ContinuousMultilinearMap.zero_apply]
+ rw [this, p.iteratedFDerivSeries_eq_zero (p.congr_zero (Nat.zero_add n).symm hn), zero_apply]
lemma ContinuousMultilinearMap.iteratedFDeriv_comp_diagonal
{n : ℕ} (f : E [×n]→L[𝕜] F) (x : E) (v : Fin n → E) :
@@ -122,7 +120,7 @@ lemma ContinuousMultilinearMap.iteratedFDeriv_comp_diagonal
change iteratedFDeriv 𝕜 n (f ∘ g) x v = _
rw [ContinuousLinearMap.iteratedFDeriv_comp_right _ f.contDiff _ le_rfl, f.iteratedFDeriv_eq]
simp only [ContinuousMultilinearMap.iteratedFDeriv,
- ContinuousMultilinearMap.compContinuousLinearMap_apply, ContinuousMultilinearMap.sum_apply,
+ ContinuousMultilinearMap.compContinuousLinearMap_apply, sum_apply,
ContinuousMultilinearMap.iteratedFDerivComponent_apply, Set.mem_range, Pi.compRightL_apply]
rw [← sum_comp (Equiv.embeddingEquivOfFinite (Fin n))]
congr with σ
diff --git a/Mathlib/Analysis/Calculus/AbsolutelyMonotone.lean b/Mathlib/Analysis/Calculus/AbsolutelyMonotone.lean
index 79825130a34cf8..2bf4bd218d8658 100644
--- a/Mathlib/Analysis/Calculus/AbsolutelyMonotone.lean
+++ b/Mathlib/Analysis/Calculus/AbsolutelyMonotone.lean
@@ -103,8 +103,7 @@ theorem add (hf : AbsolutelyMonotoneOn f s) (hg : AbsolutelyMonotoneOn g s) :
obtain ⟨p, hp, hp_nn⟩ := hf
obtain ⟨q, hq, hq_nn⟩ := hg
refine ⟨p + q, hp.add hq, fun n x hx => ?_⟩
- simp only [Pi.add_apply, FormalMultilinearSeries.add_apply,
- ContinuousMultilinearMap.add_apply]
+ simp only [Pi.add_apply, FormalMultilinearSeries.add_apply, add_apply]
exact add_nonneg (hp_nn n hx) (hq_nn n hx)
/-- A nonnegative scalar multiple of an absolutely monotone function is absolutely monotone. -/
diff --git a/Mathlib/Analysis/Calculus/ContDiff/FaaDiBruno.lean b/Mathlib/Analysis/Calculus/ContDiff/FaaDiBruno.lean
index 6b24f9ee7aaf84..5ae2154bdc47b5 100644
--- a/Mathlib/Analysis/Calculus/ContDiff/FaaDiBruno.lean
+++ b/Mathlib/Analysis/Calculus/ContDiff/FaaDiBruno.lean
@@ -1100,7 +1100,6 @@ theorem HasFTaylorSeriesUpToOn.comp {n : WithTop ℕ∞} {g : F → G} {f : E
convert! B
ext v
simp only [Nat.succ_eq_add_one, Fintype.sum_option, ContinuousMultilinearMap.curryLeft_apply,
- ContinuousMultilinearMap.sum_apply, ContinuousMultilinearMap.add_apply,
FormalMultilinearSeries.compAlongOrderedFinpartition_apply, sum_apply, add_apply]
rw [Finset.sum_sigma']
exact Fintype.sum_equiv (OrderedFinpartition.extendEquiv m) _ _ (fun p ↦ rfl)
diff --git a/Mathlib/Analysis/Calculus/ContDiff/Operations.lean b/Mathlib/Analysis/Calculus/ContDiff/Operations.lean
index 7fb93ae9a9881d..6a19277621a564 100644
--- a/Mathlib/Analysis/Calculus/ContDiff/Operations.lean
+++ b/Mathlib/Analysis/Calculus/ContDiff/Operations.lean
@@ -284,8 +284,7 @@ theorem iteratedFDerivWithin_neg_apply {f : E → F} (hu : UniqueDiffOn 𝕜 s)
_ = fderivWithin 𝕜 (-iteratedFDerivWithin 𝕜 i f s) s x (h 0) (Fin.tail h) := by
rw [fderivWithin_congr' (@hi) hx, Pi.neg_def]
_ = -(fderivWithin 𝕜 (iteratedFDerivWithin 𝕜 i f s) s) x (h 0) (Fin.tail h) := by
- rw [fderivWithin_neg (hu x hx), neg_apply,
- ContinuousMultilinearMap.neg_apply]
+ rw [fderivWithin_neg (hu x hx), neg_apply, neg_apply]
_ = -(iteratedFDerivWithin 𝕜 (i + 1) f s) x h := by
rw [iteratedFDerivWithin_succ_apply_left]
@@ -690,8 +689,7 @@ theorem iteratedFDeriv_comp_const_smul (a : 𝕜) (hf : ContDiff 𝕜 i f) :
ext v
rw [iteratedFDeriv_succ_eq_comp_left, iteratedFDeriv_succ_eq_comp_left]
simp only [Nat.succ_eq_add_one, Nat.cast_add, Nat.cast_one, self_le_add_right, hf.of_le, hi,
- comp_apply, continuousMultilinearCurryLeftEquiv_symm_apply,
- ContinuousMultilinearMap.smul_apply]
+ comp_apply, continuousMultilinearCurryLeftEquiv_symm_apply, smul_apply]
rw [fderiv_fun_const_smul, fderiv_comp_smul, smul_smul, ← pow_succ]
· simp
rw [← Function.comp_def (g := (a • ·))]
diff --git a/Mathlib/Analysis/Calculus/FDeriv/Analytic.lean b/Mathlib/Analysis/Calculus/FDeriv/Analytic.lean
index 117d0433267872..e8a1130e401d16 100644
--- a/Mathlib/Analysis/Calculus/FDeriv/Analytic.lean
+++ b/Mathlib/Analysis/Calculus/FDeriv/Analytic.lean
@@ -334,7 +334,7 @@ theorem HasFPowerSeriesWithinOnBall.hasSum_derivSeries_of_hasFDerivWithinAt
ContinuousLinearMap.compFormalMultilinearSeries_apply,
FormalMultilinearSeries.changeOriginSeries,
ContinuousLinearMap.compContinuousMultilinearMap_coe, ContinuousLinearEquiv.coe_coe,
- LinearIsometryEquiv.coe_coe, Function.comp_apply, ContinuousMultilinearMap.sum_apply, map_sum]
+ LinearIsometryEquiv.coe_coe, Function.comp_apply, sum_apply, map_sum]
rfl
/-- If a function has a power series within a set on a ball, then so does its derivative. Version
@@ -591,12 +591,13 @@ theorem changeOrigin_toFormalMultilinearSeries [DecidableEq ι] :
cases isEmpty_or_nonempty ι
· have (l : _) : 1 + l ≠ Fintype.card ι := by
rw [add_comm, Fintype.card_eq_zero]; exact Nat.succ_ne_zero _
- simp_rw [Fintype.sum_empty, changeOriginSeries_support _ (this _), zero_apply _, tsum_zero]; rfl
+ simp_rw [Fintype.sum_empty, changeOriginSeries_support _ (this _), _root_.zero_apply _,
+ tsum_zero]; rfl
rw [tsum_eq_single (Fintype.card ι - 1), changeOriginSeries]; swap
· intro m hm
rw [Ne, eq_tsub_iff_add_eq_of_le (by exact Fintype.card_pos), add_comm] at hm
- rw [f.changeOriginSeries_support hm, zero_apply]
- rw [sum_apply, ContinuousMultilinearMap.sum_apply, Fin.snoc_zero]
+ rw [f.changeOriginSeries_support hm, _root_.zero_apply]
+ rw [_root_.sum_apply, _root_.sum_apply, Fin.snoc_zero]
simp_rw [changeOriginSeriesTerm_apply]
refine (Fintype.sum_bijective (?_ ∘ Fintype.equivFinOfCardEq (Nat.add_sub_of_le
Fintype.card_pos).symm) (.comp ?_ <| Equiv.bijective _) _ _ fun i ↦ ?_).symm
@@ -795,8 +796,8 @@ theorem derivSeries_apply_diag (n : ℕ) (x : E) :
derivSeries p n (fun _ ↦ x) x = (n + 1) • p (n + 1) fun _ ↦ x := by
simp only [derivSeries, compFormalMultilinearSeries_apply, changeOriginSeries,
compContinuousMultilinearMap_coe, ContinuousLinearEquiv.coe_coe, LinearIsometryEquiv.coe_coe,
- Function.comp_apply, ContinuousMultilinearMap.sum_apply, map_sum, _root_.sum_apply,
- continuousMultilinearCurryFin1_apply, Matrix.zero_empty]
+ Function.comp_apply, map_sum, _root_.sum_apply, continuousMultilinearCurryFin1_apply,
+ Matrix.zero_empty]
convert! Finset.sum_const _
· rw [Fin.snoc_zero, changeOriginSeriesTerm_apply, Finset.piecewise_same, add_comm]
· rw [← card, card_subtype, ← Finset.powerset_univ, ← Finset.powersetCard_eq_filter,
diff --git a/Mathlib/Analysis/Calculus/IteratedDeriv/Lemmas.lean b/Mathlib/Analysis/Calculus/IteratedDeriv/Lemmas.lean
index 18b864f5d1edd5..aff008563104c5 100644
--- a/Mathlib/Analysis/Calculus/IteratedDeriv/Lemmas.lean
+++ b/Mathlib/Analysis/Calculus/IteratedDeriv/Lemmas.lean
@@ -75,8 +75,7 @@ theorem iteratedDerivWithin_add
(hf : ContDiffWithinAt 𝕜 n f s x) (hg : ContDiffWithinAt 𝕜 n g s x) :
iteratedDerivWithin n (f + g) s x =
iteratedDerivWithin n f s x + iteratedDerivWithin n g s x := by
- simp_rw [iteratedDerivWithin, iteratedFDerivWithin_add_apply hf hg h hx,
- ContinuousMultilinearMap.add_apply]
+ simp_rw [iteratedDerivWithin, iteratedFDerivWithin_add_apply hf hg h hx, add_apply]
include h hx in
theorem iteratedDerivWithin_fun_add
@@ -103,9 +102,7 @@ theorem iteratedDerivWithin_const_sub (hn : 0 < n) (c : F) :
include h hx in
theorem iteratedDerivWithin_const_smul (c : R) (hf : ContDiffWithinAt 𝕜 n f s x) :
iteratedDerivWithin n (c • f) s x = c • iteratedDerivWithin n f s x := by
- simp_rw [iteratedDerivWithin]
- rw [iteratedFDerivWithin_const_smul_apply (a := c) hf h hx]
- simp only [ContinuousMultilinearMap.smul_apply]
+ simp [iteratedDerivWithin, iteratedFDerivWithin_const_smul_apply hf h hx]
include h hx in
theorem iteratedDerivWithin_fun_const_smul (c : R) (hf : ContDiffWithinAt 𝕜 n f s x) :
diff --git a/Mathlib/Analysis/Fourier/FourierTransformDeriv.lean b/Mathlib/Analysis/Fourier/FourierTransformDeriv.lean
index 69ce46ebc9901f..5cf7150a64f798 100644
--- a/Mathlib/Analysis/Fourier/FourierTransformDeriv.lean
+++ b/Mathlib/Analysis/Fourier/FourierTransformDeriv.lean
@@ -487,8 +487,7 @@ lemma hasFTaylorSeriesUpTo_fourierIntegral {N : ℕ∞ω}
congr with v
simp only [fourierPowSMulRight_apply, mul_comm, pow_succ, neg_mul, Fin.prod_univ_succ,
Fin.cons_zero, Fin.cons_succ, neg_smul, fourierSMulRight_apply,
- ContinuousMultilinearMap.neg_apply, ContinuousMultilinearMap.smul_apply,
- smul_comm (M := ℝ) (N := ℂ) (α := E), smul_smul]
+ neg_apply, smul_apply, smul_comm (M := ℝ) (N := ℂ) (α := E), smul_smul]
exact E ▸ hasFDerivAt_fourierIntegral L I₁ I₂ w
· intro n hn
apply fourierIntegral_continuous Real.continuous_fourierChar (by apply L.continuous₂)
@@ -538,9 +537,9 @@ theorem fourierIntegral_iteratedFDeriv [FiniteDimensional ℝ V]
induction n with
| zero =>
ext w m
- simp only [iteratedFDeriv_zero_apply, fourierPowSMulRight_apply, pow_zero,
- Finset.univ_eq_empty, _root_.neg_apply, ContinuousLinearMap.flip_apply,
- Finset.prod_empty, one_smul, fourierIntegral_continuousMultilinearMap_apply' ((h'f 0 bot_le))]
+ simp only [iteratedFDeriv_zero_apply, fourierPowSMulRight_apply, pow_zero, Finset.univ_eq_empty,
+ neg_apply, ContinuousLinearMap.flip_apply, Finset.prod_empty, one_smul,
+ fourierIntegral_continuousMultilinearMap_apply' ((h'f 0 bot_le))]
| succ n ih =>
ext w m
have J : Integrable (fderiv ℝ (iteratedFDeriv ℝ n f)) μ := by
@@ -552,18 +551,17 @@ theorem fourierIntegral_iteratedFDeriv [FiniteDimensional ℝ V]
(m 0) (Fin.tail m) =
(-(2 * π * I)) ^ (n + 1) • (∏ x : Fin (n + 1), -L (m x) w) • ∫ v, 𝐞 (-L v w) • f v ∂μ by
rw [fourierIntegral_continuousMultilinearMap_apply' (h'f _ hn)]
- simp only [iteratedFDeriv_succ_apply_left, fourierPowSMulRight_apply,
- _root_.neg_apply, ContinuousLinearMap.flip_apply]
+ simp only [iteratedFDeriv_succ_apply_left, fourierPowSMulRight_apply, neg_apply,
+ ContinuousLinearMap.flip_apply]
rw [← fourierIntegral_continuousMultilinearMap_apply' ((J.apply_continuousLinearMap _)),
← fourierIntegral_continuousLinearMap_apply' J]
exact H
have h'n : n < N := (Nat.cast_lt.mpr n.lt_succ_self).trans_le hn
rw [fourierIntegral_fderiv _ (h'f n h'n.le)
(hf.differentiable_iteratedFDeriv (mod_cast h'n)) J]
- simp only [ih h'n.le, fourierSMulRight_apply, _root_.neg_apply,
- ContinuousLinearMap.flip_apply, neg_smul, smul_neg, neg_neg,
- ContinuousMultilinearMap.smul_apply, fourierPowSMulRight_apply,
- ← coe_smul (E := E), smul_smul]
+ simp only [ih h'n.le, fourierSMulRight_apply, neg_apply, ContinuousLinearMap.flip_apply,
+ neg_smul, smul_neg, neg_neg, smul_apply, fourierPowSMulRight_apply, ← coe_smul (E := E),
+ smul_smul]
congr 1
simp only [ofReal_prod, ofReal_neg, pow_succ, mul_neg, Fin.prod_univ_succ, neg_mul,
ofReal_mul, neg_neg, Fin.tail_def]
diff --git a/Mathlib/Analysis/Normed/Module/Multilinear/Basic.lean b/Mathlib/Analysis/Normed/Module/Multilinear/Basic.lean
index 16db21c902d403..aba6bf12d57fad 100644
--- a/Mathlib/Analysis/Normed/Module/Multilinear/Basic.lean
+++ b/Mathlib/Analysis/Normed/Module/Multilinear/Basic.lean
@@ -932,16 +932,15 @@ def flipMultilinear (f : G →L[𝕜] ContinuousMultilinearMap 𝕜 E G') :
MultilinearMap.mkContinuous
{ toFun := fun m =>
LinearMap.mkContinuous
- { toFun := fun x => f x m
- map_add' := fun x y => by simp only [map_add, ContinuousMultilinearMap.add_apply]
- map_smul' := fun c x => by
- simp only [ContinuousMultilinearMap.smul_apply, map_smul, RingHom.id_apply] }
+ { toFun := (f · m)
+ map_add' := by simp
+ map_smul' := by simp }
(‖f‖ * ∏ i, ‖m i‖) fun x => by
rw [mul_right_comm]
exact (f x).le_of_opNorm_le (f.le_opNorm x) _
map_update_add' := fun m i x y => by
ext1
- simp only [_root_.add_apply, ContinuousMultilinearMap.map_update_add, LinearMap.coe_mk,
+ simp only [add_apply, ContinuousMultilinearMap.map_update_add, LinearMap.coe_mk,
LinearMap.mkContinuous_apply, AddHom.coe_mk]
map_update_smul' := fun m i c x => by
ext1
diff --git a/Mathlib/Analysis/SpecialFunctions/Exponential.lean b/Mathlib/Analysis/SpecialFunctions/Exponential.lean
index d4285ca6039335..fa82b301845597 100644
--- a/Mathlib/Analysis/SpecialFunctions/Exponential.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Exponential.lean
@@ -279,8 +279,8 @@ theorem hasFDerivAt_exp_smul_const_of_mem_ball (x : 𝔸) (t : 𝕊)
have : Commute (t • x) (h • x) := ((Commute.refl x).smul_left t).smul_right h
rw [add_smul t h, exp_add_of_commute_of_mem_ball this htx hh, zero_add, zero_smul, exp_zero,
ContinuousLinearMap.smulRight_apply, one_apply_eq_self,
- _root_.smul_apply, ContinuousLinearMap.smulRight_apply,
- one_apply_eq_self, smul_eq_mul, mul_sub_left_distrib, mul_sub_left_distrib, mul_one]
+ smul_apply, ContinuousLinearMap.smulRight_apply, one_apply_eq_self, smul_eq_mul,
+ mul_sub_left_distrib, mul_sub_left_distrib, mul_one]
theorem hasFDerivAt_exp_smul_const_of_mem_ball' (x : 𝔸) (t : 𝕊)
(htx : t • x ∈ Metric.eball (0 : 𝔸) (expSeries 𝕂 𝔸).radius) :
diff --git a/Mathlib/MeasureTheory/Measure/CharacteristicFunction/TaylorExpansion.lean b/Mathlib/MeasureTheory/Measure/CharacteristicFunction/TaylorExpansion.lean
index bfb217a1a6f464..2c5f7e64be0c71 100644
--- a/Mathlib/MeasureTheory/Measure/CharacteristicFunction/TaylorExpansion.lean
+++ b/Mathlib/MeasureTheory/Measure/CharacteristicFunction/TaylorExpansion.lean
@@ -80,8 +80,8 @@ theorem iteratedFDeriv_charFun {n : ℕ} {t : E} (hint : MemLp id n μ) (x : Fin
exact contDiff_fourierIntegral _ hint'
simp only [mul_inv_rev, neg_smul]
rw [h, iteratedFDeriv_fourierIntegral _ hint' (by fun_prop) le_rfl]
- simp only [ContinuousMultilinearMap.smul_apply, real_smul, ofReal_pow, ofReal_neg, ofReal_mul,
- ofReal_inv, ofReal_ofNat, ofReal_prod]
+ simp only [smul_apply, real_smul, ofReal_pow, ofReal_neg, ofReal_mul, ofReal_inv, ofReal_ofNat,
+ ofReal_prod]
rw [fourierIntegral_continuousMultilinearMap_apply Real.continuous_fourierChar]
swap;
· exact integrable_fourierPowSMulRight _ (by simpa using hint.integrable_norm_pow') (by fun_prop)
diff --git a/Mathlib/Topology/Algebra/Module/Multilinear/Basic.lean b/Mathlib/Topology/Algebra/Module/Multilinear/Basic.lean
index 0f9c1b4807783e..e9e26963455cdd 100644
--- a/Mathlib/Topology/Algebra/Module/Multilinear/Basic.lean
+++ b/Mathlib/Topology/Algebra/Module/Multilinear/Basic.lean
@@ -126,9 +126,10 @@ instance : Zero (ContinuousMultilinearMap R M₁ M₂) :=
instance : Inhabited (ContinuousMultilinearMap R M₁ M₂) :=
⟨0⟩
-@[simp]
-theorem zero_apply (m : ∀ i, M₁ i) : (0 : ContinuousMultilinearMap R M₁ M₂) m = 0 :=
- rfl
+instance : IsZeroApply (ContinuousMultilinearMap R M₁ M₂) (∀ i, M₁ i) M₂ where
+ zero_apply _ := rfl
+
+@[deprecated (since := "2026-06-10")] protected alias zero_apply := zero_apply
@[simp]
theorem toMultilinearMap_zero : (0 : ContinuousMultilinearMap R M₁ M₂).toMultilinearMap = 0 :=
@@ -143,10 +144,10 @@ variable {R' R'' A : Type*} [Semiring A] [∀ i, Module A (M₁ i)]
instance : SMul R' (ContinuousMultilinearMap A M₁ M₂) :=
⟨fun c f => { c • f.toMultilinearMap with cont := f.cont.const_smul c }⟩
-@[simp]
-theorem smul_apply (f : ContinuousMultilinearMap A M₁ M₂) (c : R') (m : ∀ i, M₁ i) :
- (c • f) m = c • f m :=
- rfl
+instance : IsSMulApply R' (ContinuousMultilinearMap A M₁ M₂) (∀ i, M₁ i) M₂ where
+ smul_apply _ _ _ := rfl
+
+@[deprecated (since := "2026-06-10")] protected alias smul_apply := smul_apply
@[simp]
theorem toMultilinearMap_smul (c : R') (f : ContinuousMultilinearMap A M₁ M₂) :
@@ -154,15 +155,13 @@ theorem toMultilinearMap_smul (c : R') (f : ContinuousMultilinearMap A M₁ M₂
rfl
instance [SMulCommClass R' R'' M₂] : SMulCommClass R' R'' (ContinuousMultilinearMap A M₁ M₂) :=
- ⟨fun _ _ _ => ext fun _ => smul_comm _ _ _⟩
+ FunLike.smulCommClass
instance [SMul R' R''] [IsScalarTower R' R'' M₂] :
- IsScalarTower R' R'' (ContinuousMultilinearMap A M₁ M₂) :=
- ⟨fun _ _ _ => ext fun _ => smul_assoc _ _ _⟩
+ IsScalarTower R' R'' (ContinuousMultilinearMap A M₁ M₂) := FunLike.isScalarTower
instance [DistribSMul R'ᵐᵒᵖ M₂] [IsCentralScalar R' M₂] :
- IsCentralScalar R' (ContinuousMultilinearMap A M₁ M₂) :=
- ⟨fun _ _ => ext fun _ => op_smul_eq_smul _ _⟩
+ IsCentralScalar R' (ContinuousMultilinearMap A M₁ M₂) := FunLike.isCentralScalar
end SMul
@@ -183,9 +182,10 @@ variable [ContinuousAdd M₂]
instance : Add (ContinuousMultilinearMap R M₁ M₂) :=
⟨fun f f' => ⟨f.toMultilinearMap + f'.toMultilinearMap, f.cont.add f'.cont⟩⟩
-@[simp]
-theorem add_apply (m : ∀ i, M₁ i) : (f + f') m = f m + f' m :=
- rfl
+instance : IsAddApply (ContinuousMultilinearMap R M₁ M₂) (∀ i, M₁ i) M₂ where
+ add_apply _ _ _ := rfl
+
+@[deprecated (since := "2026-06-10")] protected alias add_apply := add_apply
@[simp]
theorem toMultilinearMap_add (f g : ContinuousMultilinearMap R M₁ M₂) :
@@ -205,10 +205,7 @@ def applyAddHom (m : ∀ i, M₁ i) : ContinuousMultilinearMap R M₁ M₂ →+
map_zero' := rfl
map_add' _ _ := rfl
-@[simp]
-theorem sum_apply {α : Type*} (f : α → ContinuousMultilinearMap R M₁ M₂) (m : ∀ i, M₁ i)
- {s : Finset α} : (∑ a ∈ s, f a) m = ∑ a ∈ s, f a m :=
- map_sum (applyAddHom m) f s
+@[deprecated (since := "2026-06-10")] protected alias sum_apply := sum_apply
end ContinuousAdd
@@ -482,16 +479,18 @@ variable [IsTopologicalAddGroup M₂]
instance : Neg (ContinuousMultilinearMap R M₁ M₂) :=
⟨fun f => { -f.toMultilinearMap with cont := f.cont.neg }⟩
-@[simp]
-theorem neg_apply (m : ∀ i, M₁ i) : (-f) m = -f m :=
- rfl
+instance : IsNegApply (ContinuousMultilinearMap R M₁ M₂) (∀ i, M₁ i) M₂ where
+ neg_apply _ _ := rfl
+
+@[deprecated (since := "2026-06-10")] protected alias neg_apply := neg_apply
instance : Sub (ContinuousMultilinearMap R M₁ M₂) :=
⟨fun f g => { f.toMultilinearMap - g.toMultilinearMap with cont := f.cont.sub g.cont }⟩
-@[simp]
-theorem sub_apply (m : ∀ i, M₁ i) : (f - f') m = f m - f' m :=
- rfl
+instance : IsSubApply (ContinuousMultilinearMap R M₁ M₂) (∀ i, M₁ i) M₂ where
+ sub_apply _ _ _ := rfl
+
+@[deprecated (since := "2026-06-10")] protected alias sub_apply := sub_apply
instance : AddCommGroup (ContinuousMultilinearMap R M₁ M₂) := fast_instance%
toMultilinearMap_injective.addCommGroup _ rfl (fun _ _ => rfl) (fun _ => rfl) (fun _ _ => rfl)
@@ -686,7 +685,7 @@ theorem mkPiRing_eq_iff {z₁ z₂ : M} :
exact MultilinearMap.mkPiRing_eq_iff
theorem mkPiRing_zero : ContinuousMultilinearMap.mkPiRing R ι (0 : M) = 0 := by
- ext; rw [mkPiRing_apply, smul_zero, ContinuousMultilinearMap.zero_apply]
+ ext; rw [mkPiRing_apply, smul_zero, zero_apply]
theorem mkPiRing_eq_zero_iff (z : M) : ContinuousMultilinearMap.mkPiRing R ι z = 0 ↔ z = 0 := by
rw [← mkPiRing_zero, mkPiRing_eq_iff]
From 413459545a370f9c5d0dd0151ee8f277224c23b3 Mon Sep 17 00:00:00 2001
From: Felix Pernegger <188575194+felixpernegger@users.noreply.github.com>
Date: Mon, 22 Jun 2026 07:19:47 +0000
Subject: [PATCH 0231/1300] refactor(Topology): add deprecation module for
```LocPathConnected``` (#40869)
Co-authored-by: Batixx
---
Mathlib.lean | 1 +
Mathlib/Topology/Connected/LocPathConnected.lean | 10 ++++++++++
2 files changed, 11 insertions(+)
create mode 100644 Mathlib/Topology/Connected/LocPathConnected.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index bd78be55936b6e..0b00953c68055d 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -7781,6 +7781,7 @@ public import Mathlib.Topology.Compactness.SigmaCompact
public import Mathlib.Topology.Connected.Basic
public import Mathlib.Topology.Connected.CardComponents
public import Mathlib.Topology.Connected.Clopen
+public import Mathlib.Topology.Connected.LocPathConnected
public import Mathlib.Topology.Connected.LocallyConnected
public import Mathlib.Topology.Connected.LocallyPathConnected
public import Mathlib.Topology.Connected.PathComponentOne
diff --git a/Mathlib/Topology/Connected/LocPathConnected.lean b/Mathlib/Topology/Connected/LocPathConnected.lean
new file mode 100644
index 00000000000000..cc71984b49b2fd
--- /dev/null
+++ b/Mathlib/Topology/Connected/LocPathConnected.lean
@@ -0,0 +1,10 @@
+/-
+Copyright (c) 2020 Patrick Massot. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Patrick Massot, Ben Eltschig
+-/
+module -- shake: keep-all
+
+public import Mathlib.Topology.Connected.LocallyPathConnected
+
+deprecated_module (since := "2026-06-21")
From 087fc91703c3ff630a37538ff7399a7c2972d008 Mon Sep 17 00:00:00 2001
From: Michael Stoll <99838730+MichaelStollBayreuth@users.noreply.github.com>
Date: Mon, 22 Jun 2026 07:32:35 +0000
Subject: [PATCH 0232/1300]
feat(AlgebraicGeometry/EllipticCurve/Affine/AddSubMap): sym2x + API (#40574)
This continues the development toward the approximate parallelogram law on an elliptic curve.
It introduces `WeierstrassCurve.Affine.Point.symx`, which represents the symmetric square of the `x`-coordinate map. The proof of the approximate parallelogram law is based on a commutative square relating `WeierstrassCurve.addSubMap` to `WeierstrassCurve.Affine.Point.symx`.
---
.../EllipticCurve/Affine/AddSubMap.lean | 57 ++++++++++++++++++-
1 file changed, 55 insertions(+), 2 deletions(-)
diff --git a/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/AddSubMap.lean b/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/AddSubMap.lean
index e827a3073c3ef7..a0d9e31f7c3413 100644
--- a/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/AddSubMap.lean
+++ b/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/AddSubMap.lean
@@ -5,8 +5,7 @@ Authors: Michael Stoll
-/
module
-public import Mathlib.AlgebraicGeometry.EllipticCurve.Weierstrass
-public import Mathlib.LinearAlgebra.Matrix.Notation
+public import Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Point
public import Mathlib.RingTheory.MvPolynomial.Homogeneous
public import Mathlib.Tactic.Ring.NamePolyVars
@@ -129,4 +128,58 @@ lemma addSubMap_ne_zero [IsReduced R] {x : Fin 3 → R} (hx : x ≠ 0) :
end WeierstrassCurve
+/-!
+### The symmetric square of the x-coordinate map
+
+We define `Weierstrass.Affine.Point.sym2x`, which sends a pair `P`, `Q` of nonsingular points in
+affine coordinates on a Weierstrass curve to a triple projectively equal to
+`(x(P)*x(Q), x(P)+x(Q), 1)`, and provide some API.
+-/
+
+namespace WeierstrassCurve.Affine.Point
+
+variable {R : Type*} [CommRing R] {W' : Affine R}
+
+/-- This map sends a pair `P`, `Q` of nonsingular points in affine coordinates on `W`
+to a triple projectively equivalent to `![x(P) * x(Q), x(P) + x(Q), 1]`.
+
+In more geometric terms, this is the map `Sym² W → Sym² ℙ¹ ≃ ℙ²` induced by `x : W → ℙ¹`. -/
+noncomputable def sym2x (P Q : W'.Point) : Fin 3 → R :=
+ letI Px := P.xRep
+ letI Qx := Q.xRep
+ ![Px 0 * Qx 0, Px 0 * Qx 1 + Px 1 * Qx 0, Px 1 * Qx 1]
+
+@[simp]
+lemma sym2x_zero_zero : (0 : W'.Point).sym2x 0 = ![1, 0, 0] := by
+ simp [sym2x]
+
+@[simp]
+lemma sym2x_zero_some {x y : R} (h : W'.Nonsingular x y) :
+ (0 : W'.Point).sym2x (some x y h) = ![x, 1, 0] := by
+ simp [sym2x]
+
+@[simp]
+lemma sym2x_some_zero {x y : R} (h : W'.Nonsingular x y) :
+ (some x y h).sym2x 0 = ![x, 1, 0] := by
+ simp [sym2x]
+
+@[simp]
+lemma sym2x_some_some {x y x' y' : R} (h : W'.Nonsingular x y) (h' : W'.Nonsingular x' y') :
+ (some x y h).sym2x (some x' y' h') = ![x * x', x + x', 1] := by
+ simp [sym2x]
+
+lemma sym2x_ne_zero [Nontrivial R] (P Q : W'.Point) : P.sym2x Q ≠ 0 := by
+ cases P <;> cases Q <;> simp [sym2x, xRep]
+
+lemma sym2x_comm (P Q : W'.Point) : P.sym2x Q = Q.sym2x P := by
+ cases P <;> cases Q <;> simp [← zero_def, mul_comm, add_comm]
+
+lemma sym2x_neg_left (P Q : W'.Point) : (-P).sym2x Q = P.sym2x Q := by
+ simp [sym2x]
+
+lemma sym2x_neg_right (P Q : W'.Point) : P.sym2x (-Q) = P.sym2x Q := by
+ simp [sym2x]
+
+end WeierstrassCurve.Affine.Point
+
end
From 95284251ca354e7a5d6d432f27c4b3a8c889cb1a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Violeta=20Hern=C3=A1ndez=20Palacios?=
Date: Mon, 22 Jun 2026 07:32:37 +0000
Subject: [PATCH 0233/1300] feat(Topology/SmallInductiveDimension): fix small
style issues (#40878)
We make some arguments implicit, change `Type` to `Type*`, and fix some capitalization.
---
Mathlib/Topology/SmallInductiveDimension.lean | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/Mathlib/Topology/SmallInductiveDimension.lean b/Mathlib/Topology/SmallInductiveDimension.lean
index 45e6ca64d319d1..c31d8e015a7647 100644
--- a/Mathlib/Topology/SmallInductiveDimension.lean
+++ b/Mathlib/Topology/SmallInductiveDimension.lean
@@ -48,21 +48,25 @@ class inductive HasSmallInductiveDimensionLT.{u} :
(h : ∀ U ∈ s, HasSmallInductiveDimensionLT ↑(frontier U) n) :
HasSmallInductiveDimensionLT X (n + 1)
-variable (X : Type) [TopologicalSpace X]
+variable {X : Type*} [TopologicalSpace X]
+variable (X) in
/-- A topological space has dimension `≤ n` if it has dimension `< n + 1`. -/
abbrev HasSmallInductiveDimensionLE (n : ℕ) :=
HasSmallInductiveDimensionLT X (n + 1)
+variable (X) in
/-- The small inductive dimension of a topological space. -/
noncomputable def smallInductiveDimension : WithBot ℕ∞ :=
sInf {n : WithBot ℕ∞ | ∀ (i : ℕ), n < i → HasSmallInductiveDimensionLT X i}
-lemma HasSmallInductiveDimensionLT_zero_iff :
- HasSmallInductiveDimensionLT X 0 ↔ IsEmpty X :=
+lemma hasSmallInductiveDimensionLT_zero_iff : HasSmallInductiveDimensionLT X 0 ↔ IsEmpty X :=
⟨fun h ↦ by cases h; assumption, fun _ ↦ .zero⟩
-lemma HasSmallInductiveDimensionLT_one_iff :
+@[deprecated (since := "2026-06-21")]
+alias HasSmallInductiveDimensionLT_zero_iff := hasSmallInductiveDimensionLT_zero_iff
+
+lemma hasSmallInductiveDimensionLT_one_iff :
HasSmallInductiveDimensionLT X 1 ↔ IsTopologicalBasis { s : Set X | IsClopen s } := by
constructor
· intro (.succ _ s hs h)
@@ -72,4 +76,7 @@ lemma HasSmallInductiveDimensionLT_one_iff :
rwa [isEmpty_coe_sort, (hs.isOpen hU).frontier_eq, sdiff_eq_empty] at ‹_›
· exact fun h ↦ .succ 0 _ h fun _ hU ↦ hU.frontier_eq ▸ .zero
+@[deprecated (since := "2026-06-21")]
+alias HasSmallInductiveDimensionLT_one_iff := hasSmallInductiveDimensionLT_one_iff
+
end
From ba54f179f5b1ee123ef7bba7815ea5e4ea89fd07 Mon Sep 17 00:00:00 2001
From: Felix Pernegger <188575194+felixpernegger@users.noreply.github.com>
Date: Mon, 22 Jun 2026 07:32:39 +0000
Subject: [PATCH 0234/1300] feat(Topology/MetricSpace): fix two non-terminal
simps (#40882)
Co-authored-by: Batixx
---
Mathlib/Topology/MetricSpace/PiNat.lean | 15 ++++++---------
1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/Mathlib/Topology/MetricSpace/PiNat.lean b/Mathlib/Topology/MetricSpace/PiNat.lean
index a074e24c734a3a..61b1c66783476d 100644
--- a/Mathlib/Topology/MetricSpace/PiNat.lean
+++ b/Mathlib/Topology/MetricSpace/PiNat.lean
@@ -933,21 +933,18 @@ lemma min_dist_le_dist_pi (x y : ∀ i, F i) (i : ι) :
lemma dist_le_dist_pi_of_dist_lt (h : dist x y < 2⁻¹ ^ encode i) : dist (x i) (y i) ≤ dist x y := by
simpa only [not_le.2 h, false_or] using min_le_iff.1 (min_dist_le_dist_pi x y i)
--- TODO: fix two non-terminal simps below; second one uses a long lemma list
-set_option linter.flexible false in
/-- Given a countable family of metric spaces, one may put a distance on their product `Π i, E i`.
It is highly non-canonical, though, and therefore not registered as a global instance.
The distance we use here is `dist x y = ∑' i, min (1/2)^(encode i) (dist (x i) (y i))`. -/
@[instance_reducible]
protected def pseudoMetricSpace : PseudoMetricSpace (∀ i, F i) :=
- PseudoEMetricSpace.toPseudoMetricSpaceOfDist dist
- (fun x y ↦ by simp [dist_eq_tsum]; positivity) fun x y ↦ by
- rw [edist_eq_tsum, dist_eq_tsum,
- ENNReal.ofReal_tsum_of_nonneg (fun _ ↦ by positivity) (dist_summable ..)]
- simp [edist, ENNReal.inv_pow]
- congr! with a
- exact PseudoMetricSpace.edist_dist (x a) (y a)
+ PseudoEMetricSpace.toPseudoMetricSpaceOfDist dist (fun x y ↦ by rw [dist_eq_tsum]; positivity)
+ fun x y ↦ by
+ rw [edist_eq_tsum, dist_eq_tsum,
+ ENNReal.ofReal_tsum_of_nonneg (fun _ ↦ by positivity) (dist_summable ..)]
+ congr! with a
+ simp [edist, ENNReal.inv_pow, PseudoMetricSpace.edist_dist (x a) (y a)]
end PseudoMetricSpace
From ed5d9e9233c1ea57f266569649134b163424ae9f Mon Sep 17 00:00:00 2001
From: Brian Nugent
Date: Mon, 22 Jun 2026 07:51:29 +0000
Subject: [PATCH 0235/1300] feat(CategoryTheory/Order): Lattice Homs preserve
limits and colimits (#39992)
Provides instances for when [OrderHom.toFunctor](https://leanprover-community.github.io/mathlib4_docs/Mathlib/CategoryTheory/Category/Preorder.html#OrderHom.toFunctor) preserves limits and colimits.
Co-authored-by: Brian-Nugent
---
Mathlib.lean | 1 +
.../Limits/Preserves/Lattice.lean | 104 ++++++++++++++++++
2 files changed, 105 insertions(+)
create mode 100644 Mathlib/CategoryTheory/Limits/Preserves/Lattice.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 0b00953c68055d..80bad97308d364 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -2871,6 +2871,7 @@ public import Mathlib.CategoryTheory.Limits.Preserves.Filtered
public import Mathlib.CategoryTheory.Limits.Preserves.Finite
public import Mathlib.CategoryTheory.Limits.Preserves.FunctorCategory
public import Mathlib.CategoryTheory.Limits.Preserves.Grothendieck
+public import Mathlib.CategoryTheory.Limits.Preserves.Lattice
public import Mathlib.CategoryTheory.Limits.Preserves.Limits
public import Mathlib.CategoryTheory.Limits.Preserves.Opposites
public import Mathlib.CategoryTheory.Limits.Preserves.Over
diff --git a/Mathlib/CategoryTheory/Limits/Preserves/Lattice.lean b/Mathlib/CategoryTheory/Limits/Preserves/Lattice.lean
new file mode 100644
index 00000000000000..627b4e7d2f6cab
--- /dev/null
+++ b/Mathlib/CategoryTheory/Limits/Preserves/Lattice.lean
@@ -0,0 +1,104 @@
+/-
+Copyright (c) 2026 Brian Nugent. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Brian Nugent
+-/
+module
+
+public import Mathlib.CategoryTheory.Limits.Lattice
+public import Mathlib.CategoryTheory.Limits.Preserves.Finite
+public import Mathlib.Order.ConditionallyCompleteLattice.Basic
+public import Mathlib.Order.Hom.CompleteLattice
+
+/-!
+# Lattice Homs that Preserve Limits and Colimits
+
+This file provides instances for when OrderHom.toFunctor preserves limits/colimits.
+In particular, if `f` preserves finite infs/sups (i.e. is from a InfTopHomClass/SupBotHomClass)
+then `(toOrderHom f).toFunctor` preserves finite limits/colimits. If `f` preserves
+arbitrary infs/sups (i.e. is from a sInfHomClass/sSupHomClass) then `(toOrderHom f).toFunctor`
+preserves all limits/colimits.
+
+-/
+
+public section
+
+open OrderHomClass
+
+namespace CategoryTheory.Limits.CompleteLattice
+
+universe w w' u v
+
+variable {α : Type u} {β : Type v} {F : Type*} [FunLike F α β] (f : F)
+
+section
+
+variable [SemilatticeInf α] [OrderTop α] [SemilatticeInf β] [OrderTop β] [InfTopHomClass F α β]
+
+instance preservesLimit_finite_toFunctor {J : Type w} [SmallCategory J]
+ [FinCategory J] (K : J ⥤ α) : PreservesLimit K (toOrderHom f).toFunctor :=
+ preservesLimit_of_preserves_limit_cone (finiteLimitCone K).isLimit <|
+ (finiteLimitCone _).isLimit.ofIsoLimit
+ (Cone.ext (eqToIso (show Finset.univ.inf _ = f _ by aesop)) (by subsingleton))
+
+instance preservesLimitsOfShape_finite_toFunctor {J : Type w} [SmallCategory J] [FinCategory J] :
+ PreservesLimitsOfShape J (toOrderHom f).toFunctor where
+
+instance : PreservesFiniteLimits (toOrderHom f).toFunctor where
+ preservesFiniteLimits _ _ _ := inferInstance
+
+end
+
+section
+
+variable [SemilatticeSup α] [OrderBot α] [SemilatticeSup β] [OrderBot β] [SupBotHomClass F α β]
+
+instance preservesColimit_finite_toFunctor {J : Type w} [SmallCategory J]
+ [FinCategory J] (K : J ⥤ α) : PreservesColimit K (toOrderHom f).toFunctor :=
+ preservesColimit_of_preserves_colimit_cocone (finiteColimitCocone K).isColimit <|
+ (finiteColimitCocone _).isColimit.ofIsoColimit
+ (Cocone.ext (eqToIso (show Finset.univ.sup _ = f _ by aesop)) (by subsingleton))
+
+instance preservesColimitsOfShape_finite_toFunctor {J : Type w} [SmallCategory J]
+ [FinCategory J] : PreservesColimitsOfShape J (toOrderHom f).toFunctor where
+
+instance : PreservesFiniteColimits (toOrderHom f).toFunctor where
+ preservesFiniteColimits _ _ _ := inferInstance
+
+end
+
+section
+
+variable [CompleteLattice α] [CompleteLattice β]
+
+instance preservesLimit_toFunctor [sInfHomClass F α β] {J : Type w} [Category.{w'} J]
+ (K : J ⥤ α) : PreservesLimit K (toOrderHom f).toFunctor :=
+ preservesLimit_of_preserves_limit_cone (limitCone K).isLimit <|
+ (limitCone _).isLimit.ofIsoLimit (Cone.ext (eqToIso (by aesop)) (by subsingleton))
+
+instance preservesLimitsOfShape_toFunctor [sInfHomClass F α β] {J : Type w} [Category.{w'} J] :
+ PreservesLimitsOfShape J (toOrderHom f).toFunctor where
+
+instance preservesLimitsOfSize_toFunctor [sInfHomClass F α β] :
+ PreservesLimitsOfSize.{w', w} (toOrderHom f).toFunctor where
+
+instance preservesLimits_toFunctor [sInfHomClass F α β] :
+ PreservesLimits (toOrderHom f).toFunctor where
+
+instance preservesColimit_toFunctor [sSupHomClass F α β] {J : Type w} [Category.{w'} J]
+ (K : J ⥤ α) : PreservesColimit K (toOrderHom f).toFunctor :=
+ preservesColimit_of_preserves_colimit_cocone (colimitCocone K).isColimit <|
+ (colimitCocone _).isColimit.ofIsoColimit (Cocone.ext (eqToIso (by aesop)) (by subsingleton))
+
+instance preservesColimitsOfShape_toFunctor [sSupHomClass F α β] {J : Type w} [Category.{w'} J] :
+ PreservesColimitsOfShape J (toOrderHom f).toFunctor where
+
+instance preservesColimitsOfSize_toFunctor [sSupHomClass F α β] :
+ PreservesColimitsOfSize.{w', w} (toOrderHom f).toFunctor where
+
+instance preservesColimits_toFunctor [sSupHomClass F α β] :
+ PreservesColimits (toOrderHom f).toFunctor where
+
+end
+
+end CategoryTheory.Limits.CompleteLattice
From 0489999bd41b243c63f843350f153950605f0206 Mon Sep 17 00:00:00 2001
From: Michael Rothgang <10105016+grunweg@users.noreply.github.com>
Date: Mon, 22 Jun 2026 08:35:01 +0000
Subject: [PATCH 0236/1300] fix: correct differential geometry elaborators
around the tangent bundle (#40051)
The previous logic was simply wrong --- it was just masked in practice because
the `fromBaseInfo` strategy is tried first. (Indeed, just changing the order makes
both the build in `VectorBundle/Tangent` as well as a number of tests fail.)
This bugfix is important as #40047 (another elaborators bug fix) will change things,
so the tangent space strategy is tried first.
---
Mathlib/Geometry/Manifold/Notation.lean | 4 +--
.../Notation/Advanced.lean | 29 ++++++-------------
.../Notation/Delaborators.lean | 2 +-
3 files changed, 12 insertions(+), 23 deletions(-)
diff --git a/Mathlib/Geometry/Manifold/Notation.lean b/Mathlib/Geometry/Manifold/Notation.lean
index c742d0a4e9df21..5de3d94fd0fe6e 100644
--- a/Mathlib/Geometry/Manifold/Notation.lean
+++ b/Mathlib/Geometry/Manifold/Notation.lean
@@ -394,8 +394,8 @@ where
fromTotalSpace : TermElabM FindModelResult := do
match_expr e with
| Bundle.TotalSpace _ F V => do
- if let some m ← tryStrategy m!"From base info" (fromTotalSpace.fromBaseInfo F) then return m
if let some m ← tryStrategy m!"TangentSpace" (fromTotalSpace.tangentSpace V) then return m
+ if let some m ← tryStrategy m!"From base info" (fromTotalSpace.fromBaseInfo F) then return m
throwError "Having a TotalSpace as source is not yet supported"
| _ => throwError "`{e}` is not a `Bundle.TotalSpace`."
/-- Attempt to use the provided `baseInfo` to find a model. -/
@@ -423,7 +423,7 @@ where
| TangentSpace _k _ _E _ _ _H _ I M _ _ => do
trace[Elab.DiffGeo.MDiff] "`{V}` is the total space of the `TangentBundle` of `{M}`"
let srcIT : Term ← Term.exprToSyntax I
- let resTerm : Term ← ``(ModelWithCorners.prod $srcIT (ModelWithCorners.tangent $srcIT))
+ let resTerm : Term ← ``(ModelWithCorners.tangent $srcIT)
Term.elabTerm resTerm none
| _ => throwError "`{V}` is not a `TangentSpace`"
/-- Attempt to find a model on a `TangentBundle` -/
diff --git a/MathlibTest/DifferentialGeometry/Notation/Advanced.lean b/MathlibTest/DifferentialGeometry/Notation/Advanced.lean
index 2355bf61ff17c2..f6e277d4f8ae00 100644
--- a/MathlibTest/DifferentialGeometry/Notation/Advanced.lean
+++ b/MathlibTest/DifferentialGeometry/Notation/Advanced.lean
@@ -195,9 +195,7 @@ section interaction
-- Note: these tests might be incomplete; extend as needed!
-/--
-info: MDifferentiableAt I (I.prod (modelWithCornersSelf 𝕜 E)) fun m ↦ TotalSpace.mk' E m (X m) : M → Prop
--/
+/-- info: MDifferentiableAt I I.tangent fun m ↦ TotalSpace.mk' E m (X m) : M → Prop -/
#guard_msgs in
#check MDiffAt (T% X)
@@ -234,13 +232,10 @@ Hint: Additional diagnostic information may be available using the `set_option d
---
trace: [Elab.DiffGeo.MDiff] Finding a model with corners for: `TotalSpace F (TangentSpace I)`
[Elab.DiffGeo.MDiff] ✅️ TotalSpace
- [Elab.DiffGeo.MDiff] 💥️ From base info
- [Elab.DiffGeo.MDiff] Failed with error:
- No `baseInfo` provided
[Elab.DiffGeo.MDiff] ✅️ TangentSpace
[Elab.DiffGeo.MDiff] `TangentSpace I` is the total space of the `TangentBundle` of `M`
- [Elab.DiffGeo.MDiff] Found model: `I.prod I.tangent`
- [Elab.DiffGeo.MDiff] Found model: `I.prod I.tangent`
+ [Elab.DiffGeo.MDiff] Found model: `I.tangent`
+ [Elab.DiffGeo.MDiff] Found model: `I.tangent`
[Elab.DiffGeo.MDiff] Finding a model with corners for: `F`
[Elab.DiffGeo.MDiff] 💥️ TotalSpace
[Elab.DiffGeo.MDiff] Failed with error:
@@ -956,9 +951,8 @@ variable {σ : Π x : M, V x} {σ' : (x : E) → Trivial E E' x} {s : E → E'}
variable (X : (m : M) → TangentSpace I m) [IsManifold I 1 M] {x : M}
/--
-info: mfderiv I (I.prod (modelWithCornersSelf 𝕜 E)) (fun m ↦ TotalSpace.mk' E m (X m))
- x : ContinuousLinearMap (RingHom.id 𝕜) (TangentSpace I x)
- (TangentSpace (I.prod (modelWithCornersSelf 𝕜 E)) (TotalSpace.mk' E x (X x)))
+info: mfderiv I I.tangent (fun m ↦ TotalSpace.mk' E m (X m))
+ x : ContinuousLinearMap (RingHom.id 𝕜) (TangentSpace I x) (TangentSpace I.tangent (TotalSpace.mk' E x (X x)))
-/
#guard_msgs in
#check mfderiv% (T% X) x
@@ -1099,24 +1093,19 @@ variable {σ : Π x : M, V x} {σ' : (x : E) → Trivial E E' x} {s : E → E'}
variable (X : (m : M) → TangentSpace I m) [IsManifold I 1 M] {x : M}
/--
-info: mfderiv I (I.prod (modelWithCornersSelf 𝕜 E)) (fun m ↦ TotalSpace.mk' E m (X m))
- x : ContinuousLinearMap (RingHom.id 𝕜) (TangentSpace I x)
- (TangentSpace (I.prod (modelWithCornersSelf 𝕜 E)) (TotalSpace.mk' E x (X x)))
+info: mfderiv I I.tangent (fun m ↦ TotalSpace.mk' E m (X m))
+ x : ContinuousLinearMap (RingHom.id 𝕜) (TangentSpace I x) (TangentSpace I.tangent (TotalSpace.mk' E x (X x)))
-/
#guard_msgs in
#check mfderiv% (T% X) x
variable {dXm : TangentSpace I x →L[𝕜] TangentSpace (I.prod 𝓘(𝕜, E)) (TotalSpace.mk' E x (X x))}
-/--
-info: HasMFDerivAt I (I.prod (modelWithCornersSelf 𝕜 E)) (fun m ↦ TotalSpace.mk' E m (X m)) x dXm : Prop
--/
+/-- info: HasMFDerivAt I I.tangent (fun m ↦ TotalSpace.mk' E m (X m)) x dXm : Prop -/
#guard_msgs in
#check HasMFDerivAt% (T% X) x dXm
-/--
-info: HasMFDerivWithinAt I (I.prod (modelWithCornersSelf 𝕜 E)) (fun m ↦ TotalSpace.mk' E m (X m)) t x dXm : Prop
--/
+/-- info: HasMFDerivWithinAt I I.tangent (fun m ↦ TotalSpace.mk' E m (X m)) t x dXm : Prop -/
#guard_msgs in
variable {t : Set M} in
#check HasMFDerivAt[t] (T% X) x dXm
diff --git a/MathlibTest/DifferentialGeometry/Notation/Delaborators.lean b/MathlibTest/DifferentialGeometry/Notation/Delaborators.lean
index f943715dd466e2..be21a824b56d34 100644
--- a/MathlibTest/DifferentialGeometry/Notation/Delaborators.lean
+++ b/MathlibTest/DifferentialGeometry/Notation/Delaborators.lean
@@ -70,7 +70,7 @@ variable
#guard_msgs in
#check mfderiv% f x
-/-- info: mfderiv% (T% v) x : TangentSpace I x →L[ℝ] TangentSpace (I.prod 𝓘(ℝ, E)) ⟨x, v x⟩ -/
+/-- info: mfderiv% (T% v) x : TangentSpace I x →L[ℝ] TangentSpace I.tangent ⟨x, v x⟩ -/
#guard_msgs in
#check mfderiv% (T% v) x
From af5bc61fbfcda97e746a8fbd5c3097ff4442034e Mon Sep 17 00:00:00 2001
From: Yizheng Zhu
Date: Mon, 22 Jun 2026 09:12:55 +0000
Subject: [PATCH 0237/1300] chore(ModelTheory): fix hypo of `realize_liftAt`
(#39004)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
changes the hypo of `realize_liftAt` from the incorrect `(hmn : m + n' ≤ n + 1)` to the correct `(hmn : m ≤ n)`
---
Mathlib/ModelTheory/Semantics.lean | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Mathlib/ModelTheory/Semantics.lean b/Mathlib/ModelTheory/Semantics.lean
index dfcdfdc43b2eed..e10d87952cd7e7 100644
--- a/Mathlib/ModelTheory/Semantics.lean
+++ b/Mathlib/ModelTheory/Semantics.lean
@@ -391,7 +391,7 @@ theorem realize_relabel {m n : ℕ} {φ : L.BoundedFormula α n} {g : α → β
apply realize_mapTermRel_add_castLe <;> simp
theorem realize_liftAt {n n' m : ℕ} {φ : L.BoundedFormula α n} {v : α → M} {xs : Fin (n + n') → M}
- (hmn : m + n' ≤ n + 1) :
+ (hmn : m ≤ n) :
(φ.liftAt n' m).Realize v xs ↔
φ.Realize v (xs ∘ fun i => if ↑i < m then Fin.castAdd n' i else Fin.addNat i n') := by
rw [liftAt]
@@ -402,7 +402,7 @@ theorem realize_liftAt {n n' m : ℕ} {φ : L.BoundedFormula α n} {v : α → M
| imp _ _ ih1 ih2 => simp only [mapTermRel, Realize, ih1 hmn, ih2 hmn]
| @all k _ ih3 =>
have h : k + 1 + n' = k + n' + 1 := by rw [add_assoc, add_comm 1 n', ← add_assoc]
- simp only [mapTermRel, Realize, realize_castLE_of_eq h, ih3 (hmn.trans k.succ.le_succ)]
+ simp only [mapTermRel, Realize, realize_castLE_of_eq h, ih3 (hmn.trans k.le_succ)]
refine forall_congr' fun x => iff_eq_eq.mpr (congr rfl (funext (Fin.lastCases ?_ fun i => ?_)))
· simp only [Function.comp_apply, val_last, snoc_last]
refine (congr rfl (Fin.ext ?_)).trans (snoc_last _ _)
From 22616d001e1813f1fff7c6f07ec73d0a94d2ac92 Mon Sep 17 00:00:00 2001
From: Floris van Doorn
Date: Mon, 22 Jun 2026 09:25:55 +0000
Subject: [PATCH 0238/1300] feat: add specialized grind sets (#39370)
This adds two grind attributes for specialized tactics. See module doc for more information and motivation. This PR does not tag any lemmas yet.
[Zulip thread](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/small.20specialized.20grind.20sets/with/595132109)
---
Mathlib.lean | 1 +
Mathlib/Tactic.lean | 1 +
Mathlib/Tactic/Common.lean | 1 +
Mathlib/Tactic/GrindAttrs.lean | 110 +++++++++++++++++++++++++++++++++
4 files changed, 113 insertions(+)
create mode 100644 Mathlib/Tactic/GrindAttrs.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 80bad97308d364..e5d08925dd03bc 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -7289,6 +7289,7 @@ public import Mathlib.Tactic.GRewrite
public import Mathlib.Tactic.GRewrite.Core
public import Mathlib.Tactic.GRewrite.Elab
public import Mathlib.Tactic.Generalize
+public import Mathlib.Tactic.GrindAttrs
public import Mathlib.Tactic.Group
public import Mathlib.Tactic.GuardGoalNums
public import Mathlib.Tactic.GuardHypNums
diff --git a/Mathlib/Tactic.lean b/Mathlib/Tactic.lean
index 50686dca468d2e..02d86792ef5678 100644
--- a/Mathlib/Tactic.lean
+++ b/Mathlib/Tactic.lean
@@ -140,6 +140,7 @@ public import Mathlib.Tactic.GRewrite
public import Mathlib.Tactic.GRewrite.Core
public import Mathlib.Tactic.GRewrite.Elab
public import Mathlib.Tactic.Generalize
+public import Mathlib.Tactic.GrindAttrs
public import Mathlib.Tactic.Group
public import Mathlib.Tactic.GuardGoalNums
public import Mathlib.Tactic.GuardHypNums
diff --git a/Mathlib/Tactic/Common.lean b/Mathlib/Tactic/Common.lean
index 6eb2bcff5c13ad..e35e792b618078 100644
--- a/Mathlib/Tactic/Common.lean
+++ b/Mathlib/Tactic/Common.lean
@@ -63,6 +63,7 @@ public import Mathlib.Tactic.Find
public import Mathlib.Tactic.FunProp
public import Mathlib.Tactic.GCongr
public import Mathlib.Tactic.GRewrite
+public import Mathlib.Tactic.GrindAttrs
public import Mathlib.Tactic.GuardGoalNums
public import Mathlib.Tactic.GuardHypNums
public import Mathlib.Tactic.HigherOrder
diff --git a/Mathlib/Tactic/GrindAttrs.lean b/Mathlib/Tactic/GrindAttrs.lean
new file mode 100644
index 00000000000000..f36fea44632151
--- /dev/null
+++ b/Mathlib/Tactic/GrindAttrs.lean
@@ -0,0 +1,110 @@
+/-
+Copyright (c) 2026 Floris van Doorn. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Floris van Doorn
+-/
+
+module
+
+public import Lean.Meta.Tactic.Grind.RegisterCommand
+public import Mathlib.Init
+
+/-!
+# Custom grind-sets
+
+In this file we declare custom grind attributes and tactics that call grind using only these grind
+attributes. These grind sets are helpful because they can contain a lot of specialized ways to
+prove a particular problem.
+
+Currently, this implements the `compactness` and `closedness` grind attribute and tactic.
+
+## Usage Notes
+
+These tactics can be useful for various purposes:
+* directly as a tactic: `by compactness`
+* as auto-params for lemmas: `(h : IsCompact K := by compactness)`
+* as discharger for other tactics, e.g. `fun_prop (disch := compactness)`
+* You can also use the grind sets directly: `grind only [compactness, closedness]`.
+ This is especially useful if you want to combine multiple grind sets.
+
+## Implementation Notes
+
+We define these grind sets so that we can aggressively tag lemmas in one particular topic as
+grind lemmas for a particular grind set. For the default grind set we should be a lot
+more careful with tagging lemmas, to avoid slowing down `grind`, but since these specialized grind
+attributes don't have any tagged lemmas outside its specialized domain, it should still be
+performant.
+
+These tactics will not use the full power of grind, and could disable some of the grind engines if
+these would slow down these tactics. We could have alternatively used a tactic similar to
+`apply_rules` here, but we think that the efficient implementation of `grind` is helpful even for
+these simpler tactics. For example, we can safely tag both the following lemmas, and `grind` will
+add both pairs of hypotheses to the whiteboard without having to backtrack.
+```
+IsCompact.inter_left : IsClosed s → IsCompact t → IsCompact (s ∩ t)
+IsCompact.inter_right : IsCompact s → IsClosed t → IsCompact (s ∩ t)
+```
+
+We will tag transition theorems, e.g. `Set.Finite.isCompact : Finite s → IsCompact s` should be
+tagged `@[compactness .]`, even if `compactness` won't contain lemmas about finite sets.
+The advantages of this are that we can use local finiteness hypotheses, and this will ensure that
+the different grind sets will interact well with each other. For the same reason we tag lemmas
+that involve other properties, e.g. `IsCompact.inter_left`
+
+## To do
+
+* Implement other grind sets, e.g. `boundedness`, `countability`, `connectedness`, ...
+
+-/
+
+open Lean Parser Tactic
+
+/-- A hash set of the grind attributes in Mathlib.
+
+When adding a new grind attribute, manually add it to this hash set as well. -/
+def Mathlib.grindAttrs : Std.HashSet Name :=
+ {`compactness, `closedness}
+
+/-- The `compactness` attribute is a custom grind-set specialized to prove that sets are compact.
+It is called by the `compactness` tactic. -/
+register_grind_attr compactness
+
+/--
+`compactness` is a simple tactic that tries various lemmas to prove that a set is compact.
+It is implemented using `grind`, and has the same configuration options as `grind`.
+
+Use `grind only [compactness, closedness]` instead if you want to prove that the closure of sets are
+compact.
+
+It also exists as a grind attribute, and can be combined with other grind attributes using
+`grind only [compactness, ...]`.
+-/
+macro (name := compactnessTac) "compactness" config:optConfig : tactic =>
+ -- note: directly giving `compactness` as argument in the syntax quotation below is treated
+ -- as an unknown identifier by the hygiene system.
+ `(tactic|grind $config only [$(mkIdent `compactness):term])
+
+@[inherit_doc compactnessTac]
+macro "compactness?" config:optConfig : tactic =>
+ `(tactic|grind? $config only [$(mkIdent `compactness):term])
+
+/-- The `closedness` attribute is a custom grind-set specialized to prove that sets are closed.
+It is called by the `closedness` tactic. -/
+register_grind_attr closedness
+
+/--
+`closedness` is a simple tactic that tries various lemmas to prove that a set is closed,
+and reasoning about the closure of sets.
+It is implemented using `grind`, and has the same configuration options as `grind`.
+
+It also exists as a grind attribute, and can be combined with other grind attributes using
+`grind only [closedness, ...]`.
+-/
+macro (name := closednessTac) "closedness" config:optConfig : tactic =>
+ -- note: directly giving `closedness` as argument in the syntax quotation below is treated
+ -- as an unknown identifier by the hygiene system.
+ `(tactic|grind $config only [$(mkIdent `closedness):term])
+
+@[inherit_doc closednessTac]
+macro "closedness?" config:optConfig : tactic =>
+ `(tactic|grind? $config only [$(mkIdent `closedness):term])
From 2c7836ba02cbff369f358674eff9877f27cbf0ac Mon Sep 17 00:00:00 2001
From: Michael Rothgang <10105016+grunweg@users.noreply.github.com>
Date: Mon, 22 Jun 2026 10:16:19 +0000
Subject: [PATCH 0239/1300] chore(Geometry/Manifold): make some doc-strings
follow the style guide (#39729)
such as, by them beginning with the object they are defining as a subject.
This will yield much better doc-strings for the differential geometry elaborators in #39677.
It also increases conformance with the [documentation style guide](https://github.com/leanprover/lean4/blob/master/doc/style.md).
Inspired by the MI retreat in Lisbon.
---
Mathlib/Geometry/Manifold/ContMDiff/Defs.lean | 31 +++++++++-----
Mathlib/Geometry/Manifold/MFDeriv/Defs.lean | 17 ++++----
.../Manifold/MFDeriv/NormedSpace.lean | 40 +++++++++++++------
3 files changed, 57 insertions(+), 31 deletions(-)
diff --git a/Mathlib/Geometry/Manifold/ContMDiff/Defs.lean b/Mathlib/Geometry/Manifold/ContMDiff/Defs.lean
index e823fee3758e25..c4a9815c985c17 100644
--- a/Mathlib/Geometry/Manifold/ContMDiff/Defs.lean
+++ b/Mathlib/Geometry/Manifold/ContMDiff/Defs.lean
@@ -160,18 +160,22 @@ theorem contDiffWithinAtProp_id (x : H) : ContDiffWithinAtProp I I n id univ x :
· simp only [mfld_simps]
variable (I I') in
-/-- A function is `n` times continuously differentiable within a set at a point in a manifold if
-it is continuous and it is `n` times continuously differentiable in this set around this point, when
-read in the preferred chart at this point.
+/-- `ContMDiffWithinAt I I' n f x` indicates that the function `f : M → M'` between manifolds
+is `n` times continuously differentiable at `x : M` within the set `s`.
+
+`f` is `n` times continuously differentiable within `s` at `x` if it is continuous and it is `n`
+times continuously differentiable in this set around `x`, when read in the preferred chart at `x`.
The parameter `n` belongs to `ℕ∞ω` (accessible in the `ContDiff` scope), i.e. it can be a natural
number, `∞`, or `ω`, where `C^ω` corresponds to analytic functions. -/
def ContMDiffWithinAt (n : ℕ∞ω) (f : M → M') (s : Set M) (x : M) :=
LiftPropWithinAt (ContDiffWithinAtProp I I' n) f s x
variable (I I') in
-/-- A function is `n` times continuously differentiable at a point in a manifold if
-it is continuous and it is `n` times continuously differentiable around this point, when
-read in the preferred chart at this point.
+/-- `ContMDiffAt I I' n f x` indicates that the function `f : M → M'` between manifolds
+is `n` times continuously differentiable at `x : M`.
+
+`f` is `n` times continuously differentiable at `x` if it is continuous and it is `n` times
+continuously differentiable around `x`, when read in the preferred chart at `x`.
The parameter `n` belongs to `ℕ∞ω` (accessible in the `ContDiff` scope), i.e. it can be a natural
number, `∞`, or `ω`, where `C^ω` corresponds to analytic functions. -/
def ContMDiffAt (n : ℕ∞ω) (f : M → M') (x : M) :=
@@ -185,8 +189,11 @@ theorem contMDiffAt_iff {n : ℕ∞ω} {f : M → M'} {x : M} :
liftPropAt_iff.trans <| by rw [ContDiffWithinAtProp, preimage_univ, univ_inter]; rfl
variable (I I') in
-/-- A function is `n` times continuously differentiable in a set of a manifold if it is continuous
-and, for any pair of points, it is `n` times continuously differentiable on this set in the charts
+/-- `ContMDiffOn I I' n f s` indicates that the function `f : M → M'` between manifolds
+is `n` times continuously differentiable in a set `s : Set M`.
+
+`f` is `n` times continuously differentiable on `s` if it is continuous on `s` and,
+for any pair of points, it is `n` times continuously differentiable `s` in the charts
around these points.
The parameter `n` belongs to `ℕ∞ω` (accessible in the `ContDiff` scope), i.e. it can be a natural
number, `∞`, or `ω`, where `C^ω` corresponds to analytic functions. -/
@@ -194,9 +201,11 @@ def ContMDiffOn (n : ℕ∞ω) (f : M → M') (s : Set M) :=
∀ x ∈ s, ContMDiffWithinAt I I' n f s x
variable (I I') in
-/-- A function is `n` times continuously differentiable in a manifold if it is continuous
-and, for any pair of points, it is `n` times continuously differentiable in the charts
-around these points.
+/-- `ContMDiff I I' n f` indicates that the function `f : M → M'` between manifolds
+is `n` times continuously differentiable.
+
+`f` is `n` times continuously differentiable if it is continuous and, for any pair of points,
+it is `n` times continuously differentiable in the charts around these points.
The parameter `n` belongs to `ℕ∞ω` (accessible in the `ContDiff` scope), i.e. it can be a natural
number, `∞`, or `ω`, where `C^ω` corresponds to analytic functions. -/
def ContMDiff (n : ℕ∞ω) (f : M → M') :=
diff --git a/Mathlib/Geometry/Manifold/MFDeriv/Defs.lean b/Mathlib/Geometry/Manifold/MFDeriv/Defs.lean
index f7e08d8e323f6f..886ab2f23b95bb 100644
--- a/Mathlib/Geometry/Manifold/MFDeriv/Defs.lean
+++ b/Mathlib/Geometry/Manifold/MFDeriv/Defs.lean
@@ -316,9 +316,9 @@ def HasMFDerivAt (f : M → M') (x : M) (f' : TangentSpace I x →L[𝕜] Tangen
open Classical in
variable (I I') in
-/-- Let `f` be a function between two manifolds. Then `mfderivWithin I I' f s x` is the
-derivative of `f` at `x` within `s`, as a continuous linear map from the tangent space at `x` to the
-tangent space at `f x`. -/
+/-- `mfderivWithin I I' f s x`, given a function `f` between two manifolds,
+is the derivative of `f` at `x` within `s`,
+as a continuous linear map from the tangent space at `x` to the tangent space at `f x`. -/
def mfderivWithin (f : M → M') (s : Set M) (x : M) : TangentSpace I x →L[𝕜] TangentSpace I' (f x) :=
if MDifferentiableWithinAt I I' f s x then
(fderivWithin 𝕜 (writtenInExtChartAt I I' x f) ((extChartAt I x).symm ⁻¹' s ∩ range I)
@@ -328,21 +328,22 @@ def mfderivWithin (f : M → M') (s : Set M) (x : M) : TangentSpace I x →L[
open Classical in
variable (I I') in
-/-- Let `f` be a function between two manifolds. Then `mfderiv I I' f x` is the derivative of
-`f` at `x`, as a continuous linear map from the tangent space at `x` to the tangent space at
-`f x`. -/
+/-- `mfderiv I I' f x`, given a function `f` between two manifolds, is the derivative of `f` at `x`,
+as a continuous linear map from the tangent space at `x` to the tangent space at `f x`. -/
def mfderiv (f : M → M') (x : M) : TangentSpace I x →L[𝕜] TangentSpace I' (f x) :=
if MDifferentiableAt I I' f x then
(fderivWithin 𝕜 (writtenInExtChartAt I I' x f : E → E') (range I) ((extChartAt I x) x) :)
else 0
variable (I I') in
-/-- The derivative within a set, as a map between the tangent bundles -/
+/-- `tangentMapWithin I I' f s` is the derivative of `f : M → M'` within a set `s`,
+as a map between the tangent bundles `TM` and `TM'`. -/
def tangentMapWithin (f : M → M') (s : Set M) : TangentBundle I M → TangentBundle I' M' := fun p =>
⟨f p.1, (mfderivWithin I I' f s p.1 : TangentSpace I p.1 → TangentSpace I' (f p.1)) p.2⟩
variable (I I') in
-/-- The derivative, as a map between the tangent bundles -/
+/-- `tangentMap I I' f` is the derivative of `f : M → M'` as a map between the tangent bundles
+`TM` and `TM'`. -/
def tangentMap (f : M → M') : TangentBundle I M → TangentBundle I' M' := fun p =>
⟨f p.1, (mfderiv I I' f p.1 : TangentSpace I p.1 → TangentSpace I' (f p.1)) p.2⟩
diff --git a/Mathlib/Geometry/Manifold/MFDeriv/NormedSpace.lean b/Mathlib/Geometry/Manifold/MFDeriv/NormedSpace.lean
index 1329b9ab42ef3b..f41ccc404fbbf6 100644
--- a/Mathlib/Geometry/Manifold/MFDeriv/NormedSpace.lean
+++ b/Mathlib/Geometry/Manifold/MFDeriv/NormedSpace.lean
@@ -9,22 +9,34 @@ public import Mathlib.Geometry.Manifold.Algebra.SMul
public import Mathlib.Geometry.Manifold.ContMDiff.NormedSpace
public import Mathlib.Geometry.Manifold.MFDeriv.SpecificFunctions
-/-! ## Equivalence of manifold differentiability with the basic definition for functions between
+/-! # Equivalence of manifold differentiability with the basic definition for functions between
vector spaces
The API in this file is mostly copied from `Mathlib/Geometry/Manifold/ContMDiff/NormedSpace.lean`,
providing the same statements for higher smoothness. In this file, we do the same for
differentiability.
-In addition to the above, this file provides
-* results about the differentiability of scalar multiplication (`mfderiv_smul` and friends),
-* `mvfderiv`: the exterior derivative of a vector-valued function, as a section of the
- cotangent bundle; adds notation `d% f` for `mvfderiv I f` via a custom elaborator scoped to the
- `Manifold` namespace, with a corresponding delaborator, and
- adds basic lemmas about `mvfderiv` (such as addition, subtraction, multiplication and constants).
-* `mvfderivWithin` with notation `d[s]f` for `mvfderivWithin I f s` in the `Manifold` namespace:
+## Main definitions
+
+In addition to the above, this file provides two important definitions.
+* `mvfderiv I f x` is the manifold Fréchet derivative at `x : M` of a vector-valued function
+ `f : M → V`, but taking values in the target normed space `V` instead of `TangentSpace% (f x) V`.
+ Mathematically, this uses the global trivialization `T V ≅ V × V`, yielding an identification
+ `T_v V ≅ V` for each `v : V`. In Lean, we post-compose the differential `mfderiv% f x` with
+ `NormedSpace.fromTangentSpace`. If `V` is a field, this coincides with the exterior derivative
+ of `f` as a section of the cotangent bundle.
+ There is notation `d% f` for `mvfderiv I f` via a custom elaborator scoped to the
+ `Manifold` namespace, with a corresponding delaborator,
+* `mvfderivWithin` with notation `d[s] f` for `mvfderivWithin I f s` in the `Manifold` namespace:
the analogous concept within a set, with analogous API lemmas
+## Main results
+
+This file contains
+* results about the differentiability of scalar multiplication (`mfderiv_smul` and friends),
+* basic lemmas about `mvfderiv` (such as addition, subtraction, multiplication and constants),
+* analogous lemmas about `mvfderivWithin`.
+
-/
public section
@@ -410,8 +422,10 @@ end smul
/-! ### Exterior derivative of a vector-valued function -/
variable (I) in
-/-- `mvfderiv I J f x` is the exterior derivative of a vector-valued function `g` on `M`,
-as a section of the cotangent bundle.
+/-- `mvfderivWithin I J f s x` is the `mfderiv` of a vector-valued function `f` on `M` at `x`
+within the set `s`, but taking values in the target normed space directly.
+The difference to `mfderivWithin` is explained in the module-docstring for
+`Mathlib/Geometry/Manifold/MFDeriv/NormedSpace.lean`.
Future: this could be generalised to functions into additive torsors over abelian Lie groups.
-/
@@ -421,8 +435,10 @@ noncomputable def mvfderivWithin (g : M → F) (s : Set M) :
fun x ↦ (NormedSpace.fromTangentSpace <| g x).toContinuousLinearMap ∘L (mfderiv[s] g x)
variable (I) in
-/-- The exterior derivative of a vector-valued function on `M`,
-as a section of the cotangent bundle.
+/-- `mvfderiv I J f x` is the `mfderiv` of a vector-valued function `f` on `M` at `x`,
+but taking values in the target normed space directly.
+The difference to `mfderiv` is explained in the module-docstring for
+`Mathlib/Geometry/Manifold/MFDeriv/NormedSpace.lean`.
Future: this could be generalised to functions into additive torsors over abelian Lie groups.
-/
From afa3779b5e1174c6e9203946595e42e9361db91d Mon Sep 17 00:00:00 2001
From: Michael Rothgang <10105016+grunweg@users.noreply.github.com>
Date: Mon, 22 Jun 2026 10:16:22 +0000
Subject: [PATCH 0240/1300] fix: more complete model finding for a given
manifold in context (#40075)
When searching for a model with corners on a manifold, also try finding a model which is not just a local assumption, but the trivial model on a normed space or a non-trivially normed field.
Minimised from an elaborator failure in Kevin Buzzard's Jacobian challenge.
---
Mathlib/Geometry/Manifold/Notation.lean | 27 +++++++++++---
.../DifferentialGeometry/Notation/Basic.lean | 37 +++++++++++++++++++
2 files changed, 59 insertions(+), 5 deletions(-)
diff --git a/Mathlib/Geometry/Manifold/Notation.lean b/Mathlib/Geometry/Manifold/Notation.lean
index 5de3d94fd0fe6e..0ed3b4eb3b185e 100644
--- a/Mathlib/Geometry/Manifold/Notation.lean
+++ b/Mathlib/Geometry/Manifold/Notation.lean
@@ -476,8 +476,8 @@ where
| ChartedSpace H _ M _ =>
if ← withReducible (pureIsDefEq M e) then
trace[Elab.DiffGeo.MDiff] "`{e}` is a charted space over `{H}` via `{inst}`"
- return some H else
- if ← withReducible (pureIsDefEq H e) then
+ return some H
+ else if ← withReducible (pureIsDefEq H e) then
trace[Elab.DiffGeo.MDiff] "`{e}` is the charted space of `{M}` via `{inst}`"
return some H else return none
| _ => return none
@@ -488,7 +488,24 @@ where
| ModelWithCorners _ _ _ _ _ H' _ => do
if ← withReducible (pureIsDefEq H' H) then return some fvar else return none
| _ => return none
- | throwError "Couldn't find a `ModelWithCorners` with model space `{H}` in the local context."
+ | trace[Elab.DiffGeo.MDiff]
+ "Couldn't find a `ModelWithCorners` with model space `{H}` in the local context."
+ -- Try a normed space, and a normed field as last alternatives.
+ let a ← findSomeLocalInstanceOf? ``NormedSpace fun inst type ↦ do
+ match_expr type with
+ | NormedSpace K E _ _ =>
+ if ← withReducible (pureIsDefEq E H) then return some (inst, K)
+ else return none
+ | _ => return none
+ if let some (inst, K) := a then
+ trace[Elab.DiffGeo.MDiff] "`{H}` is a normed space over the field `{K}`"
+ return ← mkAppOptM ``modelWithCornersSelf #[K, none, H, none, inst]
+ trace[Elab.DiffGeo.MDiff] "Couldn't find a normed space structure on {H}` either: \
+ assuming it is a non-trivially normed field"
+ -- Return the trivial model with corners: this will work if `H` is a normed field.
+ let eT : Term ← Term.exprToSyntax H
+ let iTerm : Term ← ``(𝓘($eT))
+ Term.elabTerm iTerm none
return m
/-- Attempt to find a model with corners on a space of continuous linear maps -/
-- Note that (continuous) linear equivalences are not an abelian group, so are not a model with
@@ -729,8 +746,8 @@ partial def findModel (e : Expr) (baseInfo : Option (Expr × Expr) := none) : Te
let hint : MessageData := if e.hasExprMVar then
.hint' "the expected type contains metavariables, \
maybe you need to provide an implicit argument"
- else if tracing then m!"" else
- .hint' "failures to find a model with corners can be debugged with the \
+ else if tracing then m!""
+ else .hint' "failures to find a model with corners can be debugged with the \
command `set_option trace.Elab.DiffGeo.MDiff true`."
throwError "Could not find a model with corners for `{e}`.{hint}"
where
diff --git a/MathlibTest/DifferentialGeometry/Notation/Basic.lean b/MathlibTest/DifferentialGeometry/Notation/Basic.lean
index 36f1634c00a05f..4461fab938c58c 100644
--- a/MathlibTest/DifferentialGeometry/Notation/Basic.lean
+++ b/MathlibTest/DifferentialGeometry/Notation/Basic.lean
@@ -1035,6 +1035,43 @@ open ContDiff in -- for the ∞ notation
end
+/-! Inferring a model with corners on a normed space, for an `IsManifold` hypothesis -/
+section
+
+open scoped ContDiff
+
+variable {X Y : Type*} [TopologicalSpace X] [ChartedSpace ℝ X] [IsManifold 𝓘(ℝ) ω X]
+ [TopologicalSpace Y] [ChartedSpace ℝ Y] [IsManifold 𝓘(ℝ) ω Y] {f : X → Y}
+
+/--
+info: ContMDiff (modelWithCornersSelf Real Real) (modelWithCornersSelf Real Real) Top.top f : Prop
+-/
+#guard_msgs in
+#check CMDiff ω f
+
+variable {f : X → ℝ} in /--
+info: MDifferentiable (modelWithCornersSelf Real Real) (modelWithCornersSelf Real Real) f : Prop
+-/
+#guard_msgs in #check MDiff f
+
+variable {X : Type*} [TopologicalSpace X] [ChartedSpace F X] [IsManifold 𝓘(𝕜, F) ω X] {f : X → 𝕜} in
+/-- info: MDifferentiable (modelWithCornersSelf 𝕜 F) (modelWithCornersSelf 𝕜 𝕜) f : Prop -/
+#guard_msgs in
+#check MDiff f
+
+-- This test is expected to fail: it passing would amount to guessing a model with corners on
+-- a product of two normed spaces (which is ambiguous).
+variable {X : Type*} [TopologicalSpace X] [ChartedSpace (F × F) X] [IsManifold 𝓘(𝕜, F × F) ω X] {f : X → 𝕜} in
+/--
+error: Could not find a model with corners for `X`.
+
+Hint: failures to find a model with corners can be debugged with the command `set_option trace.Elab.DiffGeo.MDiff true`.
+-/
+#guard_msgs in
+#check MDiff f
+
+end
+
/-! Tests for the elaborators for `tangentMap(Within)` and `TangentSpace` -/
section
From 3ee3f85eb48438ad4f8ce98b79a198f6573d5f70 Mon Sep 17 00:00:00 2001
From: Leo Diedering <129694072+ldiedering@users.noreply.github.com>
Date: Mon, 22 Jun 2026 10:16:24 +0000
Subject: [PATCH 0241/1300]
refactor(MeasureTheory/Measure/Typeclasses/NoAtoms): rename `NoAtoms` to
`NullSingletonClass` (#40809)
Rename the class `NoAtoms` to `NullSingletonClass` as discussed [here](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/MeasureTheory.2ENoAtoms/with/574638121) and change all derivative names.
---
Counterexamples/Phillips.lean | 2 +-
Mathlib.lean | 2 +-
Mathlib/Analysis/Convolution.lean | 6 +--
.../Constructions/BorelSpace/Real.lean | 4 +-
Mathlib/MeasureTheory/Constructions/Pi.lean | 32 ++++++++----
.../Constructions/UnitInterval.lean | 4 +-
Mathlib/MeasureTheory/Group/Measure.lean | 15 +++---
.../MeasureTheory/Integral/Bochner/Set.lean | 4 +-
.../Integral/DominatedConvergence.lean | 18 +++----
.../MeasureTheory/Integral/IntegrableOn.lean | 4 +-
.../Integral/IntegralEqImproper.lean | 2 +-
.../Integral/IntervalAverage.lean | 16 +++---
.../Integral/IntervalIntegral/Basic.lean | 28 +++++-----
Mathlib/MeasureTheory/Integral/Layercake.lean | 2 +-
Mathlib/MeasureTheory/Measure/Dirac.lean | 5 +-
.../Measure/Haar/NormedSpace.lean | 8 +--
Mathlib/MeasureTheory/Measure/Hausdorff.lean | 17 ++++---
.../MeasureTheory/Measure/Lebesgue/Basic.lean | 13 +++--
Mathlib/MeasureTheory/Measure/OpenPos.lean | 4 +-
Mathlib/MeasureTheory/Measure/Prod.lean | 8 +--
.../{NoAtoms.lean => NullSingletonClass.lean} | 51 +++++++++++--------
.../MeasureTheory/Measure/WithDensity.lean | 6 ++-
Mathlib/MeasureTheory/Topology.lean | 4 +-
.../NumberField/CanonicalEmbedding/Basic.lean | 13 ++---
.../Distributions/Gaussian/Fernique.lean | 11 ++--
.../Distributions/Gaussian/Real.lean | 6 ++-
scripts/nolints_prime_decls.txt | 2 +-
27 files changed, 167 insertions(+), 120 deletions(-)
rename Mathlib/MeasureTheory/Measure/Typeclasses/{NoAtoms.lean => NullSingletonClass.lean} (76%)
diff --git a/Counterexamples/Phillips.lean b/Counterexamples/Phillips.lean
index 1fc73e0eda9341..384d1931679214 100644
--- a/Counterexamples/Phillips.lean
+++ b/Counterexamples/Phillips.lean
@@ -432,7 +432,7 @@ theorem toFunctions_toMeasure [MeasurableSpace α] (μ : Measure α) [IsFiniteMe
set_option backward.isDefEq.respectTransparency false in
theorem toFunctions_toMeasure_continuousPart [MeasurableSpace α] [MeasurableSingletonClass α]
- (μ : Measure α) [IsFiniteMeasure μ] [NoAtoms μ] (s : Set α) (hs : MeasurableSet s) :
+ (μ : Measure α) [IsFiniteMeasure μ] [NullSingletonClass μ] (s : Set α) (hs : MeasurableSet s) :
μ.extensionToBoundedFunctions.toBoundedAdditiveMeasure.continuousPart s = μ.real s := by
let f := μ.extensionToBoundedFunctions.toBoundedAdditiveMeasure
change f (univ \ f.discreteSupport ∩ s) = μ.real s
diff --git a/Mathlib.lean b/Mathlib.lean
index e5d08925dd03bc..7a3a3e97044a3a 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -5590,7 +5590,7 @@ public import Mathlib.MeasureTheory.Measure.TightNormed
public import Mathlib.MeasureTheory.Measure.Tilted
public import Mathlib.MeasureTheory.Measure.Trim
public import Mathlib.MeasureTheory.Measure.Typeclasses.Finite
-public import Mathlib.MeasureTheory.Measure.Typeclasses.NoAtoms
+public import Mathlib.MeasureTheory.Measure.Typeclasses.NullSingletonClass
public import Mathlib.MeasureTheory.Measure.Typeclasses.Probability
public import Mathlib.MeasureTheory.Measure.Typeclasses.SFinite
public import Mathlib.MeasureTheory.Measure.Typeclasses.ZeroOne
diff --git a/Mathlib/Analysis/Convolution.lean b/Mathlib/Analysis/Convolution.lean
index b35fb5ee876b75..6b01e33224dd8d 100644
--- a/Mathlib/Analysis/Convolution.lean
+++ b/Mathlib/Analysis/Convolution.lean
@@ -946,7 +946,7 @@ noncomputable def posConvolution (f : ℝ → E) (g : ℝ → E') (L : E →L[
indicator (Ioi (0 : ℝ)) fun x => ∫ t in 0..x, L (f t) (g (x - t)) ∂ν
theorem posConvolution_eq_convolution_indicator (f : ℝ → E) (g : ℝ → E') (L : E →L[ℝ] E' →L[ℝ] F)
- (ν : Measure ℝ := by volume_tac) [NoAtoms ν] :
+ (ν : Measure ℝ := by volume_tac) [NullSingletonClass ν] :
posConvolution f g L ν = convolution (indicator (Ioi 0) f) (indicator (Ioi 0) g) L ν := by
ext1 x
rw [convolution, posConvolution, indicator]
@@ -974,7 +974,7 @@ theorem posConvolution_eq_convolution_indicator (f : ℝ → E) (g : ℝ → E')
· rw [indicator_of_notMem (mem_Ioi.not.mpr ht), map_zero, zero_apply]
theorem integrable_posConvolution {f : ℝ → E} {g : ℝ → E'} {μ ν : Measure ℝ} [SFinite μ]
- [SFinite ν] [IsAddRightInvariant μ] [NoAtoms ν] (hf : IntegrableOn f (Ioi 0) ν)
+ [SFinite ν] [IsAddRightInvariant μ] [NullSingletonClass ν] (hf : IntegrableOn f (Ioi 0) ν)
(hg : IntegrableOn g (Ioi 0) μ) (L : E →L[ℝ] E' →L[ℝ] F) :
Integrable (posConvolution f g L ν) μ := by
rw [← integrable_indicator_iff (measurableSet_Ioi : MeasurableSet (Ioi (0 : ℝ)))] at hf hg
@@ -985,7 +985,7 @@ theorem integrable_posConvolution {f : ℝ → E} {g : ℝ → E'} {μ ν : Meas
of their integrals over this set. (Compare `integral_convolution` for the two-sided convolution.) -/
theorem integral_posConvolution [CompleteSpace E] [CompleteSpace E'] [CompleteSpace F]
{μ ν : Measure ℝ}
- [SFinite μ] [SFinite ν] [IsAddRightInvariant μ] [NoAtoms ν] {f : ℝ → E} {g : ℝ → E'}
+ [SFinite μ] [SFinite ν] [IsAddRightInvariant μ] [NullSingletonClass ν] {f : ℝ → E} {g : ℝ → E'}
(hf : IntegrableOn f (Ioi 0) ν) (hg : IntegrableOn g (Ioi 0) μ) (L : E →L[ℝ] E' →L[ℝ] F) :
∫ x : ℝ in Ioi 0, ∫ t : ℝ in 0..x, L (f t) (g (x - t)) ∂ν ∂μ =
L (∫ x : ℝ in Ioi 0, f x ∂ν) (∫ x : ℝ in Ioi 0, g x ∂μ) := by
diff --git a/Mathlib/MeasureTheory/Constructions/BorelSpace/Real.lean b/Mathlib/MeasureTheory/Constructions/BorelSpace/Real.lean
index c47e8ea5d91b12..f33dd9c3dc6795 100644
--- a/Mathlib/MeasureTheory/Constructions/BorelSpace/Real.lean
+++ b/Mathlib/MeasureTheory/Constructions/BorelSpace/Real.lean
@@ -7,7 +7,7 @@ module
public import Mathlib.MeasureTheory.Constructions.BorelSpace.Order
public import Mathlib.MeasureTheory.MeasurableSpace.Prod
-public import Mathlib.MeasureTheory.Measure.Typeclasses.NoAtoms
+public import Mathlib.MeasureTheory.Measure.Typeclasses.NullSingletonClass
public import Mathlib.Topology.Instances.Real.Lemmas
/-!
@@ -573,7 +573,7 @@ lemma tendsto_measure_Icc_nhdsWithin_right (b : ℝ) :
intro s hs
simpa using mem_of_mem_nhds hs
-lemma tendsto_measure_Icc [NoAtoms μ] (b : ℝ) :
+lemma tendsto_measure_Icc [NullSingletonClass μ] (b : ℝ) :
Tendsto (fun δ ↦ μ (Icc (b - δ) (b + δ))) (𝓝 (0 : ℝ)) (𝓝 0) := by
rw [← nhdsLT_sup_nhdsGE, tendsto_sup]
constructor
diff --git a/Mathlib/MeasureTheory/Constructions/Pi.lean b/Mathlib/MeasureTheory/Constructions/Pi.lean
index eb13f8536072d9..a6f81db9f328c4 100644
--- a/Mathlib/MeasureTheory/Constructions/Pi.lean
+++ b/Mathlib/MeasureTheory/Constructions/Pi.lean
@@ -410,11 +410,12 @@ lemma _root_.MeasureTheory.measurePreserving_eval [∀ i, IsProbabilityMeasure (
rw [Measure.pi_map_eval, Finset.prod_eq_one, one_smul]
exact fun _ _ ↦ measure_univ
-theorem pi_hyperplane (i : ι) [NoAtoms (μ i)] (x : α i) :
+theorem pi_hyperplane (i : ι) [NullSingletonClass (μ i)] (x : α i) :
Measure.pi μ { f : ∀ i, α i | f i = x } = 0 :=
show Measure.pi μ (eval i ⁻¹' {x}) = 0 from pi_eval_preimage_null _ (measure_singleton x)
-theorem ae_eval_ne (i : ι) [NoAtoms (μ i)] (x : α i) : ∀ᵐ y : ∀ i, α i ∂Measure.pi μ, y i ≠ x :=
+theorem ae_eval_ne (i : ι) [NullSingletonClass (μ i)] (x : α i) :
+ ∀ᵐ y : ∀ i, α i ∂Measure.pi μ, y i ≠ x :=
compl_mem_ae_iff.2 (pi_hyperplane μ i x)
theorem restrict_pi_pi (s : (i : ι) → Set (α i)) :
@@ -467,7 +468,7 @@ lemma pi_map_piOptionEquivProd {β : Option ι → Type*} [∀ i, MeasurableSpac
section Intervals
-variable [∀ i, PartialOrder (α i)] [∀ i, NoAtoms (μ i)]
+variable [∀ i, PartialOrder (α i)] [∀ i, NullSingletonClass (μ i)]
theorem pi_Iio_ae_eq_pi_Iic {s : Set ι} {f : ∀ i, α i} :
(pi s fun i => Iio (f i)) =ᵐ[Measure.pi μ] pi s fun i => Iic (f i) :=
@@ -515,18 +516,27 @@ theorem univ_pi_Ico_ae_eq_Icc {f g : ∀ i, α i} :
end Intervals
-/-- If one of the measures `μ i` has no atoms, them `Measure.pi µ`
-has no atoms. The instance below assumes that all `μ i` have no atoms. -/
-theorem pi_noAtoms (i : ι) [NoAtoms (μ i)] : NoAtoms (Measure.pi μ) :=
+/-- If one of the measures `μ i` has value zero on singeltons, them `Measure.pi µ`
+has value zero on singletons. The instance below assumes that all `μ i` have value zero on
+singletons. -/
+theorem pi_nullSingletonClass (i : ι) [NullSingletonClass (μ i)] :
+ NullSingletonClass (Measure.pi μ) :=
⟨fun x => flip measure_mono_null (pi_hyperplane μ i (x i)) (singleton_subset_iff.2 rfl)⟩
-instance pi_noAtoms' [h : Nonempty ι] [∀ i, NoAtoms (μ i)] : NoAtoms (Measure.pi μ) :=
- h.elim fun i => pi_noAtoms i
+@[deprecated (since := "2026-06-09")]
+alias pi_noAtoms := pi_nullSingletonClass
+
+instance pi_nullSingletonClass' [h : Nonempty ι] [∀ i, NullSingletonClass (μ i)] :
+ NullSingletonClass (Measure.pi μ) :=
+ h.elim fun i => pi_nullSingletonClass i
+
+@[deprecated (since := "2026-06-09")]
+alias pi_noAtoms' := pi_nullSingletonClass'
instance {α : ι → Type*} [Nonempty ι] [∀ i, MeasureSpace (α i)]
- [∀ i, SigmaFinite (volume : Measure (α i))] [∀ i, NoAtoms (volume : Measure (α i))] :
- NoAtoms (volume : Measure (∀ i, α i)) :=
- pi_noAtoms'
+ [∀ i, SigmaFinite (volume : Measure (α i))] [∀ i, NullSingletonClass (volume : Measure (α i))] :
+ NullSingletonClass (volume : Measure (∀ i, α i)) :=
+ pi_nullSingletonClass'
instance pi.isLocallyFiniteMeasure
[∀ i, TopologicalSpace (α i)] [∀ i, IsLocallyFiniteMeasure (μ i)] :
diff --git a/Mathlib/MeasureTheory/Constructions/UnitInterval.lean b/Mathlib/MeasureTheory/Constructions/UnitInterval.lean
index b84fdf2a05dbae..0b39ca13edfa63 100644
--- a/Mathlib/MeasureTheory/Constructions/UnitInterval.lean
+++ b/Mathlib/MeasureTheory/Constructions/UnitInterval.lean
@@ -11,7 +11,7 @@ public import Mathlib.MeasureTheory.Measure.Haar.Unique
# The canonical measure on the unit interval
This file provides a `MeasureTheory.MeasureSpace` instance on `unitInterval`,
-and shows it is a probability measure with no atoms.
+and shows it is a probability measure with value zero on singletons.
It also contains some basic results on the volume of various interval sets.
-/
@@ -43,7 +43,7 @@ lemma volume_apply {s : Set I} : volume s = volume (Subtype.val '' s) :=
lemma measurePreserving_coe : MeasurePreserving ((↑) : I → ℝ) volume (volume.restrict I) :=
measurePreserving_subtype_coe measurableSet_Icc
-instance : NoAtoms (volume : Measure I) where
+instance : NullSingletonClass (volume : Measure I) where
measure_singleton x := by simp [volume_apply]
@[fun_prop]
diff --git a/Mathlib/MeasureTheory/Group/Measure.lean b/Mathlib/MeasureTheory/Group/Measure.lean
index e38b897cfb8d77..b90e3c15db511f 100644
--- a/Mathlib/MeasureTheory/Group/Measure.lean
+++ b/Mathlib/MeasureTheory/Group/Measure.lean
@@ -918,20 +918,20 @@ instance prod.instIsHaarMeasure {G : Type*} [Group G] [TopologicalSpace G] {_ :
(ν : Measure H) [IsHaarMeasure μ] [IsHaarMeasure ν] [SFinite μ] [SFinite ν]
[MeasurableMul G] [MeasurableMul H] : IsHaarMeasure (μ.prod ν) where
-/-- If the neutral element of a group is not isolated, then a Haar measure on this group has
-no atoms.
+/-- If the neutral element of a group is not isolated, then a Haar measure on this group has value
+zero on singletons.
The additive version of this instance applies in particular to show that an additive Haar
measure on a nontrivial finite-dimensional real vector space has no atom. -/
@[to_additive
/-- If the zero element of an additive group is not isolated, then an additive Haar measure on this
-group has no atoms.
+group has value zero on singletons.
This applies in particular to show that an additive Haar measure on a nontrivial
finite-dimensional real vector space has no atom. -/]
-instance (priority := 100) IsHaarMeasure.noAtoms [IsTopologicalGroup G] [BorelSpace G] [T1Space G]
- [WeaklyLocallyCompactSpace G] [(𝓝[≠] (1 : G)).NeBot] (μ : Measure G) [μ.IsHaarMeasure] :
- NoAtoms μ := by
+instance (priority := 100) IsHaarMeasure.nullSingletonClass [IsTopologicalGroup G] [BorelSpace G]
+ [T1Space G] [WeaklyLocallyCompactSpace G] [(𝓝[≠] (1 : G)).NeBot] (μ : Measure G)
+ [μ.IsHaarMeasure] : NullSingletonClass μ := by
cases eq_or_ne (μ 1) 0 with
| inl h => constructor; simpa
| inr h =>
@@ -940,6 +940,9 @@ instance (priority := 100) IsHaarMeasure.noAtoms [IsTopologicalGroup G] [BorelSp
exact absurd (K_inf.meas_eq_top ⟨_, h, fun x _ ↦ (haar_singleton _ _).ge⟩)
K_compact.measure_lt_top.ne
+@[deprecated (since := "2026-06-09")]
+alias IsHaarMeasure.noAtoms := IsHaarMeasure.nullSingletonClass
+
instance IsAddHaarMeasure.domSMul {G A : Type*} [Group G] [AddCommGroup A] [DistribMulAction G A]
[MeasurableSpace A] [TopologicalSpace A] [BorelSpace A] [IsTopologicalAddGroup A]
[ContinuousConstSMul G A] {μ : Measure A} [μ.IsAddHaarMeasure] (g : Gᵈᵐᵃ) :
diff --git a/Mathlib/MeasureTheory/Integral/Bochner/Set.lean b/Mathlib/MeasureTheory/Integral/Bochner/Set.lean
index 8d5f7a068e9b7f..f226cd4de8fb9d 100644
--- a/Mathlib/MeasureTheory/Integral/Bochner/Set.lean
+++ b/Mathlib/MeasureTheory/Integral/Bochner/Set.lean
@@ -666,7 +666,7 @@ theorem setIntegral_trim {X} {m m0 : MeasurableSpace X} {μ : Measure X} (hm : m
/-! ### Lemmas about adding and removing interval boundaries
The primed lemmas take explicit arguments about the endpoint having zero measure, while the
-unprimed ones use `[NoAtoms μ]`.
+unprimed ones use `[NullSingletonClass μ]`.
-/
section PartialOrder
@@ -701,7 +701,7 @@ theorem integral_Ici_eq_integral_Ioi' (hx : μ {x} = 0) :
∫ t in Ici x, f t ∂μ = ∫ t in Ioi x, f t ∂μ :=
setIntegral_congr_set (Ioi_ae_eq_Ici' hx).symm
-variable [NoAtoms μ]
+variable [NullSingletonClass μ]
theorem integral_Icc_eq_integral_Ioc : ∫ t in Icc x y, f t ∂μ = ∫ t in Ioc x y, f t ∂μ :=
integral_Icc_eq_integral_Ioc' <| measure_singleton x
diff --git a/Mathlib/MeasureTheory/Integral/DominatedConvergence.lean b/Mathlib/MeasureTheory/Integral/DominatedConvergence.lean
index 76231ac56f25e2..0b6c569d990aff 100644
--- a/Mathlib/MeasureTheory/Integral/DominatedConvergence.lean
+++ b/Mathlib/MeasureTheory/Integral/DominatedConvergence.lean
@@ -435,7 +435,7 @@ theorem continuousAt_parametric_primitive_of_dominated [FirstCountableTopology X
rw [nhds_prod_eq]
exact (continuous_abs.tendsto' _ _ abs_zero).comp (this.comp tendsto_snd)
-variable [NoAtoms μ]
+variable [NullSingletonClass μ]
theorem continuousOn_primitive (h_int : IntegrableOn f (Icc a b) μ) :
ContinuousOn (fun x => ∫ t in Ioc a x, f t ∂μ) (Icc a b) := by
@@ -636,8 +636,8 @@ theorem continuousWithinAt_Ici_primitive_Ioi {a₀ : ℝ} (hf : IntegrableOn f (
· filter_upwards [mem_nhdsWithin_of_mem_nhds (Iio_mem_nhds hx)] with a ha using by grind
· filter_upwards [self_mem_nhdsWithin] with a ha using by grind
-theorem continuousOn_Ici_primitive_Ioi [NoAtoms μ] {a₀ : ℝ} (hf : IntegrableOn f (Ioi a₀) μ) :
- ContinuousOn (fun b ↦ ∫ x in Ioi b, f x ∂μ) (Ici a₀) := by
+theorem continuousOn_Ici_primitive_Ioi [NullSingletonClass μ] {a₀ : ℝ}
+ (hf : IntegrableOn f (Ioi a₀) μ) : ContinuousOn (fun b ↦ ∫ x in Ioi b, f x ∂μ) (Ici a₀) := by
intro a (ha : a₀ ≤ a)
rw [continuousWithinAt_iff_continuous_left_right]
constructor
@@ -671,8 +671,8 @@ theorem continuousWithinAt_Iic_primitive_Iio {a₀ : ℝ} (hf : IntegrableOn f (
· filter_upwards [mem_nhdsWithin_of_mem_nhds (Ioi_mem_nhds hx)] with a ha using by grind
· filter_upwards [self_mem_nhdsWithin] with a ha using by grind
-theorem continuousOn_Iic_primitive_Iio [NoAtoms μ] {a₀ : ℝ} (hf : IntegrableOn f (Iio a₀) μ) :
- ContinuousOn (fun b ↦ ∫ x in Iio b, f x ∂μ) (Iic a₀) := by
+theorem continuousOn_Iic_primitive_Iio [NullSingletonClass μ] {a₀ : ℝ}
+ (hf : IntegrableOn f (Iio a₀) μ) : ContinuousOn (fun b ↦ ∫ x in Iio b, f x ∂μ) (Iic a₀) := by
intro a (ha : a ≤ a₀)
rw [continuousWithinAt_iff_continuous_left_right]
constructor
@@ -688,13 +688,13 @@ theorem continuousOn_Iic_primitive_Iio [NoAtoms μ] {a₀ : ℝ} (hf : Integrabl
continuousWithinAt_primitive (measure_singleton a) (by simpa [ha])
exact (continuousWithinAt_const.add h_cwa).congr h_split (h_split a (left_mem_Icc.2 ha))
-theorem continuousOn_Ici_primitive_Ici [NoAtoms μ] {a₀ : ℝ} (hf : IntegrableOn f (Ici a₀) μ) :
- ContinuousOn (fun b ↦ ∫ x in Ici b, f x ∂μ) (Ici a₀) := by
+theorem continuousOn_Ici_primitive_Ici [NullSingletonClass μ] {a₀ : ℝ}
+ (hf : IntegrableOn f (Ici a₀) μ) : ContinuousOn (fun b ↦ ∫ x in Ici b, f x ∂μ) (Ici a₀) := by
simp_rw [integral_Ici_eq_integral_Ioi]
exact (hf.mono_set Ioi_subset_Ici_self).continuousOn_Ici_primitive_Ioi
-theorem continuousOn_Iic_primitive_Iic [NoAtoms μ] {a₀ : ℝ} (hf : IntegrableOn f (Iic a₀) μ) :
- ContinuousOn (fun b ↦ ∫ x in Iic b, f x ∂μ) (Iic a₀) := by
+theorem continuousOn_Iic_primitive_Iic [NullSingletonClass μ] {a₀ : ℝ}
+ (hf : IntegrableOn f (Iic a₀) μ) : ContinuousOn (fun b ↦ ∫ x in Iic b, f x ∂μ) (Iic a₀) := by
simp_rw [integral_Iic_eq_integral_Iio]
exact (hf.mono_set Iio_subset_Iic_self).continuousOn_Iic_primitive_Iio
diff --git a/Mathlib/MeasureTheory/Integral/IntegrableOn.lean b/Mathlib/MeasureTheory/Integral/IntegrableOn.lean
index 2c5825b528ca0f..f487a4f34cfd7c 100644
--- a/Mathlib/MeasureTheory/Integral/IntegrableOn.lean
+++ b/Mathlib/MeasureTheory/Integral/IntegrableOn.lean
@@ -850,7 +850,7 @@ theorem ContinuousOn.stronglyMeasurableAtFilter_nhdsWithin {α β : Type*} [Meas
/-! ### Lemmas about adding and removing interval boundaries
The primed lemmas take explicit arguments about the measure being finite at the endpoint, while
-the unprimed ones use `[NoAtoms μ]`.
+the unprimed ones use `[NullSingletonClass μ]`.
-/
@@ -908,7 +908,7 @@ theorem integrableOn_Iic_iff_integrableOn_Iio'
IntegrableOn f (Iic b) μ ↔ IntegrableOn f (Iio b) μ := by
rw [← Iio_union_right, integrableOn_union, eq_true (integrableOn_singleton hb'), and_true]
-variable [NoAtoms μ]
+variable [NullSingletonClass μ]
theorem integrableOn_Icc_iff_integrableOn_Ioc (ha : ‖f a‖ₑ ≠ ∞ := by finiteness) :
IntegrableOn f (Icc a b) μ ↔ IntegrableOn f (Ioc a b) μ :=
diff --git a/Mathlib/MeasureTheory/Integral/IntegralEqImproper.lean b/Mathlib/MeasureTheory/Integral/IntegralEqImproper.lean
index a2553d7d8ab4cb..20e07b068a4085 100644
--- a/Mathlib/MeasureTheory/Integral/IntegralEqImproper.lean
+++ b/Mathlib/MeasureTheory/Integral/IntegralEqImproper.lean
@@ -247,7 +247,7 @@ include ha hb in
theorem aecover_Ioo_of_Ioc : AECover (μ.restrict <| Ioo A B) l fun i => Ioc (a i) (b i) :=
(aecover_Ioo_of_Ioo ha hb).superset (fun _ ↦ Ioo_subset_Ioc_self) fun _ ↦ measurableSet_Ioc
-variable [NoAtoms μ]
+variable [NullSingletonClass μ]
theorem aecover_Ioc_of_Icc (ha : Tendsto a l (𝓝 A)) (hb : Tendsto b l (𝓝 B)) :
AECover (μ.restrict <| Ioc A B) l fun i => Icc (a i) (b i) :=
diff --git a/Mathlib/MeasureTheory/Integral/IntervalAverage.lean b/Mathlib/MeasureTheory/Integral/IntervalAverage.lean
index 1e35b907617a8d..455b155d94887c 100644
--- a/Mathlib/MeasureTheory/Integral/IntervalAverage.lean
+++ b/Mathlib/MeasureTheory/Integral/IntervalAverage.lean
@@ -19,7 +19,7 @@ formulas for this average:
* `interval_average_eq_div`: `⨍ x in a..b, f x = (∫ x in a..b, f x) / (b - a)`;
* `exists_eq_interval_average_of_measure`:
`∃ c ∈ Ι a b, f c = ⨍ x in Ι a b, f x ∂μ`.
-* `exists_eq_interval_average_of_noAtoms`:
+* `exists_eq_interval_average_of_nullSingletonClass`:
`∃ c ∈ uIoo a b, f c = ⨍ x in Ι a b, f x ∂μ`.
* `exists_eq_interval_average`:
`∃ c ∈ uIoo a b, f c = ⨍ x in a..b, f x`.
@@ -78,10 +78,10 @@ theorem exists_eq_interval_average_of_measure
isCompact_uIcc measurableSet_uIoc uIoc_subset_uIcc hμfin) hμfin hμ0
/-- If `f : ℝ → ℝ` is continuous on `uIcc a b`, the interval has finite and nonzero `μ`-measure,
-and `μ` has no atoms, then `∃ c ∈ uIoo a b, f c = ⨍ x in Ι a b, f x ∂μ`. -/
-theorem exists_eq_interval_average_of_noAtoms
- [NoAtoms μ] (hf : ContinuousOn f (uIcc a b)) (hμfin : μ (Ι a b) ≠ ⊤) (hμ0 : μ (Ι a b) ≠ 0) :
- ∃ c ∈ uIoo a b, f c = ⨍ x in Ι a b, f x ∂μ := by
+and `μ` has value zero on singletons, then `∃ c ∈ uIoo a b, f c = ⨍ x in Ι a b, f x ∂μ`. -/
+theorem exists_eq_interval_average_of_nullSingletonClass
+ [NullSingletonClass μ] (hf : ContinuousOn f (uIcc a b)) (hμfin : μ (Ι a b) ≠ ⊤)
+ (hμ0 : μ (Ι a b) ≠ 0) : ∃ c ∈ uIoo a b, f c = ⨍ x in Ι a b, f x ∂μ := by
have hint : IntegrableOn f (Ι a b) μ := hf.integrableOn_of_subset_isCompact
isCompact_uIcc measurableSet_uIoc uIoc_subset_uIcc hμfin
have h : a ≠ b := by intro hab; simp [hab] at hμ0
@@ -95,10 +95,14 @@ theorem exists_eq_interval_average_of_noAtoms
(hint.mono_set hs') (measure_ne_top_of_subset hs' hμfin) hμ0'
exact ⟨c, hc, by rwa [← setAverage_congr hs_ev]⟩
+@[deprecated (since := "2026-06-09")]
+alias exists_eq_interval_average_of_noAtoms := exists_eq_interval_average_of_nullSingletonClass
+
/-- The mean value theorem for integrals:
There exists a point in an interval such that the mean of a continuous function over the interval
equals the value of the function at the point. -/
theorem exists_eq_interval_average
(hab : a ≠ b) (hf : ContinuousOn f (uIcc a b)) :
∃ c ∈ uIoo a b, f c = ⨍ x in a..b, f x :=
- exists_eq_interval_average_of_noAtoms hf (by simp) (by simpa using sub_ne_zero.mpr hab.symm)
+ exists_eq_interval_average_of_nullSingletonClass hf (by simp)
+ (by simpa using sub_ne_zero.mpr hab.symm)
diff --git a/Mathlib/MeasureTheory/Integral/IntervalIntegral/Basic.lean b/Mathlib/MeasureTheory/Integral/IntervalIntegral/Basic.lean
index f0b1203c751e17..e7b07212a97022 100644
--- a/Mathlib/MeasureTheory/Integral/IntervalIntegral/Basic.lean
+++ b/Mathlib/MeasureTheory/Integral/IntervalIntegral/Basic.lean
@@ -94,7 +94,7 @@ theorem intervalIntegrable_congr_ae {g : ℝ → ε} (h : f =ᵐ[μ.restrict (Ι
IntervalIntegrable f μ a b ↔ IntervalIntegrable g μ a b := by
rw [intervalIntegrable_iff, integrableOn_congr_fun_ae h, intervalIntegrable_iff]
-theorem intervalIntegrable_congr_uIoo [NoAtoms μ] {g : ℝ → ε} (h : EqOn f g (uIoo a b)) :
+theorem intervalIntegrable_congr_uIoo [NullSingletonClass μ] {g : ℝ → ε} (h : EqOn f g (uIoo a b)) :
IntervalIntegrable f μ a b ↔ IntervalIntegrable g μ a b := by
apply intervalIntegrable_congr_ae
rw [uIoc, ← restrict_Ioo_eq_restrict_Ioc]
@@ -105,8 +105,8 @@ theorem IntervalIntegrable.congr_ae {g : ℝ → ε} (hf : IntervalIntegrable f
IntervalIntegrable g μ a b := by
rwa [← intervalIntegrable_congr_ae h]
-theorem IntervalIntegrable.congr_uIoo [NoAtoms μ] {g : ℝ → ε} (hf : IntervalIntegrable f μ a b)
- (h : EqOn f g (uIoo a b)) : IntervalIntegrable g μ a b :=
+theorem IntervalIntegrable.congr_uIoo [NullSingletonClass μ] {g : ℝ → ε}
+ (hf : IntervalIntegrable f μ a b) (h : EqOn f g (uIoo a b)) : IntervalIntegrable g μ a b :=
intervalIntegrable_congr_uIoo h |>.mp hf
theorem intervalIntegrable_congr {g : ℝ → ε} (h : EqOn f g (Ι a b)) :
@@ -116,13 +116,13 @@ theorem intervalIntegrable_congr {g : ℝ → ε} (h : EqOn f g (Ι a b)) :
alias ⟨IntervalIntegrable.congr, _⟩ := intervalIntegrable_congr
/-- Interval integrability is invariant when functions change along discrete sets. -/
-theorem IntervalIntegrable.congr_codiscreteWithin {g : ℝ → ε} [NoAtoms μ]
+theorem IntervalIntegrable.congr_codiscreteWithin {g : ℝ → ε} [NullSingletonClass μ]
(h : f =ᶠ[codiscreteWithin (Ι a b)] g) (hf : IntervalIntegrable f μ a b) :
IntervalIntegrable g μ a b :=
hf.congr_ae (ae_restrict_le_codiscreteWithin measurableSet_Ioc h)
/-- Interval integrability is invariant when functions change along discrete sets. -/
-theorem intervalIntegrable_congr_codiscreteWithin {g : ℝ → ε} [NoAtoms μ]
+theorem intervalIntegrable_congr_codiscreteWithin {g : ℝ → ε} [NullSingletonClass μ]
(h : f =ᶠ[codiscreteWithin (Ι a b)] g) :
IntervalIntegrable f μ a b ↔ IntervalIntegrable g μ a b :=
⟨(IntervalIntegrable.congr_codiscreteWithin h ·),
@@ -132,22 +132,22 @@ theorem intervalIntegrable_iff_integrableOn_Ioc_of_le (hab : a ≤ b) :
IntervalIntegrable f μ a b ↔ IntegrableOn f (Ioc a b) μ := by
rw [intervalIntegrable_iff, uIoc_of_le hab]
-theorem intervalIntegrable_iff' [NoAtoms μ] (h : ‖f (min a b)‖ₑ ≠ ∞ := by finiteness) :
+theorem intervalIntegrable_iff' [NullSingletonClass μ] (h : ‖f (min a b)‖ₑ ≠ ∞ := by finiteness) :
IntervalIntegrable f μ a b ↔ IntegrableOn f (uIcc a b) μ := by
rw [intervalIntegrable_iff, ← Icc_min_max, uIoc, integrableOn_Icc_iff_integrableOn_Ioc h]
-theorem intervalIntegrable_iff_integrableOn_Icc_of_le [NoAtoms μ]
+theorem intervalIntegrable_iff_integrableOn_Icc_of_le [NullSingletonClass μ]
(hab : a ≤ b) (ha : ‖f a‖ₑ ≠ ∞ := by finiteness) :
IntervalIntegrable f μ a b ↔ IntegrableOn f (Icc a b) μ := by
rw [intervalIntegrable_iff_integrableOn_Ioc_of_le hab, integrableOn_Icc_iff_integrableOn_Ioc ha]
-theorem intervalIntegrable_iff_integrableOn_Ico_of_le [NoAtoms μ]
+theorem intervalIntegrable_iff_integrableOn_Ico_of_le [NullSingletonClass μ]
(hab : a ≤ b) (ha : ‖f a‖ₑ ≠ ∞ := by finiteness) (hb : ‖f b‖ₑ ≠ ∞ := by finiteness) :
IntervalIntegrable f μ a b ↔ IntegrableOn f (Ico a b) μ := by
rw [intervalIntegrable_iff_integrableOn_Icc_of_le hab ha,
integrableOn_Icc_iff_integrableOn_Ico hb]
-theorem intervalIntegrable_iff_integrableOn_Ioo_of_le [NoAtoms μ]
+theorem intervalIntegrable_iff_integrableOn_Ioo_of_le [NullSingletonClass μ]
(hab : a ≤ b) (ha : ‖f a‖ₑ ≠ ∞ := by finiteness) (hb : ‖f b‖ₑ ≠ ∞ := by finiteness) :
IntervalIntegrable f μ a b ↔ IntegrableOn f (Ioo a b) μ := by
rw [intervalIntegrable_iff_integrableOn_Icc_of_le hab ha,
@@ -1187,7 +1187,7 @@ theorem integral_Iio_sub_Iio (hf : IntegrableOn f (Iio b) μ) (hab : a ≤ b) :
rw [sub_eq_iff_eq_add', ← setIntegral_union (by grind) measurableSet_Ico ha h,
Iio_union_Ico_eq_Iio hab]
-theorem integral_Iio_sub_Iio' [NoAtoms μ] (hf : IntegrableOn f (Iio b) μ)
+theorem integral_Iio_sub_Iio' [NullSingletonClass μ] (hf : IntegrableOn f (Iio b) μ)
(hg : IntegrableOn f (Iio a) μ) :
∫ x in Iio b, f x ∂μ - ∫ x in Iio a, f x ∂μ = ∫ x in a..b, f x ∂μ := by
wlog! hab : a ≤ b generalizing a b
@@ -1201,7 +1201,7 @@ theorem integral_Ici_sub_Ici (hf : IntegrableOn f (Ici a) μ) (hab : a ≤ b) :
rw [sub_eq_iff_eq_add', ← setIntegral_union (by grind) measurableSet_Ico ha h, union_comm,
Ico_union_Ici_eq_Ici hab]
-theorem integral_Ici_sub_Ici' [NoAtoms μ] (hf : IntegrableOn f (Ici a) μ)
+theorem integral_Ici_sub_Ici' [NullSingletonClass μ] (hf : IntegrableOn f (Ici a) μ)
(hg : IntegrableOn f (Ici b) μ) :
∫ x in Ici a, f x ∂μ - ∫ x in Ici b, f x ∂μ = ∫ x in a..b, f x ∂μ := by
wlog! hab : a ≤ b generalizing a b
@@ -1242,13 +1242,13 @@ theorem integral_congr_ae (h : ∀ᵐ x ∂μ, x ∈ Ι a b → f x = g x) :
∫ x in a..b, f x ∂μ = ∫ x in a..b, g x ∂μ :=
integral_congr_ae' (ae_uIoc_iff.mp h).1 (ae_uIoc_iff.mp h).2
-theorem integral_congr_uIoo [NoAtoms μ] (h : (uIoo a b).EqOn f g) :
+theorem integral_congr_uIoo [NullSingletonClass μ] (h : (uIoo a b).EqOn f g) :
∫ x in a..b, f x ∂μ = ∫ x in a..b, g x ∂μ := by
apply integral_congr_ae
filter_upwards [μ.ae_ne <| a ⊔ b] with x _ hx
exact h ⟨hx.left, lt_of_le_of_ne hx.right ‹_›⟩
-theorem integral_congr_Ioo_of_le [NoAtoms μ] (hab : a ≤ b) (h : (Ioo a b).EqOn f g) :
+theorem integral_congr_Ioo_of_le [NullSingletonClass μ] (hab : a ≤ b) (h : (Ioo a b).EqOn f g) :
∫ x in a..b, f x ∂μ = ∫ x in a..b, g x ∂μ :=
integral_congr_uIoo <| uIoo_of_le hab ▸ h
@@ -1429,7 +1429,7 @@ theorem integral_mono_on (h : ∀ x ∈ Icc a b, f x ≤ g x) :
let H x hx := h x <| Ioc_subset_Icc_self hx
simpa only [integral_of_le hab] using setIntegral_mono_on hf.1 hg.1 measurableSet_Ioc H
-theorem integral_mono_on_of_le_Ioo [NoAtoms μ] (h : ∀ x ∈ Ioo a b, f x ≤ g x) :
+theorem integral_mono_on_of_le_Ioo [NullSingletonClass μ] (h : ∀ x ∈ Ioo a b, f x ≤ g x) :
(∫ u in a..b, f u ∂μ) ≤ ∫ u in a..b, g u ∂μ := by
simp only [integral_of_le hab, integral_Ioc_eq_integral_Ioo]
apply setIntegral_mono_on
diff --git a/Mathlib/MeasureTheory/Integral/Layercake.lean b/Mathlib/MeasureTheory/Integral/Layercake.lean
index 3a5b7c938a8f4f..59e6abe4dd4696 100644
--- a/Mathlib/MeasureTheory/Integral/Layercake.lean
+++ b/Mathlib/MeasureTheory/Integral/Layercake.lean
@@ -84,7 +84,7 @@ theorem countable_meas_le_ne_meas_lt (g : α → R) :
exact ⟨μ {a | t < g a}, this, fun s hs ↦ measure_mono (fun a ha ↦ hs.trans_le ha)⟩
theorem meas_le_ae_eq_meas_lt {R : Type*} [LinearOrder R] [MeasurableSpace R]
- (ν : Measure R) [NoAtoms ν] (g : α → R) :
+ (ν : Measure R) [NullSingletonClass ν] (g : α → R) :
(fun t => μ {a : α | t ≤ g a}) =ᵐ[ν] fun t => μ {a : α | t < g a} :=
Set.Countable.measure_zero (countable_meas_le_ne_meas_lt μ g) _
diff --git a/Mathlib/MeasureTheory/Measure/Dirac.lean b/Mathlib/MeasureTheory/Measure/Dirac.lean
index 6dee190008edeb..c2bcd777e868f6 100644
--- a/Mathlib/MeasureTheory/Measure/Dirac.lean
+++ b/Mathlib/MeasureTheory/Measure/Dirac.lean
@@ -7,7 +7,7 @@ module
public import Mathlib.MeasureTheory.MeasurableSpace.CountablyGenerated
public import Mathlib.MeasureTheory.Measure.MutuallySingular
-public import Mathlib.MeasureTheory.Measure.Typeclasses.NoAtoms
+public import Mathlib.MeasureTheory.Measure.Typeclasses.NullSingletonClass
public import Mathlib.MeasureTheory.Measure.Typeclasses.Probability
public import Mathlib.MeasureTheory.Measure.Typeclasses.SFinite
@@ -278,7 +278,8 @@ theorem restrict_dirac [MeasurableSingletonClass α] [Decidable (a ∈ s)] :
rwa [ae_dirac_eq]
· rw [restrict_eq_zero, dirac_apply, indicator_of_notMem has]
-lemma mutuallySingular_dirac [MeasurableSingletonClass α] (x : α) (μ : Measure α) [NoAtoms μ] :
+lemma mutuallySingular_dirac [MeasurableSingletonClass α] (x : α) (μ : Measure α)
+ [NullSingletonClass μ] :
Measure.dirac x ⟂ₘ μ :=
⟨{x}ᶜ, (MeasurableSet.singleton x).compl, by simp, by simp⟩
diff --git a/Mathlib/MeasureTheory/Measure/Haar/NormedSpace.lean b/Mathlib/MeasureTheory/Measure/Haar/NormedSpace.lean
index a22ac24d3625df..156f47097e9380 100644
--- a/Mathlib/MeasureTheory/Measure/Haar/NormedSpace.lean
+++ b/Mathlib/MeasureTheory/Measure/Haar/NormedSpace.lean
@@ -25,10 +25,12 @@ namespace MeasureTheory
namespace Measure
-/-- The instance `MeasureTheory.Measure.IsAddHaarMeasure.noAtoms` applies in particular to show that
-an additive Haar measure on a nontrivial finite-dimensional real vector space has no atom. -/
+/-- The instance `MeasureTheory.Measure.IsAddHaarMeasure.nullSingletonClass` applies in particular
+to show that an additive Haar measure on a nontrivial finite-dimensional real vector space has no
+atom. -/
example {E : Type*} [NormedAddCommGroup E] [NormedSpace ℝ E] [Nontrivial E] [FiniteDimensional ℝ E]
- [MeasurableSpace E] [BorelSpace E] (μ : Measure E) [IsAddHaarMeasure μ] : NoAtoms μ := by
+ [MeasurableSpace E] [BorelSpace E] (μ : Measure E) [IsAddHaarMeasure μ] :
+ NullSingletonClass μ := by
infer_instance
section LinearEquiv
diff --git a/Mathlib/MeasureTheory/Measure/Hausdorff.lean b/Mathlib/MeasureTheory/Measure/Hausdorff.lean
index 4377338e2a1943..81c855665198b3 100644
--- a/Mathlib/MeasureTheory/Measure/Hausdorff.lean
+++ b/Mathlib/MeasureTheory/Measure/Hausdorff.lean
@@ -80,7 +80,8 @@ measures.
equal to infinity on some ray `(-∞, D)` and is equal to zero on `(D, +∞)`, where `D` is a possibly
infinite number called the *Hausdorff dimension* of `s`; `μH[D] s` can be zero, infinity, or
anything in between.
-* `MeasureTheory.Measure.noAtoms_hausdorff`: Hausdorff measure has no atoms.
+* `MeasureTheory.Measure.nullSingletonClass_hausdorff`: Hausdorff measure has value zero on
+ singletons.
### Hausdorff measure in `ℝⁿ`
@@ -601,7 +602,8 @@ theorem hausdorffMeasure_mono {d₁ d₂ : ℝ} (h : d₁ ≤ d₂) (s : Set X)
rcases hausdorffMeasure_zero_or_top h s with hs | hs <;> simp [hs]
variable (X) in
-theorem noAtoms_hausdorff {d : ℝ} (hd : 0 < d) : NoAtoms (hausdorffMeasure d : Measure X) := by
+theorem nullSingletonClass_hausdorff {d : ℝ} (hd : 0 < d) :
+ NullSingletonClass (hausdorffMeasure d : Measure X) := by
refine ⟨fun x => ?_⟩
rw [← nonpos_iff_eq_zero, hausdorffMeasure_apply]
refine iSup₂_le fun ε _ => iInf₂_le_of_le (fun _ => {x}) ?_ <| iInf_le_of_le (fun _ => ?_) ?_
@@ -609,6 +611,9 @@ theorem noAtoms_hausdorff {d : ℝ} (hd : 0 < d) : NoAtoms (hausdorffMeasure d :
· simp only [ediam_singleton, zero_le]
· simp [hd]
+@[deprecated (since := "2026-06-09")]
+alias noAtoms_hausdorff := nullSingletonClass_hausdorff
+
@[simp]
theorem hausdorffMeasure_zero_singleton (x : X) : μH[0] ({x} : Set X) = 1 := by
apply le_antisymm
@@ -647,7 +652,7 @@ theorem hausdorffMeasure_le_one_of_subsingleton {s : Set X} (hs : s.Subsingleton
· rw [(subsingleton_iff_singleton hx).1 hs]
rcases eq_or_lt_of_le hd with (rfl | dpos)
· simp only [le_refl, hausdorffMeasure_zero_singleton]
- · haveI := noAtoms_hausdorff X dpos
+ · haveI := nullSingletonClass_hausdorff X dpos
simp only [zero_le, measure_singleton]
end Measure
@@ -685,7 +690,7 @@ theorem hausdorffMeasure_image_le (h : HolderOnWith C r f s) (hr : 0 < r) {d :
· simp only [ENNReal.rpow_zero, one_mul, mul_zero]
rw [hausdorffMeasure_zero_singleton]
exact one_le_hausdorffMeasure_zero_of_nonempty ⟨x, hx⟩
- · haveI := noAtoms_hausdorff Y h'd
+ · haveI := nullSingletonClass_hausdorff Y h'd
simp only [zero_le, measure_singleton]
-- Now assume `C ≠ 0`
· have hCd0 : (C : ℝ≥0∞) ^ d ≠ 0 := by simp [hC0.ne']
@@ -775,7 +780,7 @@ theorem hausdorffMeasure_preimage_le (hf : AntilipschitzWith K f) (hd : 0 ≤ d)
· simp only [ENNReal.rpow_zero, one_mul]
rw [hausdorffMeasure_zero_singleton]
exact one_le_hausdorffMeasure_zero_of_nonempty ⟨f x, hx⟩
- · haveI := noAtoms_hausdorff X h'd
+ · haveI := nullSingletonClass_hausdorff X h'd
simp only [zero_le, measure_singleton]
have hKd0 : (K : ℝ≥0∞) ^ d ≠ 0 := by simp [h0]
have hKd : (K : ℝ≥0∞) ^ d ≠ ∞ := by simp [hd]
@@ -1023,7 +1028,7 @@ theorem hausdorffMeasure_smul_right_image [NormedAddCommGroup E] [NormedSpace
[MeasurableSpace E] [BorelSpace E] (v : E) (s : Set ℝ) :
μH[1] ((fun r => r • v) '' s) = ‖v‖₊ • μH[1] s := by
obtain rfl | hv := eq_or_ne v 0
- · haveI := noAtoms_hausdorff E one_pos
+ · haveI := nullSingletonClass_hausdorff E one_pos
obtain rfl | hs := s.eq_empty_or_nonempty
· simp
simp [hs]
diff --git a/Mathlib/MeasureTheory/Measure/Lebesgue/Basic.lean b/Mathlib/MeasureTheory/Measure/Lebesgue/Basic.lean
index a72b18d4548e7a..a94cef29b9a1a3 100644
--- a/Mathlib/MeasureTheory/Measure/Lebesgue/Basic.lean
+++ b/Mathlib/MeasureTheory/Measure/Lebesgue/Basic.lean
@@ -170,9 +170,12 @@ theorem volume_closedEBall (a : ℝ) (r : ℝ≥0∞) : volume (Metric.closedEBa
@[deprecated (since := "2026-01-24")]
alias volume_emetric_closedBall := volume_closedEBall
-instance noAtoms_volume : NoAtoms (volume : Measure ℝ) :=
+instance nullSingletonClass_volume : NullSingletonClass (volume : Measure ℝ) :=
⟨fun _ => volume_singleton⟩
+@[deprecated (since := "2026-06-09")]
+alias noAtoms_volume := nullSingletonClass_volume
+
@[simp]
theorem volume_interval {a b : ℝ} : volume (uIcc a b) = ofReal |b - a| := by
rw [← Icc_min_max, volume_Icc, max_sub_min_eq_abs]
@@ -619,8 +622,8 @@ end regionBetween
/-- Consider a real set `s`. If a property is true almost everywhere in `s ∩ (a, b)` for
all `a, b ∈ s`, then it is true almost everywhere in `s`. Formulated with `μ.restrict`.
See also `ae_of_mem_of_ae_of_mem_inter_Ioo`. -/
-theorem ae_restrict_of_ae_restrict_inter_Ioo {μ : Measure ℝ} [NoAtoms μ] {s : Set ℝ} {p : ℝ → Prop}
- (h : ∀ a b, a ∈ s → b ∈ s → a < b → ∀ᵐ x ∂μ.restrict (s ∩ Ioo a b), p x) :
+theorem ae_restrict_of_ae_restrict_inter_Ioo {μ : Measure ℝ} [NullSingletonClass μ] {s : Set ℝ}
+ {p : ℝ → Prop} (h : ∀ a b, a ∈ s → b ∈ s → a < b → ∀ᵐ x ∂μ.restrict (s ∩ Ioo a b), p x) :
∀ᵐ x ∂μ.restrict s, p x := by
/- By second-countability, we cover `s` by countably many intervals `(a, b)` (except maybe for
two endpoints, which don't matter since `μ` does not have any atom). -/
@@ -653,8 +656,8 @@ theorem ae_restrict_of_ae_restrict_inter_Ioo {μ : Measure ℝ} [NoAtoms μ] {s
/-- Consider a real set `s`. If a property is true almost everywhere in `s ∩ (a, b)` for
all `a, b ∈ s`, then it is true almost everywhere in `s`. Formulated with bare membership.
See also `ae_restrict_of_ae_restrict_inter_Ioo`. -/
-theorem ae_of_mem_of_ae_of_mem_inter_Ioo {μ : Measure ℝ} [NoAtoms μ] {s : Set ℝ} {p : ℝ → Prop}
- (h : ∀ a b, a ∈ s → b ∈ s → a < b → ∀ᵐ x ∂μ, x ∈ s ∩ Ioo a b → p x) :
+theorem ae_of_mem_of_ae_of_mem_inter_Ioo {μ : Measure ℝ} [NullSingletonClass μ] {s : Set ℝ}
+ {p : ℝ → Prop} (h : ∀ a b, a ∈ s → b ∈ s → a < b → ∀ᵐ x ∂μ, x ∈ s ∩ Ioo a b → p x) :
∀ᵐ x ∂μ, x ∈ s → p x := by
/- By second-countability, we cover `s` by countably many intervals `(a, b)` (except maybe for
two endpoints, which don't matter since `μ` does not have any atom). -/
diff --git a/Mathlib/MeasureTheory/Measure/OpenPos.lean b/Mathlib/MeasureTheory/Measure/OpenPos.lean
index d5e7375f730923..46c9fb552b916b 100644
--- a/Mathlib/MeasureTheory/Measure/OpenPos.lean
+++ b/Mathlib/MeasureTheory/Measure/OpenPos.lean
@@ -6,7 +6,7 @@ Authors: Yury Kudryashov
module
public import Mathlib.MeasureTheory.Constructions.BorelSpace.Basic
-public import Mathlib.MeasureTheory.Measure.Typeclasses.NoAtoms
+public import Mathlib.MeasureTheory.Measure.Typeclasses.NullSingletonClass
public import Mathlib.MeasureTheory.Measure.Typeclasses.Probability
/-!
@@ -217,7 +217,7 @@ theorem measure_closedBall_pos (x : X) {r : ℝ} (hr : 0 < r) : 0 < μ (closedBa
(measure_ball_pos μ x hr).trans_le (measure_mono ball_subset_closedBall)
@[simp] lemma measure_closedBall_pos_iff {X : Type*} [MetricSpace X] {m : MeasurableSpace X}
- (μ : Measure X) [IsOpenPosMeasure μ] [NoAtoms μ] {x : X} {r : ℝ} :
+ (μ : Measure X) [IsOpenPosMeasure μ] [NullSingletonClass μ] {x : X} {r : ℝ} :
0 < μ (closedBall x r) ↔ 0 < r := by
refine ⟨fun h ↦ ?_, measure_closedBall_pos μ x⟩
contrapose! h
diff --git a/Mathlib/MeasureTheory/Measure/Prod.lean b/Mathlib/MeasureTheory/Measure/Prod.lean
index e9f92b71955eb6..90ee8c5123e0f3 100644
--- a/Mathlib/MeasureTheory/Measure/Prod.lean
+++ b/Mathlib/MeasureTheory/Measure/Prod.lean
@@ -202,16 +202,16 @@ theorem prod_prod_le (s : Set α) (t : Set β) : μ.prod ν (s ×ˢ t) ≤ μ s
restrict_apply_univ, mul_comm]
_ = μ s * ν t := by rw [measure_toMeasurable, measure_toMeasurable]
-instance prod.instNoAtoms_fst [NoAtoms μ] :
- NoAtoms (Measure.prod μ ν) where
+instance prod.instNullSingletonClass_fst [NullSingletonClass μ] :
+ NullSingletonClass (Measure.prod μ ν) where
measure_singleton
| (x, y) => nonpos_iff_eq_zero.mp <| calc
μ.prod ν {(x, y)} = μ.prod ν ({x} ×ˢ {y}) := by rw [singleton_prod_singleton]
_ ≤ μ {x} * ν {y} := prod_prod_le _ _
_ = 0 := by simp
-instance prod.instNoAtoms_snd [NoAtoms ν] :
- NoAtoms (Measure.prod μ ν) where
+instance prod.instNullSingletonClass_snd [NullSingletonClass ν] :
+ NullSingletonClass (Measure.prod μ ν) where
measure_singleton
| (x, y) => nonpos_iff_eq_zero.mp <| calc
μ.prod ν {(x, y)} = μ.prod ν ({x} ×ˢ {y}) := by rw [singleton_prod_singleton]
diff --git a/Mathlib/MeasureTheory/Measure/Typeclasses/NoAtoms.lean b/Mathlib/MeasureTheory/Measure/Typeclasses/NullSingletonClass.lean
similarity index 76%
rename from Mathlib/MeasureTheory/Measure/Typeclasses/NoAtoms.lean
rename to Mathlib/MeasureTheory/Measure/Typeclasses/NullSingletonClass.lean
index c2688f3a1427d9..05a501fdba695a 100644
--- a/Mathlib/MeasureTheory/Measure/Typeclasses/NoAtoms.lean
+++ b/Mathlib/MeasureTheory/Measure/Typeclasses/NullSingletonClass.lean
@@ -9,13 +9,13 @@ public import Mathlib.MeasureTheory.Measure.Restrict
public import Mathlib.Topology.DiscreteSubset
/-!
-# Measures having no atoms
-
-A measure `μ` has no atoms if the measure of each singleton is zero.
+# Measures having value zero on singletons
## TODO
-Should `NoAtoms` be redefined as `∀ s, 0 < μ s → ∃ t ⊆ s, 0 < μ t ∧ μ t < μ s`?
+Add a `NoAtoms` class defined as
+`∀ s, MeasurableSet s → 0 < μ s → ∃ t ⊆ s, MeasurableSet t ∧ 0 < μ t ∧ μ t < μ s`.
+This implies `NullSingletonClass` but the converse is not true.
-/
public section
@@ -26,47 +26,49 @@ open Set Measure Filter TopologicalSpace
variable {α : Type*} {m0 : MeasurableSpace α} {μ : Measure α} {s : Set α}
-/-- Measure `μ` *has no atoms* if the measure of each singleton is zero.
-
-NB: Wikipedia assumes that for any measurable set `s` with positive `μ`-measure,
-there exists a measurable `t ⊆ s` such that `0 < μ t < μ s`. While this implies `μ {x} = 0`,
-the converse is not true. -/
-class NoAtoms {m0 : MeasurableSpace α} (μ : Measure α) : Prop where
+/-- Measure `μ` has value zero on singletons. -/
+class NullSingletonClass {m0 : MeasurableSpace α} (μ : Measure α) : Prop where
measure_singleton : ∀ x, μ {x} = 0
-export MeasureTheory.NoAtoms (measure_singleton)
+@[deprecated (since := "2026-06-09")]
+alias NoAtoms := NullSingletonClass
+
+export MeasureTheory.NullSingletonClass (measure_singleton)
attribute [simp] measure_singleton
-variable [NoAtoms μ]
+variable [NullSingletonClass μ]
-theorem _root_.Set.Subsingleton.measure_zero (hs : s.Subsingleton) (μ : Measure α) [NoAtoms μ] :
+theorem _root_.Set.Subsingleton.measure_zero (hs : s.Subsingleton) (μ : Measure α)
+ [NullSingletonClass μ] :
μ s = 0 :=
hs.induction_on (p := fun s => μ s = 0) measure_empty measure_singleton
theorem Measure.restrict_singleton' {a : α} : μ.restrict {a} = 0 := by
simp only [measure_singleton, Measure.restrict_eq_zero]
-instance Measure.restrict.instNoAtoms (s : Set α) : NoAtoms (μ.restrict s) := by
+instance Measure.restrict.instNullSingletonClass (s : Set α) :
+ NullSingletonClass (μ.restrict s) := by
refine ⟨fun x => ?_⟩
obtain ⟨t, hxt, ht1, ht2⟩ := exists_measurable_superset_of_null (measure_singleton x : μ {x} = 0)
apply measure_mono_null hxt
rw [Measure.restrict_apply ht1]
apply measure_mono_null inter_subset_left ht2
-theorem _root_.Set.Countable.measure_zero (h : s.Countable) (μ : Measure α) [NoAtoms μ] :
+theorem _root_.Set.Countable.measure_zero (h : s.Countable) (μ : Measure α) [NullSingletonClass μ] :
μ s = 0 := by
rw [← biUnion_of_singleton s, measure_biUnion_null_iff h]
simp
-theorem _root_.Set.Countable.ae_notMem (h : s.Countable) (μ : Measure α) [NoAtoms μ] :
+theorem _root_.Set.Countable.ae_notMem (h : s.Countable) (μ : Measure α) [NullSingletonClass μ] :
∀ᵐ x ∂μ, x ∉ s := by
simpa only [ae_iff, Classical.not_not] using! h.measure_zero μ
-lemma Measure.ae_ne (μ : Measure α) [NoAtoms μ] (a : α) : ∀ᵐ x ∂μ, x ≠ a :=
+lemma Measure.ae_ne (μ : Measure α) [NullSingletonClass μ] (a : α) : ∀ᵐ x ∂μ, x ≠ a :=
(countable_singleton a).ae_notMem μ
-lemma _root_.Set.Countable.measure_restrict_compl (h : s.Countable) (μ : Measure α) [NoAtoms μ] :
+lemma _root_.Set.Countable.measure_restrict_compl (h : s.Countable) (μ : Measure α)
+ [NullSingletonClass μ] :
μ.restrict sᶜ = μ :=
restrict_eq_self_of_ae_mem <| h.ae_notMem μ
@@ -74,10 +76,12 @@ lemma _root_.Set.Countable.measure_restrict_compl (h : s.Countable) (μ : Measur
lemma restrict_compl_singleton (a : α) : μ.restrict ({a}ᶜ) = μ :=
(countable_singleton _).measure_restrict_compl μ
-theorem _root_.Set.Finite.measure_zero (h : s.Finite) (μ : Measure α) [NoAtoms μ] : μ s = 0 :=
+theorem _root_.Set.Finite.measure_zero (h : s.Finite) (μ : Measure α) [NullSingletonClass μ] :
+ μ s = 0 :=
h.countable.measure_zero μ
-theorem _root_.Finset.measure_zero (s : Finset α) (μ : Measure α) [NoAtoms μ] : μ s = 0 :=
+theorem _root_.Finset.measure_zero (s : Finset α) (μ : Measure α) [NullSingletonClass μ] :
+ μ s = 0 :=
s.finite_toSet.measure_zero μ
theorem insert_ae_eq_self (a : α) (s : Set α) : (insert a s : Set α) =ᵐ[μ] s :=
@@ -86,13 +90,16 @@ theorem insert_ae_eq_self (a : α) (s : Set α) : (insert a s : Set α) =ᵐ[μ]
/-
If a set has positive measure under an atomless measure, then it has an accumulation point.
-/
-theorem exists_accPt_of_noAtoms {X : Type*} [TopologicalSpace X] [MeasurableSpace X]
- {μ : Measure X} [NoAtoms μ] {E : Set X} [SeparableSpace E] (hE : 0 < μ E) :
+theorem exists_accPt_of_nullSingletonClass {X : Type*} [TopologicalSpace X] [MeasurableSpace X]
+ {μ : Measure X} [NullSingletonClass μ] {E : Set X} [SeparableSpace E] (hE : 0 < μ E) :
∃ x, AccPt x (𝓟 E) := by
by_contra! h
haveI : DiscreteTopology E := discreteTopology_of_noAccPts fun x _ => h x
exact hE.ne' <| (Set.countable_coe_iff.mp <| separableSpace_iff_countable.mp ‹_›).measure_zero μ
+@[deprecated (since := "2026-06-09")]
+alias exists_accPt_of_noAtoms := exists_accPt_of_nullSingletonClass
+
section
variable [PartialOrder α] {a b : α}
diff --git a/Mathlib/MeasureTheory/Measure/WithDensity.lean b/Mathlib/MeasureTheory/Measure/WithDensity.lean
index 404bcf0aa490ea..2cdfce72212c92 100644
--- a/Mathlib/MeasureTheory/Measure/WithDensity.lean
+++ b/Mathlib/MeasureTheory/Measure/WithDensity.lean
@@ -160,9 +160,13 @@ theorem withDensity_apply₀ (f : α → ℝ≥0∞) {s : Set α} (hs : NullMeas
rw [← A, ← B]
exact withDensity_apply _ (measurableSet_toMeasurable μ s)
-instance noAtoms_withDensity [NoAtoms μ] (f : α → ℝ≥0∞) : NoAtoms (μ.withDensity f) where
+instance nullSingletonClass_withDensity [NullSingletonClass μ] (f : α → ℝ≥0∞) :
+ NullSingletonClass (μ.withDensity f) where
measure_singleton _ := withDensity_absolutelyContinuous μ f (measure_singleton _)
+@[deprecated (since := "2026-06-09")]
+alias noAtoms_withDensity := nullSingletonClass_withDensity
+
@[simp]
theorem withDensity_zero : μ.withDensity 0 = 0 := by
ext1 s hs
diff --git a/Mathlib/MeasureTheory/Topology.lean b/Mathlib/MeasureTheory/Topology.lean
index f411e33eff8304..0775598d56be28 100644
--- a/Mathlib/MeasureTheory/Topology.lean
+++ b/Mathlib/MeasureTheory/Topology.lean
@@ -5,7 +5,7 @@ Authors: Stefan Kebekus
-/
module
-public import Mathlib.MeasureTheory.Measure.Typeclasses.NoAtoms
+public import Mathlib.MeasureTheory.Measure.Typeclasses.NullSingletonClass
public import Mathlib.Topology.DiscreteSubset
/-!
@@ -22,7 +22,7 @@ open Filter MeasureTheory
everywhere" filter of co-null sets. -/
theorem ae_restrict_le_codiscreteWithin
{α : Type*} [MeasurableSpace α] [TopologicalSpace α] [SecondCountableTopology α]
- {μ : Measure α} [NoAtoms μ] {U : Set α} (hU : MeasurableSet U) :
+ {μ : Measure α} [NullSingletonClass μ] {U : Set α} (hU : MeasurableSet U) :
ae (μ.restrict U) ≤ codiscreteWithin U := by
intro s hs
have : DiscreteTopology ↑(sᶜ ∩ U) := isDiscrete_iff_discreteTopology.mp
diff --git a/Mathlib/NumberTheory/NumberField/CanonicalEmbedding/Basic.lean b/Mathlib/NumberTheory/NumberField/CanonicalEmbedding/Basic.lean
index b2a46262a4ce6e..928a5fbe2c5dfc 100644
--- a/Mathlib/NumberTheory/NumberField/CanonicalEmbedding/Basic.lean
+++ b/Mathlib/NumberTheory/NumberField/CanonicalEmbedding/Basic.lean
@@ -231,14 +231,15 @@ instance : IsAddHaarMeasure (volume : Measure (mixedSpace K)) :=
prod.instIsAddHaarMeasure volume volume
open Classical in
-instance : NoAtoms (volume : Measure (mixedSpace K)) := by
+instance : NullSingletonClass (volume : Measure (mixedSpace K)) := by
obtain ⟨w⟩ := (inferInstance : Nonempty (InfinitePlace K))
by_cases hw : IsReal w
- · have : NoAtoms (volume : Measure ({w : InfinitePlace K // IsReal w} → ℝ)) := pi_noAtoms ⟨w, hw⟩
- exact prod.instNoAtoms_fst
- · have : NoAtoms (volume : Measure ({w : InfinitePlace K // IsComplex w} → ℂ)) :=
- pi_noAtoms ⟨w, not_isReal_iff_isComplex.mp hw⟩
- exact prod.instNoAtoms_snd
+ · have : NullSingletonClass (volume : Measure ({w : InfinitePlace K // IsReal w} → ℝ)) :=
+ pi_nullSingletonClass ⟨w, hw⟩
+ exact prod.instNullSingletonClass_fst
+ · have : NullSingletonClass (volume : Measure ({w : InfinitePlace K // IsComplex w} → ℂ)) :=
+ pi_nullSingletonClass ⟨w, not_isReal_iff_isComplex.mp hw⟩
+ exact prod.instNullSingletonClass_snd
variable {K} in
open Classical in
diff --git a/Mathlib/Probability/Distributions/Gaussian/Fernique.lean b/Mathlib/Probability/Distributions/Gaussian/Fernique.lean
index f8e1985aea8a16..c911150d3c42e7 100644
--- a/Mathlib/Probability/Distributions/Gaussian/Fernique.lean
+++ b/Mathlib/Probability/Distributions/Gaussian/Fernique.lean
@@ -220,16 +220,16 @@ lemma eq_dirac_of_variance_eq_zero (h : ∀ L : StrongDual ℝ E, Var[L; μ] = 0
rw [charFunDual_dirac, charFunDual_eq L, h L, integral_complex_ofReal, integral_dual L]
simp
-/-- If a Gaussian measure is not a Dirac, then it has no atoms. -/
-lemma noAtoms (h : ∀ x, μ ≠ Measure.dirac x) : NoAtoms μ where
+/-- If a Gaussian measure is not a Dirac, then it has value zero on singletons. -/
+lemma nullSingletonClass (h : ∀ x, μ ≠ Measure.dirac x) : NullSingletonClass μ where
measure_singleton x := by
obtain ⟨L, hL⟩ : ∃ L : StrongDual ℝ E, Var[L; μ] ≠ 0 := by
contrapose! h
exact ⟨_, eq_dirac_of_variance_eq_zero h⟩
have hL_zero : μ.map L {L x} = 0 := by
- have : NoAtoms (μ.map L) := by
+ have : NullSingletonClass (μ.map L) := by
rw [map_eq_gaussianReal L]
- refine noAtoms_gaussianReal ?_
+ refine nullSingletonClass_gaussianReal ?_
simp only [ne_eq, Real.toNNReal_eq_zero, not_le]
exact lt_of_le_of_ne (variance_nonneg _ _) hL.symm
rw [measure_singleton]
@@ -237,6 +237,9 @@ lemma noAtoms (h : ∀ x, μ ≠ Measure.dirac x) : NoAtoms μ where
refine measure_mono_null ?_ hL_zero
exact fun ⦃a⦄ ↦ congrArg ⇑L
+@[deprecated (since := "2026-06-09")]
+alias noAtoms := nullSingletonClass
+
/-- Characteristic function of a centered Gaussian measure. -/
lemma charFunDual_eq_of_integral_eq_zero (hμ : μ[id] = 0) (L : StrongDual ℝ E) :
charFunDual μ L = exp (- Var[L; μ] / 2) := by
diff --git a/Mathlib/Probability/Distributions/Gaussian/Real.lean b/Mathlib/Probability/Distributions/Gaussian/Real.lean
index 9b7b05e1bbfa7b..89ef930b8563ac 100644
--- a/Mathlib/Probability/Distributions/Gaussian/Real.lean
+++ b/Mathlib/Probability/Distributions/Gaussian/Real.lean
@@ -232,10 +232,14 @@ instance instIsProbabilityMeasureGaussianReal (μ : ℝ) (v : ℝ≥0) :
IsProbabilityMeasure (gaussianReal μ v) where
measure_univ := by by_cases h : v = 0 <;> simp [gaussianReal_of_var_ne_zero, h]
-lemma noAtoms_gaussianReal {μ : ℝ} {v : ℝ≥0} (h : v ≠ 0) : NoAtoms (gaussianReal μ v) := by
+lemma nullSingletonClass_gaussianReal {μ : ℝ} {v : ℝ≥0} (h : v ≠ 0) :
+ NullSingletonClass (gaussianReal μ v) := by
rw [gaussianReal_of_var_ne_zero _ h]
infer_instance
+@[deprecated (since := "2026-06-09")]
+alias noAtoms_gaussianReal := nullSingletonClass_gaussianReal
+
lemma gaussianReal_apply (μ : ℝ) {v : ℝ≥0} (hv : v ≠ 0) (s : Set ℝ) :
gaussianReal μ v s = ∫⁻ x in s, gaussianPDF μ v x := by
rw [gaussianReal_of_var_ne_zero _ hv, withDensity_apply' _ s]
diff --git a/scripts/nolints_prime_decls.txt b/scripts/nolints_prime_decls.txt
index 98c8ef29d15948..ac4950dd3c6013 100644
--- a/scripts/nolints_prime_decls.txt
+++ b/scripts/nolints_prime_decls.txt
@@ -2651,7 +2651,7 @@ MeasureTheory.Measure.map_id'
MeasureTheory.Measure.measurable_bind'
MeasureTheory.Measure.MeasureDense.nonempty'
MeasureTheory.Measure.nonpos_iff_eq_zero'
-MeasureTheory.Measure.pi_noAtoms'
+MeasureTheory.Measure.pi_nullSingletonClass'
MeasureTheory.MeasurePreserving.integral_comp'
MeasureTheory.Measure.restrict_apply₀'
MeasureTheory.Measure.restrict_apply_eq_zero'
From a66a71b38c4515dcad4e816123e1cd4abd8b8c8f Mon Sep 17 00:00:00 2001
From: Christian Merten <136261474+chrisflav@users.noreply.github.com>
Date: Mon, 22 Jun 2026 11:37:58 +0000
Subject: [PATCH 0242/1300] chore(CategoryTheory): generalise
`Comma.initial_fst` and `Comma.isCofiltered_of_initial` (#40556)
We replace the `Functor.Initial L` assumption in `Comma.initial_fst` and `Comma.isCofiltered_of_initial` by the assumption that `CostructuredArrow L (R.obj b)` is cofiltered for all `b` (`L.Initial` corresponds to all `CostructuredArrow L t` being cofiltered). We also provide the dual versions.
We also give a new proof for `Comma.initial_snd`, only assuming connectedness of certain `CostructuredArrow` categories, by transporting along an adjunction.
From Proetale and then subsequently cleaned up and generalised by Claude Fable 5. This generalisation is used in a follow-up PR to show that morphisms between cofiltered limits where the components of the target are finitely presented can be written as a cofiltered limit of morphisms.
---
Mathlib/CategoryTheory/Comma/Final.lean | 183 +++++++++---------
.../Comma/StructuredArrow/Basic.lean | 40 ++++
Mathlib/CategoryTheory/Filtered/Final.lean | 8 +
.../Limits/Indization/Category.lean | 1 +
4 files changed, 143 insertions(+), 89 deletions(-)
diff --git a/Mathlib/CategoryTheory/Comma/Final.lean b/Mathlib/CategoryTheory/Comma/Final.lean
index 471c7c667af357..04608f5d124a3e 100644
--- a/Mathlib/CategoryTheory/Comma/Final.lean
+++ b/Mathlib/CategoryTheory/Comma/Final.lean
@@ -5,12 +5,8 @@ Authors: Jakob von Raumer
-/
module
-public import Mathlib.CategoryTheory.Functor.KanExtension.Adjunction
public import Mathlib.CategoryTheory.Limits.IsConnected
-public import Mathlib.CategoryTheory.Limits.Sifted
public import Mathlib.CategoryTheory.Filtered.Final
-public import Mathlib.CategoryTheory.Filtered.Flat
-public import Mathlib.CategoryTheory.Grothendieck
public import Mathlib.CategoryTheory.Comma.StructuredArrow.CommaMap
/-!
@@ -41,60 +37,95 @@ namespace Comma
open Limits Functor CostructuredArrow
-section Small
-
-variable {A : Type v₁} [Category.{v₁} A]
-variable {B : Type v₁} [Category.{v₁} B]
-variable {T : Type v₁} [Category.{v₁} T]
-variable (L : A ⥤ T) (R : B ⥤ T)
-
-set_option backward.defeqAttrib.useBackward true in
-set_option backward.isDefEq.respectTransparency false in
-private lemma final_fst_small [R.Final] : (fst L R).Final := by
- rw [Functor.final_iff_isIso_colimit_pre]
- intro G
- let i : colimit G ≅ colimit (fst L R ⋙ G) :=
- colimitIsoColimitGrothendieck L G ≪≫
- (Final.colimitIso (Grothendieck.pre (functor L) R) (grothendieckProj L ⋙ G)).symm ≪≫
- HasColimit.isoOfNatIso (Iso.refl _) ≪≫
- Final.colimitIso (grothendieckPrecompFunctorEquivalence L R).functor (fst L R ⋙ G)
- convert! i.isIso_inv
- apply colimit.hom_ext
- intro ⟨a, b, f⟩
- simp only [colimit.ι_pre, comp_obj, fst_obj, grothendieckPrecompFunctorEquivalence_functor,
- Iso.trans_inv, Iso.symm_inv, Category.assoc, i]
- change _ = colimit.ι (fst L R ⋙ G)
- ((grothendieckPrecompFunctorToComma L R).obj ⟨b, CostructuredArrow.mk f⟩) ≫ _
- simp
-
-end Small
-
-section NonSmall
-
variable {A : Type u₁} [Category.{v₁} A]
variable {B : Type u₂} [Category.{v₂} B]
variable {T : Type u₃} [Category.{v₃} T]
variable (L : A ⥤ T) (R : B ⥤ T)
-instance final_fst [R.Final] : (fst L R).Final := by
- let sA : A ≌ AsSmall.{max u₁ u₂ u₃ v₁ v₂ v₃} A := AsSmall.equiv
- let sB : B ≌ AsSmall.{max u₁ u₂ u₃ v₁ v₂ v₃} B := AsSmall.equiv
- let sT : T ≌ AsSmall.{max u₁ u₂ u₃ v₁ v₂ v₃} T := AsSmall.equiv
- let L' := sA.inverse ⋙ L ⋙ sT.functor
- let R' := sB.inverse ⋙ R ⋙ sT.functor
- let fC : Comma L R ⥤ Comma L' R' :=
- map (F₁ := sA.functor) (F := sT.functor) (F₂ := sB.functor)
- (isoWhiskerRight sA.unitIso (L ⋙ sT.functor)).hom
- (isoWhiskerRight sB.unitIso (R ⋙ sT.functor)).hom
- have : Final (fst L' R') := final_fst_small _ _
- apply final_of_natIso (F := (fC ⋙ fst L' R' ⋙ sA.inverse))
- exact (Functor.associator _ _ _).symm.trans (Iso.compInverseIso (mapFst _ _))
-
-instance initial_snd [L.Initial] : (snd L R).Initial := by
- have : ((opFunctor L R).leftOp ⋙ fst R.op L.op).Final :=
- final_equivalence_comp (opEquiv L R).functor.leftOp (fst R.op L.op)
- have : (snd L R).op.Final := final_of_natIso (opFunctorCompFst _ _)
- apply initial_of_final_op
+section Relative
+
+lemma isCofiltered_of_isCofiltered_costructuredArrow [IsCofiltered A] [IsCofiltered B]
+ [∀ b, IsCofiltered (CostructuredArrow L (R.obj b))] : IsCofiltered (Comma L R) where
+ nonempty := by
+ obtain ⟨b⟩ := IsCofiltered.nonempty (C := B)
+ obtain ⟨X⟩ : Nonempty (CostructuredArrow L (R.obj b)) := IsCofiltered.nonempty
+ exact ⟨⟨X.left, b, X.hom⟩⟩
+ toIsCofilteredOrEmpty := by
+ refine ⟨fun j₁ j₂ ↦ ?_, fun j₁ j₂ u v ↦ ?_⟩
+ · obtain ⟨Q⟩ : Nonempty (CostructuredArrow L (R.obj (IsCofiltered.min j₁.right j₂.right))) :=
+ IsCofiltered.nonempty
+ obtain ⟨ia, va₁, va₂, heqa⟩ := exists_eq_of_isCofiltered_costructuredArrow L
+ (Q.hom ≫ R.map (IsCofiltered.minToLeft j₁.right j₂.right)) j₁.hom
+ obtain ⟨ib, vb₁, vb₂, heqb⟩ := exists_eq_of_isCofiltered_costructuredArrow L
+ (Q.hom ≫ R.map (IsCofiltered.minToRight j₁.right j₂.right)) j₂.hom
+ obtain ⟨i₀, il₀, ir₀, heq⟩ := IsCofiltered.cospan va₁ vb₁
+ exact ⟨⟨i₀, IsCofiltered.min j₁.right j₂.right, L.map (il₀ ≫ va₁) ≫ Q.hom⟩,
+ ⟨il₀ ≫ va₂, IsCofiltered.minToLeft _ _, by simp [← heqa]⟩,
+ ⟨ir₀ ≫ vb₂, IsCofiltered.minToRight _ _, by cat_disch⟩, trivial⟩
+ · obtain ⟨Q⟩ : Nonempty (CostructuredArrow L (R.obj (IsCofiltered.eq u.right v.right))) :=
+ IsCofiltered.nonempty
+ obtain ⟨ia, va₁, va₂, heqa⟩ := exists_eq_of_isCofiltered_costructuredArrow L
+ (Q.hom ≫ R.map (IsCofiltered.eqHom u.right v.right)) j₁.hom
+ obtain ⟨i₀, α, β, hα, hβ⟩ := IsCofiltered.bowtie u.left (va₂ ≫ v.left) (𝟙 _) va₂
+ have := IsCofiltered.eq_condition u.right v.right
+ exact ⟨⟨i₀, IsCofiltered.eq u.right v.right, L.map (β ≫ va₁) ≫ Q.hom⟩,
+ ⟨β ≫ va₂, IsCofiltered.eqHom u.right v.right, by cat_disch⟩, by cat_disch⟩
+
+set_option backward.isDefEq.respectTransparency false in
+lemma initial_fst_of_isCofiltered_costructuredArrow [IsCofiltered A] [IsCofiltered B]
+ [∀ b, IsCofiltered (CostructuredArrow L (R.obj b))] : (fst L R).Initial := by
+ have := isCofiltered_of_isCofiltered_costructuredArrow L R
+ rw [Functor.initial_iff_of_isCofiltered]
+ refine ⟨fun a ↦ ?_, fun {a} A' s s' ↦ ?_⟩
+ · obtain ⟨b⟩ := IsCofiltered.nonempty (C := B)
+ obtain ⟨X⟩ : Nonempty (CostructuredArrow L (R.obj b)) := IsCofiltered.nonempty
+ exact ⟨⟨IsCofiltered.min a X.left, b, L.map (IsCofiltered.minToRight a X.left) ≫ X.hom⟩,
+ ⟨IsCofiltered.minToLeft a X.left⟩⟩
+ · exact ⟨⟨_, A'.right, L.map (IsCofiltered.eqHom s s') ≫ A'.hom⟩,
+ ⟨IsCofiltered.eqHom s s', 𝟙 A'.right, by simp⟩, IsCofiltered.eq_condition s s'⟩
+
+lemma initial_snd_of_isConnected_costructuredArrow
+ [∀ b, IsConnected (CostructuredArrow L (R.obj b))] : (snd L R).Initial where
+ out b := by
+ have := final_of_adjunction (costructuredArrowSndAdjunction L R b)
+ rw [← isConnected_iff_of_final (costructuredArrowSndInclusion L R b)]
+ infer_instance
+
+lemma isFiltered_of_isFiltered_structuredArrow [IsFiltered A] [IsFiltered B]
+ [∀ a, IsFiltered (StructuredArrow (L.obj a) R)] : IsFiltered (Comma L R) := by
+ have (a : Aᵒᵖ) : IsCofiltered (CostructuredArrow R.op (L.op.obj a)) :=
+ IsCofiltered.of_equivalence (structuredArrowOpEquivalence R (L.obj a.unop))
+ have : IsCofiltered (Comma R.op L.op) := isCofiltered_of_isCofiltered_costructuredArrow _ _
+ exact IsFiltered.of_equivalence (opEquiv L R).symm
+
+lemma final_fst_of_isConnected_structuredArrow
+ [∀ a, IsConnected (StructuredArrow (L.obj a) R)] : (fst L R).Final := by
+ have (a : Aᵒᵖ) : IsConnected (CostructuredArrow R.op (L.op.obj a)) :=
+ (isConnected_iff_of_equivalence (structuredArrowOpEquivalence R (L.obj a.unop))).mp
+ inferInstance
+ have : (snd R.op L.op).Initial := initial_snd_of_isConnected_costructuredArrow _ _
+ have : ((opFunctor L R).leftOp ⋙ snd R.op L.op).Initial :=
+ initial_equivalence_comp (opEquiv L R).functor.leftOp _
+ have : (fst L R).op.Initial := initial_of_natIso <| opFunctorCompSnd _ _
+ apply final_of_initial_op
+
+lemma final_snd_of_isFiltered_structuredArrow [IsFiltered A] [IsFiltered B]
+ [∀ a, IsFiltered (StructuredArrow (L.obj a) R)] : (snd L R).Final := by
+ have (a : Aᵒᵖ) : IsCofiltered (CostructuredArrow R.op (L.op.obj a)) :=
+ IsCofiltered.of_equivalence (structuredArrowOpEquivalence R (L.obj a.unop))
+ have : (fst R.op L.op).Initial := initial_fst_of_isCofiltered_costructuredArrow _ _
+ have : ((opFunctor L R).leftOp ⋙ fst R.op L.op).Initial :=
+ initial_equivalence_comp (opEquiv L R).functor.leftOp _
+ have : (snd L R).op.Initial := initial_of_natIso <| opFunctorCompFst _ _
+ apply final_of_initial_op
+
+end Relative
+
+instance initial_snd [L.Initial] : (snd L R).Initial :=
+ initial_snd_of_isConnected_costructuredArrow L R
+
+instance final_fst [R.Final] : (fst L R).Final :=
+ final_fst_of_isConnected_structuredArrow L R
/-- `Comma L R` with `L : A ⥤ T` and `R : B ⥤ T` is connected if `R` is final and `A` is
connected. -/
@@ -106,8 +137,6 @@ connected. -/
instance isConnected_comma_of_initial [IsConnected B] [L.Initial] : IsConnected (Comma L R) := by
rwa [isConnected_iff_of_initial (snd L R)]
-end NonSmall
-
set_option backward.defeqAttrib.useBackward true in
/-- Let the following diagram commute up to isomorphism:
@@ -148,54 +177,30 @@ lemma map_final {A : Type u₁} [Category.{v₁} A] {B : Type u₂} [Category.{v
section Filtered
-variable {A : Type u₁} [Category.{v₁} A]
-variable {B : Type u₂} [Category.{v₂} B]
-variable {T : Type u₃} [Category.{v₃} T]
-variable (L : A ⥤ T) (R : B ⥤ T)
-
-set_option backward.defeqAttrib.useBackward true in
-attribute [local instance] map_final in
/-- Let `A` and `B` be filtered categories, `R : B ⥤ T` be final and `L : A ⥤ T`. Then, the
comma category `Comma L R` is filtered. -/
instance isFiltered_of_final [IsFiltered A] [IsFiltered B] [R.Final] : IsFiltered (Comma L R) := by
- haveI (a : A) : IsFiltered (Comma (fromPUnit (L.obj a)) R) :=
- R.final_iff_isFiltered_structuredArrow.mp inferInstance (L.obj a)
- have (a : A) : (fromPUnit (Over.mk (𝟙 a))).Final := final_const_of_isTerminal Over.mkIdTerminal
- let η (a : A) : fromPUnit (Over.mk (𝟙 a)) ⋙ Over.forget a ⋙ L ≅ fromPUnit (L.obj a) :=
- NatIso.ofComponents (fun _ => Iso.refl _)
- have (a : A) := IsFiltered.of_final (map (L := fromPUnit (L.obj a)) (F := 𝟭 T) (η a).hom
- ((Iso.refl (𝟭 B ⋙ R)).inv))
- have : RepresentablyCoflat (fst L R) :=
- ⟨fun a => IsFiltered.of_equivalence (CostructuredArrow.ofCommaFstEquivalence L R a).symm⟩
- apply isFiltered_of_representablyCoflat (fst L R)
-
-attribute [local instance] isFiltered_of_final in
+ have := R.final_iff_isFiltered_structuredArrow.mp inferInstance
+ exact isFiltered_of_isFiltered_structuredArrow L R
+
/-- Let `A` and `B` be cofiltered categories, `L : A ⥤ T` be initial and `R : B ⥤ T`. Then, the
comma category `Comma L R` is cofiltered. -/
lemma isCofiltered_of_initial [IsCofiltered A] [IsCofiltered B] [L.Initial] :
- IsCofiltered (Comma L R) :=
- IsCofiltered.of_equivalence (Comma.opEquiv _ _).symm
+ IsCofiltered (Comma L R) := by
+ have := L.initial_iff_isCofiltered_costructuredArrow.mp inferInstance
+ exact isCofiltered_of_isCofiltered_costructuredArrow L R
-set_option backward.defeqAttrib.useBackward true in
-attribute [local instance] final_of_isFiltered_of_pUnit in
/-- Let `A` and `B` be filtered categories, `R : B ⥤ T` be final and `R : A ⥤ T`. Then, the
projection `snd L R : Comma L R ⥤ B` is final. -/
instance final_snd [IsFiltered A] [IsFiltered B] [R.Final] : (snd L R).Final := by
- let iL : star.{1} A ⋙ 𝟭 _ ≅ L ⋙ star _ := Iso.refl _
- let iR : 𝟭 B ⋙ star.{1} B ≅ R ⋙ star _ := Iso.refl _
- have := map_final iL iR
- let s := (equivProd (𝟭 _) (star B)).trans <| prod.leftUnitorEquivalence B
- let iS : map iL.hom iR.inv ⋙ s.functor ≅ snd L R :=
- NatIso.ofComponents (fun _ => Iso.refl _) (fun f => by simp [iL, iR, s])
- apply final_of_natIso iS
+ have := R.final_iff_isFiltered_structuredArrow.mp inferInstance
+ exact final_snd_of_isFiltered_structuredArrow L R
/-- Let `A` and `B` be cofiltered categories, `L : A ⥤ T` be initial and `R : B ⥤ T`. Then, the
projection `fst L R : Comma L R ⥤ A` is initial. -/
instance initial_fst [IsCofiltered A] [IsCofiltered B] [L.Initial] : (fst L R).Initial := by
- have : ((opFunctor L R).leftOp ⋙ snd R.op L.op).Final :=
- final_equivalence_comp (opEquiv L R).functor.leftOp _
- have : (fst L R).op.Final := final_of_natIso <| opFunctorCompSnd _ _
- apply initial_of_final_op
+ have := L.initial_iff_isCofiltered_costructuredArrow.mp inferInstance
+ exact initial_fst_of_isCofiltered_costructuredArrow L R
end Filtered
diff --git a/Mathlib/CategoryTheory/Comma/StructuredArrow/Basic.lean b/Mathlib/CategoryTheory/Comma/StructuredArrow/Basic.lean
index 320b6d70cd3c9e..5e5e0000dd8dcf 100644
--- a/Mathlib/CategoryTheory/Comma/StructuredArrow/Basic.lean
+++ b/Mathlib/CategoryTheory/Comma/StructuredArrow/Basic.lean
@@ -5,6 +5,7 @@ Authors: Adam Topaz, Kim Morrison
-/
module
+public import Mathlib.CategoryTheory.Adjunction.Basic
public import Mathlib.CategoryTheory.PUnit
public import Mathlib.CategoryTheory.Limits.Shapes.IsTerminal
public import Mathlib.CategoryTheory.Functor.EpiMono
@@ -1271,4 +1272,43 @@ end
end Prod
+namespace Comma
+
+variable {A : Type u₁} [Category.{v₁} A] {B : Type u₂} [Category.{v₂} B]
+ {T : Type u₃} [Category.{v₃} T] (L : A ⥤ T) (R : B ⥤ T)
+
+set_option backward.defeqAttrib.useBackward true in
+/-- The functor from the costructured arrow category on `snd L R` over `b : B` to the
+costructured arrow category on `L` over `R.obj b`. It is left adjoint to
+`costructuredArrowSndInclusion`, see `costructuredArrowSndAdjunction`. -/
+@[simps]
+def costructuredArrowSndProj (b : B) :
+ CostructuredArrow (snd L R) b ⥤ CostructuredArrow L (R.obj b) where
+ obj X := CostructuredArrow.mk (X.left.hom ≫ R.map X.hom)
+ map f := CostructuredArrow.homMk f.left.left <| by
+ dsimp
+ rw [reassoc_of% f.left.w, ← R.map_comp, dsimp% CostructuredArrow.w f]
+
+set_option backward.defeqAttrib.useBackward true in
+/-- The functor from the costructured arrow category on `L` over `R.obj b` to the costructured
+arrow category on `snd L R` over `b : B`. -/
+@[simps]
+def costructuredArrowSndInclusion (b : B) :
+ CostructuredArrow L (R.obj b) ⥤ CostructuredArrow (snd L R) b where
+ obj X := ⟨⟨X.left, b, X.hom⟩, ⟨⟨⟩⟩, 𝟙 b⟩
+ map f := CostructuredArrow.homMk ⟨f.left, 𝟙 b, by simp⟩ (by simp)
+
+set_option backward.defeqAttrib.useBackward true in
+/-- The functor `costructuredArrowSndProj` is left adjoint to `costructuredArrowSndInclusion`. -/
+@[simps]
+def costructuredArrowSndAdjunction (b : B) :
+ costructuredArrowSndProj L R b ⊣ costructuredArrowSndInclusion L R b where
+ unit.app X := CostructuredArrow.homMk ⟨𝟙 X.left.left, X.hom, by simp⟩ (by simp)
+ unit.naturality _ _ f := by
+ have := CostructuredArrow.w f
+ cat_disch
+ counit.app X := CostructuredArrow.homMk (𝟙 X.left) (by simp)
+
+end Comma
+
end CategoryTheory
diff --git a/Mathlib/CategoryTheory/Filtered/Final.lean b/Mathlib/CategoryTheory/Filtered/Final.lean
index edd610db14229d..604403e69da7d9 100644
--- a/Mathlib/CategoryTheory/Filtered/Final.lean
+++ b/Mathlib/CategoryTheory/Filtered/Final.lean
@@ -92,6 +92,14 @@ theorem isCofiltered_costructuredArrow_of_isCofiltered_of_exists [IsCofilteredOr
obtain ⟨c', t, ht⟩ := h₂ s.unop s'.unop
exact ⟨op c', Quiver.Hom.op t, Quiver.Hom.unop_inj ht⟩
+theorem exists_eq_of_isCofiltered_costructuredArrow {d : D}
+ [IsCofiltered (CostructuredArrow F d)] {c₁ c₂ : C}
+ (s₁ : F.obj c₁ ⟶ d) (s₂ : F.obj c₂ ⟶ d) :
+ ∃ (c : C) (t₁ : c ⟶ c₁) (t₂ : c ⟶ c₂), F.map t₁ ≫ s₁ = F.map t₂ ≫ s₂ := by
+ obtain ⟨W, p₁, p₂, -⟩ := IsCofilteredOrEmpty.cone_objs
+ (CostructuredArrow.mk s₁) (CostructuredArrow.mk s₂)
+ exact ⟨W.left, p₁.left, p₂.left, (CostructuredArrow.w p₁).trans (CostructuredArrow.w p₂).symm⟩
+
/-- If `C` is filtered, then we can give an explicit condition for a functor `F : C ⥤ D` to
be final. The converse is also true, see `final_iff_of_isFiltered`. -/
theorem Functor.final_of_exists_of_isFiltered [IsFilteredOrEmpty C]
diff --git a/Mathlib/CategoryTheory/Limits/Indization/Category.lean b/Mathlib/CategoryTheory/Limits/Indization/Category.lean
index 8d05c3bbbb0845..30937a6d5a8a6a 100644
--- a/Mathlib/CategoryTheory/Limits/Indization/Category.lean
+++ b/Mathlib/CategoryTheory/Limits/Indization/Category.lean
@@ -5,6 +5,7 @@ Authors: Markus Himmel
-/
module
+public import Mathlib.CategoryTheory.Functor.Flat
public import Mathlib.CategoryTheory.Limits.Constructions.Filtered
public import Mathlib.CategoryTheory.Limits.FullSubcategory
public import Mathlib.CategoryTheory.Limits.ExactFunctor
From 096aa7bdd13870ac40178ca4bdb8dc9298ed77dc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=ABl=20Riou?=
<37772949+joelriou@users.noreply.github.com>
Date: Mon, 22 Jun 2026 12:14:32 +0000
Subject: [PATCH 0243/1300] feat(Algebra/Homology/Embedding): various results
about truncations (#40885)
---
.../Homology/Embedding/CochainComplex.lean | 57 +++++++++++++++++++
.../Homology/Embedding/TruncGEHomology.lean | 5 ++
.../Homology/Embedding/TruncLEHomology.lean | 5 ++
3 files changed, 67 insertions(+)
diff --git a/Mathlib/Algebra/Homology/Embedding/CochainComplex.lean b/Mathlib/Algebra/Homology/Embedding/CochainComplex.lean
index cd3908b34d4fc7..800a7708629da9 100644
--- a/Mathlib/Algebra/Homology/Embedding/CochainComplex.lean
+++ b/Mathlib/Algebra/Homology/Embedding/CochainComplex.lean
@@ -361,6 +361,63 @@ end
end Preadditive
+section HasZeroMorphisms
+
+variable {C : Type*} [Category C] [HasZeroMorphisms C] [HasZeroObject C]
+ (K L : CochainComplex C ℤ) (φ : K ⟶ L) (e : K ≅ L)
+ [∀ (i : ℤ), K.HasHomology i] [∀ (i : ℤ), L.HasHomology i] (n : ℤ)
+
+set_option backward.defeqAttrib.useBackward true in
+/-- When `K` is a cochain complex indexed by `ℤ` and `n < i`, this is
+the isomorphism `(K.truncGE n).X i ≅ K.X i`. -/
+noncomputable def truncGEXIso (n i : ℤ) (hi : n < i := by lia) :
+ (K.truncGE n).X i ≅ K.X i :=
+ HomologicalComplex.truncGEXIso K (embeddingUpIntGE n) (i := (i - n).natAbs) (by
+ dsimp
+ rw [Int.natAbs_of_nonneg (by lia), add_sub_cancel])
+ (fun h ↦ by
+ rw [boundaryGE_embeddingUpIntGE_iff, Int.natAbs_eq_zero] at h
+ lia)
+
+set_option backward.defeqAttrib.useBackward true in
+/-- When `K` is a cochain complex indexed by `ℤ` and `i < n`, this is
+the isomorphism `(K.truncLE n).X i ≅ K.X i`. -/
+noncomputable def truncLEXIso (n i : ℤ) (hi : i < n := by lia) :
+ (K.truncLE n).X i ≅ K.X i :=
+ HomologicalComplex.truncLEXIso K (embeddingUpIntLE n) (i := (n - i).natAbs) (by
+ dsimp
+ rw [Int.natAbs_of_nonneg (by lia), sub_sub_cancel])
+ (fun h ↦ by
+ rw [boundaryLE_embeddingUpIntLE_iff, Int.natAbs_eq_zero] at h
+ lia)
+
+/-- When `K` is a cochain complex indexed by `ℤ`, this is the isomorphism
+`(K.truncGE n).X n ≅ K.opcycles n`. -/
+noncomputable def truncGEXIsoOpcycles (n : ℤ) :
+ (K.truncGE n).X n ≅ K.opcycles n :=
+ HomologicalComplex.truncGEXIsoOpcycles K (embeddingUpIntGE n) (i := 0) (by simp)
+ (by rw [boundaryGE_embeddingUpIntGE_iff])
+
+/-- When `K` is a cochain complex indexed by `ℤ`, this is the isomorphism
+`(K.truncLE n).X n ≅ K.cycles n`. -/
+noncomputable def truncLEXIsoCycles (n : ℤ) :
+ (K.truncLE n).X n ≅ K.cycles n :=
+ HomologicalComplex.truncLEXIsoCycles K (embeddingUpIntLE n) (i := 0) (by simp)
+ (by rw [boundaryLE_embeddingUpIntLE_iff])
+
+lemma acyclic_truncGE_iff (n₀ n₁ : ℤ) (h : n₀ + 1 = n₁ := by lia) :
+ (K.truncGE n₁).Acyclic ↔ K.IsLE n₀ := by
+ dsimp [truncGE]
+ rw [acyclic_truncGE_iff_isSupportedOutside,
+ (Embedding.embeddingUpInt_areComplementary n₀ n₁ h).isSupportedOutside₂_iff]
+
+lemma acyclic_truncLE_iff (n₀ n₁ : ℤ) (h : n₀ + 1 = n₁ := by lia) :
+ (K.truncLE n₀).Acyclic ↔ K.IsGE n₁ := by
+ dsimp [truncLE]
+ rw [acyclic_truncLE_iff_isSupportedOutside,
+ (Embedding.embeddingUpInt_areComplementary n₀ n₁ h).isSupportedOutside₁_iff]
+
+end HasZeroMorphisms
section Abelian
diff --git a/Mathlib/Algebra/Homology/Embedding/TruncGEHomology.lean b/Mathlib/Algebra/Homology/Embedding/TruncGEHomology.lean
index 4a3dfcb22871f7..bc11bec38b692a 100644
--- a/Mathlib/Algebra/Homology/Embedding/TruncGEHomology.lean
+++ b/Mathlib/Algebra/Homology/Embedding/TruncGEHomology.lean
@@ -226,6 +226,11 @@ lemma acyclic_truncGE_iff_isSupportedOutside :
variable {K L}
+lemma Acyclic.truncGE (hK : K.Acyclic) (e : c.Embedding c') [e.IsTruncGE] :
+ (K.truncGE e).Acyclic := by
+ rw [acyclic_truncGE_iff_isSupportedOutside]
+ exact ⟨fun _ ↦ hK _⟩
+
lemma quasiIso_truncGEMap_iff :
QuasiIso (truncGEMap φ e) ↔ ∀ (i : ι) (i' : ι') (_ : e.f i = i'), QuasiIsoAt φ i' := by
have : ∀ (i : ι) (i' : ι') (_ : e.f i = i'),
diff --git a/Mathlib/Algebra/Homology/Embedding/TruncLEHomology.lean b/Mathlib/Algebra/Homology/Embedding/TruncLEHomology.lean
index 4dce5e6f3311e7..d502131ca9842d 100644
--- a/Mathlib/Algebra/Homology/Embedding/TruncLEHomology.lean
+++ b/Mathlib/Algebra/Homology/Embedding/TruncLEHomology.lean
@@ -84,6 +84,11 @@ lemma acyclic_truncLE_iff_isSupportedOutside :
variable {K L}
+lemma Acyclic.truncLE (hK : K.Acyclic) (e : c.Embedding c') [e.IsTruncLE] :
+ (K.truncLE e).Acyclic := by
+ rw [acyclic_truncLE_iff_isSupportedOutside]
+ exact ⟨fun _ ↦ hK _⟩
+
lemma quasiIso_truncLEMap_iff :
QuasiIso (truncLEMap φ e) ↔ ∀ (i : ι) (i' : ι') (_ : e.f i = i'), QuasiIsoAt φ i' := by
rw [← quasiIso_opFunctor_map_iff]
From 7303d3ac82e38d773c820841c73fffeaaf8fd46e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=ABl=20Riou?=
<37772949+joelriou@users.noreply.github.com>
Date: Mon, 22 Jun 2026 12:31:45 +0000
Subject: [PATCH 0244/1300] feat(CategoryTheory/Shift): left composition with a
shift sequence (#40888)
---
.../CategoryTheory/Shift/ShiftSequence.lean | 38 +++++++++++++++++--
1 file changed, 35 insertions(+), 3 deletions(-)
diff --git a/Mathlib/CategoryTheory/Shift/ShiftSequence.lean b/Mathlib/CategoryTheory/Shift/ShiftSequence.lean
index 47ada4ddad480f..a94a1a3ff4b091 100644
--- a/Mathlib/CategoryTheory/Shift/ShiftSequence.lean
+++ b/Mathlib/CategoryTheory/Shift/ShiftSequence.lean
@@ -5,7 +5,7 @@ Authors: Joël Riou
-/
module
-public import Mathlib.CategoryTheory.Shift.Basic
+public import Mathlib.CategoryTheory.Shift.CommShift
public import Mathlib.CategoryTheory.Preadditive.AdditiveFunctor
/-! # Sequences of functors from a category equipped with a shift
@@ -33,8 +33,9 @@ set_option backward.defeqAttrib.useBackward true
open CategoryTheory Category ZeroObject Limits
-variable {C A : Type*} [Category* C] [Category* A] (F : C ⥤ A)
- (M : Type*) [AddMonoid M] [HasShift C M]
+variable {C D A : Type*} [Category* C] [Category* D] [Category* A] (F : C ⥤ A)
+ {π : C ⥤ D} {H : D ⥤ A} (e : π ⋙ H ≅ F)
+ (M : Type*) [AddMonoid M] [HasShift C M] [HasShift D M]
{G : Type*} [AddGroup G] [HasShift C G]
namespace CategoryTheory
@@ -283,6 +284,37 @@ end
end
+namespace ShiftSequence
+
+variable {F} in
+set_option backward.isDefEq.respectTransparency false in
+/-- Given an isomorphism `π ⋙ H ≅ F`, where `π` is a functor which commutes
+with the shift by `M` and `H` is equipped with a shift sequence,
+then this is the shift sequence for `F` induced by composition. -/
+@[implicit_reducible, simps]
+def leftComp [π.CommShift M] [H.ShiftSequence M] : F.ShiftSequence M where
+ sequence n := π ⋙ H.shift n
+ isoZero := isoWhiskerLeft π (H.isoShiftZero M) ≪≫ e
+ shiftIso n a a' ha' :=
+ (Functor.associator _ _ _).symm ≪≫
+ isoWhiskerRight (π.commShiftIso n) _ ≪≫ Functor.associator _ _ _ ≪≫
+ isoWhiskerLeft π (H.shiftIso n a a' ha')
+ shiftIso_zero a := by
+ ext K
+ simp [← Functor.map_comp, commShiftIso_zero]
+ shiftIso_add n m a a' a'' ha' ha'':= by
+ ext K
+ dsimp
+ simp only [H.shiftIso_add_hom_app n m a a' a'' ha' ha'', assoc,
+ commShiftIso_add, CommShift.isoAdd_hom_app, ← Functor.map_comp_assoc,
+ id_comp, Iso.inv_hom_id_app, comp_obj, comp_id]
+ simp
+
+instance [π.CommShift M] [H.ShiftSequence M] : (π ⋙ H).ShiftSequence M :=
+ leftComp (Iso.refl _) _
+
+end ShiftSequence
+
end Functor
end CategoryTheory
From 3052b11bee97540fb6feb75f8e4a392be7618add Mon Sep 17 00:00:00 2001
From: Christian Merten <136261474+chrisflav@users.noreply.github.com>
Date: Mon, 22 Jun 2026 12:53:37 +0000
Subject: [PATCH 0245/1300] feat(Algebra/Category): pushforward of
quasi-coherent sheafs (#40231)
We show that the pushforward of a quasi-coherent sheaf along a continuous and cocontinuous functor is quasi-coherent, if it preserves the unit.
---
.../Sheaf/PushforwardContinuous.lean | 17 ++++
.../ModuleCat/Sheaf/Quasicoherent.lean | 81 +++++++++++++++++++
.../CategoryTheory/Comma/Over/Pullback.lean | 6 ++
Mathlib/CategoryTheory/Sites/Continuous.lean | 5 ++
.../CategoryTheory/Sites/CoverLifting.lean | 7 ++
Mathlib/CategoryTheory/Sites/Over.lean | 6 ++
6 files changed, 122 insertions(+)
diff --git a/Mathlib/Algebra/Category/ModuleCat/Sheaf/PushforwardContinuous.lean b/Mathlib/Algebra/Category/ModuleCat/Sheaf/PushforwardContinuous.lean
index 3686a18ae2f518..ba6bd327e13c56 100644
--- a/Mathlib/Algebra/Category/ModuleCat/Sheaf/PushforwardContinuous.lean
+++ b/Mathlib/Algebra/Category/ModuleCat/Sheaf/PushforwardContinuous.lean
@@ -292,6 +292,23 @@ lemma pushforwardPushforwardAdj_counit_app_val_app (M U x) :
((pushforwardPushforwardAdj adj φ ψ H₁ H₂).counit.app M).val.app U x =
M.val.map (adj.unit.app U.unop).op x := rfl
+set_option backward.defeqAttrib.useBackward true in
+instance isLeftAdjoint_pushforward_of_isIso [F.IsCocontinuous J K] [IsIso φ] [F.IsLeftAdjoint] :
+ (pushforward.{u} φ).IsLeftAdjoint := by
+ let adj := Adjunction.ofIsLeftAdjoint F
+ let shAdj := adj.sheafPushforwardContinuous (E := RingCat.{u}) J K
+ let ψ : R ⟶ (F.rightAdjoint.sheafPushforwardContinuous RingCat.{u} K J).obj S :=
+ shAdj.unit.app R ≫ (F.rightAdjoint.sheafPushforwardContinuous _ _ _).map (inv φ)
+ refine (SheafOfModules.pushforwardPushforwardAdj adj φ ψ ?_ ?_).isLeftAdjoint
+ · ext U : 2
+ simp [ψ, shAdj]
+ · ext U : 2
+ have := (inv φ).hom.naturality
+ dsimp at this
+ simp only [ObjectProperty.hom_inv, NatIso.isIso_inv_app, sheafPushforwardContinuous_obj_obj_obj,
+ IsIso.eq_inv_comp] at this
+ simp [ψ, shAdj, ← this, ← Functor.map_comp_assoc, ← op_comp]
+
noncomputable section
open CategoryTheory Limits
diff --git a/Mathlib/Algebra/Category/ModuleCat/Sheaf/Quasicoherent.lean b/Mathlib/Algebra/Category/ModuleCat/Sheaf/Quasicoherent.lean
index 0356d546d27ba4..dabdb76cef0767 100644
--- a/Mathlib/Algebra/Category/ModuleCat/Sheaf/Quasicoherent.lean
+++ b/Mathlib/Algebra/Category/ModuleCat/Sheaf/Quasicoherent.lean
@@ -282,6 +282,81 @@ instance (M : SheafOfModules.{u} R) [M.IsFinitePresentation] :
obtain ⟨σ, _⟩ := IsFinitePresentation.exists_quasicoherentData M
exact ⟨σ.localGeneratorsData, inferInstance⟩
+section map
+
+variable {D : Type u₂} [Category.{v₂, u₂} D] {K : GrothendieckTopology D}
+ {S : Sheaf K RingCat.{u}} [∀ (X : D), (K.over X).WEqualsLocallyBijective AddCommGrpCat]
+ [∀ (X : D), (K.over X).HasSheafCompose (forget₂ RingCat AddCommGrpCat)]
+
+variable [J.HasSheafCompose (forget₂ RingCat AddCommGrpCat)]
+ [K.HasSheafCompose (forget₂ RingCat.{u} AddCommGrpCat.{u})]
+ [∀ (X : C), HasSheafify (J.over X) AddCommGrpCat.{u}]
+ [∀ (X : D), HasSheafify (K.over X) AddCommGrpCat.{u}]
+
+variable (G : D ⥤ C) [G.IsContinuous K J] [G.IsCocontinuous K J]
+ (φ : S ⟶ (G.sheafPushforwardContinuous RingCat.{u} K J).obj R)
+
+/-- The pushforward of `SheafOfModules.QuasicoherentData` along a continuous
+and cocontinuous functor. -/
+-- TODO: Remove the continuous assumption on `Over.post` here and below.
+@[simps I X]
+noncomputable def QuasicoherentData.pushforward (η : (pushforward φ).obj (unit R) ≅ unit S)
+ [∀ (X : D), (Over.post G).IsContinuous (K.over X) (J.over _)]
+ (h : ∀ (X : D) (Y : C) (f : G.obj X ⟶ Y),
+ PreservesColimitsOfSize.{u, u} <|
+ pushforward.{u} (R := (R.over Y)) (F := Over.post (X := X) G ⋙ Over.map f)
+ (((Over.forget X).sheafPushforwardContinuous RingCat.{u} (K.over X) K).map φ))
+ {M : SheafOfModules.{u} R} (P : M.QuasicoherentData) :
+ QuasicoherentData ((pushforward φ).obj M) where
+ I := Σ (X : D) (i : P.I), G.obj X ⟶ P.X i
+ X i := i.1
+ coversTop Y := by
+ refine K.superset_covering ?_ <| G.cover_lift K _ (P.coversTop (G.obj Y))
+ intro Z g ⟨i, ⟨v⟩⟩
+ exact ⟨⟨Z, i, v⟩, ⟨𝟙 _⟩⟩
+ presentation i := by
+ letI overS : SheafOfModules.{u} S ⥤ SheafOfModules.{u} (S.over i.1) :=
+ SheafOfModules.pushforward (𝟙 _)
+ letI G' := Over.post (X := i.1) G ⋙ Over.map i.2.2
+ letI ψ : S.over i.1 ⟶
+ (G'.sheafPushforwardContinuous RingCat.{u} (K.over i.1) (J.over (P.X i.2.1))).obj
+ (R.over (P.X i.2.1)) :=
+ ((Over.forget i.1).sheafPushforwardContinuous RingCat.{u} (K.over i.1) K).map φ
+ letI e : (SheafOfModules.pushforward ψ).obj (unit (R.over (P.X i.snd.fst))) ≅
+ unit (S.over i.fst) := overS.mapIso η
+ haveI : PreservesColimitsOfSize.{u, u, _} (SheafOfModules.pushforward ψ) := h _ _ _
+ exact (P.presentation i.2.1).map (SheafOfModules.pushforward ψ) e.symm
+
+lemma isQuasicoherent_pushforward (η : (pushforward φ).obj (unit R) ≅ unit S)
+ [∀ (X : D), (Over.post G).IsContinuous (K.over X) (J.over _)]
+ (h : ∀ (X : D) (Y : C) (f : G.obj X ⟶ Y),
+ PreservesColimitsOfSize.{u, u} <|
+ pushforward.{u} (R := (R.over Y)) (F := Over.post (X := X) G ⋙ Over.map f)
+ (((Over.forget X).sheafPushforwardContinuous RingCat.{u} (K.over X) K).map φ))
+ {M : SheafOfModules.{u} R} [IsQuasicoherent M] :
+ IsQuasicoherent ((pushforward φ).obj M) :=
+ IsQuasicoherent.nonempty_quasicoherentData.some.pushforward G φ η h |>.isQuasicoherent
+
+set_option backward.isDefEq.respectTransparency false in
+lemma isQuasicoherent_pushforward_of_isLeftAdjoint (η : (pushforward φ).obj (unit R) ≅ unit S)
+ [G.IsLeftAdjoint] [IsIso φ]
+ [∀ X, Functor.IsContinuous (Over.post (X := X) G) (K.over _) (J.over _)]
+ [HasPullbacks C] [HasPullbacks D]
+ {M : SheafOfModules.{u} R} [IsQuasicoherent M] :
+ IsQuasicoherent ((pushforward φ).obj M) := by
+ apply +allowSynthFailures isQuasicoherent_pushforward G φ η _
+ intro X Y f
+ let G' := Over.post (X := X) G ⋙ Over.map f
+ have : G'.IsContinuous (K.over X) (J.over Y) := Functor.isContinuous_comp _ _ _ (J.over _) _
+ have : G'.IsCocontinuous (K.over X) (J.over Y) := isCocontinuous_comp _ _ _ (J.over _)
+ let a : S.over X ⟶
+ (G'.sheafPushforwardContinuous RingCat.{u} (K.over X) (J.over Y)).obj (R.over Y) :=
+ ((Over.forget X).sheafPushforwardContinuous RingCat.{u} (K.over X) K).map φ
+ have : (pushforward.{u} a).IsLeftAdjoint := isLeftAdjoint_pushforward_of_isIso a
+ infer_instance
+
+end map
+
end
noncomputable section
@@ -375,6 +450,12 @@ lemma IsQuasicoherent.of_coversTop {R : Sheaf J RingCat.{u}}
(QuasicoherentData.bind M X hX fun _ ↦
IsQuasicoherent.nonempty_quasicoherentData.some).isQuasicoherent
+set_option backward.isDefEq.respectTransparency false in
+lemma isQuasicoherent_over [J.HasSheafCompose (forget₂ RingCat.{u} AddCommGrpCat.{u})]
+ [HasPullbacks C] [HasBinaryProducts C] (M : SheafOfModules.{u} R) (X : C) [IsQuasicoherent M] :
+ IsQuasicoherent (M.over X) :=
+ isQuasicoherent_pushforward_of_isLeftAdjoint _ _ (Iso.refl _)
+
end bind
end SheafOfModules
diff --git a/Mathlib/CategoryTheory/Comma/Over/Pullback.lean b/Mathlib/CategoryTheory/Comma/Over/Pullback.lean
index 4eb249b5a8a64d..45e64ac8d25c1c 100644
--- a/Mathlib/CategoryTheory/Comma/Over/Pullback.lean
+++ b/Mathlib/CategoryTheory/Comma/Over/Pullback.lean
@@ -87,6 +87,12 @@ def mapPullbackAdj {X Y : C} (f : X ⟶ Y) [HasPullbacksAlong f] :
· simp
· simpa using (Over.w v).symm } }
+instance {X Y : C} (f : X ⟶ Y) [HasPullbacksAlong f] : (Over.map f).IsLeftAdjoint :=
+ (Over.mapPullbackAdj f).isLeftAdjoint
+
+instance {X Y : C} (f : X ⟶ Y) [HasPullbacksAlong f] : (Over.pullback f).IsRightAdjoint :=
+ (Over.mapPullbackAdj f).isRightAdjoint
+
set_option backward.isDefEq.respectTransparency false in
/-- The pullback along an epi that's preserved under pullbacks is faithful.
diff --git a/Mathlib/CategoryTheory/Sites/Continuous.lean b/Mathlib/CategoryTheory/Sites/Continuous.lean
index 51995a1d8a49f6..48d970342868c4 100644
--- a/Mathlib/CategoryTheory/Sites/Continuous.lean
+++ b/Mathlib/CategoryTheory/Sites/Continuous.lean
@@ -80,6 +80,11 @@ lemma map_id : E.map (𝟭 _) = E :=
lemma map_comp {D' : Type*} [Category* D'] (G : D ⥤ D') : E.map (F ⋙ G) = (E.map F).map G :=
rfl
+lemma sieve₀_map : (E.map F).sieve₀ = Sieve.functorPushforward _ E.sieve₀ := by
+ rw [PreZeroHypercover.sieve₀, Sieve.ofArrows, ← PreZeroHypercover.presieve₀,
+ PreOneHypercover.map_toPreZeroHypercover, PreZeroHypercover.presieve₀_map,
+ Sieve.generate_map_eq_functorPushforward]
+
/-- If `F : C ⥤ D`, `P : Dᵒᵖ ⥤ A` and `E` is a 1-pre-hypercover of an object of `X`,
then `(E.map F).multifork P` is a limit iff `E.multifork (F.op ⋙ P)` is a limit. -/
def isLimitMapMultiforkEquiv {A : Type u} [Category.{t} A] (P : Dᵒᵖ ⥤ A) :
diff --git a/Mathlib/CategoryTheory/Sites/CoverLifting.lean b/Mathlib/CategoryTheory/Sites/CoverLifting.lean
index daec56154214c6..7590a8bf0ece84 100644
--- a/Mathlib/CategoryTheory/Sites/CoverLifting.lean
+++ b/Mathlib/CategoryTheory/Sites/CoverLifting.lean
@@ -102,6 +102,13 @@ lemma Functor.IsCocontinuous.iff_of_iso {F G : C ⥤ D} (e : F ≅ G) :
F.IsCocontinuous J K ↔ G.IsCocontinuous J K :=
⟨fun _ ↦ .of_iso e, fun _ ↦ .of_iso e.symm⟩
+lemma CoverPreserving.of_comp_of_isCocontinuous {F : C ⥤ D} (G : D ⥤ E)
+ (h : CoverPreserving J L (F ⋙ G)) [G.IsCocontinuous K L] [G.Full] [G.Faithful] :
+ CoverPreserving J K F where
+ cover_preserve {U} S hS := by
+ refine K.superset_covering ?_ (G.cover_lift K _ (h.cover_preserve hS))
+ rw [Sieve.functorPushforward_comp, Sieve.functorPullback_functorPushforward_eq G]
+
section
variable {F : C ⥤ D} {G : D ⥤ C}
diff --git a/Mathlib/CategoryTheory/Sites/Over.lean b/Mathlib/CategoryTheory/Sites/Over.lean
index fcbd6425dcac7a..b008c77f36e1fb 100644
--- a/Mathlib/CategoryTheory/Sites/Over.lean
+++ b/Mathlib/CategoryTheory/Sites/Over.lean
@@ -361,6 +361,12 @@ instance {D : Type*} [Category* D] {J : GrothendieckTopology C} {K : Grothendiec
← PreOneHypercover.map_comp, Over.post_forget_eq_forget_comp, PreOneHypercover.map_comp]
exact E'.mem₁ _ _ _ _ congr($(w).left)
+instance {D : Type*} [Category* D] {J : GrothendieckTopology C} {K : GrothendieckTopology D}
+ {F : C ⥤ D} (X : C) (Y : D) (f : F.obj X ⟶ Y)
+ [(Over.post F).IsContinuous (J.over X) (K.over _)] :
+ (Over.post F ⋙ Over.map f).IsContinuous (J.over X) (K.over Y) :=
+ Functor.isContinuous_comp _ _ _ (K.over _) _
+
open Limits
lemma coverPreserving_overPullback [HasPullbacks C] {X Y : C} (f : X ⟶ Y) :
From aa67fe566fe0ce9a8013fe869459f81b4b0a7c1a Mon Sep 17 00:00:00 2001
From: Felix Pernegger <188575194+felixpernegger@users.noreply.github.com>
Date: Mon, 22 Jun 2026 13:37:06 +0000
Subject: [PATCH 0246/1300] chore(Topology): remove nonterminal simps (#40883)
Together with #40882, this removes all occurences of `set_option linter.flexible false in` in `Mathlib/Topology`.
Co-authored-by: Batixx
---
Mathlib/Topology/Algebra/InfiniteSum/SummationFilter.lean | 3 +--
Mathlib/Topology/UniformSpace/Ultra/Completion.lean | 8 +++-----
Mathlib/Topology/UniformSpace/Ultra/Constructions.lean | 6 ++----
3 files changed, 6 insertions(+), 11 deletions(-)
diff --git a/Mathlib/Topology/Algebra/InfiniteSum/SummationFilter.lean b/Mathlib/Topology/Algebra/InfiniteSum/SummationFilter.lean
index 85e6942e6f3c1c..293cc2d1ff385e 100644
--- a/Mathlib/Topology/Algebra/InfiniteSum/SummationFilter.lean
+++ b/Mathlib/Topology/Algebra/InfiniteSum/SummationFilter.lean
@@ -220,9 +220,8 @@ instance : (conditional β).LeAtTop := ⟨support_eq_univ_iff.mp <| by
simpa [eq_univ_iff_forall, support, -eventually_and]
using! fun x ↦ prod_mem_prod (eventually_le_atBot x) (eventually_ge_atTop x)⟩
-set_option linter.flexible false in -- simp followed by infer_instance
instance [Nonempty β] [IsDirectedOrder β] [IsCodirectedOrder β] : (conditional β).NeBot :=
- ⟨by simp; infer_instance⟩
+ ⟨by rw [conditional_filter]; infer_instance⟩
instance [IsCountablyGenerated (atTop : Filter β)] [IsCountablyGenerated (atBot : Filter β)] :
IsCountablyGenerated (conditional β).filter :=
diff --git a/Mathlib/Topology/UniformSpace/Ultra/Completion.lean b/Mathlib/Topology/UniformSpace/Ultra/Completion.lean
index 5aec77b1f95289..d06e74a7cea05e 100644
--- a/Mathlib/Topology/UniformSpace/Ultra/Completion.lean
+++ b/Mathlib/Topology/UniformSpace/Ultra/Completion.lean
@@ -36,26 +36,24 @@ instance CauchyFilter.isSymm_gen {s : SetRel X X} [s.IsSymm] : (gen s).IsSymm wh
instance CauchyFilter.isTrans_gen {s : SetRel X X} [s.IsTrans] : (gen s).IsTrans where
trans _ _ _ := IsTransitiveRel.mem_filter_prod_trans
-set_option linter.flexible false in -- simp followed by infer_instance
instance IsUltraUniformity.cauchyFilter [IsUltraUniformity X] :
IsUltraUniformity (CauchyFilter X) := by
apply mk_of_hasBasis (CauchyFilter.basis_uniformity IsUltraUniformity.hasBasis)
- · exact fun _ ⟨_, hU, _⟩ ↦ by simp; infer_instance
- · exact fun _ ⟨_, _, hU⟩ ↦ by simp; infer_instance
+ · exact fun _ ⟨_, hU, _⟩ ↦ by simpa using CauchyFilter.isSymm_gen
+ · exact fun _ ⟨_, _, hU⟩ ↦ by simpa using CauchyFilter.isTrans_gen
@[simp] lemma IsUltraUniformity.cauchyFilter_iff :
IsUltraUniformity (CauchyFilter X) ↔ IsUltraUniformity X :=
⟨fun _ ↦ CauchyFilter.isUniformInducing_pureCauchy.isUltraUniformity,
fun _ ↦ inferInstance⟩
-set_option linter.flexible false in -- simp followed by infer_instance
instance IsUltraUniformity.separationQuotient [IsUltraUniformity X] :
IsUltraUniformity (SeparationQuotient X) := by
have := IsUltraUniformity.hasBasis.map
(Prod.map SeparationQuotient.mk (SeparationQuotient.mk (X := X)))
rw [← SeparationQuotient.uniformity_eq] at this
apply mk_of_hasBasis this
- · exact fun _ ⟨_, hU, _⟩ ↦ by simp; infer_instance
+ · exact fun _ ⟨_, hU, _⟩ ↦ by rw [id_eq]; infer_instance
· rintro U ⟨hU', _, hU⟩
constructor
rintro x y z
diff --git a/Mathlib/Topology/UniformSpace/Ultra/Constructions.lean b/Mathlib/Topology/UniformSpace/Ultra/Constructions.lean
index 04bc6ab33be7c0..452694a5ae1732 100644
--- a/Mathlib/Topology/UniformSpace/Ultra/Constructions.lean
+++ b/Mathlib/Topology/UniformSpace/Ultra/Constructions.lean
@@ -77,15 +77,13 @@ instance IsUltraUniformity.pi {ι : Type*} {X : ι → Type*} [U : Π i, Uniform
simpa +instances [Pi.uniformSpace_eq _] using this
exact .iInf fun i ↦ .comap (h i) (Function.eval i)
-set_option linter.flexible false in -- simp followed by infer_instance
instance IsUltraUniformity.bot [UniformSpace X] [DiscreteUniformity X] : IsUltraUniformity X := by
have := Filter.hasBasis_principal (SetRel.id (α := X))
rw [← DiscreteUniformity.eq_principal_setRelId] at this
- apply mk_of_hasBasis this <;> { simp; infer_instance }
+ apply mk_of_hasBasis this <;> { rw [forall_const]; infer_instance }
-set_option linter.flexible false in -- simp followed by infer_instance
lemma IsUltraUniformity.top : @IsUltraUniformity X (⊤ : UniformSpace X) := by
letI : UniformSpace X := ⊤
have := Filter.hasBasis_top (α := (X × X))
rw [← top_uniformity] at this
- apply mk_of_hasBasis this <;> { simp; infer_instance }
+ apply mk_of_hasBasis this <;> { rw [forall_const]; infer_instance }
From 42cb1fb8e141dde687c38791d4e1822bba057b86 Mon Sep 17 00:00:00 2001
From: Whysoserioushah <109107491+Whysoserioushah@users.noreply.github.com>
Date: Mon, 22 Jun 2026 14:05:21 +0000
Subject: [PATCH 0247/1300] feat(Projectivization/PSL/PSL2): PSL(2, F) is
simple (#40000)
---
Mathlib.lean | 1 +
.../Matrix/SpecialLinearGroup.lean | 222 +++++++++++++++++-
.../Projectivization/PSL/PSL2.lean | 137 +++++++++++
3 files changed, 347 insertions(+), 13 deletions(-)
create mode 100644 Mathlib/LinearAlgebra/Projectivization/PSL/PSL2.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 7a3a3e97044a3a..319c63732033c9 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -5167,6 +5167,7 @@ public import Mathlib.LinearAlgebra.Projectivization.Cardinality
public import Mathlib.LinearAlgebra.Projectivization.Collinear
public import Mathlib.LinearAlgebra.Projectivization.Constructions
public import Mathlib.LinearAlgebra.Projectivization.Independence
+public import Mathlib.LinearAlgebra.Projectivization.PSL.PSL2
public import Mathlib.LinearAlgebra.Projectivization.PSL.Stabilizer
public import Mathlib.LinearAlgebra.Projectivization.Subspace
public import Mathlib.LinearAlgebra.QuadraticForm.AlgClosed
diff --git a/Mathlib/LinearAlgebra/Matrix/SpecialLinearGroup.lean b/Mathlib/LinearAlgebra/Matrix/SpecialLinearGroup.lean
index c1d4b8090487ed..1ba86b265e8534 100644
--- a/Mathlib/LinearAlgebra/Matrix/SpecialLinearGroup.lean
+++ b/Mathlib/LinearAlgebra/Matrix/SpecialLinearGroup.lean
@@ -489,22 +489,38 @@ variable {ι F : Type*} [DecidableEq ι] [Fintype ι] [CommRing F]
/-- The transvection `1 + b · E_{i,j}` (the identity plus `b` in position `(i, j)`)
as an element of `SL ι F`, when `i ≠ j`. -/
-noncomputable def SpecialLinearGroup.transvection {i j : ι} (hij : i ≠ j) (b : F) :
+def SpecialLinearGroup.transvection {i j : ι} (hij : i ≠ j) (b : F) :
Matrix.SpecialLinearGroup ι F :=
- ⟨(1 : Matrix ι ι F) + single i j b, Matrix.det_transvection_of_ne i j hij b⟩
+ ⟨Matrix.transvection i j b, Matrix.det_transvection_of_ne i j hij b⟩
namespace SpecialLinearGroup
lemma transvection_coe {i j : ι} (hij : i ≠ j) (b : F) :
(transvection hij b) = (1 : Matrix ι ι F) + single i j b := rfl
+@[simp]
+lemma transvection_coeff_zero {i j : ι} (hij : i ≠ j) :
+ transvection hij (0 : F) = 1 := by ext; simp [transvection_coe]
+
/-- The transvection `transvection i j hij b` acts on `e_i = Pi.single i 1` as the identity. -/
+lemma transvection_smul_single_fst {i j : ι} (hij : i ≠ j) (b : F) :
+ (transvection hij b) • (Pi.single i 1 : ι → F) = Pi.single i 1 := by
+ simp [SpecialLinearGroup.smul_def, -mulVec_single, transvection_coe,
+ add_mulVec, single_mulVec_eq, hij]
+
+@[deprecated transvection_smul_single_fst (since := "2026-06-22")]
lemma transvection_mulVec_single_self {i j : ι} (hij : i ≠ j) (b : F) :
(transvection hij b).1 *ᵥ (Pi.single i (1 : F)) = Pi.single i 1 := by
rw [transvection_coe]
simp [-mulVec_single, add_mulVec, single_mulVec_eq, hij]
/-- The transvection `transvection i j hij b` acts on `e_j = Pi.single j 1` by adding `b·e_i`. -/
+lemma transvection_smul_single_snd {i j : ι} (hij : i ≠ j) (b : F) :
+ (transvection hij b) • (Pi.single j 1 : ι → F) = Pi.single j 1 + b • Pi.single i 1 := by
+ simp [SpecialLinearGroup.smul_def, transvection_coe, -mulVec_single,
+ add_mulVec, single_mulVec_eq]
+
+@[deprecated transvection_smul_single_snd (since := "2026-06-22")]
lemma transvection_mulVec_single_other {i j : ι} (hij : i ≠ j) (b : F) :
(transvection hij b).1 *ᵥ (Pi.single j (1 : F)) = Pi.single j 1 + b • Pi.single i 1 := by
rw [transvection_coe]
@@ -512,8 +528,7 @@ lemma transvection_mulVec_single_other {i j : ι} (hij : i ≠ j) (b : F) :
/-- Inverse of a transvection: `transvection i j hij b * transvection i j hij (-b) = 1`. -/
lemma transvection_mul_neg {i j : ι} (hij : i ≠ j) (b : F) :
- transvection hij b * transvection hij (-b) = 1 := by
- ext : 1
+ transvection hij b * transvection hij (-b) = 1 := Subtype.ext <| by
simp [transvection_coe, mul_add, add_mul,
single_mul_single_of_ne _ _ _ _ hij.symm, ← single_neg]
@@ -537,6 +552,29 @@ lemma transvection_mem_center_iff {i j : ι} (hij : i ≠ j) (b : F) :
end SpecialLinearGroup
+namespace TransvectionStruct
+
+variable {n R : Type*} [Fintype n] [DecidableEq n] [CommRing R]
+
+/-- Any transvection structure can be converted to a special linear matrix. -/
+def toSpecialLinearGroup (t : TransvectionStruct ι F) :
+ SpecialLinearGroup ι F :=
+ SpecialLinearGroup.transvection t.hij t.c
+
+lemma toSpecialLinearGroup_def (t : TransvectionStruct ι F) :
+ t.toSpecialLinearGroup = SpecialLinearGroup.transvection t.hij t.c := rfl
+
+@[simp]
+lemma toSpecialLinearGroup_coe (t : TransvectionStruct ι F) :
+ (t.toSpecialLinearGroup : Matrix ι ι F) = t.toMatrix := rfl
+
+@[simp]
+lemma toSpecialLinearGroup_mk {i j : ι} (hij : i ≠ j) (c : F) :
+ (TransvectionStruct.mk i j hij c).toSpecialLinearGroup =
+ SpecialLinearGroup.transvection hij c := rfl
+
+end TransvectionStruct
+
end transvection
section SL2
@@ -545,6 +583,8 @@ variable {F : Type*} [Field F]
open MatrixGroups
+namespace SpecialLinearGroup
+
/-- An element in SLₙ(F) induced by a diagonal matrix `1` on any other entries and `a`, `a⁻¹` on
positition `i` and `j` respectively where `i ≠ j`. -/
noncomputable def diag2n {ι : Type*} [Fintype ι] [DecidableEq ι] {i j : ι} (hij : i ≠ j) (a : F)
@@ -562,18 +602,23 @@ lemma diag2n_coe {ι : Type*} [Fintype ι] [DecidableEq ι] {i j : ι} (hij : i
noncomputable abbrev diag2 (a : F) (ha : a ≠ 0) : SL(2, F) :=
diag2n zero_ne_one a ha
+lemma diag2_def {a : F} (ha : a ≠ 0) : diag2 a ha = diag2n zero_ne_one a ha := rfl
+
lemma diag2_coe (a : F) (ha : a ≠ 0) :
(diag2 a ha).1 = diagonal (fun i ↦ match i with | 0 => a|1 => a⁻¹) := by simp [diag2n_coe]
-lemma diag2_mulVec_single_i₁ (a : F) (ha : a ≠ 0) :
- (diag2 a ha).1 *ᵥ (Pi.single 0 (1 : F)) = a • Pi.single 0 (1 : F) := by
- ext k; fin_cases k <;> simp [diag2_coe]
+lemma diag2_coe' {a : F} (ha : a ≠ 0) :
+ (diag2 a ha).1 = !![a, 0; 0, a⁻¹] := by
+ ext i j
+ fin_cases i <;> fin_cases j <;> simp [diag2n_coe]
+
+lemma diag2_smul_single_i₁ {a : F} (ha : a ≠ 0) :
+ diag2 a ha • (Pi.single 0 1 : Fin 2 → F) = a • Pi.single 0 (1 : F) := by
+ ext k; fin_cases k <;> simp [Matrix.SpecialLinearGroup.smul_def, diag2_coe]
-lemma diag2_mulVec_single_i₂
- (a : F) (ha : a ≠ 0) :
- (diag2 a ha).1 *ᵥ (Pi.single 1 (1 : F)) =
- a⁻¹ • Pi.single 1 (1 : F) := by
- ext k; fin_cases k <;> simp [diag2_coe]
+lemma diag2_smul_single_i₂ {a : F} (ha : a ≠ 0) :
+ diag2 a ha • (Pi.single 1 1 : Fin 2 → F) = a⁻¹ • Pi.single 1 (1 : F) := by
+ ext k; fin_cases k <;> simp [Matrix.SpecialLinearGroup.smul_def, diag2_coe]
lemma diag2_mul_inv (a : F) (ha : a ≠ 0) :
diag2 a ha * diag2 a⁻¹ (inv_ne_zero ha) = 1 := Subtype.ext <| by
@@ -584,6 +629,102 @@ lemma diag2_inv (a : F) (ha : a ≠ 0) :
apply inv_eq_of_mul_eq_one_right
exact diag2_mul_inv a ha
+section induction
+
+variable {ι R : Type*} [Fintype ι] [DecidableEq ι] [CommRing R]
+
+/-- the coercion to `Matrix ι ι R` as a monoid homomorphism -/
+def coeMonoidHom : SpecialLinearGroup ι R →* Matrix ι ι R where
+ toFun := Subtype.val
+ map_one' := rfl
+ map_mul' _ _ := rfl
+
+@[simp]
+lemma coeMonoidHom_apply (g : SpecialLinearGroup ι R) : coeMonoidHom g = (g : Matrix ι ι R) := rfl
+
+lemma coeMonoidHom_injective : Function.Injective (coeMonoidHom : SpecialLinearGroup ι R → _) :=
+ Subtype.val_injective
+
+private lemma diag_decompose (i₀ : ι) (D : ι → F) (hD : det (diagonal D) = 1) :
+ Finset.prod {i | i ≠ i₀} (fun i k ↦ if k = i then D i else
+ if k = i₀ then (D i)⁻¹ else 1 : ι → ι → F) = D := by
+ rw [det_diagonal, show Finset.univ = insert i₀ ({i | i ≠ i₀} : Finset ι) by grind,
+ Finset.prod_insert (by grind), mul_eq_one_iff_eq_inv₀ (by grind),
+ ← Finset.prod_inv_distrib] at hD
+ ext x
+ by_cases hx : x = i₀
+ · simpa [hx, hD, -Finset.prod_inv_distrib] using Finset.prod_congr rfl (by grind)
+ · simp [hx]
+
+lemma diagonal_neZero (D : ι → F) (hD : det (diagonal D) = 1) (j : ι) :
+ D j ≠ 0 := fun h ↦ by
+ rw [det_diagonal, show Finset.univ = insert j ({i | i ≠ j} : Finset ι) by grind,
+ Finset.prod_insert (by grind), h, zero_mul] at hD
+ exact zero_ne_one hD
+
+lemma diag_commute (i₀ : ι) (D : ι → F) (hD : det (diagonal D) = 1) :
+ (({i | i ≠ i₀} : Finset ι) : Set ι).Pairwise (Function.onFun Commute fun i ↦
+ if hi : i ≠ i₀ then diag2n hi (D i) (diagonal_neZero D hD i) else 1) := by
+ intro i1 hi1 i2 hi2 hi12
+ ext i j
+ simp [apply_dite, diag2n_coe]
+ split_ifs <;> simp [diagonal_apply]; grind
+
+lemma diag_eq_diag2n_prod (i₀ : ι) (D : ι → F) (hD : det (diagonal D) = 1) :
+ (⟨diagonal D, hD⟩ : SpecialLinearGroup ι F) =
+ Finset.noncommProd {i : ι | i ≠ i₀} (fun i ↦ if hi : i ≠ i₀ then
+ diag2n hi (D i) (diagonal_neZero D hD i) else 1) (diag_commute i₀ D hD) := by
+ classical
+ set g : ι → ι → F := fun i k ↦ if k = i then D i else if k = i₀ then (D i)⁻¹ else 1 with hg_def
+ apply coeMonoidHom_injective
+ rw [Finset.map_noncommProd]
+ simp_rw [coeMonoidHom_apply, apply_dite, coe_one]
+ rw [Finset.noncommProd_congr (s₂ := {i | i ≠ i₀}) rfl (fun i hi ↦
+ (dif_pos (Finset.mem_filter.1 hi).2 : _ = (diag2n (Finset.mem_filter.1 hi).2 _ _).1))]
+ convert_to! _ = Finset.noncommProd {i | i ≠ i₀} (fun x ↦ diagonal (g x)) _
+ simp_rw [← diagonalRingHom_apply]
+ rw [← Finset.map_noncommProd _ _ (fun _ _ _ _ _ ↦ Commute.all _ _), Finset.noncommProd_eq_prod]
+ rw [diag_decompose i₀ D hD]
+
+/-- The `SpecialLinearGroup` analogue of
+ `Matrix.Pivot.exists_list_transvec_mul_diagonal_mul_list_transvec`:
+ every element of `SL(ι, F)` is a product of transvections,
+ a diagonal matrix of determinant `1`, and transvections. -/
+theorem exists_list_transvec_mul_diagonal_mul_list_transvec (M : SpecialLinearGroup ι F) :
+ ∃ (L L' : List (TransvectionStruct ι F)) (D : ι → F) (hD : det (diagonal D) = 1),
+ M = (L.map TransvectionStruct.toSpecialLinearGroup).prod * ⟨diagonal D, hD⟩ *
+ (L'.map TransvectionStruct.toSpecialLinearGroup).prod := by
+ obtain ⟨L, L', D, hM⟩ := Pivot.exists_list_transvec_mul_diagonal_mul_list_transvec M.1
+ refine ⟨L, L', D, by simpa [hM] using M.2, Subtype.ext <| ?_⟩
+ simp_rw [coe_mul, ← coeMonoidHom_apply, map_list_prod, List.map_map, Function.comp_def,
+ coeMonoidHom_apply, TransvectionStruct.toSpecialLinearGroup_coe, hM]
+
+theorem diagonal_transvection_induction' [Nontrivial ι] (P : SpecialLinearGroup ι F → Prop)
+ (M : SpecialLinearGroup ι F)
+ (hdiag : ∀ (i j : ι) (hij : i ≠ j) {c : F} (hc : c ≠ 0), P (diag2n hij c hc))
+ (htransvec : ∀ (i j : ι) (hij : i ≠ j) (a : F), P (transvection hij a))
+ (hmul : ∀ A B, P A → P B → P (A * B)) : P M := by
+ obtain ⟨i₀, j₀, hij₀⟩ := exists_pair_ne ι
+ have hP1 : P 1 := transvection_coeff_zero (F := F) hij₀ ▸ htransvec i₀ j₀ hij₀ 0
+ have hdiagonal (D : ι → F) (hD : det (diagonal D) = 1) : P ⟨diagonal D, hD⟩ := by
+ rw [diag_eq_diag2n_prod i₀ D hD]
+ refine Finset.noncommProd_induction _ _ _ P hmul hP1 fun i hi => ?_
+ simp [(Finset.mem_filter.1 hi).2, hdiag]
+ have hlist (L : List (TransvectionStruct ι F)) :
+ P (L.map TransvectionStruct.toSpecialLinearGroup).prod := by
+ induction L with
+ | nil => simpa using hP1
+ | cons t L ih =>
+ rw [List.map_cons, List.prod_cons, t.toSpecialLinearGroup_def]
+ exact hmul _ _ (htransvec t.i t.j t.hij t.c) ih
+ obtain ⟨L, L', D, hD, hM⟩ := exists_list_transvec_mul_diagonal_mul_list_transvec M
+ exact hM ▸ hmul _ _ (hmul _ _ (hlist L) (hdiagonal D hD)) (hlist L')
+
+end induction
+
+end SpecialLinearGroup
+
+open Matrix.SpecialLinearGroup
open scoped commutatorElement
lemma commutator_diag2_transvection (a : F) (ha : a ≠ 0) (b c : F)
@@ -598,12 +739,67 @@ lemma commutator_diag2_transvection (a : F) (ha : a ≠ 0) (b c : F)
/-- For any `c : F`, given `a ≠ 0` and `a² ≠ 1`, the transvection `transvection i₁ i₂ hij c` is
a commutator in `SL ι F`, hence lies in `commutator (SL ι F)`. -/
-lemma transvection_mem_commutator (a : F) (ha : a ≠ 0) (hasq : a ^ 2 ≠ 1) (c : F) :
+lemma transvection_mem_commutator₀ {a : F} (ha : a ≠ 0) (hasq : a ^ 2 ≠ 1) (c : F) :
SpecialLinearGroup.transvection zero_ne_one c ∈ commutator SL(2, F) := by
rw [← commutator_diag2_transvection a ha (c / (a ^ 2 - 1)) c
(div_mul_cancel₀ c (sub_ne_zero_of_ne hasq)).symm]
exact Subgroup.commutator_mem_commutator (Subgroup.mem_top _) (Subgroup.mem_top _)
+lemma transvection_mem_commutator₁ {a : F} (ha : a ≠ 0) (hasq : a ^ 2 ≠ 1) (c : F) :
+ SpecialLinearGroup.transvection one_ne_zero c ∈ commutator SL(2, F) := by
+ have (b c' : F) (hc : c' = b * (a ^ 2 - 1)) :
+ ⁅diag2 a⁻¹ (inv_ne_zero ha), SpecialLinearGroup.transvection one_ne_zero b⁆ =
+ (SpecialLinearGroup.transvection one_ne_zero c' : SL(2, F)) := by
+ rw [commutatorElement_def, diag2_inv a⁻¹ (inv_ne_zero ha),
+ SpecialLinearGroup.transvection_inv one_ne_zero b]
+ refine Subtype.ext <| Matrix.ext fun i j ↦ ?_
+ fin_cases i <;> fin_cases j <;>
+ simp [hc, SpecialLinearGroup.transvection_coe, diag2_coe, inv_inv, mul_add, add_mul,
+ mul_inv_cancel₀ ha, inv_mul_cancel₀ ha, mul_comm a b, mul_assoc b a a, ← pow_two,
+ mul_sub_one, ← sub_eq_add_neg]
+ rw [← this (c / (a ^ 2 - 1)) c (div_mul_cancel₀ c (sub_ne_zero_of_ne hasq)).symm]
+ exact Subgroup.commutator_mem_commutator (Subgroup.mem_top _) (Subgroup.mem_top _)
+
+lemma transvection_mem_commutator {a : F} (ha : a ≠ 0) (hasq : a ^ 2 ≠ 1) {i j : Fin 2} (h : i ≠ j)
+ (c : F) : SpecialLinearGroup.transvection h c ∈ commutator SL(2, F) := by
+ fin_cases i
+ · obtain rfl : j = 1 := by fin_cases j <;> tauto
+ exact transvection_mem_commutator₀ ha hasq c
+ · obtain rfl : j = 0 := by fin_cases j <;> tauto
+ exact transvection_mem_commutator₁ ha hasq c
+
+lemma diag2_decompose (a : F) (ha : a ≠ 0) :
+ diag2 a ha = SpecialLinearGroup.transvection zero_ne_one a *
+ SpecialLinearGroup.transvection one_ne_zero (- a⁻¹) *
+ SpecialLinearGroup.transvection zero_ne_one a *
+ SpecialLinearGroup.transvection zero_ne_one (-1) *
+ SpecialLinearGroup.transvection one_ne_zero 1 *
+ SpecialLinearGroup.transvection zero_ne_one (-1) := by
+ ext i j
+ fin_cases i <;> fin_cases j <;>
+ simp [diag2_coe', transvection_coe, mul_add, add_mul, mul_inv_cancel₀ ha, inv_mul_cancel₀ ha]
+
+theorem SL2.transvection_induction (P : SL(2, F) → Prop)
+ (htransvec : ∀ (i j : Fin 2) (h : i ≠ j) c, P (SpecialLinearGroup.transvection h c))
+ (hmul : ∀ A B, P A → P B → P (A * B)) (A : SL(2, F)) : P A := by
+ refine diagonal_transvection_induction' P _ (fun i j hij c hc ↦ ?_) htransvec hmul
+ fin_cases i
+ · obtain rfl : j = 1 := by fin_cases j <;> tauto
+ change P (diag2 c hc)
+ rw [diag2_decompose c hc]
+ refine hmul _ _ (hmul _ _ (hmul _ _ (hmul _ _ (hmul _ _ ?_ ?_) ?_) ?_) ?_) ?_
+ all_goals exact htransvec _ _ _ _
+ · obtain rfl : j = 0 := by fin_cases j <;> tauto
+ rw [show diag2n hij c hc = diag2 c⁻¹ (inv_ne_zero hc) by
+ ext; simp [diag2n_coe, diagonal_apply]; grind, diag2_decompose c⁻¹ (inv_ne_zero hc)]
+ refine hmul _ _ (hmul _ _ (hmul _ _ (hmul _ _ (hmul _ _ ?_ ?_) ?_) ?_) ?_) ?_
+ all_goals exact htransvec _ _ _ _
+
+lemma SL2.commutator_eq_top {a : F} (ha : a ≠ 0) (hasq : a ^ 2 ≠ 1) :
+ commutator SL(2, F) = ⊤ :=
+ le_antisymm le_top (fun A _ ↦ SL2.transvection_induction _
+ (fun _ _ ↦ transvection_mem_commutator ha hasq) (fun _ _ ↦ mul_mem) A)
+
end SL2
end Matrix
diff --git a/Mathlib/LinearAlgebra/Projectivization/PSL/PSL2.lean b/Mathlib/LinearAlgebra/Projectivization/PSL/PSL2.lean
new file mode 100644
index 00000000000000..32a6e3158224af
--- /dev/null
+++ b/Mathlib/LinearAlgebra/Projectivization/PSL/PSL2.lean
@@ -0,0 +1,137 @@
+/-
+Copyright (c) 2026 Yunzhou Xie. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Edison Xie
+-/
+module
+
+public import Mathlib.GroupTheory.GroupAction.Iwasawa
+public import Mathlib.GroupTheory.IsPerfect
+public import Mathlib.LinearAlgebra.Projectivization.PSL.Stabilizer
+
+/-!
+-/
+
+@[expose] public section
+
+variable {ι F : Type*} [Field F] [DecidableEq ι] [Fintype ι]
+
+open Matrix Matrix.SpecialLinearGroup
+
+open scoped MatrixGroups
+
+namespace SL2Gen
+
+/-- A transvection `transvection i j hij b` lies in `lineStab (span F {Pi.single i 1})`. -/
+lemma transvection_mem_lineStab {i j : ι} (hij : i ≠ j) (b : F) :
+ transvection hij b ∈ lineStab (Submodule.span F {(Pi.single i (1 : F) : ι → F)}) :=
+ fun w ↦ Submodule.mem_span_singleton.2 ⟨b * w j, by simp [mul_smul,
+ Matrix.SpecialLinearGroup.smul_def, transvection_coe, add_smul, Matrix.single_mulVec_eq]⟩
+
+/-- Every transvection in `SL ι F` whose indices are `(i₁, i₂)` or `(i₂, i₁)` is in the join
+of `lineStab(span F {e_{i₁}})` and `lineStab(span F {e_{i₂}})`. -/
+lemma transvection_mem_lineStab_sup (t : TransvectionStruct (Fin 2) F) :
+ t.toSpecialLinearGroup ∈
+ lineStab (Submodule.span F {(Pi.single 0 1 : Fin 2 → F)})
+ ⊔ lineStab (Submodule.span F {(Pi.single 1 1 : Fin 2 → F)}) := by
+ obtain ⟨i, j, hij, c⟩ := t
+ simp only [Fin.isValue, TransvectionStruct.toSpecialLinearGroup_mk]
+ fin_cases i <;> fin_cases j <;> try tauto
+ · exact Subgroup.mem_sup_left <| transvection_mem_lineStab zero_ne_one c
+ · exact Subgroup.mem_sup_right <| transvection_mem_lineStab one_ne_zero c
+
+/-- SL-level generation: in the 2-element-index case, the join of the two `lineStab` subgroups
+attached to the two coordinate axes is all of `SL ι F`. -/
+lemma SL_card_two_lineStab_sup_eq_top :
+ lineStab (Submodule.span F {(Pi.single 0 1: Fin 2 → F)}) ⊔
+ lineStab (Submodule.span F {(Pi.single 1 1: Fin 2 → F)}) =
+ (⊤ : Subgroup SL(2, F)) :=
+ le_antisymm le_top fun M _ ↦ SL2.transvection_induction _
+ (fun i j hij a ↦ by simpa using transvection_mem_lineStab_sup ⟨i, j, hij, a⟩)
+ (fun _ _ ↦ mul_mem) M
+
+end SL2Gen
+
+open scoped LinearAlgebra.Projectivization
+
+/-- At the SL level: when `Fintype.card ι = 2`, the supremum over all projective points of
+the `lineStab` subgroups equals `⊤` in `SL ι F`. -/
+lemma PSL.iSup_lineStab_eq_top :
+ (⨆ p : ℙ F (Fin 2 → F), lineStab p.submodule) = (⊤ : Subgroup SL(2, F)) := by
+ refine le_antisymm le_top (SL2Gen.SL_card_two_lineStab_sup_eq_top (F := F) ▸
+ sup_le ?_ ?_)
+ <;> rw [← Projectivization.submodule_mk (K := F) _ (Pi.single_ne_zero_iff.2 one_ne_zero)]
+ <;> exact le_iSup_iff.2 fun b a ↦ a _
+
+/-- The Iwasawa generator property: when `Fintype.card ι = 2`, the supremum of the
+`iwasawaT` subgroups equals all of `PSL`. -/
+lemma PSL.iSup_iwasawaT_eq_top :
+ iSup (PSL.iwasawaT (F := F) (ι := Fin 2)) = ⊤ := by
+ have step1 : iSup (PSL.iwasawaT (F := F) (ι := Fin 2)) =
+ Subgroup.map (QuotientGroup.mk' (Subgroup.center (Matrix.SpecialLinearGroup (Fin 2) F)))
+ (⨆ p : ℙ F (Fin 2 → F),
+ Matrix.SpecialLinearGroup.lineStab (F := F) (ι := Fin 2) p.submodule) := by
+ rw [Subgroup.map_iSup]
+ rw [step1, PSL.iSup_lineStab_eq_top]
+ exact Subgroup.map_top_of_surjective _ (QuotientGroup.mk'_surjective _)
+
+open MulAction
+
+/-- The Iwasawa structure on PSL(2, F). -/
+noncomputable abbrev PSL2.Iwasawa : IwasawaStructure PSL(2, F) (ℙ F (Fin 2 → F)) where
+ T := PSL.iwasawaT
+ is_comm p := by
+ have hSL : IsMulCommutative (lineStab (F := F) (ι := Fin 2) p.submodule) := by
+ rw [← Projectivization.mk_rep p, Projectivization.submodule_mk]
+ exact lineStab_isMulCommutative_of_span p.rep p.rep_nonzero
+ exact Subgroup.map_isMulCommutative _ _
+ is_conj g p := by
+ obtain ⟨g_SL, rfl⟩ := QuotientGroup.mk_surjective g
+ rw [Matrix.ProjectiveSpecialLinearGroup.smul_proj_mk]
+ change Subgroup.map _ _ = _
+ rw [PSL.smul_submodule, Matrix.SpecialLinearGroup.lineStab_smul,
+ PSL.iwasawaT_map_conj]
+ is_generator := PSL.iSup_iwasawaT_eq_top
+
+namespace SL2Simple
+
+open Matrix.SpecialLinearGroup
+
+/-- `commutator (PSL ι F) = ⊤`. -/
+lemma PSL_commutator_eq_top (hF : ∃ a : F, a ≠ 0 ∧ a ^ 2 ≠ 1) :
+ commutator PSL(2, F) = ⊤ := by
+ obtain ⟨a, ha, hasq⟩ := hF
+ haveI : Group.IsPerfect SL(2, F) := ⟨SL2.commutator_eq_top ha hasq⟩
+ have : Group.IsPerfect (Matrix.ProjectiveSpecialLinearGroup (Fin 2) F) := inferInstance
+ exact this.commutator_eq_top
+
+/-- `PSL ι F` is nontrivial whenever `ι` has at least two elements (and `F` is a field,
+hence in particular nontrivial). -/
+instance PSL_nontrivial [Nontrivial ι] :
+ Nontrivial (Matrix.ProjectiveSpecialLinearGroup ι F) := by
+ obtain ⟨i₁, i₂, hij⟩ := exists_pair_ne ι
+ set g : Matrix.SpecialLinearGroup ι F := transvection hij 1
+ refine ⟨⟨(QuotientGroup.mk g : Matrix.ProjectiveSpecialLinearGroup ι F),
+ 1, fun h ↦ one_ne_zero (α := F) ?_⟩⟩
+ rwa [QuotientGroup.eq_one_iff, transvection_mem_center_iff] at h
+
+end SL2Simple
+
+theorem Matrix.ProjectiveSpecialLinearGroup.rank_two_simple'
+ (hF : ∃ a : F, a ≠ 0 ∧ a ^ 2 ≠ 1) :
+ IsSimpleGroup PSL(2, F) :=
+ MulAction.IwasawaStructure.isSimpleGroup
+ (SL2Simple.PSL_commutator_eq_top hF) PSL2.Iwasawa inferInstance
+
+private lemma field_cond_of_four_le_card (hF : 4 ≤ Nat.card F) :
+ ∃ a : F, a ≠ 0 ∧ a ^ 2 ≠ 1 := by
+ have : Finite F := (Nat.card_pos_iff.1 (by omega)).2
+ obtain ⟨x, hx⟩ : IsCyclic Fˣ := by infer_instance
+ refine ⟨x, Units.ne_zero x, fun h ↦ ?_⟩
+ grw [Nat.card_eq_card_units_add_one F, ← orderOf_eq_card_of_forall_mem_zpowers hx,
+ orderOf_le_of_pow_eq_one zero_lt_two (Units.ext <| by simpa using h)] at hF
+ omega
+
+theorem Matrix.ProjectiveSpecialLinearGroup.rank_two_simple (hF : 4 ≤ Nat.card F) :
+ IsSimpleGroup PSL(2, F) :=
+ Matrix.ProjectiveSpecialLinearGroup.rank_two_simple' (field_cond_of_four_le_card hF)
From bbd5fcf3ae648b745376f9f06b251e6b6d168e92 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ya=C3=ABl=20Dillies?=
Date: Mon, 22 Jun 2026 14:05:23 +0000
Subject: [PATCH 0248/1300] chore(LinearAlgebra/Matrix): make `det_zero` simp
(#40699)
This used to be provable by simp back when `det` was an `abbrev`. This changed in #33590.
This dates back all the way from https://github.com/leanprover-community/mathlib3/pull/404, and in fact the code came from the even earlier https://github.com/leanprover-community/mathlib3/pull/378. Neither of the PRs offers an explanation for why this argument was explicit.
From BrauerGroup and RealRooted
---
Mathlib/LinearAlgebra/Determinant.lean | 2 +-
Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean | 2 +-
Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/FinTwo.lean | 2 +-
Mathlib/LinearAlgebra/Matrix/NonsingularInverse.lean | 2 +-
Mathlib/LinearAlgebra/Matrix/ZPow.lean | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/Mathlib/LinearAlgebra/Determinant.lean b/Mathlib/LinearAlgebra/Determinant.lean
index 80c91075db806c..9253e784a05321 100644
--- a/Mathlib/LinearAlgebra/Determinant.lean
+++ b/Mathlib/LinearAlgebra/Determinant.lean
@@ -268,7 +268,7 @@ theorem det_zero' {ι : Type*} [Finite ι] [Nonempty ι] (b : Basis ι A M) :
LinearMap.det (0 : M →ₗ[A] M) = 0 := by
haveI := Classical.decEq ι
cases nonempty_fintype ι
- rwa [← det_toMatrix b, map_zero, det_zero]
+ rw [← det_toMatrix b, map_zero, det_zero]
/-- In a finite-dimensional vector space, the zero map has determinant `1` in dimension `0`,
and `0` otherwise. We give a formula that also works in infinite dimension, where we define
diff --git a/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean b/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean
index 87b6da663393c1..40eface05904fd 100644
--- a/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean
+++ b/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean
@@ -85,7 +85,7 @@ theorem det_diagonal {d : n → R} : det (diagonal d) = ∏ i, d i := by
· simp
· simp
-theorem det_zero (_ : Nonempty n) : det (0 : Matrix n n R) = 0 :=
+@[simp] theorem det_zero [Nonempty n] : det (0 : Matrix n n R) = 0 :=
(detRowAlternating : (n → R) [⋀^n]→ₗ[R] R).map_zero
@[simp]
diff --git a/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/FinTwo.lean b/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/FinTwo.lean
index 0b8ea843931ff7..85a3ed415fca23 100644
--- a/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/FinTwo.lean
+++ b/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/FinTwo.lean
@@ -274,7 +274,7 @@ lemma IsParabolic.pow {g : GL (Fin 2) K} (hg : IsParabolic g) [CharZero K]
refine fun ha ↦ (g ^ 2).det_ne_zero ?_
rw [ha, map_zero, zero_add] at hg
rw [← hg] at hmsq
- rw [Units.val_pow_eq_pow_val, hmsq, det_zero ⟨0⟩]
+ rw [Units.val_pow_eq_pow_val, hmsq, det_zero]
lemma isParabolic_iff_of_upperTriangular {g : GL (Fin 2) K} (hg : g 1 0 = 0) :
g.IsParabolic ↔ g 0 0 = g 1 1 ∧ g 0 1 ≠ 0 :=
diff --git a/Mathlib/LinearAlgebra/Matrix/NonsingularInverse.lean b/Mathlib/LinearAlgebra/Matrix/NonsingularInverse.lean
index 21703a4f35bf9d..0cda6cc66b8de5 100644
--- a/Mathlib/LinearAlgebra/Matrix/NonsingularInverse.lean
+++ b/Mathlib/LinearAlgebra/Matrix/NonsingularInverse.lean
@@ -416,7 +416,7 @@ theorem det_nonsing_inv : A⁻¹.det = A.det⁻¹ʳ := by
rw [Ring.inverse_invertible, ← invOf_eq_nonsing_inv, det_invOf]
cases isEmpty_or_nonempty n
· rw [det_isEmpty, det_isEmpty, Ring.inverse_one]
- · rw [Ring.inverse_non_unit _ h, nonsing_inv_apply_not_isUnit _ h, det_zero ‹_›]
+ · rw [Ring.inverse_non_unit _ h, nonsing_inv_apply_not_isUnit _ h, det_zero]
theorem isUnit_nonsing_inv_det (h : IsUnit A.det) : IsUnit A⁻¹.det :=
.of_mul_eq_one _ (A.det_nonsing_inv_mul_det h)
diff --git a/Mathlib/LinearAlgebra/Matrix/ZPow.lean b/Mathlib/LinearAlgebra/Matrix/ZPow.lean
index 14df76dae44d4f..b32635593790f5 100644
--- a/Mathlib/LinearAlgebra/Matrix/ZPow.lean
+++ b/Mathlib/LinearAlgebra/Matrix/ZPow.lean
@@ -250,7 +250,7 @@ theorem zpow_ne_zero_of_isUnit_det [Nonempty n'] [Nontrivial R] {A : M} (ha : Is
(z : ℤ) : A ^ z ≠ 0 := by
have := ha.det_zpow z
contrapose this
- rw [this, det_zero ‹_›]
+ rw [this, det_zero]
exact not_isUnit_zero
theorem zpow_sub {A : M} (ha : IsUnit A.det) (z1 z2 : ℤ) : A ^ (z1 - z2) = A ^ z1 / A ^ z2 := by
From 9f59f3b81d68fc6805c32ad1e595317b356fd748 Mon Sep 17 00:00:00 2001
From: Fabrizio Barroero <23321199+fbarroero@users.noreply.github.com>
Date: Mon, 22 Jun 2026 14:05:27 +0000
Subject: [PATCH 0249/1300] feat(RingTheory/Ideal/Operations): `pow_eq_bot`
(#40836)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
We add
```
theorem Ideal.pow_eq_bot.{u} {R : Type u} [Semiring R] {I : Ideal R} [isReduced R] {n : ℕ} (hn : n ≠ 0) :
I ^ n = ⊥ ↔ I = ⊥
```
Co-authored-by: fbarroero
---
Mathlib/Algebra/Algebra/Operations.lean | 10 ++++++++++
Mathlib/RingTheory/Ideal/Operations.lean | 8 +++++---
2 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/Mathlib/Algebra/Algebra/Operations.lean b/Mathlib/Algebra/Algebra/Operations.lean
index 54267e7ffb5618..d31f10ca1de085 100644
--- a/Mathlib/Algebra/Algebra/Operations.lean
+++ b/Mathlib/Algebra/Algebra/Operations.lean
@@ -367,6 +367,16 @@ lemma restrictScalars_pow {A B C : Type*} [Semiring A] [Semiring B]
| n + 2, _ => by
simp [Submodule.pow_succ (n := n + 1), restrictScalars_mul, restrictScalars_pow n.succ_ne_zero]
+instance instIsReduced [IsReduced A] : IsReduced (Submodule R A) where
+ eq_zero M hM := by
+ rw [Submodule.zero_eq_bot, Submodule.eq_bot_iff]
+ rintro m hm
+ obtain ⟨n, hn⟩ := hM
+ exact eq_zero_of_pow_eq_zero <| (M ^ n).eq_bot_iff.mp hn _ (pow_mem_pow M hm n)
+
+theorem pow_eq_bot [IsReduced A] {M : Submodule R A} {n : ℕ} (hn : n ≠ 0) :
+ M ^ n = ⊥ ↔ M = ⊥ := by refine ⟨eq_zero_of_pow_eq_zero, by aesop⟩
+
end Module
variable {ι : Sort uι}
diff --git a/Mathlib/RingTheory/Ideal/Operations.lean b/Mathlib/RingTheory/Ideal/Operations.lean
index e0824b1e53b0ca..58c363276417d0 100644
--- a/Mathlib/RingTheory/Ideal/Operations.lean
+++ b/Mathlib/RingTheory/Ideal/Operations.lean
@@ -397,6 +397,9 @@ end IsTwoSided
theorem mul_eq_bot [NoZeroDivisors R] : I * J = ⊥ ↔ I = ⊥ ∨ J = ⊥ := Submodule.mul_eq_bot
+theorem pow_eq_bot [IsReduced R] {n : ℕ} (hn : n ≠ 0) : I ^ n = ⊥ ↔ I = ⊥ :=
+ Submodule.pow_eq_bot hn
+
instance {S A : Type*} [Semiring S] [SMul R S] [AddCommMonoid A] [Module R A] [Module S A]
[IsScalarTower R S A] [IsTorsionFree R A] {I : Submodule S A} : IsTorsionFree R I :=
(I.restrictScalars R).instIsTorsionFree
@@ -886,12 +889,11 @@ variable {I J} in
theorem IsRadical.inf (hI : IsRadical I) (hJ : IsRadical J) : IsRadical (I ⊓ J) := by
rw [IsRadical, radical_inf]; exact inf_le_inf hI hJ
-lemma isRadical_bot_iff :
- (⊥ : Ideal R).IsRadical ↔ IsReduced R := by
+lemma isRadical_bot_iff : (⊥ : Ideal R).IsRadical ↔ IsReduced R := by
simp only [IsRadical, SetLike.le_def, Ideal.mem_radical_iff, Ideal.mem_bot,
forall_exists_index, isReduced_iff, IsNilpotent]
-lemma isRadical_bot [IsReduced R] : (⊥ : Ideal R).IsRadical := by rwa [Ideal.isRadical_bot_iff]
+lemma isRadical_bot [IsReduced R] : (⊥ : Ideal R).IsRadical := by rwa [isRadical_bot_iff]
/-- `Ideal.radical` as an `InfTopHom`, bundling in that it distributes over `inf`. -/
def radicalInfTopHom : InfTopHom (Ideal R) (Ideal R) where
From 3c96a300e3cf9ff4c414de6cf73e87d7f28c0bb9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zhao=20Yuyang=20=E8=B5=B5=E9=9B=A8=E6=89=AC?=
Date: Mon, 22 Jun 2026 14:41:30 +0000
Subject: [PATCH 0250/1300] chore: make `SMul.comp.smul` `implicit_reducible`
(#38098)
---
Mathlib/Algebra/Group/Action/Defs.lean | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/Mathlib/Algebra/Group/Action/Defs.lean b/Mathlib/Algebra/Group/Action/Defs.lean
index 0859764ee46a7d..54914db1eafdfb 100644
--- a/Mathlib/Algebra/Group/Action/Defs.lean
+++ b/Mathlib/Algebra/Group/Action/Defs.lean
@@ -284,7 +284,8 @@ variable [SMul M α]
/-- Auxiliary definition for `SMul.comp`, `MulAction.compHom`,
`DistribMulAction.compHom`, `Module.compHom`, etc. -/
-@[to_additive (attr := simp) /-- Auxiliary definition for `VAdd.comp`, `AddAction.compHom`, etc. -/]
+@[to_additive (attr := simp, implicit_reducible)
+/-- Auxiliary definition for `VAdd.comp`, `AddAction.compHom`, etc. -/]
def comp.smul (g : N → M) (n : N) (a : α) : α := g n • a
variable (α)
From c3bb4de85ee9ac96248acdd017f9e7d9e921e72a Mon Sep 17 00:00:00 2001
From: Riccardo Brasca
Date: Mon, 22 Jun 2026 14:41:34 +0000
Subject: [PATCH 0251/1300] feat: add LinearIndependent.update (#40219)
From flt-regular.
---
.../LinearIndependent/Lemmas.lean | 21 +++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/Mathlib/LinearAlgebra/LinearIndependent/Lemmas.lean b/Mathlib/LinearAlgebra/LinearIndependent/Lemmas.lean
index 4c5aef9b9bc3e3..ab631ec1d803f9 100644
--- a/Mathlib/LinearAlgebra/LinearIndependent/Lemmas.lean
+++ b/Mathlib/LinearAlgebra/LinearIndependent/Lemmas.lean
@@ -523,6 +523,27 @@ theorem LinearIndependent.of_pairwise_dual_eq_zero_one (v : ι → M) (f : ι
end Module
+open Finsupp in
+/-- A linearly independent family of vectors `f` remains linearly independent when we substitute one
+of the terms with a vector `m` provided there exists a non-zero divisor `r`, such that `r • m`
+belongs to the span of `f` with non-zero-divisor coefficients. -/
+lemma LinearIndependent.update [DecidableEq ι] [CommRing R] [AddCommGroup M] [Module R M]
+ {f : ι → M} (hf : LinearIndependent R f) (i : ι) (m : M)
+ (hg : ∃ r ∈ nonZeroDivisors R, ∃ l : ι →₀ R,
+ l i ∈ nonZeroDivisors R ∧ r • m = linearCombination R f l) :
+ LinearIndependent R (Function.update f i m) := by
+ rw [linearIndependent_iff] at hf ⊢
+ obtain ⟨r, hr, l, hl, hg⟩ := hg
+ intros l' hl'
+ apply_fun (r • ·) at hl'
+ simp_rw [Pi.update_eq_sub_add_single, ← bilinearCombination_apply _ (S := R), map_add, map_sub,
+ bilinearCombination_apply, LinearMap.add_apply, LinearMap.sub_apply,
+ linearCombination_single_index, smul_add, smul_sub, smul_zero, smul_comm r (l' i) m,
+ hg, ← LinearMap.map_smul, smul_smul, ← linearCombination_single, ← map_sub, ← map_add] at hl'
+ replace hl' : ∀ j, (r * l' j - (single i (r * l' i)) j) + l' i * l j = 0 :=
+ fun j ↦ DFunLike.congr_fun (hf _ hl') j
+ grind [mem_nonZeroDivisors_iff]
+
/-!
### Properties which require `DivisionRing K`
From 0e09f45c127ea9f43151a07c6e92d7f553deebfe Mon Sep 17 00:00:00 2001
From: smorel394 <67864981+smorel394@users.noreply.github.com>
Date: Mon, 22 Jun 2026 14:41:37 +0000
Subject: [PATCH 0252/1300] feat(CategoryTheory/Preadditive): the comma
category is preadditive (#40890)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
If we have additive functors `L : A ⥤ T` and `R : B ⥤ T` between preadditive categories, then there is a structure of preadditive category on `Comma L R` such that addition commutes with the left and right projections.
We then specialize to the arrow category of a preadditive category.
Co-authored-by: morel
---
Mathlib.lean | 1 +
Mathlib/CategoryTheory/Preadditive/Comma.lean | 122 ++++++++++++++++++
2 files changed, 123 insertions(+)
create mode 100644 Mathlib/CategoryTheory/Preadditive/Comma.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 319c63732033c9..52a90964f54ae6 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -3226,6 +3226,7 @@ public import Mathlib.CategoryTheory.Preadditive.AdditiveFunctor
public import Mathlib.CategoryTheory.Preadditive.Basic
public import Mathlib.CategoryTheory.Preadditive.Biproducts
public import Mathlib.CategoryTheory.Preadditive.CommGrp_
+public import Mathlib.CategoryTheory.Preadditive.Comma
public import Mathlib.CategoryTheory.Preadditive.EilenbergMoore
public import Mathlib.CategoryTheory.Preadditive.EndoFunctor
public import Mathlib.CategoryTheory.Preadditive.FunctorCategory
diff --git a/Mathlib/CategoryTheory/Preadditive/Comma.lean b/Mathlib/CategoryTheory/Preadditive/Comma.lean
new file mode 100644
index 00000000000000..0037e50fefdb7a
--- /dev/null
+++ b/Mathlib/CategoryTheory/Preadditive/Comma.lean
@@ -0,0 +1,122 @@
+/-
+Copyright (c) 2026 Sophie Morel. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Sophie Morel
+-/
+module
+
+public import Mathlib.CategoryTheory.Preadditive.AdditiveFunctor
+
+/-!
+# The comma category is preadditive
+
+If we have additive functors `L : A ⥤ T` and `R : B ⥤ T` between preadditive categories,
+then there is a structure of preadditive category on `Comma L R` such that addition commutes
+with the left and right projections.
+
+We then apply this to `Arrow T` for `T` a preadditive category.
+
+## Tags
+
+comma, arrow, preadditive
+-/
+
+@[expose] public section
+
+namespace CategoryTheory
+
+open Category
+
+universe v₁ v₂ v₃ u₁ u₂ u₃
+
+variable {A : Type u₁} [Category.{v₁} A] [Preadditive A]
+variable {B : Type u₂} [Category.{v₂} B] [Preadditive B]
+variable {T : Type u₃} [Category.{v₃} T] [Preadditive T]
+variable (L : A ⥤ T) [L.Additive] (R : B ⥤ T) [R.Additive]
+variable {u v : Comma L R}
+
+section Comma
+
+namespace CommaMorphism
+
+@[simps!]
+instance : Add (u ⟶ v) where
+ add α β := CommaMorphism.mk (α.left + β.left) (α.right + β.right) (by simp)
+
+@[simps!]
+instance : Sub (u ⟶ v) where
+ sub α β := CommaMorphism.mk (α.left - β.left) (α.right - β.right) (by simp)
+
+@[simps!]
+instance : Zero (u ⟶ v) where
+ zero := CommaMorphism.mk 0 0
+
+@[simps!]
+instance : Neg (u ⟶ v) where
+ neg α := CommaMorphism.mk (-α.left) (-α.right)
+
+end CommaMorphism
+
+instance : AddCommGroup (u ⟶ v) where
+ add_assoc _ _ _ := by ext <;> simp [add_assoc]
+ zero_add _ := by cat_disch
+ add_zero _ := by cat_disch
+ add_comm _ _ := by ext <;> simp [add_comm]
+ neg_add_cancel _ := by cat_disch
+ sub_eq_add_neg _ _ := by ext <;> simp [sub_eq_add_neg]
+ nsmul n α := CommaMorphism.mk (n • α.left) (n • α.right)
+ (by simp [Functor.map_nsmul, Preadditive.comp_nsmul, Preadditive.nsmul_comp])
+ zsmul n α := CommaMorphism.mk (n • α.left) (n • α.right)
+ (by simp [Functor.map_zsmul, Preadditive.comp_zsmul, Preadditive.zsmul_comp])
+ nsmul_zero := by cat_disch
+ nsmul_succ _ _ := by ext <;> dsimp <;> simp [add_nsmul]
+ zsmul_zero' := by cat_disch
+ zsmul_succ' _ _ := by ext <;> dsimp <;> simp [add_zsmul]
+ zsmul_neg' _ _ := by ext <;> dsimp <;> simp [add_nsmul, add_zsmul]
+
+/-- If we have additive functors `L : A ⥤ T` and `R : B ⥤ T` between preadditive categories,
+then the category `Comma L R` is preadditive.
+-/
+instance : Preadditive (Comma L R) where
+
+instance : (Comma.fst L R).Additive where
+
+instance : (Comma.snd L R).Additive where
+
+end Comma
+
+section Arrow
+
+/-- If a category `T` is preadditive, then so is its category of arrows.
+-/
+instance : Preadditive (Arrow T) := inferInstanceAs (Preadditive (Comma (𝟭 T) (𝟭 T)))
+
+instance : (Arrow.leftFunc (C := T)).Additive :=
+ inferInstanceAs ((Comma.fst (𝟭 T) (𝟭 T))).Additive
+
+instance : (Arrow.rightFunc (C := T)).Additive :=
+ inferInstanceAs ((Comma.snd (𝟭 T) (𝟭 T))).Additive
+
+variable {u v : Arrow T}
+
+@[simp]
+lemma Arrow.Hom.add_left (α β : u ⟶ v) : (α + β).left = α.left + β.left := rfl
+
+@[simp]
+lemma Arrow.Hom.add_right (α β : u ⟶ v) : (α + β).right = α.right + β.right := rfl
+
+@[simp]
+lemma Arrow.Hom.zero_left : (0 : u ⟶ v).left = 0 := rfl
+
+@[simp]
+lemma Arrow.Hom.zero_right : (0 : u ⟶ v).right = 0 := rfl
+
+@[simp]
+lemma Arrow.Hom.neg_left (α : u ⟶ v) : (-α).left = -α.left := rfl
+
+@[simp]
+lemma Arrow.Hom.neg_right (α : u ⟶ v) : (-α).right = -α.right := rfl
+
+end Arrow
+
+end CategoryTheory
From 901340bc3f4d6fc607b6bef9fc1aab988635444e Mon Sep 17 00:00:00 2001
From: Hannah Scholz <70071345+scholzhannah@users.noreply.github.com>
Date: Mon, 22 Jun 2026 15:26:47 +0000
Subject: [PATCH 0253/1300] feat: more informative output messages in the
flexible linter (#39296)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The current output messages in the flexible linter are:
- If the stained location is a hypothesis:
`'exact h' uses 'h'!`
- If the stained location is the goal:
`'exact Nat.le_succ_of_le h' uses '⊢'!`
I think the first is not very informative and the second is additionally confusing. So this PR changes them to read:
- For a hypothesis:
`'exact h' uses 'h', which was modified by the flexible tactic 'simp' on line 40!`
- And for the goal:
`'exact Nat.le_succ_of_le h' modifies the current goal, which was modified by the flexible tactic 'simp_all' on line 56!`
I also added some more explanation to the messages in the case that all the hypotheses and the goal were stained by `simp at *`.
---
Mathlib/Tactic/Linter/FlexibleLinter.lean | 21 +-
MathlibTest/Linter/Flexible/Basic.lean | 193 +++++++++++++++----
MathlibTest/Linter/Flexible/ImportHeavy.lean | 15 +-
3 files changed, 187 insertions(+), 42 deletions(-)
diff --git a/Mathlib/Tactic/Linter/FlexibleLinter.lean b/Mathlib/Tactic/Linter/FlexibleLinter.lean
index b7de42cc5db889..87635d731ab532 100644
--- a/Mathlib/Tactic/Linter/FlexibleLinter.lean
+++ b/Mathlib/Tactic/Linter/FlexibleLinter.lean
@@ -522,8 +522,12 @@ def flexibleLinter : Linter where run := withSetOptionIn fun _stx => do
let suggestion? ← liftCoreM <| generateSimpSuggestion stainData stainStx
-- Emit warning and suggestion
let msg := match stainStx.getKind with
- | ``Lean.Parser.Tactic.simp =>
- m!"`{stainStr}` is a flexible tactic modifying `{d}`. \
+ | ``Lean.Parser.Tactic.simp => match d with
+ | .wildcard => m!"`{stainStr}` is a flexible tactic that potentially modifies all \
+ hypotheses and the current goal with a wildcard `*`. \
+ Try `simp?` and use the suggested `simp only [...]`. \
+ Alternatively, use `suffices` to explicitly state the simplified form."
+ | _ => m!"`{stainStr}` is a flexible tactic modifying `{d}`. \
Try `simp?` and use the suggested `simp only [...]`. \
Alternatively, use `suffices` to explicitly state the simplified form."
| ``Lean.Parser.Tactic.simpAll =>
@@ -539,7 +543,18 @@ def flexibleLinter : Linter where run := withSetOptionIn fun _stx => do
if let some suggStx := suggestion? then
liftCoreM <| Lean.Meta.Tactic.TryThis.addSuggestion stainStx
{ suggestion := .tsyntax (kind := `tactic) ⟨suggStx⟩ } (origSpan? := stainStx)
- logInfoAt s m!"`{s}` uses `{d}`!"
+ let fm ← getFileMap
+ let stainLine? := stainStx.getPos?.map (Position.line ∘ fm.toPosition)
+ let lineStr := if let some line := stainLine? then s!" on line {line}" else ""
+ let atomStr := match stainStx[0] with
+ | .atom _ val => "the flexible tactic " ++ m!"`{val}`"
+ | _ => "a flexible tactic"
+ logInfoAt s <| match d with
+ | .name _ => m!"`{.group s}`\nuses `{d}`, which was modified by {atomStr}{lineStr}!"
+ | .goal =>
+ m!"`{.group s}`\nmodifies the current goal, which was modified by {atomStr}{lineStr}!"
+ | .wildcard => m!"`{.group s}`\nuses a rigid tactic. Previously, {atomStr}, which \
+ potentially modified all hypotheses and the goal with a wildcard `*`, was used{lineStr}."
initialize addLinter flexibleLinter
diff --git a/MathlibTest/Linter/Flexible/Basic.lean b/MathlibTest/Linter/Flexible/Basic.lean
index a4bd116944c0ea..c4ea9105ae5abb 100644
--- a/MathlibTest/Linter/Flexible/Basic.lean
+++ b/MathlibTest/Linter/Flexible/Basic.lean
@@ -14,10 +14,12 @@ set_option linter.unusedVariables false
This file contains basic tests for the flexible linter, which do not require any advanced imports.
Anything which requires groups, rings or algebraic structures is considered advanced, and
-tests for these can be found in `MathlibTest/ImportHeavyFlexibleLinter.lean`
+tests for these can be found in `MathlibTest/Linter/Flexible/ImportHeavy.lean`
+TODO: make output message appear only once for wildcard
-/
+
def n : Nat := 1
def m : Nat := 1
@@ -27,18 +29,33 @@ example : n = m := by
simp [n]
simp [m]
+-- the given line number is correct
/--
warning: `simp at h` is a flexible tactic modifying `h`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form.
Note: This linter can be disabled with `set_option linter.flexible false`
---
-info: `exact h` uses `h`!
+info: `exact h`
+uses `h`, which was modified by the flexible tactic `simp` on line 43!
-/
#guard_msgs in
example (h : 0 + 0 = 0) : True := by
simp at h
try exact h
+/--
+warning: `simp at h` is a flexible tactic modifying `h`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form.
+
+Note: This linter can be disabled with `set_option linter.flexible false`
+---
+info: `exact h`
+uses `h`, which was modified by the flexible tactic `simp` on line
+-/
+#guard_msgs (substring := true) in
+example (h : 0 + 0 = 0) : True := by
+ simp at h
+ try exact h
+
/--
warning: `simp_all` is a flexible tactic modifying `⊢`. Try `simp_all?` and use the suggested `simp_all only [...]`. Alternatively, use `suffices` to explicitly state the simplified form.
@@ -47,13 +64,106 @@ Note: This linter can be disabled with `set_option linter.flexible false`
info: Try this:
[apply] simp_all only [Nat.add_zero]
---
-info: `exact Nat.le_succ_of_le h` uses `⊢`!
+info: `exact Nat.le_succ_of_le h`
+modifies the current goal, which was modified by the flexible tactic `simp_all` on line
-/
-#guard_msgs in
+#guard_msgs (substring := true) in
example {a b : Nat} (h : a ≤ b) : a + 0 ≤ b + 1 := by
simp_all
exact Nat.le_succ_of_le h
+/--
+warning: `simp at *` is a flexible tactic that potentially modifies all hypotheses and the current goal with a wildcard `*`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form.
+
+Note: This linter can be disabled with `set_option linter.flexible false`
+---
+info: Try this:
+ [apply] simp only [Nat.add_zero] at *
+---
+info: `exact Nat.le_succ_of_le h`
+uses a rigid tactic. Previously, the flexible tactic `simp`, which potentially modified all hypotheses and the goal with a wildcard `*`, was used on line 98.
+---
+warning: `simp at *` is a flexible tactic that potentially modifies all hypotheses and the current goal with a wildcard `*`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form.
+
+Note: This linter can be disabled with `set_option linter.flexible false`
+---
+info: Try this:
+ [apply] simp only [Nat.add_zero] at *
+---
+info: `exact Nat.le_succ_of_le h`
+uses a rigid tactic. Previously, the flexible tactic `simp`, which potentially modified all hypotheses and the goal with a wildcard `*`, was used on line
+-/
+#guard_msgs (substring := true) in
+example {a b : Nat} (h : a ≤ b) : a + 0 ≤ b + 1 := by
+ simp at *
+ exact Nat.le_succ_of_le h
+
+/--
+warning: `simp at *` is a flexible tactic that potentially modifies all hypotheses and the current goal with a wildcard `*`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form.
+
+Note: This linter can be disabled with `set_option linter.flexible false`
+---
+info: `exact h2`
+uses a rigid tactic. Previously, the flexible tactic `simp`, which potentially modified all hypotheses and the goal with a wildcard `*`, was used on line 118.
+---
+warning: `simp at *` is a flexible tactic that potentially modifies all hypotheses and the current goal with a wildcard `*`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form.
+
+Note: This linter can be disabled with `set_option linter.flexible false`
+---
+info: `exact h2`
+uses a rigid tactic. Previously, the flexible tactic `simp`, which potentially modified all hypotheses and the goal with a wildcard `*`, was used on line
+-/
+#guard_msgs (substring := true) in
+example {a b : Nat} (h1 : 0 + 0 = 0) (h2 : a ≤ b) : a ≤ b := by
+ simp at *
+ exact h2
+
+/--
+warning: `simp at *` is a flexible tactic that potentially modifies all hypotheses and the current goal with a wildcard `*`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form.
+
+Note: This linter can be disabled with `set_option linter.flexible false`
+---
+info: `exact h2`
+uses a rigid tactic. Previously, the flexible tactic `simp`, which potentially modified all hypotheses and the goal with a wildcard `*`, was used on line 138.
+---
+warning: `simp at *` is a flexible tactic that potentially modifies all hypotheses and the current goal with a wildcard `*`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form.
+
+Note: This linter can be disabled with `set_option linter.flexible false`
+---
+info: `exact h2`
+uses a rigid tactic. Previously, the flexible tactic `simp`, which potentially modified all hypotheses and the goal with a wildcard `*`, was used on line
+-/
+#guard_msgs (substring := true) in
+example {a b : Nat} (h1 : 0 + 0 = 0) (h2 : a ≤ b) : a ≤ b := by
+ simp at *
+ exact h2
+
+/--
+warning: `simp at *` is a flexible tactic that potentially modifies all hypotheses and the current goal with a wildcard `*`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form.
+
+Note: This linter can be disabled with `set_option linter.flexible false`
+---
+info: Try this:
+ [apply] simp only [Nat.add_zero] at *
+---
+info: `exact h`
+uses a rigid tactic. Previously, the flexible tactic `simp`, which potentially modified all hypotheses and the goal with a wildcard `*`, was used on line 164.
+---
+warning: `simp at *` is a flexible tactic that potentially modifies all hypotheses and the current goal with a wildcard `*`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form.
+
+Note: This linter can be disabled with `set_option linter.flexible false`
+---
+info: Try this:
+ [apply] simp only [Nat.add_zero] at *
+---
+info: `exact h`
+uses a rigid tactic. Previously, the flexible tactic `simp`, which potentially modified all hypotheses and the goal with a wildcard `*`, was used on line
+-/
+#guard_msgs (substring := true) in
+example {a b : Nat} (h : a = b) : a + 0 = b := by
+ simp at *
+ exact h
+
-- `subst` does not use the goal
#guard_msgs in
example {a b : Nat} (h : a = b) : a + 0 = b := by
@@ -78,7 +188,8 @@ Note: This linter can be disabled with `set_option linter.flexible false`
info: Try this:
[apply] simp only [Nat.add_zero]
---
-info: `assumption` uses `⊢`!
+info: `assumption`
+modifies the current goal, which was modified by the flexible tactic `simp` on line 206!
---
warning: `simp` is a flexible tactic modifying `⊢`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form.
@@ -87,9 +198,10 @@ Note: This linter can be disabled with `set_option linter.flexible false`
info: Try this:
[apply] simp only [Nat.add_zero]
---
-info: `assumption` uses `⊢`!
+info: `assumption`
+modifies the current goal, which was modified by the flexible tactic `simp` on line
-/
-#guard_msgs in
+#guard_msgs (substring := true) in
example {a b : Nat} (h : a = b) : a + 0 = b := by
simp
induction a <;> assumption
@@ -99,9 +211,10 @@ warning: `simp at h` is a flexible tactic modifying `h`. Try `simp?` and use the
Note: This linter can be disabled with `set_option linter.flexible false`
---
-info: `exact h` uses `h`!
+info: `exact h`
+uses `h`, which was modified by the flexible tactic `simp` on line
-/
-#guard_msgs in
+#guard_msgs (substring := true) in
example (h : 0 = 0 ∨ 0 = 0) : True := by
cases h <;>
rename_i h <;>
@@ -117,7 +230,8 @@ Note: This linter can be disabled with `set_option linter.flexible false`
info: Try this:
[apply] simp only [Nat.zero_ne_one, and_self]
---
-info: `on_goal 2 => · contradiction` uses `⊢`!
+info: `on_goal 2 => · contradiction`
+modifies the current goal, which was modified by the flexible tactic `simp` on line 248!
---
warning: `simp` is a flexible tactic modifying `⊢`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form.
@@ -126,9 +240,10 @@ Note: This linter can be disabled with `set_option linter.flexible false`
info: Try this:
[apply] simp only [Nat.zero_ne_one, and_self]
---
-info: `contradiction` uses `⊢`!
+info: `contradiction`
+modifies the current goal, which was modified by the flexible tactic `simp` on line
-/
-#guard_msgs in
+#guard_msgs (substring := true) in
example (h : 0 = 1 ∨ 0 = 1) : 0 = 1 ∧ 0 = 1 := by
cases h <;> simp
on_goal 2 => · contradiction
@@ -146,7 +261,8 @@ Note: This linter can be disabled with `set_option linter.flexible false`
info: Try this:
[apply] simp only [Nat.zero_ne_one, and_self]
---
-info: `contradiction` uses `⊢`!
+info: `contradiction`
+modifies the current goal, which was modified by the flexible tactic `simp` on line 279!
---
warning: `simp` is a flexible tactic modifying `⊢`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form.
@@ -155,9 +271,10 @@ Note: This linter can be disabled with `set_option linter.flexible false`
info: Try this:
[apply] simp only [Nat.zero_ne_one, and_self]
---
-info: `contradiction` uses `⊢`!
+info: `contradiction`
+modifies the current goal, which was modified by the flexible tactic `simp` on line
-/
-#guard_msgs in
+#guard_msgs (substring := true) in
example (h : 0 = 1 ∨ 0 = 1) : 0 = 1 ∧ 0 = 1 := by
cases h <;> simp
· contradiction
@@ -168,15 +285,17 @@ warning: `simp at h k` is a flexible tactic modifying `k`. Try `simp?` and use t
Note: This linter can be disabled with `set_option linter.flexible false`
---
-info: `rw [← Classical.not_not (a := True)] at k` uses `k`!
+info: `rw [← Classical.not_not (a := True)] at k`
+uses `k`, which was modified by the flexible tactic `simp` on line 301!
---
warning: `simp at h k` is a flexible tactic modifying `h`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form.
Note: This linter can be disabled with `set_option linter.flexible false`
---
-info: `rw [← Classical.not_not (a := True)] at h` uses `h`!
+info: `rw [← Classical.not_not (a := True)] at h`
+uses `h`, which was modified by the flexible tactic `simp` on line
-/
-#guard_msgs in
+#guard_msgs (substring := true) in
-- `simp at h` stains `h` but not other locations
example {h : 0 = 0} {k : 1 = 1} : True := by
simp at h k;
@@ -201,9 +320,10 @@ Note: This linter can be disabled with `set_option linter.flexible false`
info: Try this:
[apply] simp only [Nat.add_zero]
---
-info: `exact h.symm` uses `⊢`!
+info: `exact h.symm`
+modifies the current goal, which was modified by the flexible tactic `simp` on line
-/
-#guard_msgs in
+#guard_msgs (substring := true) in
-- `congr` is allowed after `simp`, but "passes along the stain".
example {a b : Nat} (h : a = b) : a + b + 0 = b + a := by
simp
@@ -248,9 +368,10 @@ Note: This linter can be disabled with `set_option linter.flexible false`
info: Try this:
[apply] simp only [Nat.zero_ne_one, and_self]
---
-info: `contradiction` uses `⊢`!
+info: `contradiction`
+modifies the current goal, which was modified by the flexible tactic `simp` on line
-/
-#guard_msgs in
+#guard_msgs (substring := true) in
example (h : 0 = 1 ∨ 0 = 1) : 0 = 1 ∧ 0 = 1 := by
cases h <;> simp
· simp_all
@@ -277,9 +398,10 @@ Note: This linter can be disabled with `set_option linter.flexible false`
info: Try this:
[apply] simp only [not_true_eq_false, not_false_eq_true] at h
---
-info: `rw [← Classical.not_not (a := True)] at h` uses `h`!
+info: `rw [← Classical.not_not (a := True)] at h`
+uses `h`, which was modified by the flexible tactic `simp` on line
-/
-#guard_msgs in
+#guard_msgs (substring := true) in
-- `simp at h` stains `h` but not other locations
example {h : 0 = 0} {k : 1 = 1} : ¬ ¬ True := by
simp at h
@@ -294,15 +416,17 @@ warning: `simp at h k` is a flexible tactic modifying `k`. Try `simp?` and use t
Note: This linter can be disabled with `set_option linter.flexible false`
---
-info: `rw [← Classical.not_not (a := True)] at k` uses `k`!
+info: `rw [← Classical.not_not (a := True)] at k`
+uses `k`, which was modified by the flexible tactic `simp` on line 432!
---
warning: `simp at h k` is a flexible tactic modifying `h`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form.
Note: This linter can be disabled with `set_option linter.flexible false`
---
-info: `rw [← Classical.not_not (a := True)] at h` uses `h`!
+info: `rw [← Classical.not_not (a := True)] at h`
+uses `h`, which was modified by the flexible tactic `simp` on line
-/
-#guard_msgs in
+#guard_msgs (substring := true) in
-- `simp at h` stains `h` but not other locations
example {h : 0 = 0} {k : 1 = 1} : True := by
simp at h k
@@ -317,9 +441,10 @@ warning: `simp at h` is a flexible tactic modifying `h`. Try `simp?` and use the
Note: This linter can be disabled with `set_option linter.flexible false`
---
-info: `rw [← Classical.not_not (a := True)] at h` uses `h`!
+info: `rw [← Classical.not_not (a := True)] at h`
+uses `h`, which was modified by the flexible tactic `simp` on line
-/
-#guard_msgs in
+#guard_msgs (substring := true) in
-- `simp at h` stains `h` but not other locations
example {h : 0 = 0} : True := by
simp at h
@@ -336,9 +461,10 @@ Note: This linter can be disabled with `set_option linter.flexible false`
info: Try this:
[apply] simp only [Nat.zero_ne_one]
---
-info: `rwa [← Classical.not_not (a := False)]` uses `⊢`!
+info: `rwa [← Classical.not_not (a := False)]`
+modifies the current goal, which was modified by the flexible tactic `simp` on line
-/
-#guard_msgs in
+#guard_msgs (substring := true) in
example {h : False} : 0 = 1 := by
simp
rw [← Classical.not_not (a := False)] at h
@@ -353,9 +479,10 @@ Note: This linter can be disabled with `set_option linter.flexible false`
info: Try this:
[apply] simp only [Nat.zero_ne_one]
---
-info: `rwa [← Classical.not_not (a := False)]` uses `⊢`!
+info: `rwa [← Classical.not_not (a := False)]`
+modifies the current goal, which was modified by the flexible tactic `simp` on line
-/
-#guard_msgs in
+#guard_msgs (substring := true) in
example {h : False} : 0 = 1 ∧ 0 = 1 := by
constructor
· simpa
diff --git a/MathlibTest/Linter/Flexible/ImportHeavy.lean b/MathlibTest/Linter/Flexible/ImportHeavy.lean
index 59b961058a98f6..b9d0f275ff350a 100644
--- a/MathlibTest/Linter/Flexible/ImportHeavy.lean
+++ b/MathlibTest/Linter/Flexible/ImportHeavy.lean
@@ -46,9 +46,10 @@ Note: This linter can be disabled with `set_option linter.flexible false`
info: Try this:
[apply] simp only [zero_add]
---
-info: `rw [add_comm]` uses `⊢`!
+info: `rw [add_comm]`
+modifies the current goal, which was modified by the flexible tactic `simp` on line
-/
-#guard_msgs in
+#guard_msgs (substring := true) in
-- `norm_num` is allowed after `simp`, but "passes along the stain".
example {a : Rat} : a + (0 + 2 + 1 : Rat) = 3 + a := by
simp
@@ -81,9 +82,10 @@ Note: This linter can be disabled with `set_option linter.flexible false`
info: Try this:
[apply] simp only [mul_zero, add_zero]
---
-info: `positivity` uses `⊢`!
+info: `positivity`
+modifies the current goal, which was modified by the flexible tactic `simp` on line
-/
-#guard_msgs in
+#guard_msgs (substring := true) in
example {k l : ℤ} : 0 ≤ k ^ 2 + 4 * l * 0 := by
simp
positivity
@@ -124,9 +126,10 @@ Note: This linter can be disabled with `set_option linter.flexible false`
info: Try this:
[apply] simp only [Function.comp_apply, add_zero]
---
-info: `fun_prop` uses `⊢`!
+info: `fun_prop`
+modifies the current goal, which was modified by the flexible tactic `simp` on line
-/
-#guard_msgs in
+#guard_msgs (substring := true) in
example {X : Type*} [TopologicalSpace X] {f : X → ℕ} {g : ℕ → X}
(hf : Continuous f) (hg : Continuous g) :
Continuous (fun x ↦ (f ∘ g) x + 0) := by
From cba41737d24dd73bad77f2a1b10aaf1b84dea640 Mon Sep 17 00:00:00 2001
From: Riccardo Brasca
Date: Mon, 22 Jun 2026 15:26:50 +0000
Subject: [PATCH 0254/1300] feat: add Algebra.IsUnramifiedIn (#40886)
---
.../RamificationInertia/Unramified.lean | 84 ++++++++++++++++++-
Mathlib/RingTheory/Unramified/Locus.lean | 15 ++++
2 files changed, 97 insertions(+), 2 deletions(-)
diff --git a/Mathlib/NumberTheory/RamificationInertia/Unramified.lean b/Mathlib/NumberTheory/RamificationInertia/Unramified.lean
index ccac5888695ab6..42ec187589d036 100644
--- a/Mathlib/NumberTheory/RamificationInertia/Unramified.lean
+++ b/Mathlib/NumberTheory/RamificationInertia/Unramified.lean
@@ -76,12 +76,16 @@ lemma IsUnramifiedAt.of_liesOver_of_ne_bot
refine this (H.trans (Ideal.pow_right_mono ?_ _))
exact Ideal.map_le_iff_le_comap.mpr Ideal.LiesOver.over.le
+section IsUnramifiedIn
+
+namespace Algebra
+
variable (R) in
/--
Up to technical conditions, If `T/S/R` is a tower of algebras, `P` is a prime of `T` unramified
in `R`, then `P ∩ S` (as a prime of `S`) is also unramified in `R`.
-/
-lemma Algebra.IsUnramifiedAt.of_liesOver
+lemma IsUnramifiedAt.of_liesOver
(p : Ideal S) (P : Ideal T) [P.LiesOver p] [p.IsPrime] [P.IsPrime]
[IsUnramifiedAt R P] [EssFiniteType R S] [EssFiniteType R T]
[IsDedekindDomain S] [IsDomain T] [Module.IsTorsionFree S T] : IsUnramifiedAt R p :=
@@ -90,7 +94,7 @@ lemma Algebra.IsUnramifiedAt.of_liesOver
/-- Let `R` be a domain of characteristic 0, finite rank over `ℤ`, `S` be a Dedekind domain
that is a finite `R`-algebra. Let `p` be a prime of `S`, then `p` is unramified iff `e(p) = 1`. -/
-lemma Algebra.isUnramifiedAt_iff_of_isDedekindDomain
+lemma isUnramifiedAt_iff_of_isDedekindDomain
{p : Ideal S} [p.IsPrime] [IsDedekindDomain S] [EssFiniteType R S] [IsDomain R]
[Module.Finite ℤ R] [CharZero R] [Algebra.IsIntegral R S]
(hp : p ≠ ⊥) :
@@ -103,3 +107,79 @@ lemma Algebra.isUnramifiedAt_iff_of_isDedekindDomain
have : Finite ((p.under R).ResidueField) := IsLocalization.finite _
(nonZeroDivisors (R ⧸ p.under R))
infer_instance
+
+/-- In characteristic zero the generic point is unramified: if `S` is a domain that is integral
+over a characteristic-zero domain `R` and `R → S` is injective, then `S` is unramified at the zero
+ideal. -/
+theorem isUnramifiedAt_bot [IsDomain R] [IsDomain S] [Module.IsTorsionFree R S] [CharZero R]
+ [Algebra.IsIntegral R S] : IsUnramifiedAt R (⊥ : Ideal S) := by
+ have : IsFractionRing S (Localization.AtPrime (⊥ : Ideal S)) := by
+ simpa [Ideal.primeCompl_bot] using Localization.isLocalization (M := (⊥ : Ideal S).primeCompl)
+ let : Field (Localization.AtPrime (⊥ : Ideal S)) := IsFractionRing.toField S
+ have : FaithfulSMul R (Localization.AtPrime (⊥ : Ideal S)) := by
+ rw [faithfulSMul_iff_algebraMap_injective,
+ IsScalarTower.algebraMap_eq R S (Localization.AtPrime ⊥)]
+ exact (IsFractionRing.injective S _).comp (FaithfulSMul.algebraMap_injective R S)
+ let := FractionRing.liftAlgebra R (Localization.AtPrime (⊥ : Ideal S))
+ have : Algebra.IsAlgebraic (FractionRing R) (Localization.AtPrime ⊥) :=
+ isAlgebraic_of_isFractionRing R S (FractionRing R) (Localization.AtPrime (⊥ : Ideal S))
+ have : FormallyUnramified (FractionRing R) (Localization.AtPrime (⊥ : Ideal S)) :=
+ FormallyUnramified.of_isSeparable _ _
+ exact FormallyUnramified.comp R (FractionRing R) (Localization.AtPrime ⊥)
+
+/-- In characteristic zero, the zero ideal is unramified in an integral domain extension. -/
+theorem isUnramifiedIn_bot [IsDomain R] [IsDomain S] [FaithfulSMul R S] [CharZero R]
+ [Algebra.IsIntegral R S] : IsUnramifiedIn S (⊥ : Ideal R) := by
+ intro P _ hP
+ simpa [Ideal.eq_bot_of_liesOver_bot R P] using isUnramifiedAt_bot
+
+/-- Let `S` be a Dedekind domain that is torsion-free over a domain `R`, and let `p ≠ ⊥` be an
+ideal of `R`. Then `p` is unramified in `S` if and only if `S` is unramified at every maximal
+ideal `P` of `S` lying over `p`.
+
+See `Algebra.isUnramifiedIn_iff_forall_of_isDedekindDomain` if `R` is of characteristic zero. -/
+theorem isUnramifiedIn_iff_forall_of_isDedekindDomain' [IsDomain R] [IsDedekindDomain S]
+ [Module.IsTorsionFree R S] {p : Ideal R} (hp : p ≠ ⊥) :
+ IsUnramifiedIn S p ↔
+ ∀ (P : Ideal S) (_ : P.IsMaximal), P.LiesOver p → IsUnramifiedAt R P :=
+ ⟨fun h P hP hlo ↦ h P hP.isPrime hlo,
+ fun h P hP hlo ↦ h P (hP.isMaximal (Ideal.ne_bot_of_liesOver_of_ne_bot hp P)) hlo⟩
+
+/-- Let `S` be a Dedekind domain that is integral and torsion-free over a characteristic-zero
+domain `R`. Then an ideal `p` of `R` is unramified in `S` if and only if `S` is unramified at every
+maximal ideal `P` of `S` lying over `p`. -/
+theorem isUnramifiedIn_iff_forall_of_isDedekindDomain [IsDomain R] [IsDedekindDomain S]
+ [Module.IsTorsionFree R S] [CharZero R] [Algebra.IsIntegral R S] {p : Ideal R} :
+ IsUnramifiedIn S p ↔
+ ∀ (P : Ideal S) (_ : P.IsMaximal), P.LiesOver p → IsUnramifiedAt R P := by
+ refine ⟨fun h P hP hlo ↦ h P hP.isPrime hlo, fun h P hP hlo ↦ ?_⟩
+ rcases eq_or_ne P ⊥ with rfl | hPbot
+ · exact isUnramifiedAt_bot
+ · exact h P (hP.isMaximal hPbot) hlo
+
+/-- For a prime `𝔓` of `S` lying over an unramified prime `𝔭` of `R`, the ramification index
+`e(𝔓 ∣ 𝔭)` equals `1`. -/
+theorem IsUnramifiedIn.ramificationIdx_eq_one [IsDomain R] [IsDedekindDomain S]
+ [Module.IsTorsionFree R S] [Module.Finite ℤ R] [CharZero R] [EssFiniteType R S]
+ [Algebra.IsIntegral R S] {𝔭 : Ideal R} (hunr : IsUnramifiedIn S 𝔭) (h𝔭 : 𝔭 ≠ ⊥) {𝔓 : Ideal S}
+ [𝔓.IsPrime] (hP : 𝔓.LiesOver 𝔭) : Ideal.ramificationIdx 𝔭 𝔓 = 1 := by
+ rw [(Ideal.liesOver_iff 𝔓 𝔭).mp hP]
+ exact (isUnramifiedAt_iff_of_isDedekindDomain (Ideal.ne_bot_of_liesOver_of_ne_bot h𝔭 𝔓)).mp
+ (hunr 𝔓 inferInstance hP)
+
+/-- A nonzero ideal of `R` is unramified in `S` if and only if every prime ideal of `S` lying
+over it has ramification index `1`. -/
+theorem isUnramifiedIn_iff_forall_ramificationIdx_eq_one [IsDomain R] [IsDedekindDomain S]
+ [Module.IsTorsionFree R S] [Module.Finite ℤ R] [CharZero R] [EssFiniteType R S]
+ [Algebra.IsIntegral R S] {𝔭 : Ideal R} (h𝔭 : 𝔭 ≠ ⊥) :
+ IsUnramifiedIn S 𝔭 ↔
+ ∀ (𝔓 : Ideal S) [𝔓.IsPrime], 𝔓.LiesOver 𝔭 → Ideal.ramificationIdx 𝔭 𝔓 = 1 := by
+ refine ⟨fun hunr 𝔓 _ hP ↦ hunr.ramificationIdx_eq_one h𝔭 hP, fun h 𝔓 _ hP ↦ ?_⟩
+ apply (isUnramifiedAt_iff_of_isDedekindDomain
+ (Ideal.ne_bot_of_liesOver_of_ne_bot h𝔭 𝔓)).mpr
+ rw [← (Ideal.liesOver_iff 𝔓 𝔭).mp hP]
+ exact h 𝔓 hP
+
+end Algebra
+
+end IsUnramifiedIn
diff --git a/Mathlib/RingTheory/Unramified/Locus.lean b/Mathlib/RingTheory/Unramified/Locus.lean
index f227c41f69e57f..07c2c4d447fe01 100644
--- a/Mathlib/RingTheory/Unramified/Locus.lean
+++ b/Mathlib/RingTheory/Unramified/Locus.lean
@@ -89,6 +89,21 @@ theorem IsUnramifiedAt.residueField
end
+section IsUnramifiedIn
+
+variable {R : Type*} [CommRing R]
+
+/-- A prime `𝔭` of `R` is unramified in `A` if every prime ideal `𝔓` of `A` lying over `𝔭` is
+unramified . -/
+def IsUnramifiedIn (A : Type*) [CommRing A] [Algebra R A] (𝔭 : Ideal R) : Prop :=
+ ∀ (𝔓 : Ideal A) (_ : 𝔓.IsPrime), 𝔓.LiesOver 𝔭 → Algebra.IsUnramifiedAt R 𝔓
+
+variable (A : Type*) [CommRing A] [Algebra R A]
+
+theorem isUnramifiedIn_top : IsUnramifiedIn A (⊤ : Ideal R) :=
+ fun P hP _ ↦ (hP.ne_top ((Ideal.eq_top_iff_of_liesOver P (⊤ : Ideal R)).mpr rfl)).elim
+
+end IsUnramifiedIn
section
variable {R A : Type*} [CommRing R] [CommRing A] [Algebra R A]
From 06e4a530c2ee8e5c0fc6ba40a38d4814102c8fa2 Mon Sep 17 00:00:00 2001
From: Anatole Dedecker
Date: Mon, 22 Jun 2026 15:26:53 +0000
Subject: [PATCH 0255/1300] feat: a bit more API around
`ContinuousAffineEquiv.pointReflection` (#40889)
Following its introduction in #40637.
---
.../AffineSpace/AffineEquiv.lean | 8 ++-
.../Algebra/ContinuousAffineEquiv.lean | 69 +++++++++++++------
2 files changed, 55 insertions(+), 22 deletions(-)
diff --git a/Mathlib/LinearAlgebra/AffineSpace/AffineEquiv.lean b/Mathlib/LinearAlgebra/AffineSpace/AffineEquiv.lean
index 75f205c9b55110..5c600a01296781 100644
--- a/Mathlib/LinearAlgebra/AffineSpace/AffineEquiv.lean
+++ b/Mathlib/LinearAlgebra/AffineSpace/AffineEquiv.lean
@@ -427,6 +427,7 @@ def vaddConst (b : P₁) : V₁ ≃ᵃ[k] P₁ where
map_vadd' _ _ := add_vadd _ _ _
/-- `p' ↦ p -ᵥ p'` as an equivalence. -/
+@[simps! linear apply symm_apply]
def constVSub (p : P₁) : P₁ ≃ᵃ[k] V₁ where
toEquiv := Equiv.constVSub p
linear := LinearEquiv.neg k
@@ -514,10 +515,13 @@ This is `Equiv.pointReflection` as an `AffineEquiv`. -/
def pointReflection (x : P₁) : P₁ ≃ᵃ[k] P₁ :=
(constVSub k x).trans (vaddConst k x)
-@[simp] lemma pointReflection_apply_eq_equivPointReflection_apply (x y : P₁) :
- pointReflection k x y = Equiv.pointReflection x y :=
+@[simp]
+lemma coe_pointReflection (x y : P₁) : pointReflection k x y = Equiv.pointReflection x y :=
rfl
+@[deprecated (since := "2026-06-22")]
+alias pointReflection_apply_eq_equivPointReflection_apply := coe_pointReflection
+
theorem pointReflection_apply (x y : P₁) : pointReflection k x y = (x -ᵥ y) +ᵥ x :=
rfl
diff --git a/Mathlib/Topology/Algebra/ContinuousAffineEquiv.lean b/Mathlib/Topology/Algebra/ContinuousAffineEquiv.lean
index 3605d581af24e8..e0f4fb8983310f 100644
--- a/Mathlib/Topology/Algebra/ContinuousAffineEquiv.lean
+++ b/Mathlib/Topology/Algebra/ContinuousAffineEquiv.lean
@@ -97,18 +97,6 @@ theorem coe_coe (e : P₁ ≃ᴬ[k] P₂) : ⇑(e : P₁ ≃ᵃ[k] P₂) = e :=
theorem coe_toEquiv (e : P₁ ≃ᴬ[k] P₂) : ⇑e.toEquiv = e :=
rfl
-/-- See Note [custom simps projection].
- We need to specify this projection explicitly in this case,
- because it is a composition of multiple projections. -/
-def Simps.apply (e : P₁ ≃ᴬ[k] P₂) : P₁ → P₂ :=
- e
-
-/-- See Note [custom simps projection]. -/
-def Simps.symm_apply (e : P₁ ≃ᴬ[k] P₂) : P₂ → P₁ :=
- e.symm
-
-initialize_simps_projections ContinuousAffineEquiv (toFun → apply, invFun → symm_apply)
-
@[ext]
theorem ext {e e' : P₁ ≃ᴬ[k] P₂} (h : ∀ x, e x = e' x) : e = e' :=
DFunLike.ext _ _ h
@@ -178,6 +166,18 @@ def symm (e : P₁ ≃ᴬ[k] P₂) : P₂ ≃ᴬ[k] P₁ where
continuous_toFun := e.continuous_invFun
continuous_invFun := e.continuous_toFun
+/-- See Note [custom simps projection].
+ We need to specify this projection explicitly in this case,
+ because it is a composition of multiple projections. -/
+def Simps.apply (e : P₁ ≃ᴬ[k] P₂) : P₁ → P₂ :=
+ e
+
+/-- See Note [custom simps projection]. -/
+def Simps.symm_apply (e : P₁ ≃ᴬ[k] P₂) : P₂ → P₁ :=
+ e.symm
+
+initialize_simps_projections ContinuousAffineEquiv (toFun → apply, invFun → symm_apply)
+
@[simp]
theorem toAffineEquiv_symm (e : P₁ ≃ᴬ[k] P₂) : e.symm.toAffineEquiv = e.toAffineEquiv.symm :=
rfl
@@ -312,16 +312,34 @@ section
variable (k)
variable [TopologicalSpace V₁] [IsTopologicalAddTorsor P₁]
+/-- The affine homeomorphism `V ≃ᴬ[k] P` given by `v ↦ v +ᵥ p`. This is `Equiv.vaddConst`
+as a `ContinuousAffineEquiv`. -/
+@[simps! apply symm_apply]
+def vaddConst (p : P₁) : V₁ ≃ᴬ[k] P₁ where
+ __ := AffineEquiv.vaddConst k p
+ __ := Homeomorph.vaddConst p
+
+@[simp]
+lemma toAffineEquiv_vaddConst {p : P₁} : vaddConst k p = AffineEquiv.vaddConst k p := rfl
+
+/-- The affine homeomorphism given by `p' ↦ p -ᵥ p'`. This is `Equiv.constVSub` as a
+`ContinuousAffineEquiv`. -/
+@[simps! apply symm_apply]
+def constVSub (p : P₁) : P₁ ≃ᴬ[k] V₁ where
+ __ := AffineEquiv.constVSub k p
+ __ := Homeomorph.constVSub p
+
+@[simp]
+lemma toAffineEquiv_constVSub {p : P₁} : constVSub k p = AffineEquiv.constVSub k p := rfl
+
/-- The affine homeomorphism given by reflection about the point `x`.
This is `Equiv.pointReflection` as a `ContinuousAffineEquiv`. -/
-@[simps toAffineEquiv]
-def pointReflection (x : P₁) : P₁ ≃ᴬ[k] P₁ where
- toAffineEquiv := AffineEquiv.pointReflection k x
- continuous_toFun := by dsimp [Equiv.pointReflection]; fun_prop
- continuous_invFun := by
- let : ContinuousNeg V₁ :=
- IsTopologicalAddTorsor.to_isTopologicalAddGroup (V := V₁) (P := P₁) |>.toContinuousNeg
- dsimp [Equiv.pointReflection]; fun_prop
+def pointReflection (x : P₁) : P₁ ≃ᴬ[k] P₁ :=
+ (constVSub k x).trans (vaddConst k x)
+
+@[simp]
+lemma coe_pointReflection (x : P₁) :
+ (pointReflection k x : P₁ → P₁) = Equiv.pointReflection x := rfl
theorem pointReflection_apply (x y : P₁) : pointReflection k x y = (x -ᵥ y) +ᵥ x :=
rfl
@@ -330,6 +348,17 @@ theorem pointReflection_apply (x y : P₁) : pointReflection k x y = (x -ᵥ y)
theorem pointReflection_symm (x : P₁) : (pointReflection k x).symm = pointReflection k x :=
toAffineEquiv_injective <| AffineEquiv.pointReflection_symm k x
+@[simp]
+theorem toAffineEquiv_pointReflection (x : P₁) :
+ (pointReflection k x).toAffineEquiv = AffineEquiv.pointReflection k x :=
+ rfl
+
+theorem pointReflection_self (x : P₁) : pointReflection k x x = x :=
+ vsub_vadd _ _
+
+theorem pointReflection_involutive (x : P₁) : Involutive (pointReflection k x : P₁ → P₁) :=
+ Equiv.pointReflection_involutive x
+
end
section
From 8480e753dfcf2bacd13fad632ba8ed853f7c3dab Mon Sep 17 00:00:00 2001
From: Christian Merten <136261474+chrisflav@users.noreply.github.com>
Date: Mon, 22 Jun 2026 16:23:51 +0000
Subject: [PATCH 0256/1300] chore(Algebra): some API for
`IsLocalizedModule.Away` (#40454)
These are mostly analogues of what we have for `IsLocalization.Away`. The `Away` file was previously deprecated, but seems like the natural fit for the material, since the `Basic` file is currently just below the 1.5k length limit.
---
.../Algebra/Module/LocalizedModule/Away.lean | 80 ++++++++++++++++++-
1 file changed, 78 insertions(+), 2 deletions(-)
diff --git a/Mathlib/Algebra/Module/LocalizedModule/Away.lean b/Mathlib/Algebra/Module/LocalizedModule/Away.lean
index 1513c0b071c8c5..9d1f97c89ee111 100644
--- a/Mathlib/Algebra/Module/LocalizedModule/Away.lean
+++ b/Mathlib/Algebra/Module/LocalizedModule/Away.lean
@@ -1,5 +1,81 @@
-module -- shake: keep-all
+/-
+Copyright (c) 2026 Christian Merten. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Christian Merten
+-/
+module
public import Mathlib.Algebra.Module.LocalizedModule.Basic
-deprecated_module (since := "2026-04-23")
+/-!
+# API for localized modules away from an element
+
+We provide some specialized API for the localization of a module away from an element.
+-/
+
+public section
+
+namespace IsLocalizedModule.Away
+
+variable {R : Type*} [CommSemiring R] {M N : Type*} [AddCommMonoid M] [AddCommMonoid N]
+ [Module R M] [Module R N] {f : M →ₗ[R] N} {r : R}
+
+lemma mk (h₁ : IsUnit (algebraMap R (Module.End R N) r))
+ (h₂ : ∀ (x : N), ∃ (n : ℕ) (y : M), r ^ n • x = f y)
+ (h₃ : ∀ (x y : M), f x = f y → ∃ (n : ℕ), r ^ n • x = r ^ n • y) :
+ IsLocalizedModule.Away r f where
+ map_units := fun ⟨_, ⟨n, rfl⟩⟩ ↦ by simp [h₁.pow]
+ surj x := by
+ obtain ⟨n, y, hy⟩ := h₂ x
+ use ⟨y, ⟨_, n, rfl⟩⟩, hy
+ exists_of_eq {x y} hxy := by
+ obtain ⟨n, hn⟩ := h₃ _ _ hxy
+ use ⟨_, n, rfl⟩, hn
+
+lemma mk_of_addCommGroup {M N : Type*} [AddCommGroup M] [AddCommGroup N] [Module R M] [Module R N]
+ {f : M →ₗ[R] N} {r : R} (h₁ : IsUnit (algebraMap R (Module.End R N) r))
+ (h₂ : ∀ (x : N), ∃ (n : ℕ) (y : M), r ^ n • x = f y)
+ (h₃ : ∀ (x : M), f x = 0 → ∃ (n : ℕ), r ^ n • x = 0) :
+ IsLocalizedModule.Away r f := by
+ refine IsLocalizedModule.Away.mk h₁ h₂ fun x y hxy ↦ ?_
+ have : f (x - y) = 0 := by simp [hxy]
+ obtain ⟨n, hn⟩ := h₃ _ this
+ use n
+ simpa [smul_sub, sub_eq_zero] using hn
+
+variable (r) [IsLocalizedModule.Away r f]
+
+variable (f) in
+include f in
+lemma isUnit_algebraMap : IsUnit (algebraMap R (Module.End R N) r) :=
+ IsLocalizedModule.map_units (S := .powers r) f ⟨_, 1, by simp⟩
+
+lemma exists_of_eq {x y : M} (h : f x = f y) : ∃ (n : ℕ), r ^ n • x = r ^ n • y := by
+ obtain ⟨⟨_, n, rfl⟩, hn⟩ := IsLocalizedModule.exists_of_eq (S := .powers r) h
+ use n, hn
+
+variable (f) in
+lemma surj (y : N) : ∃ (n : ℕ) (x : M), r ^ n • y = f x := by
+ obtain ⟨⟨x, ⟨_, n, rfl⟩⟩, h⟩ := IsLocalizedModule.surj (S := .powers r) f y
+ use n, x, h
+
+lemma of_associated {r r' : R} (h : Associated r r') [IsLocalizedModule.Away r f] :
+ IsLocalizedModule.Away r' f := by
+ obtain ⟨u, rfl⟩ := h
+ rw [mul_comm]
+ refine .mk ?_ ?_ ?_
+ · simp [IsUnit.mul, isUnit_algebraMap f r, u.isUnit.map _]
+ · intro y
+ obtain ⟨n, x, hx⟩ := surj f r y
+ use n, (u ^ n) • x
+ simp [mul_pow, ← hx, mul_smul, Units.smul_def]
+ · intro x y hxy
+ obtain ⟨n, hn⟩ := exists_of_eq r hxy
+ use n
+ simp [mul_pow, mul_smul, hn]
+
+lemma iff_of_associated {r r' : R} (h : Associated r r') :
+ IsLocalizedModule.Away r f ↔ IsLocalizedModule.Away r' f :=
+ ⟨fun _ ↦ .of_associated h, fun _ ↦ .of_associated h.symm⟩
+
+end IsLocalizedModule.Away
From 8ade642d99dbfbfcdcaad4965baba903385a1e51 Mon Sep 17 00:00:00 2001
From: Christian Merten <136261474+chrisflav@users.noreply.github.com>
Date: Mon, 22 Jun 2026 16:23:53 +0000
Subject: [PATCH 0257/1300] feat(CategoryTheory/Sites): pushforward and
pullback of presieves (#40527)
We define analogues of `Sieve.pushforward` and `Sieve.pullback` for `Presieve`s and show the Galois connection.
---
.../Sites/CoproductSheafCondition.lean | 3 +-
.../CategoryTheory/Sites/Hypercover/Zero.lean | 60 ++++++++++++
Mathlib/CategoryTheory/Sites/IsSheafFor.lean | 4 +-
Mathlib/CategoryTheory/Sites/Sieves.lean | 92 ++++++++++++++++++-
4 files changed, 155 insertions(+), 4 deletions(-)
diff --git a/Mathlib/CategoryTheory/Sites/CoproductSheafCondition.lean b/Mathlib/CategoryTheory/Sites/CoproductSheafCondition.lean
index 9ebc9725d813d7..7c09039b9dde8d 100644
--- a/Mathlib/CategoryTheory/Sites/CoproductSheafCondition.lean
+++ b/Mathlib/CategoryTheory/Sites/CoproductSheafCondition.lean
@@ -86,7 +86,8 @@ lemma Presieve.isSheafFor_sigmaDesc_iff {ι : Type*} {X : ι → C} (f : ∀ i,
[∀ i, HasPullback (f i) (Cofan.IsColimit.desc hc f)]
(F : Cᵒᵖ ⥤ Type*)
[PreservesLimit (Discrete.functor <| fun i ↦ op (X i)) F]
- [PreservesLimit (Discrete.functor fun (ij : ι × ι) ↦ op (pullback (f ij.1) (f ij.2))) F] :
+ [PreservesLimit (Discrete.functor fun (ij : ι × ι) ↦
+ op (Limits.pullback (f ij.1) (f ij.2))) F] :
Presieve.IsSheafFor F (.singleton <| Cofan.IsColimit.desc hc f) ↔
Presieve.IsSheafFor F (.ofArrows X f) := by
let E := PreZeroHypercover.mk _ _ f
diff --git a/Mathlib/CategoryTheory/Sites/Hypercover/Zero.lean b/Mathlib/CategoryTheory/Sites/Hypercover/Zero.lean
index 45384739957a84..b245a70e8c9c81 100644
--- a/Mathlib/CategoryTheory/Sites/Hypercover/Zero.lean
+++ b/Mathlib/CategoryTheory/Sites/Hypercover/Zero.lean
@@ -377,6 +377,56 @@ lemma Hom.sieve₀_le_sieve₀ {E F : PreZeroHypercover S} (f : E.Hom F) : E.sie
lemma sieve₀_eq_of_iso {E F : PreZeroHypercover S} (e : E ≅ F) : E.sieve₀ = F.sieve₀ :=
le_antisymm e.hom.sieve₀_le_sieve₀ e.inv.sieve₀_le_sieve₀
+/-- The equivalence on index types induced by an isomorphism of pre-`0`-hypercovers. -/
+@[simps]
+def equivOfIso {E F : PreZeroHypercover.{w} S} (e : E ≅ F) : E.I₀ ≃ F.I₀ where
+ toFun := e.hom.s₀
+ invFun := e.inv.s₀
+ left_inv _ := by simp
+ right_inv _ := by simp
+
+lemma mem_of_iso {K : Precoverage C} [K.IsStableUnderComposition] [K.HasIsos] {X : C}
+ {E F : PreZeroHypercover.{w} X} (e : E ≅ F) (hE : E.presieve₀ ∈ K X) :
+ F.presieve₀ ∈ K X := by
+ have : F.presieve₀ =
+ Presieve.ofArrows (fun (i : Σ (_ : F.I₀), Unit) ↦ _) (fun i ↦ e.inv.h₀ i.1 ≫ E.f _) := by
+ simp only [Hom.w₀]
+ refine le_antisymm ?_ ?_
+ · rw [Presieve.ofArrows_le_iff]
+ intro i
+ exact .mk (⟨i, ⟨⟩⟩ : Σ (_ : F.I₀), Unit)
+ · simp [Presieve.ofArrows_le_iff]
+ rw [this]
+ refine K.comp_mem_coverings (fun i ↦ E.f (e.inv.s₀ i)) ?_ (fun i (k : Unit) ↦ e.inv.h₀ i) ?_
+ · rwa [← E.presieve₀_reindex (PreZeroHypercover.equivOfIso e.symm)] at hE
+ · intro i
+ rw [Presieve.ofArrows_pUnit]
+ exact K.mem_coverings_of_isIso _
+
+lemma mem_iff_of_iso {K : Precoverage C} [K.IsStableUnderComposition] [K.HasIsos] {X : C}
+ {E F : PreZeroHypercover.{w} X} (e : E ≅ F) :
+ E.presieve₀ ∈ K X ↔ F.presieve₀ ∈ K X :=
+ ⟨fun h ↦ PreZeroHypercover.mem_of_iso e h, fun h ↦ PreZeroHypercover.mem_of_iso e.symm h⟩
+
+/-- Compose a pre-`0`-hypercover with a morphism on the right. -/
+@[simps]
+def pushforward {X Y : C} (f : X ⟶ Y) (E : PreZeroHypercover.{w} X) :
+ PreZeroHypercover.{w} Y where
+ I₀ := E.I₀
+ X := E.X
+ f i := E.f i ≫ f
+
+lemma presieve₀_pushforward {X Y : C} (f : X ⟶ Y) (E : PreZeroHypercover.{w} X) :
+ (E.pushforward f).presieve₀ = E.presieve₀.pushforward f := by
+ simp [presieve₀, Presieve.pushforward_ofArrows, pushforward]
+
+set_option backward.isDefEq.respectTransparency false in
+/-- Pushforward along a morphism is the same as refining the singleton pre-`0`-hypercover. -/
+@[simps!]
+def pushforwardIsoBind {X Y : C} (f : X ⟶ Y) (E : PreZeroHypercover.{w} X) :
+ E.pushforward f ≅ (singleton f).bind fun _ ↦ E :=
+ isoMk ((Equiv.uniqueSigma fun i ↦ E.I₀).symm) (fun _ ↦ Iso.refl _)
+
end Category
section Functoriality
@@ -728,6 +778,16 @@ def weaken {K L : Precoverage C} {X : C} (E : Precoverage.ZeroHypercover K X) (h
__ := E
mem₀ := h _ E.mem₀
+/-- Compose a `0`-hypercover with a morphism on the right. -/
+@[simps toPreZeroHypercover]
+def pushforward [J.IsStableUnderComposition] [J.HasIsos] {X Y : C} (f : X ⟶ Y)
+ (hf : .singleton f ∈ J _) (E : ZeroHypercover.{w} J X) :
+ ZeroHypercover.{w} J Y where
+ __ := E.toPreZeroHypercover.pushforward f
+ mem₀ := by
+ rw [PreZeroHypercover.mem_iff_of_iso (E.pushforwardIsoBind _)]
+ exact ((ZeroHypercover.singleton f hf).bind _).mem₀
+
instance (K : Precoverage C) [K.HasPullbacks] {X Y : C} (E : K.ZeroHypercover X) (f : Y ⟶ X) :
E.presieve₀.HasPullbacks f :=
K.hasPullbacks_of_mem _ E.mem₀
diff --git a/Mathlib/CategoryTheory/Sites/IsSheafFor.lean b/Mathlib/CategoryTheory/Sites/IsSheafFor.lean
index 7044c4e243f591..139fcc18e1d29f 100644
--- a/Mathlib/CategoryTheory/Sites/IsSheafFor.lean
+++ b/Mathlib/CategoryTheory/Sites/IsSheafFor.lean
@@ -1062,10 +1062,10 @@ theorem isSheafFor_trans (P : Cᵒᵖ ⥤ Type*) (R S : Sieve X)
apply Presieve.isSheafFor_subsieve_aux P this
· apply isSheafFor_bind _ _ _ hR hS
intro Y f hf Z g
- rw [← pullback_comp]
+ rw [← Sieve.pullback_comp]
apply (hS (R.downward_closed hf _)).isSeparatedFor
· intro Y f hf
- have : Sieve.pullback f (Sieve.bind R fun T (k : T ⟶ X) (_ : R k) => pullback k S) =
+ have : Sieve.pullback f (Sieve.bind R fun T (k : T ⟶ X) (_ : R k) => Sieve.pullback k S) =
R.pullback f := by
ext Z g
constructor
diff --git a/Mathlib/CategoryTheory/Sites/Sieves.lean b/Mathlib/CategoryTheory/Sites/Sieves.lean
index 7838be1c2e50fd..8a23a45ba149dd 100644
--- a/Mathlib/CategoryTheory/Sites/Sieves.lean
+++ b/Mathlib/CategoryTheory/Sites/Sieves.lean
@@ -295,6 +295,82 @@ lemma bindOfArrows_ofArrows {ι : Type*} {S : C} {X : ι → C} (f : (i : ι)
rintro W u ⟨i, v, ⟨j⟩⟩
exact ⟨Sigma.mk i j⟩
+/-- Compose a presieve on the right with a morphism. -/
+def pushforward {X Y : C} (f : X ⟶ Y) (R : Presieve X) : Presieve Y :=
+ fun Z fg ↦ ∃ (g : Z ⟶ X), g ≫ f = fg ∧ R g
+
+@[grind .]
+lemma pushforward_apply_comp {X Y Z : C} {f : X ⟶ Y} {R : Presieve X} {g : Z ⟶ X} (hg : R g) :
+ R.pushforward f (g ≫ f) :=
+ ⟨g, rfl, hg⟩
+
+lemma pushforward_ofArrows {ι : Type*} {U : ι → C} {X Y : C} (g : ∀ i, U i ⟶ X)
+ (f : X ⟶ Y) : (ofArrows _ g).pushforward f = ofArrows _ (g · ≫ f) := by
+ refine le_antisymm ?_ ?_
+ · rintro _ _ ⟨u, rfl, ⟨i⟩⟩
+ exact ⟨i⟩
+ · rw [ofArrows_le_iff]
+ intro i
+ use g i, rfl
+ exact ⟨i⟩
+
+lemma pushforward_singleton {X Y Z : C} (f : X ⟶ Y) (g : Y ⟶ Z) :
+ (singleton f).pushforward g = .singleton (f ≫ g) := by
+ rw [← ofArrows_pUnit.{_, _, 0}, pushforward_ofArrows, ofArrows_pUnit.{_, _, 0}]
+
+/-- The pullback of a presieve `R` on `Y` along a morphism `f : X ⟶ Y` is the presieve on `X`
+given by all morphisms `g : Z ⟶ X` such that `f ≫ g` is in `R`. -/
+def pullback {X Y : C} (f : X ⟶ Y) (R : Presieve Y) : Presieve X :=
+ fun _ g ↦ R (g ≫ f)
+
+variable {f} in
+@[simp, grind =]
+lemma pullback_iff {R : Presieve X} {Z : C} {g : Z ⟶ Y} :
+ R.pullback f g ↔ R (g ≫ f) :=
+ .rfl
+
+lemma pushforward_le_iff_le_pullback (R : Presieve Y) (T : Presieve X) :
+ R.pushforward f ≤ T ↔ R ≤ T.pullback f := by
+ refine ⟨fun hle Z g hg ↦ hle _ _ (pushforward_apply_comp hg), ?_⟩
+ rintro hle Z - ⟨g, rfl, hg⟩
+ exact hle _ _ hg
+
+lemma galoisConnection_pushforward_pullback :
+ GaloisConnection (pushforward f) (pullback f) :=
+ pushforward_le_iff_le_pullback f
+
+lemma monotone_pushforward : Monotone (pushforward f) :=
+ (galoisConnection_pushforward_pullback f).monotone_l
+
+lemma monotone_pullback : Monotone (pullback f) :=
+ (galoisConnection_pushforward_pullback f).monotone_u
+
+lemma pushforward_pullback_le (R : Presieve X) : (R.pullback f).pushforward f ≤ R :=
+ (galoisConnection_pushforward_pullback f).l_u_le _
+
+lemma le_pullback_pushforward (R : Presieve Y) : R ≤ (R.pushforward f).pullback f :=
+ (galoisConnection_pushforward_pullback f).le_u_l _
+
+@[simp]
+lemma pullback_id (R : Presieve X) : R.pullback (𝟙 X) = R := by
+ funext
+ simp
+
+lemma pullback_comp (R : Presieve Z) (g : X ⟶ Z) :
+ R.pullback (f ≫ g) = (R.pullback g).pullback f := by
+ funext
+ simp
+
+@[simp]
+lemma pushforward_id (R : Presieve X) : R.pushforward (𝟙 X) = R := by
+ funext
+ simp [pushforward]
+
+lemma pushforward_comp (R : Presieve Y) (g : X ⟶ Z) :
+ R.pushforward (f ≫ g) = (R.pushforward f).pushforward g := by
+ funext
+ simp [pushforward]
+
/-- Given a presieve on `F(X)`, we can define a presieve on `X` by taking the preimage via `F`. -/
def functorPullback (R : Presieve (F.obj X)) : Presieve X := fun _ f => R (F.map f)
@@ -462,7 +538,8 @@ def uncurry : Set (Σ Y, Y ⟶ X) :=
obtain ⟨rfl, h⟩ := h; subst h; constructor
@[simp] theorem uncurry_pullbackArrows [HasPullbacks C] {B : C} (b : B ⟶ X) :
- (pullbackArrows b s).uncurry = (fun f ↦ ⟨pullback f.2 b, pullback.snd _ _⟩) '' s.uncurry := by
+ (pullbackArrows b s).uncurry =
+ (fun f ↦ ⟨Limits.pullback f.2 b, pullback.snd _ _⟩) '' s.uncurry := by
ext ⟨Z, v⟩; constructor
· rintro ⟨Y, u, hu⟩; exact ⟨⟨Y, u⟩, hu, rfl⟩
· rintro ⟨⟨Y, u⟩, hu, h⟩
@@ -956,6 +1033,19 @@ theorem pullbackArrows_comm {X Y : C} (f : Y ⟶ X) (R : Presieve X) [R.HasPullb
have := R.hasPullback f hk
exact ⟨_, _, _, Presieve.pullbackArrows.mk _ _ hk, pullback.lift_snd _ _ comm⟩
+lemma pullback_arrows {X Y : C} (f : X ⟶ Y) (S : Sieve Y) :
+ (S.pullback f).arrows = S.arrows.pullback f :=
+ rfl
+
+lemma pushforward_arrows {X Y : C} (f : X ⟶ Y) (S : Sieve X) :
+ (S.pushforward f).arrows = S.arrows.pushforward f :=
+ rfl
+
+lemma generate_pushforward {X Y : C} (f : X ⟶ Y) (R : Presieve X) :
+ generate (R.pushforward f) = (generate R).pushforward f := by
+ ext
+ grind [generate_apply, Presieve.pushforward, pushforward_apply]
+
section Functor
variable {E : Type u₃} [Category.{v₃} E] (G : D ⥤ E)
From c27dae6d362add64b9c8778a6184ca90992211bb Mon Sep 17 00:00:00 2001
From: Bolton Bailey
Date: Mon, 22 Jun 2026 16:23:56 +0000
Subject: [PATCH 0258/1300] feat(NumberTheory/FactorizationProperties): add
positivity lemmas (#40562)
This PR proves the basic fact that abundant, deficient, and weird numbers are positive.
This was done as a part of Project Numina's LeanTriathlon project with the help of AI (Claude Code and Numina's lean agent)
---
Mathlib/NumberTheory/FactorisationProperties.lean | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/Mathlib/NumberTheory/FactorisationProperties.lean b/Mathlib/NumberTheory/FactorisationProperties.lean
index 00ed37e5c471c2..cea8ccdfdc3df0 100644
--- a/Mathlib/NumberTheory/FactorisationProperties.lean
+++ b/Mathlib/NumberTheory/FactorisationProperties.lean
@@ -81,6 +81,9 @@ theorem not_pseudoperfect_iff_forall :
¬ Pseudoperfect n ↔ n = 0 ∨ ∀ s ⊆ properDivisors n, ∑ i ∈ s, i ≠ n := by
grind [Pseudoperfect]
+theorem not_deficient_zero : ¬ Deficient 0 := by
+ decide
+
theorem deficient_one : Deficient 1 := by
decide
@@ -96,9 +99,21 @@ theorem not_abundant_zero : ¬ Abundant 0 := by
theorem abundant_twelve : Abundant 12 := by
decide
+theorem not_weird_zero : ¬ Weird 0 := by
+ decide
+
theorem weird_seventy : Weird 70 := by
decide +kernel
+lemma Deficient.pos (h : Deficient n) : 0 < n := by
+ grind only [not_deficient_zero]
+
+lemma Abundant.pos (h : Abundant n) : 0 < n := by
+ grind only [not_abundant_zero]
+
+lemma Weird.pos (h : Weird n) : 0 < n := by
+ grind only [not_weird_zero]
+
lemma deficient_iff_not_abundant_and_not_perfect (hn : n ≠ 0) :
Deficient n ↔ ¬ Abundant n ∧ ¬ Perfect n := by
grind [Perfect, Abundant, Deficient]
From 553eb7263d544ddfb32d37456245d9358fa4a4cd Mon Sep 17 00:00:00 2001
From: Ben Eltschig <43812953+peabrainiac@users.noreply.github.com>
Date: Mon, 22 Jun 2026 16:23:59 +0000
Subject: [PATCH 0259/1300] chore(Topology): namespace lemmas around
`IsInducing`, `IsQuotientMap` etc (#40891)
The predicates `IsInducing`, `IsEmbedding`, `IsQuotientMap` etc. were moved to the `Topology` namespace in #15993, but some lemmas were not moved with them, or were added outside of the namespace later. This PR moves these lemmas to the correct namespace to enable dot notation.
We do not add deprecation aliases since the lemmas only get namespaced, not renamed, and having both the lemmas and their deprecated aliases available inside the namespace could lead to trouble.
---
.../AlgebraicGeometry/Morphisms/UnderlyingMap.lean | 4 ++--
Mathlib/Condensed/TopComparison.lean | 2 +-
.../MeasureTheory/Constructions/BorelSpace/Basic.lean | 3 ++-
Mathlib/Topology/Constructions/SumProd.lean | 11 ++++++-----
Mathlib/Topology/DiscreteSubset.lean | 2 +-
Mathlib/Topology/Inseparable.lean | 2 +-
Mathlib/Topology/KrullDimension.lean | 8 ++++----
Mathlib/Topology/LocalAtTarget.lean | 5 +----
Mathlib/Topology/Separation/Hausdorff.lean | 2 +-
9 files changed, 19 insertions(+), 20 deletions(-)
diff --git a/Mathlib/AlgebraicGeometry/Morphisms/UnderlyingMap.lean b/Mathlib/AlgebraicGeometry/Morphisms/UnderlyingMap.lean
index f505798c3260fe..64b3f6a6381810 100644
--- a/Mathlib/AlgebraicGeometry/Morphisms/UnderlyingMap.lean
+++ b/Mathlib/AlgebraicGeometry/Morphisms/UnderlyingMap.lean
@@ -314,8 +314,8 @@ end SpecializingMap
section GeneralizingMap
instance : (topologically GeneralizingMap).RespectsIso :=
- topologically_respectsIso _ (fun f ↦ f.isOpenEmbedding.generalizingMap
- f.isOpenEmbedding.isOpen_range.stableUnderGeneralization) (fun _ _ hf hg ↦ hf.comp hg)
+ topologically_respectsIso _ (fun f ↦ f.isOpenEmbedding.generalizingMap)
+ (fun _ _ hf hg ↦ hf.comp hg)
instance : IsZariskiLocalAtSource (topologically GeneralizingMap) :=
topologically_isZariskiLocalAtSource' (fun _ ↦ _) fun _ _ _ hU _ ↦ hU.generalizingMap_iff_comp
diff --git a/Mathlib/Condensed/TopComparison.lean b/Mathlib/Condensed/TopComparison.lean
index 0fd74643ae8dbf..11985db7efee7a 100644
--- a/Mathlib/Condensed/TopComparison.lean
+++ b/Mathlib/Condensed/TopComparison.lean
@@ -117,7 +117,7 @@ def TopCat.toSheafCompHausLike :
apply +allowSynthFailures equalizerCondition_yonedaPresheaf
(CompHausLike.compHausLikeToTop.{u} P) X
intro Z B π he
- apply IsQuotientMap.of_surjective_continuous (hs _ he) π.hom.hom.continuous
+ exact .of_surjective_continuous (hs _ he) π.hom.hom.continuous
/--
`TopCat.toSheafCompHausLike` yields a functor from `TopCat.{max u w}` to
diff --git a/Mathlib/MeasureTheory/Constructions/BorelSpace/Basic.lean b/Mathlib/MeasureTheory/Constructions/BorelSpace/Basic.lean
index 9bf51c1660b896..6d155826e04879 100644
--- a/Mathlib/MeasureTheory/Constructions/BorelSpace/Basic.lean
+++ b/Mathlib/MeasureTheory/Constructions/BorelSpace/Basic.lean
@@ -500,7 +500,8 @@ is ae-measurable. -/
theorem Continuous.aemeasurable {f : α → γ} (h : Continuous f) {μ : Measure α} : AEMeasurable f μ :=
h.measurable.aemeasurable
-theorem IsClosedEmbedding.measurable {f : α → γ} (hf : IsClosedEmbedding f) : Measurable f :=
+theorem Topology.IsClosedEmbedding.measurable {f : α → γ} (hf : IsClosedEmbedding f) :
+ Measurable f :=
hf.continuous.measurable
/-- If a function is defined piecewise in terms of functions which are continuous on their
diff --git a/Mathlib/Topology/Constructions/SumProd.lean b/Mathlib/Topology/Constructions/SumProd.lean
index 8c417843dbf7e1..ab4185bf312be4 100644
--- a/Mathlib/Topology/Constructions/SumProd.lean
+++ b/Mathlib/Topology/Constructions/SumProd.lean
@@ -800,7 +800,7 @@ theorem IsOpenMap.sumElim {f : X → Z} {g : Y → Z} (hf : IsOpenMap f) (hg : I
IsOpenMap (Sum.elim f g) :=
isOpenMap_sumElim.2 ⟨hf, hg⟩
-lemma IsOpenEmbedding.sumElim {f : X → Z} {g : Y → Z}
+lemma Topology.IsOpenEmbedding.sumElim {f : X → Z} {g : Y → Z}
(hf : IsOpenEmbedding f) (hg : IsOpenEmbedding g) (h : Injective (Sum.elim f g)) :
IsOpenEmbedding (Sum.elim f g) := by
rw [isOpenEmbedding_iff_continuous_injective_isOpenMap] at hf hg ⊢
@@ -830,7 +830,7 @@ theorem IsClosedMap.sumElim {f : X → Z} {g : Y → Z} (hf : IsClosedMap f) (hg
IsClosedMap (Sum.elim f g) :=
isClosedMap_sumElim.2 ⟨hf, hg⟩
-lemma IsClosedEmbedding.sumElim {f : X → Z} {g : Y → Z}
+lemma Topology.IsClosedEmbedding.sumElim {f : X → Z} {g : Y → Z}
(hf : IsClosedEmbedding f) (hg : IsClosedEmbedding g) (h : Injective (Sum.elim f g)) :
IsClosedEmbedding (Sum.elim f g) := by
rw [IsClosedEmbedding.isClosedEmbedding_iff_continuous_injective_isClosedMap] at hf hg ⊢
@@ -974,16 +974,17 @@ theorem Topology.IsInducing.disjoint_of_sumElim_aux (h : IsInducing (Sum.elim f
exact disjoint_image_inl_image_inr
exact B.mono_left A
-theorem IsOpenEmbedding.sumSwap : IsOpenEmbedding (@Sum.swap X Y) :=
+theorem Topology.IsOpenEmbedding.sumSwap : IsOpenEmbedding (@Sum.swap X Y) :=
(Homeomorph.sumComm X Y).isOpenEmbedding
-theorem IsInducing.sumSwap : IsInducing (@Sum.swap X Y) := IsOpenEmbedding.sumSwap.isInducing
+theorem Topology.IsInducing.sumSwap : IsInducing (@Sum.swap X Y) :=
+ IsOpenEmbedding.sumSwap.isInducing
theorem isInducing_sumElim :
IsInducing (Sum.elim f g) ↔ IsInducing f ∧ IsInducing g ∧
Disjoint (closure (range f)) (range g) ∧ Disjoint (range f) (closure (range g)) :=
⟨fun h ↦ ⟨h.sumElim_left, h.sumElim_right, h.disjoint_of_sumElim_aux,
- ((Sum.elim_swap ▸ h.comp IsInducing.sumSwap).disjoint_of_sumElim_aux ).symm⟩,
+ ((Sum.elim_swap ▸ h.comp .sumSwap).disjoint_of_sumElim_aux ).symm⟩,
fun ⟨hf, hg, hFg, hfG⟩ ↦ hf.sumElim hg hFg hfG⟩
lemma Topology.IsInducing.sumElim_of_separatedNhds
diff --git a/Mathlib/Topology/DiscreteSubset.lean b/Mathlib/Topology/DiscreteSubset.lean
index 108b7ca92a5d8d..6cfe19a31b5217 100644
--- a/Mathlib/Topology/DiscreteSubset.lean
+++ b/Mathlib/Topology/DiscreteSubset.lean
@@ -112,7 +112,7 @@ lemma IsOpenMap.isDiscrete_range [DiscreteTopology X] (hf : IsOpenMap f) :
lemma IsDiscrete.image (hs : IsDiscrete s) (hf : IsInducing f) : IsDiscrete (f '' s) := by
simp_all [isDiscrete_iff_nhdsWithin, ← hf.map_nhdsWithin_eq s]
-lemma IsInducing.isDiscrete_range [DiscreteTopology X] (hf : IsInducing f) :
+lemma Topology.IsInducing.isDiscrete_range [DiscreteTopology X] (hf : IsInducing f) :
IsDiscrete (Set.range f) := by
simpa using IsDiscrete.univ.image hf
diff --git a/Mathlib/Topology/Inseparable.lean b/Mathlib/Topology/Inseparable.lean
index cd48ad7ae8dc44..548e63f3a83ded 100644
--- a/Mathlib/Topology/Inseparable.lean
+++ b/Mathlib/Topology/Inseparable.lean
@@ -413,7 +413,7 @@ lemma Topology.IsInducing.generalizingMap (hf : IsInducing f)
obtain ⟨y, rfl⟩ := h e ⟨x, rfl⟩
exact ⟨_, hf.specializes_iff.mp e, rfl⟩
-lemma IsOpenEmbedding.generalizingMap (hf : IsOpenEmbedding f) : GeneralizingMap f :=
+lemma Topology.IsOpenEmbedding.generalizingMap (hf : IsOpenEmbedding f) : GeneralizingMap f :=
hf.isInducing.generalizingMap hf.isOpen_range.stableUnderGeneralization
lemma SpecializingMap.stableUnderSpecialization_range (h : SpecializingMap f) :
diff --git a/Mathlib/Topology/KrullDimension.lean b/Mathlib/Topology/KrullDimension.lean
index 187f1e25b66e46..0b55b422398669 100644
--- a/Mathlib/Topology/KrullDimension.lean
+++ b/Mathlib/Topology/KrullDimension.lean
@@ -45,7 +45,7 @@ variable {X Y : Type*} [TopologicalSpace X] [TopologicalSpace Y]
### Main dimension theorems -/
/-- If `f : Y → X` is inducing, then `dim(Y) ≤ dim(X)`. -/
-theorem IsInducing.topologicalKrullDim_le {f : Y → X} (hf : IsInducing f) :
+theorem Topology.IsInducing.topologicalKrullDim_le {f : Y → X} (hf : IsInducing f) :
topologicalKrullDim Y ≤ topologicalKrullDim X :=
krullDim_le_of_strictMono _ (map_strictMono_of_isInducing hf)
@@ -53,16 +53,16 @@ theorem IsInducing.topologicalKrullDim_le {f : Y → X} (hf : IsInducing f) :
theorem IsHomeomorph.topologicalKrullDim_eq (f : X → Y) (h : IsHomeomorph f) :
topologicalKrullDim X = topologicalKrullDim Y :=
have fwd : topologicalKrullDim X ≤ topologicalKrullDim Y :=
- IsInducing.topologicalKrullDim_le h.isClosedEmbedding.toIsInducing
+ h.isInducing.topologicalKrullDim_le
have bwd : topologicalKrullDim Y ≤ topologicalKrullDim X :=
- IsInducing.topologicalKrullDim_le (h.homeomorph f).symm.isClosedEmbedding.toIsInducing
+ (h.homeomorph f).symm.isInducing.topologicalKrullDim_le
le_antisymm fwd bwd
/-- The topological Krull dimension of any subspace is at most the dimension of the
ambient space. -/
theorem topologicalKrullDim_subspace_le (X : Type*) [TopologicalSpace X] (Y : Set X) :
topologicalKrullDim Y ≤ topologicalKrullDim X :=
- IsInducing.topologicalKrullDim_le IsInducing.subtypeVal
+ IsInducing.subtypeVal.topologicalKrullDim_le
theorem topologicalKrullDim_zero_of_discreteTopology
(X : Type*) [TopologicalSpace X] [DiscreteTopology X] :
diff --git a/Mathlib/Topology/LocalAtTarget.lean b/Mathlib/Topology/LocalAtTarget.lean
index 6bb1eb268b78e7..1ff14ed4d98080 100644
--- a/Mathlib/Topology/LocalAtTarget.lean
+++ b/Mathlib/Topology/LocalAtTarget.lean
@@ -224,10 +224,7 @@ lemma isOpenMap_iff_comp : IsOpenMap f ↔ ∀ i, IsOpenMap (f ∘ ((↑) : U i
lemma generalizingMap_iff_comp :
GeneralizingMap f ↔ ∀ i, GeneralizingMap (f ∘ ((↑) : U i → α)) := by
- refine ⟨fun hf ↦ fun i ↦
- ((U i).isOpenEmbedding'.generalizingMap
- (U i).isOpenEmbedding'.isOpen_range.stableUnderGeneralization).comp hf,
- fun hf ↦ fun x y h ↦ ?_⟩
+ refine ⟨fun hf i ↦ ((U i).isOpenEmbedding'.generalizingMap).comp hf, fun hf ↦ fun x y h ↦ ?_⟩
obtain ⟨i, hi⟩ := hU.exists_mem x
replace h : y ⤳ (f ∘ ((↑) : U i → α)) ⟨x, hi⟩ := h
obtain ⟨a, ha, rfl⟩ := hf i h
diff --git a/Mathlib/Topology/Separation/Hausdorff.lean b/Mathlib/Topology/Separation/Hausdorff.lean
index 934cdd1aa08abf..bdcb3685f9d283 100644
--- a/Mathlib/Topology/Separation/Hausdorff.lean
+++ b/Mathlib/Topology/Separation/Hausdorff.lean
@@ -675,7 +675,7 @@ theorem Continuous.isClosedEmbedding [CompactSpace X] [T2Space Y] {f : X → Y}
.of_continuous_injective_isClosedMap h hf h.isClosedMap
/-- A continuous surjective map from a compact space to a Hausdorff space is a quotient map. -/
-theorem IsQuotientMap.of_surjective_continuous [CompactSpace X] [T2Space Y] {f : X → Y}
+theorem Topology.IsQuotientMap.of_surjective_continuous [CompactSpace X] [T2Space Y] {f : X → Y}
(hsurj : Surjective f) (hcont : Continuous f) : IsQuotientMap f :=
hcont.isClosedMap.isQuotientMap hcont hsurj
From b0af307631dda8100752be5e78e50a28b4e4fce0 Mon Sep 17 00:00:00 2001
From: smorel394 <67864981+smorel394@users.noreply.github.com>
Date: Mon, 22 Jun 2026 16:24:02 +0000
Subject: [PATCH 0260/1300] feat(CategoryTheory/Limits/Comma): comma categories
have finite (co)limits (#40896)
The original file proves the existence of limits and colimits in comma categories under suitable conditions, and prove that the forgetful functors from (co)structured arrows preserve (co)limits. This adds the specific instances for finite (co)limits.
Co-authored-by: morel
---
Mathlib/CategoryTheory/Limits/Comma.lean | 32 ++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/Mathlib/CategoryTheory/Limits/Comma.lean b/Mathlib/CategoryTheory/Limits/Comma.lean
index 8d0549e47beef3..31172ac7aeb408 100644
--- a/Mathlib/CategoryTheory/Limits/Comma.lean
+++ b/Mathlib/CategoryTheory/Limits/Comma.lean
@@ -10,6 +10,8 @@ public import Mathlib.CategoryTheory.Comma.Over.Basic
public import Mathlib.CategoryTheory.Limits.Constructions.EpiMono
public import Mathlib.CategoryTheory.Limits.Creates
public import Mathlib.CategoryTheory.Limits.Unit
+public import Mathlib.CategoryTheory.Limits.Preserves.Finite
+public import Mathlib.CategoryTheory.Limits.Preserves.Creates.Finite
/-!
# Limits and colimits in comma categories
@@ -150,6 +152,10 @@ instance hasLimitsOfSize [HasLimitsOfSize.{w, w'} A] [HasLimitsOfSize.{w, w'} B]
[PreservesLimitsOfSize.{w, w'} R] : HasLimitsOfSize.{w, w'} (Comma L R) :=
⟨fun _ _ => inferInstance⟩
+instance hasFiniteLimits [HasFiniteLimits A] [HasFiniteLimits B]
+ [PreservesFiniteLimits R] : HasFiniteLimits (Comma L R) where
+ out _ _ _ := inferInstance
+
instance hasColimit (F : J ⥤ Comma L R) [HasColimit (F ⋙ fst L R)] [HasColimit (F ⋙ snd L R)]
[PreservesColimit (F ⋙ fst L R) L] : HasColimit F :=
HasColimit.mk ⟨_, coconeOfPreservesIsColimit _ (colimit.isColimit _) (colimit.isColimit _)⟩
@@ -161,6 +167,10 @@ instance hasColimitsOfSize [HasColimitsOfSize.{w, w'} A] [HasColimitsOfSize.{w,
[PreservesColimitsOfSize.{w, w'} L] : HasColimitsOfSize.{w, w'} (Comma L R) :=
⟨fun _ _ => inferInstance⟩
+instance hasFiniteColimits [HasFiniteColimits A] [HasFiniteColimits B]
+ [PreservesFiniteColimits L] : HasFiniteColimits (Comma L R) where
+ out _ _ _ := inferInstance
+
instance preservesColimitsOfShape_fst [HasColimitsOfShape J A] [HasColimitsOfShape J B]
[PreservesColimitsOfShape J L] : PreservesColimitsOfShape J (Comma.fst L R) where
preservesColimit :=
@@ -188,6 +198,9 @@ instance hasLimit (F : J ⥤ Arrow T) [i₁ : HasLimit (F ⋙ leftFunc)] [i₂ :
instance hasLimitsOfShape [HasLimitsOfShape J T] : HasLimitsOfShape J (Arrow T) where
+instance hasFiniteLimits [HasFiniteLimits T] : HasFiniteLimits (Arrow T) where
+ out _ _ _ := inferInstance
+
instance hasLimits [HasLimits T] : HasLimits (Arrow T) :=
⟨fun _ _ => inferInstance⟩
@@ -200,6 +213,9 @@ instance hasColimit (F : J ⥤ Arrow T) [i₁ : HasColimit (F ⋙ leftFunc)]
instance hasColimitsOfShape [HasColimitsOfShape J T] : HasColimitsOfShape J (Arrow T) where
+instance hasFiniteColimits [HasFiniteColimits T] : HasFiniteColimits (Arrow T) where
+ out _ _ _ := inferInstance
+
instance hasColimits [HasColimits T] : HasColimits (Arrow T) :=
⟨fun _ _ => inferInstance⟩
@@ -230,6 +246,10 @@ instance hasLimit [i₁ : HasLimit (F ⋙ proj X G)] [i₂ : PreservesLimit (F
instance hasLimitsOfShape [HasLimitsOfShape J A] [PreservesLimitsOfShape J G] :
HasLimitsOfShape J (StructuredArrow X G) where
+instance hasFiniteLimits [HasFiniteLimits A] [PreservesFiniteLimits G] :
+ HasFiniteLimits (StructuredArrow X G) where
+ out _ _ _ := inferInstance
+
instance hasLimitsOfSize [HasLimitsOfSize.{w, w'} A] [PreservesLimitsOfSize.{w, w'} G] :
HasLimitsOfSize.{w, w'} (StructuredArrow X G) :=
⟨fun J hJ => by infer_instance⟩
@@ -246,6 +266,10 @@ noncomputable instance createsLimit [i : PreservesLimit (F ⋙ proj X G) G] :
noncomputable instance createsLimitsOfShape [PreservesLimitsOfShape J G] :
CreatesLimitsOfShape J (proj X G) where
+noncomputable instance createsFiniteLimits [PreservesFiniteLimits G] :
+ CreatesFiniteLimits (proj X G) where
+ createsFiniteLimits _ _ _ := inferInstance
+
noncomputable instance createsLimitsOfSize [PreservesLimitsOfSize.{w, w'} G] :
CreatesLimitsOfSize.{w, w'} (proj X G :) where
@@ -277,6 +301,10 @@ instance hasColimit [i₁ : HasColimit (F ⋙ proj G X)] [i₂ : PreservesColimi
instance hasColimitsOfShape [HasColimitsOfShape J A] [PreservesColimitsOfShape J G] :
HasColimitsOfShape J (CostructuredArrow G X) where
+instance hasFiniteColimits [HasFiniteColimits A] [PreservesFiniteColimits G] :
+ HasFiniteColimits (CostructuredArrow G X) where
+ out _ _ _ := inferInstance
+
instance hasColimitsOfSize [HasColimitsOfSize.{w, w'} A] [PreservesColimitsOfSize.{w, w'} G] :
HasColimitsOfSize.{w, w'} (CostructuredArrow G X) :=
⟨fun _ _ => inferInstance⟩
@@ -293,6 +321,10 @@ noncomputable instance createsColimit [i : PreservesColimit (F ⋙ proj G X) G]
noncomputable instance createsColimitsOfShape [PreservesColimitsOfShape J G] :
CreatesColimitsOfShape J (proj G X) where
+noncomputable instance createsFiniteColimits [PreservesFiniteColimits G] :
+ CreatesFiniteColimits (proj G X) where
+ createsFiniteColimits _ _ _ := inferInstance
+
noncomputable instance createsColimitsOfSize [PreservesColimitsOfSize.{w, w'} G] :
CreatesColimitsOfSize.{w, w'} (proj G X :) where
From e8f2bd8352aabd84e68ab2e392c68c9ac226931b Mon Sep 17 00:00:00 2001
From: Xavier Roblot <46200072+xroblot@users.noreply.github.com>
Date: Mon, 22 Jun 2026 16:56:57 +0000
Subject: [PATCH 0261/1300] feat(IsGaloisGroup): add `restrictHom` (#38864)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This PR adds the restriction map for the Galois group for domains.
Given a tower of domains `A ≤ B ≤ C`, `G` a Galois group for `C/A` and `G'` a Galois group for `B/A`, we define:
- `restrictHom`: the restriction of the `G`-action on `C` to the `G'`-action on `B`.
- `quotientMulEquiv`: the isomorphism between the quotient of `G` by the fixing subgroup of `B` and the Galois group of `B/A`.
Supporting lemmas include:
- `normal_of_isGalois`: if `G` is a finite Galois group for `L/K`, `H` is a Galois group for `L/E`, and `E/K` is Galois, then `H` is a normal subgroup of `G`.
- `restrictHom_surjective`: the restriction map is surjective.
---
- [x] depends on: #38902
- [x] depends on: #38464
- [x] depends on: #40804
[](https://gitpod.io/from-referrer/)
Co-authored-by: Author Name
Co-authored-by: tb65536
---
Mathlib/FieldTheory/Galois/IsGaloisGroup.lean | 112 +++++++++++++++++-
1 file changed, 109 insertions(+), 3 deletions(-)
diff --git a/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean b/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean
index 1495a47a590cae..2fcbbed0106e1f 100644
--- a/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean
+++ b/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean
@@ -374,6 +374,11 @@ theorem mulEquivCongr_symm_apply_smul (g : G') (x : B) :
@[deprecated (since := "2026-06-19")] alias mulEquivCongr' := mulEquivCongr
@[deprecated (since := "2026-06-19")] alias mulEquivCongr'_apply_smul := mulEquivCongr_apply_smul
+theorem mulEquivCongr_mapSubgroup_fixingSubgroup (S : Set B) :
+ (fixingSubgroup G S).map (mulEquivCongr G G' A B) = fixingSubgroup G' S := by
+ ext g
+ simp [Subgroup.map_equiv_eq_comap_symm, mem_fixingSubgroup_iff]
+
end IsDomain
variable (H H' : Subgroup G) (F F' : IntermediateField K L)
@@ -430,6 +435,17 @@ instance intermediateField [Finite G] [hGKL : IsGaloisGroup G K L] :
have := hGKL.isGalois
.of_mulEquiv_algEquiv e fun _ _ ↦ rfl
+include K in
+/-- If `G` is a Galois group on `L/K` and `L/E/K` is a tower of field extensions,
+then the fixing subgroup of the image of `E` in `L` is a Galois group on `L/E`. -/
+theorem of_isScalarTower [Finite G] [IsGaloisGroup G K L] (E : Type*) [Field E] [Algebra K E]
+ [Algebra E L] [IsScalarTower K E L] :
+ IsGaloisGroup (fixingSubgroup G (Set.range (algebraMap E L))) E L := by
+ rw [← IsScalarTower.toAlgHom_fieldRange K E L]
+ refine IsGaloisGroup.of_ringEquiv _ _ _ L
+ (AlgHom.equivFieldRange (IsScalarTower.toAlgHom K E L)).toRingEquiv.symm fun ⟨_, ⟨x, rfl⟩⟩ ↦ ?_
+ simp [AlgEquiv.symm_apply_eq, Subtype.ext_iff]
+
@[simp]
theorem card_fixingSubgroup_eq_finrank [Finite G] [IsGaloisGroup G K L] :
Nat.card (fixingSubgroup G (F : Set L)) = Module.finrank F L :=
@@ -566,6 +582,20 @@ theorem fixingSubgroup_range_algebraMap [Finite G] (A B C : Type*) (H : Subgroup
use algebraMap B (FractionRing B) x
rw [← IsScalarTower.algebraMap_apply, ← IsScalarTower.algebraMap_apply]
+open Pointwise in
+/-- If `G` is a finite Galois group for `L/K`, `H` is a Galois group for `L/E`, and `E/K` is
+Galois, then `H` is a normal subgroup of `G`. -/
+theorem normal_of_isGalois (E : Type*) [Field E] [Algebra K E] [Algebra E L] [IsScalarTower K E L]
+ [Finite G] [IsGaloisGroup H E L] [IsGalois K E] : H.Normal := by
+ let F := (IsScalarTower.toAlgHom K E L).fieldRange
+ have : IsGalois K F := .of_algEquiv (IsScalarTower.toAlgHom K E L).equivFieldRange
+ have hFL : IsGaloisGroup H F L := inferInstanceAs (IsGaloisGroup H (algebraMap E L).range L)
+ have := isGalois G K L
+ have : Finite Gal(L/K) := Finite.of_equiv _ (mulEquivAlgEquiv G K L).toEquiv
+ rw [← fixingSubgroup_fixedPoints G K L H, subgroup_iff.mp hFL,
+ ← mulEquivCongr_mapSubgroup_fixingSubgroup Gal(L/K) G K, MulEquiv.normal_map_iff]
+ exact IsGalois.fixingSubgroup_normal_of_isGalois F
+
end IsGaloisGroup
end GaloisCorrespondence
@@ -635,7 +665,7 @@ theorem mulSemiringActionQuotient_smul_def [MulSemiringAction G B] [SMulDistribC
refine (Quotient.liftOn'_mk'' (· • b) _ g).trans (FaithfulSMul.algebraMap_injective B C ?_)
rw [algebraMap.smul', algebraMap.smul']
-theorem isScalarTower_mulSemiringActionQuotient [MulSemiringAction G B] [SMulDistribClass G B C]
+instance isScalarTower_mulSemiringActionQuotient [MulSemiringAction G B] [SMulDistribClass G B C]
[IsGaloisGroup N B C] [N.Normal] :
letI := mulSemiringActionQuotient G B C N
IsScalarTower G (G ⧸ N) B :=
@@ -659,7 +689,7 @@ end Semiring
section Domain
variable (A B C : Type*) [CommRing A] [CommRing B] [CommRing C] [IsDomain C] [Algebra A B]
- [Algebra A C] [Algebra B C] [FaithfulSMul A C] [FaithfulSMul B C] [IsScalarTower A B C]
+ [Algebra A C] [Algebra B C] [FaithfulSMul A B] [FaithfulSMul B C] [IsScalarTower A B C]
/-- If `G` is a Galois group for `C/A`, and the normal subgroup `N ≤ G` is a Galois group for
`C/B`, then the quotient `G ⧸ N` is a Galois group for `B/A`. -/
@@ -669,6 +699,7 @@ theorem quotient [Finite G] (N : Subgroup G) [N.Normal] [MulSemiringAction G C]
[IsGaloisGroup N B C] :
IsGaloisGroup (G ⧸ N) A B where
faithful.eq_of_smul_eq_smul := fun {g₁} {g₂} ↦ Quotient.inductionOn₂' g₁ g₂ fun g₁ g₂ h ↦ by
+ have : FaithfulSMul A C := FaithfulSMul.trans A B C
have h' : ∀ g : G, (∀ x : B, g • x = x) → g ∈ N := by
simp [← fixingSubgroup_range_algebraMap G A B C N, mem_fixingSubgroup_iff, ← algebraMap.smul',
(FaithfulSMul.algebraMap_injective B C).eq_iff]
@@ -685,6 +716,80 @@ theorem quotient [Finite G] (N : Subgroup G) [N.Normal] [MulSemiringAction G C]
have := (FaithfulSMul.algebraMap_injective B C).eq_iff.mpr <| h g
rwa [MulAction.coe_quotient_smul, algebraMap.smul'] at this
+/-- If `G` is a Galois group for `C/A`, the normal subgroup `N ≤ G` is a Galois group for `C/B`,
+and `G'` is a Galois group for `B/A`, then `G ⧸ N ≃* G'`. -/
+noncomputable def quotientMulEquiv [Finite G] [Finite G'] (N : Subgroup G) [N.Normal]
+ [MulSemiringAction G C] [IsGaloisGroup G A C] [IsGaloisGroup N B C] [MulSemiringAction G' B]
+ [IsGaloisGroup G' A B] :
+ G ⧸ N ≃* G' :=
+ haveI : IsDomain B := (FaithfulSMul.algebraMap_injective B C).isDomain
+ letI := mulSemiringActionOfNormal G B C N
+ letI := mulSemiringActionQuotient G B C N
+ haveI := smulCommClassQuotient G A B C N
+ haveI := quotient G A B C N
+ mulEquivCongr (G ⧸ N) G' A B
+
+@[simp]
+theorem algebraMap_quotientMulEquiv_smul [Finite G] [Finite G'] (N : Subgroup G) [N.Normal]
+ [MulSemiringAction G C] [IsGaloisGroup G A C] [IsGaloisGroup N B C] [MulSemiringAction G' B]
+ [IsGaloisGroup G' A B] (g : G) (x : B) :
+ algebraMap B C (quotientMulEquiv G G' A B C N g • x) = g • algebraMap B C x := by
+ haveI : IsDomain B := (FaithfulSMul.algebraMap_injective B C).isDomain
+ letI := mulSemiringActionOfNormal G B C N
+ letI := mulSemiringActionQuotient G B C N
+ haveI := smulCommClassQuotient G A B C N
+ haveI := quotient G A B C N
+ rw [← algebraMap_smulOfNormal G B C N g x]
+ congr
+ apply mulEquivCongr_apply_smul
+
+attribute [local instance] FractionRing.liftAlgebra in
+/-- The restriction homomorphism from the Galois group of `C/A` to the Galois group of `B/A` where
+`C/B/A` is a tower of domains with `C/A` and `B/A` Galois. -/
+noncomputable def restrictHom [Finite G] [Finite G'] [MulSemiringAction G C] [IsGaloisGroup G A C]
+ [MulSemiringAction G' B] [IsGaloisGroup G' A B] :
+ G →* G' :=
+ haveI : IsDomain B := IsDomain.of_faithfulSMul B C
+ haveI : IsDomain A := IsDomain.of_faithfulSMul A B
+ haveI : FaithfulSMul A C := FaithfulSMul.trans A B C
+ letI : MulSemiringAction G (FractionRing C) :=
+ IsFractionRing.mulSemiringAction G C (FractionRing C)
+ letI N := fixingSubgroup G (Set.range (algebraMap (FractionRing B) (FractionRing C)))
+ haveI : IsGaloisGroup N (FractionRing B) (FractionRing C) :=
+ of_isScalarTower G (FractionRing A) (FractionRing C) (FractionRing B)
+ letI : MulSemiringAction G' (FractionRing B) :=
+ IsFractionRing.mulSemiringAction G' B (FractionRing B)
+ haveI := isGalois G' (FractionRing A) (FractionRing B)
+ haveI : N.Normal := normal_of_isGalois G (FractionRing A) (FractionRing C) N (FractionRing B)
+ (quotientMulEquiv G G' (FractionRing A) (FractionRing B) (FractionRing C) N).toMonoidHom.comp
+ (QuotientGroup.mk' N)
+
+attribute [local instance] FractionRing.liftAlgebra in
+@[simp]
+theorem algebraMap_restrictHom_smul [Finite G] [Finite G'] [MulSemiringAction G C]
+ [IsGaloisGroup G A C] [MulSemiringAction G' B] [IsGaloisGroup G' A B] (g : G) (x : B) :
+ algebraMap B C (restrictHom G G' A B C g • x) = g • algebraMap B C x := by
+ have : IsDomain B := IsDomain.of_faithfulSMul B C
+ have : IsDomain A := IsDomain.of_faithfulSMul A B
+ have : FaithfulSMul A C := FaithfulSMul.trans A B C
+ let : MulSemiringAction G (FractionRing C) :=
+ IsFractionRing.mulSemiringAction G C (FractionRing C)
+ let : MulSemiringAction G' (FractionRing B) :=
+ IsFractionRing.mulSemiringAction G' B (FractionRing B)
+ apply FaithfulSMul.algebraMap_injective C (FractionRing C)
+ rw [← IsScalarTower.algebraMap_apply,
+ IsScalarTower.algebraMap_apply B (FractionRing B) (FractionRing C)]
+ simp only [restrictHom, MulEquiv.toMonoidHom_eq_coe, MonoidHom.coe_comp, MonoidHom.coe_coe,
+ QuotientGroup.coe_mk', Function.comp_apply]
+ rw [algebraMap.smul', algebraMap_quotientMulEquiv_smul, ← IsScalarTower.algebraMap_apply,
+ algebraMap.smul', ← IsScalarTower.algebraMap_apply]
+
+attribute [local instance] FractionRing.liftAlgebra in
+theorem restrictHom_surjective [Finite G] [Finite G'] [MulSemiringAction G C]
+ [IsGaloisGroup G A C] [MulSemiringAction G' B] [IsGaloisGroup G' A B] :
+ Function.Surjective (restrictHom G G' A B C) := by
+ simpa [restrictHom] using QuotientGroup.mk_surjective
+
end Domain
noncomputable section IntermediateField
@@ -733,7 +838,8 @@ theorem map_quotientMk' [Finite G] [IsGaloisGroup G K L] (h : E ≤ F) :
isInvariant := ⟨fun x h ↦ by
obtain ⟨a, ha⟩ := hE.isInvariant.isInvariant (algebraMap F L x) (by
rintro ⟨g, hg⟩
- simpa only [← algebraMap.smul'] using! congr_arg (algebraMap F L) <| h ⟨g, ⟨g, hg, rfl⟩⟩)
+ rw [MulAction.subgroup_smul_def, ← algebraMap.smul']
+ exact congr_arg (algebraMap F L) <| h ⟨g, ⟨g, hg, rfl⟩⟩)
exact ⟨a, FaithfulSMul.algebraMap_injective F L
(by rw [← IsScalarTower.algebraMap_apply, ha])⟩⟩ }
From 85304ddf62264970fa4a289febc5b9ee02515760 Mon Sep 17 00:00:00 2001
From: Miguel Laredo <160270434+laredo02@users.noreply.github.com>
Date: Mon, 22 Jun 2026 16:57:00 +0000
Subject: [PATCH 0262/1300] feat(FinitelyPresentedGroup): quotient of a
finitely group by a subgroup which is finitely generated under normal closure
is finitely presented (#40845)
Add theorem that the quotient of a finitely presented group by a subgroup whose normal closure is finitely generated is finitely presented. #38930
Also add docstring and `@[to_additive]` annotation to `of_surjective`.
Co-authored-by: Hang Lu Su
---
Mathlib/GroupTheory/FinitelyPresentedGroup.lean | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/Mathlib/GroupTheory/FinitelyPresentedGroup.lean b/Mathlib/GroupTheory/FinitelyPresentedGroup.lean
index 57f0f1f6729ce7..c72f4d89cfe415 100644
--- a/Mathlib/GroupTheory/FinitelyPresentedGroup.lean
+++ b/Mathlib/GroupTheory/FinitelyPresentedGroup.lean
@@ -103,6 +103,10 @@ theorem equiv (iso : G ≃* H) [h : IsFinitelyPresented G] : IsFinitelyPresented
refine ⟨n, (iso : G →* H).comp φ, iso.surjective.comp hφsurj, ?_⟩
rwa [φ.ker_mulEquiv_comp iso]
+/-- The image of a finitely presented group under a surjective homomorphism whose kernel is
+finitely generated as a normal subgroup is finitely presented. -/
+@[to_additive /-- The image of a finitely presented additive group under a surjective additive
+homomorphism whose kernel is finitely generated as a normal subgroup is finitely presented. -/]
theorem of_surjective [hG : IsFinitelyPresented G] (f : G →* H)
(hf_surj : Function.Surjective f) (hf_ker : f.ker.IsNormalClosureFG) :
IsFinitelyPresented H := by
@@ -111,6 +115,15 @@ theorem of_surjective [hG : IsFinitelyPresented G] (f : G →* H)
rw [← MonoidHom.comap_ker]
exact hf_ker.comap hφ_surj hφ_ker
+/-- The quotient of a finitely presented group by a subgroup
+which is finitely generated as a normal subgroup is finitely presented. -/
+@[to_additive /-- The quotient of a finitely presented additive group by an additive subgroup
+which is finitely generated as a normal subgroup is finitely presented. -/]
+theorem quotient [hG : IsFinitelyPresented G] (N : Subgroup G) [N.Normal]
+ (hN : N.IsNormalClosureFG) : IsFinitelyPresented (G ⧸ N) :=
+ of_surjective (QuotientGroup.mk' N) (QuotientGroup.mk'_surjective N)
+ ((QuotientGroup.ker_mk' N).symm ▸ hN)
+
open QuotientGroup in
theorem exists_mulEquiv_presentedGroup [hg : IsFinitelyPresented G] :
∃ n : ℕ, ∃ s : Set (FreeGroup (Fin n)), Set.Finite s ∧ Nonempty (G ≃* PresentedGroup s) := by
From e568743e9c24da15c8f8347a47931d2a6c33ff85 Mon Sep 17 00:00:00 2001
From: Felix Pernegger <188575194+felixpernegger@users.noreply.github.com>
Date: Mon, 22 Jun 2026 16:57:04 +0000
Subject: [PATCH 0263/1300] chore(GroupTheory/FreeGroup): golf proof using
autogenerated lemma (#40902)
I forgot to add this in #40657 apparently.
It seems like we don't need this lemma anyways; but thats beyond the scope of this PR.
Co-authored-by: Batixx
---
Mathlib/GroupTheory/FreeGroup/Basic.lean | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/Mathlib/GroupTheory/FreeGroup/Basic.lean b/Mathlib/GroupTheory/FreeGroup/Basic.lean
index 3a4a00e0b12b57..caf6340f835d4e 100644
--- a/Mathlib/GroupTheory/FreeGroup/Basic.lean
+++ b/Mathlib/GroupTheory/FreeGroup/Basic.lean
@@ -332,15 +332,13 @@ protected theorem sublist : Red L₁ L₂ → L₂ <+ L₁ :=
theorem length_le (h : Red L₁ L₂) : L₂.length ≤ L₁.length :=
h.sublist.length_le
-set_option linter.auxLemma false in
@[to_additive (attr := deprecated "Should not be needed." (since := "2026-04-10"))]
theorem sizeof_of_step : ∀ {L₁ L₂ : List (α × Bool)},
Step L₁ L₂ → sizeOf L₂ < sizeOf L₁
| _, _, @Step.not _ L1 L2 x b => by
induction L1 with
| nil =>
- -- This was just `dsimp` prior to https://github.com/leanprover/lean4/pull/13320
- dsimp [sizeOf, _sizeOf_1]
+ rw [nil_append, nil_append, cons.sizeOf_spec, cons.sizeOf_spec]
lia
| cons hd tl ih =>
dsimp
From eeee79132332bd6cb594939667e6ca07b55284a5 Mon Sep 17 00:00:00 2001
From: Andrew Yang <36414270+erdOne@users.noreply.github.com>
Date: Mon, 22 Jun 2026 17:30:54 +0000
Subject: [PATCH 0264/1300] feat(Topology): sheaves of modules in `Over U`
(#36142)
---
Mathlib.lean | 1 +
Mathlib/CategoryTheory/Sites/Spaces.lean | 3 +
Mathlib/Topology/Sheaves/Module.lean | 50 ++++++++++++++++
Mathlib/Topology/Sheaves/Over.lean | 59 ++++++++++++++++---
.../Sheaves/SheafCondition/Sites.lean | 9 +++
5 files changed, 115 insertions(+), 7 deletions(-)
create mode 100644 Mathlib/Topology/Sheaves/Module.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 52a90964f54ae6..e172db56965dbd 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -8105,6 +8105,7 @@ public import Mathlib.Topology.Sheaves.Limits
public import Mathlib.Topology.Sheaves.LocalPredicate
public import Mathlib.Topology.Sheaves.LocallySurjective
public import Mathlib.Topology.Sheaves.MayerVietoris
+public import Mathlib.Topology.Sheaves.Module
public import Mathlib.Topology.Sheaves.Over
public import Mathlib.Topology.Sheaves.PUnit
public import Mathlib.Topology.Sheaves.Points
diff --git a/Mathlib/CategoryTheory/Sites/Spaces.lean b/Mathlib/CategoryTheory/Sites/Spaces.lean
index 9f424a1beef31f..364e392890809d 100644
--- a/Mathlib/CategoryTheory/Sites/Spaces.lean
+++ b/Mathlib/CategoryTheory/Sites/Spaces.lean
@@ -57,6 +57,9 @@ def grothendieckTopology : GrothendieckTopology (Opens T) where
rcases hR hf _ hU with ⟨V, g, hg, hV⟩
exact ⟨_, g ≫ f, hg, hV⟩
+lemma mem_grothendieckTopology {U : Opens T} {S : Sieve U} :
+ S ∈ Opens.grothendieckTopology T U ↔ ∀ x ∈ U, ∃ (V : _) (f : V ⟶ U), S f ∧ x ∈ V := .rfl
+
/-- The Grothendieck pretopology associated to a topological space. -/
def pretopology : Pretopology (Opens T) where
coverings X := {R | ∀ x ∈ X, ∃ (U : _) (f : U ⟶ X), R f ∧ x ∈ U}
diff --git a/Mathlib/Topology/Sheaves/Module.lean b/Mathlib/Topology/Sheaves/Module.lean
new file mode 100644
index 00000000000000..4e7f8a2f554a7c
--- /dev/null
+++ b/Mathlib/Topology/Sheaves/Module.lean
@@ -0,0 +1,50 @@
+/-
+Copyright (c) 2026 Andrew Yang. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Andrew Yang
+-/
+module
+
+public import Mathlib.Algebra.Category.ModuleCat.Sheaf.PushforwardContinuous
+public import Mathlib.Algebra.Category.Ring.Limits
+public import Mathlib.Topology.Sheaves.Over
+public import Mathlib.Topology.Sheaves.SheafCondition.Sites
+
+/-! # Specialized results for sheaves of modules over topological spaces -/
+
+@[expose] public section
+
+noncomputable section
+
+open CategoryTheory
+
+universe w v u
+
+namespace TopologicalSpace.Opens
+
+variable {X : TopCat.{u}} (U : Opens X) (R : X.Sheaf RingCat.{v})
+
+set_option backward.defeqAttrib.useBackward true in
+set_option backward.isDefEq.respectTransparency false in
+/-- Sheaves of modules over `R.over U` are equivalent to sheaves of modules over `R |_ U`. -/
+def sheafOfModulesEquivOver :
+ SheafOfModules.{w} (R.over U) ≌ SheafOfModules.{w} (U.sheafRestrict.obj R) := by
+ refine SheafOfModules.pushforwardPushforwardEquivalence (eqv := U.overEquivalence.symm)
+ (U.overPullbackSheafEquivOver.app _).inv (U.sheafRestrictSheafEquivOver.app _).inv rfl ?_
+ ext : 2
+ simp [overPullbackSheafEquivOver, sheafRestrictSheafEquivOver, eqToHom_map, overEquivalence,
+ IsOpenMap.functor]
+
+/-- `sheafOfModulesEquivOver` takes `R.over U` to `R |_ U`. -/
+def sheafOfModulesEquivOverUnit (R : X.Sheaf RingCat.{u}) :
+ (U.sheafOfModulesEquivOver R).functor.obj (SheafOfModules.unit.{u} _) ≅
+ SheafOfModules.unit.{u} _ := .refl _
+
+/-- `sheafOfModulesEquivOver.inverse` takes `R |_ U` to `R.over U`. -/
+def sheafOfModulesEquivOverInverseUnit (R : X.Sheaf RingCat.{u}) :
+ (U.sheafOfModulesEquivOver R).inverse.obj (SheafOfModules.unit.{u} _) ≅
+ SheafOfModules.unit.{u} _ :=
+ (U.sheafOfModulesEquivOver R).inverse.mapIso (U.sheafOfModulesEquivOverUnit R).symm ≪≫
+ ((U.sheafOfModulesEquivOver R).unitIso.app _).symm
+
+end TopologicalSpace.Opens
diff --git a/Mathlib/Topology/Sheaves/Over.lean b/Mathlib/Topology/Sheaves/Over.lean
index 9979a573d2427b..b8e74aa67472db 100644
--- a/Mathlib/Topology/Sheaves/Over.lean
+++ b/Mathlib/Topology/Sheaves/Over.lean
@@ -5,8 +5,10 @@ Authors: Joël Riou
-/
module
-public import Mathlib.Topology.Sets.Opens
public import Mathlib.CategoryTheory.Comma.Over.Basic
+public import Mathlib.CategoryTheory.Sites.Over
+public import Mathlib.Topology.Sets.Opens
+public import Mathlib.Topology.Sheaves.SheafCondition.Sites
/-!
# Opens and Over categories
@@ -15,11 +17,7 @@ In this file, given a topological space `X`, and `U : Opens X`,
we show that the category `Over U` (whose objects are the
`V : Opens X` equipped with a morphism `V ⟶ U`) is equivalent
to the category `Opens U`.
-
-## TODO
-* show that both functors of the equivalence `overEquivalence U` are continuous and
- induce an equivalence between `Sheaf ((Opens.grothendieckTopology X).over U) A`
- and `Sheaf (Opens.grothendieckTopology U) A` for any category `A`.
+This equivalence is bi-continuous, and thus induces an equivalence of sheaf categories.
-/
@@ -31,7 +29,7 @@ open CategoryTheory Topology
namespace TopologicalSpace
-variable {X : Type u} [TopologicalSpace X] (U : Opens X)
+variable {X : Type u} [TopologicalSpace X] (U : Opens X) {A : Type*} [Category* A]
namespace Opens
@@ -54,6 +52,53 @@ def overEquivalence : Over U ≌ Opens ↥U where
apply leOfHom V.hom)))
counitIso := NatIso.ofComponents (fun V ↦ eqToIso (by aesop))
+variable {U} in
+@[simp] lemma mem_overEquivalence_functor_obj {V : Over U} {x : U} :
+ x ∈ U.overEquivalence.functor.obj V ↔ x.1 ∈ V.left := .rfl
+
+section grothendieckTopology
+
+instance : U.overEquivalence.functor.IsDenseSubsite
+ ((Opens.grothendieckTopology X).over U) (Opens.grothendieckTopology U) where
+ functorPushforward_mem_iff {V S} := by
+ simp only [Opens.mem_grothendieckTopology, Sieve.mem_functorPushforward_functor]
+ constructor
+ · intro H x hxV
+ obtain ⟨W, f, hW, hxW⟩ := H ⟨x, V.hom.le hxV⟩ hxV
+ exact ⟨_, ((U.overEquivalence.symm.toAdjunction.homEquiv _ _ ).symm f).left,
+ ⟨_, _, 𝟙 _, hW, rfl⟩, _, hxW, rfl⟩
+ · intro H x hxV
+ obtain ⟨W, f, ⟨W', hW'V, hWW', hSW'V, rfl⟩, hxW⟩ := H x hxV
+ exact ⟨_, U.overEquivalence.functor.map hW'V,
+ S.downward_closed hSW'V (U.overEquivalence.unitInv.app W'), hWW'.le hxW⟩
+
+instance : U.overEquivalence.symm.inverse.IsDenseSubsite
+ ((Opens.grothendieckTopology X).over U) (Opens.grothendieckTopology U) :=
+ inferInstanceAs (U.overEquivalence.functor.IsDenseSubsite ..)
+
+instance : U.overEquivalence.inverse.IsDenseSubsite
+ (Opens.grothendieckTopology U) ((Opens.grothendieckTopology X).over U) :=
+ inferInstanceAs (U.overEquivalence.symm.functor.IsDenseSubsite ..)
+
+/-- Sheaves on the over category of `U` are equivalent to sheaves on `U` as a topological space. -/
+@[simps!] def sheafEquivOver :
+ Sheaf ((Opens.grothendieckTopology X).over U) A ≌ Sheaf (Opens.grothendieckTopology U) A :=
+ U.overEquivalence.sheafCongr
+ ((Opens.grothendieckTopology X).over U) (Opens.grothendieckTopology U) A
+
+/-- `overPullback` and `sheafRestrict` are isomorphic under `sheafEquivOver`. -/
+def overPullbackSheafEquivOver {X : TopCat} (U : Opens X) :
+ (Opens.grothendieckTopology X).overPullback A U ⋙ U.sheafEquivOver.functor ≅
+ U.sheafRestrict := .refl _
+
+/-- `overPullback` and `sheafRestrict` are isomorphic under `sheafEquivOver`. -/
+def sheafRestrictSheafEquivOver {X : TopCat} (U : Opens X) :
+ U.sheafRestrict ⋙ U.sheafEquivOver.inverse ≅
+ (Opens.grothendieckTopology X).overPullback A U :=
+ U.overPullbackSheafEquivOver.isoCompInverse.symm
+
+end grothendieckTopology
+
end Opens
end TopologicalSpace
diff --git a/Mathlib/Topology/Sheaves/SheafCondition/Sites.lean b/Mathlib/Topology/Sheaves/SheafCondition/Sites.lean
index 117ca1339f2113..392e27c8a3faf4 100644
--- a/Mathlib/Topology/Sheaves/SheafCondition/Sites.lean
+++ b/Mathlib/Topology/Sheaves/SheafCondition/Sites.lean
@@ -174,6 +174,15 @@ theorem TopCat.Presheaf.isSheaf_of_isOpenEmbedding (h : IsOpenEmbedding f) (hF :
have := h.functor_isContinuous
exact Functor.op_comp_isSheaf _ _ _ ⟨_, hF⟩
+/-- The restriction functor of a sheaf to an open subspace. -/
+@[simps!]
+def TopologicalSpace.Opens.sheafRestrict (U : Opens X) :
+ Sheaf (Opens.grothendieckTopology X) C ⥤ Sheaf (Opens.grothendieckTopology U) C :=
+ haveI H : IsOpenEmbedding (TopCat.Hom.hom (TopCat.ofHom ⟨_, continuous_subtype_val⟩)) :=
+ U.isOpenEmbedding
+ haveI := H.functor_isContinuous
+ H.isOpenMap.functor.sheafPushforwardContinuous C _ _
+
variable (f)
instance : RepresentablyFlat (Opens.map f) := by
From b1e3b32001fca01c651effea67312978aa30e5c7 Mon Sep 17 00:00:00 2001
From: Anatole Dedecker
Date: Mon, 22 Jun 2026 17:30:57 +0000
Subject: [PATCH 0265/1300] feat: a few misc results about open (quotient) maps
(#40715)
In particular, we upgrade `IsOpen[Quotient]Map.prodMap` to an `iff` when both factors are nonempty.
---
Mathlib/Topology/Constructions/SumProd.lean | 38 +++++++++++++++++++--
Mathlib/Topology/Homeomorph/Defs.lean | 16 ++++++++-
Mathlib/Topology/Maps/Basic.lean | 6 ++++
Mathlib/Topology/Maps/OpenQuotient.lean | 13 +++++++
4 files changed, 69 insertions(+), 4 deletions(-)
diff --git a/Mathlib/Topology/Constructions/SumProd.lean b/Mathlib/Topology/Constructions/SumProd.lean
index ab4185bf312be4..4bfea412958fbd 100644
--- a/Mathlib/Topology/Constructions/SumProd.lean
+++ b/Mathlib/Topology/Constructions/SumProd.lean
@@ -6,7 +6,7 @@ Authors: Johannes Hölzl, Mario Carneiro, Patrick Massot
module
public import Mathlib.Topology.Homeomorph.Defs
-public import Mathlib.Topology.Maps.Basic
+public import Mathlib.Topology.Maps.OpenQuotient
public import Mathlib.Topology.Separation.SeparatedNhds
/-!
@@ -358,6 +358,16 @@ theorem ContinuousAt.prodMap' {f : X → Z} {g : Y → W} {x : X} {y : Y} (hf :
(hg : ContinuousAt g y) : ContinuousAt (Prod.map f g) (x, y) :=
hf.prodMap hg
+@[simp]
+theorem continuousAt_prodMap_iff {f : X → Z} {g : Y → W} {x : X} {y : Y} :
+ ContinuousAt (Prod.map f g) (x, y) ↔ ContinuousAt f x ∧ ContinuousAt g y := by
+ simp [ContinuousAt, nhds_prod_eq, tendsto_iff_comap, comap_prodMap_prod]
+
+@[simp]
+theorem continuous_prodMap_iff [Nonempty Z] [Nonempty W] {f : Z → X} {g : W → Y} :
+ Continuous (Prod.map f g) ↔ Continuous f ∧ Continuous g := by
+ simp [continuous_iff_continuousAt, forall_and]
+
theorem ContinuousAt.comp₂ {f : Y × Z → W} {g : X → Y} {h : X → Z} {x : X}
(hf : ContinuousAt f (g x, h x)) (hg : ContinuousAt g x) (hh : ContinuousAt h x) :
ContinuousAt (fun x ↦ f (g x, h x)) x :=
@@ -494,11 +504,17 @@ theorem isOpen_prod_iff' {s : Set X} {t : Set Y} :
simp only [st.1.ne_empty, st.2.ne_empty, or_false] at H
exact H.1.prod H.2
+theorem isOpenQuotientMap_fst [Nonempty Y] : IsOpenQuotientMap (Prod.fst : X × Y → X) :=
+ ⟨Prod.fst_surjective, continuous_fst, isOpenMap_fst⟩
+
+theorem isOpenQuotientMap_snd [Nonempty X] : IsOpenQuotientMap (Prod.snd : X × Y → Y) :=
+ ⟨Prod.snd_surjective, continuous_snd, isOpenMap_snd⟩
+
theorem isQuotientMap_fst [Nonempty Y] : IsQuotientMap (Prod.fst : X × Y → X) :=
- isOpenMap_fst.isQuotientMap continuous_fst Prod.fst_surjective
+ isOpenQuotientMap_fst.isQuotientMap
theorem isQuotientMap_snd [Nonempty X] : IsQuotientMap (Prod.snd : X × Y → Y) :=
- isOpenMap_snd.isQuotientMap continuous_snd Prod.snd_surjective
+ isOpenQuotientMap_snd.isQuotientMap
theorem closure_prod_eq {s : Set X} {t : Set Y} : closure (s ×ˢ t) = closure s ×ˢ closure t :=
ext fun ⟨a, b⟩ => by
@@ -589,6 +605,15 @@ protected theorem IsOpenMap.prodMap {f : X → Y} {g : Z → W} (hf : IsOpenMap
rw [nhds_prod_eq, nhds_prod_eq, ← Filter.prod_map_map_eq']
exact Filter.prod_mono (hf.nhds_le a) (hg.nhds_le b)
+@[simp]
+theorem isOpenMap_prodMap_iff [Nonempty X] [Nonempty Z] {f : X → Y} {g : Z → W} :
+ IsOpenMap (Prod.map f g) ↔ IsOpenMap f ∧ IsOpenMap g := by
+ refine ⟨fun h ↦ ⟨?_, ?_⟩, fun ⟨hf, hg⟩ ↦ hf.prodMap hg⟩
+ · rw [(isOpenQuotientMap_fst (Y := Z)).isOpenMap_iff]
+ exact isOpenMap_fst.comp h
+ · rw [(isOpenQuotientMap_snd (X := X)).isOpenMap_iff]
+ exact isOpenMap_snd.comp h
+
protected lemma Topology.IsOpenEmbedding.prodMap {f : X → Y} {g : Z → W} (hf : IsOpenEmbedding f)
(hg : IsOpenEmbedding g) : IsOpenEmbedding (Prod.map f g) :=
.of_isEmbedding_isOpenMap (hf.1.prodMap hg.1) (hf.isOpenMap.prodMap hg.isOpenMap)
@@ -612,6 +637,13 @@ theorem IsOpenQuotientMap.prodMap {f : X → Y} {g : Z → W} (hf : IsOpenQuotie
(hg : IsOpenQuotientMap g) : IsOpenQuotientMap (Prod.map f g) :=
⟨.prodMap hf.1 hg.1, .prodMap hf.2 hg.2, .prodMap hf.3 hg.3⟩
+@[simp]
+theorem isOpenQuotientMap_prodMap_iff [Nonempty X] [Nonempty Z] {f : X → Y} {g : Z → W} :
+ IsOpenQuotientMap (Prod.map f g) ↔ IsOpenQuotientMap f ∧ IsOpenQuotientMap g := by
+ have : Nonempty Y := .map f inferInstance
+ have : Nonempty W := .map g inferInstance
+ grind [isOpenQuotientMap_iff, continuous_prodMap_iff, isOpenMap_prodMap_iff, Prod.map_surjective]
+
theorem TopologicalSpace.prod_mono {α β : Type*} {σ₁ σ₂ : TopologicalSpace α}
{τ₁ τ₂ : TopologicalSpace β} (hσ : σ₁ ≤ σ₂) (hτ : τ₁ ≤ τ₂) :
@instTopologicalSpaceProd α β σ₁ τ₁ ≤ @instTopologicalSpaceProd α β σ₂ τ₂ :=
diff --git a/Mathlib/Topology/Homeomorph/Defs.lean b/Mathlib/Topology/Homeomorph/Defs.lean
index 79ccff73ceae30..2833bea0f96d53 100644
--- a/Mathlib/Topology/Homeomorph/Defs.lean
+++ b/Mathlib/Topology/Homeomorph/Defs.lean
@@ -6,7 +6,7 @@ Authors: Johannes Hölzl, Patrick Massot, Sébastien Gouëzel, Zhouhang Zhou, Re
module
public import Mathlib.Topology.ContinuousMap.Defs
-public import Mathlib.Topology.Maps.Basic
+public import Mathlib.Topology.Maps.OpenQuotient
/-!
# Homeomorphisms
@@ -342,6 +342,20 @@ theorem comp_isOpenMap_iff' (h : X ≃ₜ Y) {f : Y → Z} : IsOpenMap (f ∘ h)
rw [← Function.comp_id f, ← h.self_comp_symm, ← Function.comp_assoc]
exact hf.comp h.symm.isOpenMap
+/-- Open quotient maps are preserved by precomposing with a homeomorphism. -/
+@[simp]
+theorem isOpenQuotient_comp_iff (e : X ≃ₜ Y) {f : Y → Z} :
+ IsOpenQuotientMap (f ∘ e) ↔ IsOpenQuotientMap f :=
+ ⟨fun h ↦ by simpa [Function.comp_assoc] using h.comp e.symm.isOpenQuotientMap,
+ fun hf ↦ hf.comp e.isOpenQuotientMap⟩
+
+/-- Open quotient maps are preserved by postcomposing with a homeomorphism. -/
+@[simp]
+theorem comp_isOpenQuotientMap_iff (e : Y ≃ₜ Z) {f : X → Y} :
+ IsOpenQuotientMap (e ∘ f) ↔ IsOpenQuotientMap f :=
+ ⟨fun h ↦ by simpa [← Function.comp_assoc] using e.symm.isOpenQuotientMap.comp h,
+ fun hf ↦ e.isOpenQuotientMap.comp hf⟩
+
variable (X Y) in
/-- If both `X` and `Y` have a unique element, then `X ≃ₜ Y`. -/
@[simps!]
diff --git a/Mathlib/Topology/Maps/Basic.lean b/Mathlib/Topology/Maps/Basic.lean
index c5f7c9ca50bb24..4efc1e7e0b3e4e 100644
--- a/Mathlib/Topology/Maps/Basic.lean
+++ b/Mathlib/Topology/Maps/Basic.lean
@@ -369,6 +369,12 @@ protected theorem id : IsOpenMap (@id X) := fun s hs => by rwa [image_id]
protected theorem comp (hg : IsOpenMap g) (hf : IsOpenMap f) :
IsOpenMap (g ∘ f) := fun s hs => by rw [image_comp]; exact hg _ (hf _ hs)
+/-- If `g ∘ f` is open, where `f` is continuous and surjective, then `g` is open. -/
+theorem of_comp (hf : Continuous f) (f_surj : Surjective f) (h : IsOpenMap (g ∘ f)) :
+ IsOpenMap g := fun s hs => by
+ rw [← f_surj.image_preimage s, ← image_comp]
+ exact h _ (hs.preimage hf)
+
theorem isOpen_range (hf : IsOpenMap f) : IsOpen (range f) := by
rw [← image_univ]
exact hf _ isOpen_univ
diff --git a/Mathlib/Topology/Maps/OpenQuotient.lean b/Mathlib/Topology/Maps/OpenQuotient.lean
index be2ce3cb93e7f3..139d7c8ec14f05 100644
--- a/Mathlib/Topology/Maps/OpenQuotient.lean
+++ b/Mathlib/Topology/Maps/OpenQuotient.lean
@@ -49,6 +49,15 @@ theorem comp {g : Y → Z} (hg : IsOpenQuotientMap g) (hf : IsOpenQuotientMap f)
IsOpenQuotientMap (g ∘ f) :=
⟨.comp hg.1 hf.1, .comp hg.2 hf.2, .comp hg.3 hf.3⟩
+theorem of_comp {g : Y → Z} (hf : Continuous f) (f_surj : Surjective f) (hg : Continuous g)
+ (h : IsOpenQuotientMap (g ∘ f)) : IsOpenQuotientMap g :=
+ ⟨.of_comp h.surjective, hg, .of_comp hf f_surj h.isOpenMap ⟩
+
+theorem of_comp_iff {g : Y → Z} (hf : IsOpenQuotientMap f) :
+ IsOpenQuotientMap (g ∘ f) ↔ IsOpenQuotientMap g :=
+ ⟨fun h ↦ .of_comp hf.continuous hf.surjective
+ (hf.isQuotientMap.continuous_iff.mpr h.continuous) h, fun hg ↦ hg.comp hf⟩
+
theorem map_nhds_eq (h : IsOpenQuotientMap f) (x : X) : map f (𝓝 x) = 𝓝 (f x) :=
le_antisymm h.continuous.continuousAt <| h.isOpenMap.nhds_le _
@@ -60,6 +69,10 @@ theorem continuousAt_comp_iff (h : IsOpenQuotientMap f) {g : Y → Z} {x : X} :
ContinuousAt (g ∘ f) x ↔ ContinuousAt g (f x) := by
simp only [ContinuousAt, ← h.map_nhds_eq, tendsto_map'_iff, comp_def]
+theorem isOpenMap_iff (hf : IsOpenQuotientMap f) {g : Y → Z} :
+ IsOpenMap g ↔ IsOpenMap (g ∘ f) :=
+ ⟨fun hg ↦ hg.comp hf.isOpenMap, fun h ↦ .of_comp hf.continuous hf.surjective h⟩
+
theorem dense_preimage_iff (h : IsOpenQuotientMap f) {s : Set Y} : Dense (f ⁻¹' s) ↔ Dense s :=
⟨fun hs ↦ h.surjective.denseRange.dense_of_mapsTo h.continuous hs (mapsTo_preimage _ _),
fun hs ↦ hs.preimage h.isOpenMap⟩
From f07d1202849c903e71038bb3ba91662694a43414 Mon Sep 17 00:00:00 2001
From: Jovan Gerbscheid <56355248+JovanGerb@users.noreply.github.com>
Date: Mon, 22 Jun 2026 18:03:39 +0000
Subject: [PATCH 0266/1300] chore(CategoryTheory/Comma/Arrow): use `to_dual`
(#40862)
This PR uses `to_dual` to dualize the `Arrow` category.
Theorems `isIso_of_isIso` and `isIso_of_isIso'` did not have a dual, and had stronger iff forms, and weren't used, so I decided to remove them (or mark private).
I skipped the parts involving `eqToHom`, since `eqToHom` hasn't been tagged with `to_dual` yet.
---
Mathlib/CategoryTheory/Comma/Arrow.lean | 146 ++++++++++++------------
1 file changed, 74 insertions(+), 72 deletions(-)
diff --git a/Mathlib/CategoryTheory/Comma/Arrow.lean b/Mathlib/CategoryTheory/Comma/Arrow.lean
index 767eae2fdee3b1..62ee97723e7a57 100644
--- a/Mathlib/CategoryTheory/Comma/Arrow.lean
+++ b/Mathlib/CategoryTheory/Comma/Arrow.lean
@@ -33,7 +33,10 @@ variable (T) in
squares in `T`. -/
def Arrow := Comma (𝟭 T) (𝟭 T)
+to_dual_name_hint Left Right
+
/-- The type of morphisms in the category `Arrow T`. -/
+@[to_dual self (reorder := f g)]
protected def Arrow.Hom (f g : Arrow T) := CommaMorphism f g
instance : Quiver (Arrow T) where
@@ -45,43 +48,31 @@ instance : Category (Arrow T) :=
namespace Arrow
/-- The left object of an arrow. -/
+@[to_dual /-- The right object of an arrow. -/]
abbrev left (X : Arrow T) : T := Comma.left X
-/-- The right object of an arrow. -/
-abbrev right (X : Arrow T) : T := Comma.right X
-
/-- Given `X : Arrow T`, this is the morphism `X.left ⟶ X.right`. -/
abbrev hom (X : Arrow T) : X.left ⟶ X.right := Comma.hom X
/-- The left part of a morphism in the category of arrows. -/
+@[to_dual /-- The right part of a morphism in the category of arrows. -/]
abbrev Hom.left {X Y : Arrow T} (f : X ⟶ Y) : X.left ⟶ Y.left := CommaMorphism.left f
-/-- The right part of a morphism in the category of arrows. -/
-abbrev Hom.right {X Y : Arrow T} (f : X ⟶ Y) : X.right ⟶ Y.right := CommaMorphism.right f
-
-@[ext]
+@[ext, to_dual self (reorder := X Y, h₁ h₂)]
lemma hom_ext {X Y : Arrow T} (f g : X ⟶ Y) (h₁ : f.left = g.left) (h₂ : f.right = g.right) :
f = g :=
CommaMorphism.ext h₁ h₂
-@[simp]
+@[to_dual (attr := simp)]
theorem id_left (f : Arrow T) : Arrow.Hom.left (𝟙 f) = 𝟙 f.left :=
rfl
-@[simp]
-theorem id_right (f : Arrow T) : Arrow.Hom.right (𝟙 f) = 𝟙 f.right :=
- rfl
-
-@[simp, reassoc]
+@[to_dual (reorder := f g) (attr := simp, reassoc)]
theorem comp_left {X Y Z : Arrow T} (f : X ⟶ Y) (g : Y ⟶ Z) :
(f ≫ g).left = f.left ≫ g.left := rfl
-@[simp, reassoc]
-theorem comp_right {X Y Z : Arrow T} (f : X ⟶ Y) (g : Y ⟶ Z) :
- (f ≫ g).right = f.right ≫ g.right := rfl
-
/-- An object in the arrow category is simply a morphism in `T`. -/
-@[simps]
+@[simps, to_dual self]
def mk {X Y : T} (f : X ⟶ Y) : Arrow T where
left := X
right := Y
@@ -92,38 +83,41 @@ theorem mk_eq (f : Arrow T) : Arrow.mk f.hom = f := by
cases f
rfl
+@[to_dual none]
lemma mk_surjective (f : Arrow T) :
∃ (X Y : T) (g : X ⟶ Y), f = Arrow.mk g :=
⟨_, _, f.hom, rfl⟩
+@[to_dual self]
theorem mk_injective (A B : T) :
Function.Injective (Arrow.mk : (A ⟶ B) → Arrow T) := fun f g h => by
cases h
rfl
+@[to_dual self]
theorem mk_inj (A B : T) {f g : A ⟶ B} : Arrow.mk f = Arrow.mk g ↔ f = g :=
(mk_injective A B).eq_iff
+@[to_dual self]
instance {X Y : T} : CoeOut (X ⟶ Y) (Arrow T) where
coe := mk
-@[reassoc (attr := simp high)]
+@[to_dual none, reassoc (attr := simp high)]
theorem w {f g : Arrow T} (sq : f ⟶ g) : sq.left ≫ g.hom = f.hom ≫ sq.right :=
CommaMorphism.w sq
-@[reassoc]
-lemma Hom.w {f g : Arrow T} (sq : f ⟶ g) : sq.left ≫ g.hom = f.hom ≫ sq.right := by
- simp
+@[to_dual none, reassoc]
+alias Hom.w := w
+@[to_dual]
theorem hom.congr_left {f g : Arrow T} {φ₁ φ₂ : f ⟶ g} (h : φ₁ = φ₂) : φ₁.left = φ₂.left := by
rw [h]
-theorem hom.congr_right {f g : Arrow T} {φ₁ φ₂ : f ⟶ g} (h : φ₁ = φ₂) : φ₁.right = φ₂.right := by
- simp [h]
-
+@[to_dual none]
theorem iso_w {f g : Arrow T} (e : f ≅ g) : g.hom = e.inv.left ≫ f.hom ≫ e.hom.right := by
simp [← Arrow.comp_right]
+@[to_dual none]
theorem iso_w' {W X Y Z : T} {f : W ⟶ X} {g : Y ⟶ Z} (e : Arrow.mk f ≅ Arrow.mk g) :
g = e.inv.left ≫ f ≫ e.hom.right :=
iso_w e
@@ -171,6 +165,14 @@ def homMk {f g : Arrow T} (u : f.left ⟶ g.left) (v : f.right ⟶ g.right)
right := v
w := w
+/-- `homMk''` is the dual of `homMk`, which we need for `to_dual`.
+Please avoid using this directly. -/
+@[to_dual existing homMk]
+abbrev homMk'' {f g : Arrow T} (u : g.right ⟶ f.right) (v : g.left ⟶ f.left)
+ (w : g.hom ≫ u = v ≫ f.hom := by cat_disch) : g ⟶ f :=
+ homMk v u
+attribute [to_dual none] homMk_left homMk_right
+
/-- We can also build a morphism in the arrow category out of any commutative square in `T`. -/
@[simps]
def homMk' {X Y : T} {f : X ⟶ Y} {P Q : T} {g : P ⟶ Q} (u : X ⟶ P) (v : Y ⟶ Q)
@@ -180,24 +182,33 @@ def homMk' {X Y : T} {f : X ⟶ Y} {P Q : T} {g : P ⟶ Q} (u : X ⟶ P) (v : Y
right := v
w := w
+/-- `homMk'''` is the dual of `homMk'`, which we need for `to_dual`.
+Please avoid using this directly. -/
+@[to_dual existing homMk']
+abbrev homMk''' {X Y : T} {f : Y ⟶ X} {P Q : T} {g : Q ⟶ P} (u : P ⟶ X) (v : Q ⟶ Y)
+ (w : g ≫ u = v ≫ f := by cat_disch) : mk g ⟶ mk f :=
+ homMk' v u
+attribute [to_dual none] homMk'_left
+
set_option backward.defeqAttrib.useBackward true in
-@[reassoc]
+@[to_dual none, reassoc]
theorem w_mk_left {X Y : T} {f : X ⟶ Y} {g : Arrow T} (sq : mk f ⟶ g) :
dsimp% sq.left ≫ g.hom = f ≫ sq.right :=
sq.w
set_option backward.defeqAttrib.useBackward true in
-@[reassoc (attr := simp)]
+@[to_dual none, reassoc (attr := simp)]
theorem w_mk_right {f : Arrow T} {X Y : T} {g : X ⟶ Y} (sq : f ⟶ mk g) :
dsimp% sq.left ≫ g = f.hom ≫ sq.right :=
sq.w
set_option backward.defeqAttrib.useBackward true in
-@[reassoc]
+@[to_dual none, reassoc]
theorem w_mk {X Y X' Y' : T} {f : X ⟶ Y} {g : X' ⟶ Y'} (sq : mk f ⟶ mk g) :
dsimp% sq.left ≫ g = f ≫ sq.right :=
sq.w
+@[to_dual self (reorder := f g, 6 7)]
theorem isIso_of_isIso_left_of_isIso_right {f g : Arrow T} (ff : f ⟶ g) [IsIso ff.left]
[IsIso ff.right] : IsIso ff where
out := ⟨homMk (inv ff.left) (inv ff.right), by cat_disch⟩
@@ -210,59 +221,70 @@ def isoMk {f g : Arrow T} (l : f.left ≅ g.left) (r : f.right ≅ g.right)
(h : l.hom ≫ g.hom = f.hom ≫ r.hom := by cat_disch) : f ≅ g :=
Comma.isoMk l r h
+/-- `isoMk''` is the dual of `isoMk`, which we need for `to_dual`.
+Please avoid using this directly. -/
+@[to_dual existing isoMk]
+abbrev isoMk'' {f g : Arrow T} (l : f.right ≅ g.right) (r : f.left ≅ g.left)
+ (h : g.hom ≫ l.inv = r.inv ≫ f.hom := by cat_disch) : f ≅ g :=
+ isoMk r l (by rwa [Iso.comp_inv_eq, Category.assoc, Iso.eq_inv_comp] at h)
+attribute [to_dual none] isoMk_hom_left isoMk_hom_right isoMk_inv_left isoMk_inv_right
+
/-- A variant of `Arrow.isoMk` that creates an iso between two `Arrow.mk`s with a better type
signature. -/
abbrev isoMk' {W X Y Z : T} (f : W ⟶ X) (g : Y ⟶ Z) (e₁ : W ≅ Y) (e₂ : X ≅ Z)
(h : e₁.hom ≫ g = f ≫ e₂.hom := by cat_disch) : Arrow.mk f ≅ Arrow.mk g :=
Arrow.isoMk e₁ e₂ h
+/-- `isoMk'''` is the dual of `isoMk'`, which we need for `to_dual`.
+Please avoid using this directly. -/
+@[to_dual existing isoMk']
+abbrev isoMk''' {W X Y Z : T} (f : X ⟶ W) (g : Z ⟶ Y) (e₁ : W ≅ Y)
+ (e₂ : X ≅ Z) (h : g ≫ e₁.inv = e₂.inv ≫ f := by cat_disch) : mk f ≅ mk g :=
+ isoMk' f g e₂ e₁ (by rwa [Iso.comp_inv_eq, Category.assoc, Iso.eq_inv_comp] at h)
+
section
variable {f g : Arrow T} (sq : f ⟶ g)
+@[to_dual]
instance isIso_left [IsIso sq] : IsIso sq.left :=
⟨(inv sq).left, by simp [← comp_left]⟩
-instance isIso_right [IsIso sq] : IsIso sq.right :=
- ⟨(inv sq).right, by simp [← comp_right]⟩
-
-lemma isIso_of_isIso' {f g : Arrow T} (sq : f ⟶ g) [IsIso sq] [IsIso f.hom] :
+@[to_dual none]
+private lemma isIso_of_isIso' {f g : Arrow T} (sq : f ⟶ g) [IsIso sq] [IsIso f.hom] :
IsIso g.hom := by
rw [iso_w (asIso sq)]
infer_instance
-lemma isIso_of_isIso {X Y : T} {f : X ⟶ Y} {g : Arrow T} (sq : mk f ⟶ g) [IsIso sq] [IsIso f] :
- IsIso g.hom := by
- have : IsIso (mk f).hom := by assumption
- apply isIso_of_isIso' sq
-
+@[to_dual none]
lemma isIso_hom_iff_isIso_hom_of_isIso {f g : Arrow T} (sq : f ⟶ g) [IsIso sq] :
IsIso f.hom ↔ IsIso g.hom :=
⟨fun _ => isIso_of_isIso' sq, fun _ => isIso_of_isIso' (inv sq)⟩
+@[to_dual none]
lemma isIso_iff_isIso_of_isIso {W X Y Z : T} {f : W ⟶ X} {g : Y ⟶ Z} (sq : mk f ⟶ mk g) [IsIso sq] :
IsIso f ↔ IsIso g :=
isIso_hom_iff_isIso_hom_of_isIso sq
+@[to_dual none]
lemma isIso_hom_iff_isIso_of_isIso {Y Z : T} {f : Arrow T} {g : Y ⟶ Z} (sq : f ⟶ mk g) [IsIso sq] :
IsIso f.hom ↔ IsIso g :=
isIso_hom_iff_isIso_hom_of_isIso sq
-@[simp]
+@[to_dual (attr := simp, push ←)]
theorem inv_left [IsIso sq] : (inv sq).left = inv sq.left :=
IsIso.eq_inv_of_hom_inv_id (by simp [← comp_left])
-@[simp]
-theorem inv_right [IsIso sq] : (inv sq).right = inv sq.right :=
- IsIso.eq_inv_of_hom_inv_id (by simp [← comp_right])
-
+@[to_dual none]
theorem left_hom_inv_right [IsIso sq] : sq.left ≫ g.hom ≫ inv sq.right = f.hom := by
simp only [← Category.assoc, IsIso.comp_inv_eq, w]
+@[to_dual none]
theorem inv_left_hom_right [IsIso sq] : inv sq.left ≫ f.hom ≫ sq.right = g.hom := by
simp only [w, IsIso.inv_comp_eq]
set_option backward.defeqAttrib.useBackward true in
+@[to_dual epi_right]
instance mono_left [Mono sq] : Mono sq.left where
right_cancellation {Z} φ ψ h := by
let aux : (Z ⟶ f.left) → (Arrow.mk (𝟙 Z) ⟶ f) := fun φ =>
@@ -276,34 +298,14 @@ instance mono_left [Mono sq] : Mono sq.left where
· exact h
· simp [this, ← Arrow.w_mk_right, reassoc_of% h]
-set_option backward.defeqAttrib.useBackward true in
-instance epi_right [Epi sq] : Epi sq.right where
- left_cancellation {Z} φ ψ h := by
- let aux : (g.right ⟶ Z) → (g ⟶ Arrow.mk (𝟙 Z)) := fun φ =>
- Arrow.homMk (g.hom ≫ φ) φ
- change (aux φ).right = (aux ψ).right
- congr 1
- rw [← cancel_epi sq]
- ext
- · simp only [comp_left, comp_left, aux, mk_left, homMk_left, w_assoc, h]
- · exact h
-
-@[reassoc (attr := simp)]
+@[to_dual (attr := reassoc (attr := simp))]
lemma hom_inv_id_left (e : f ≅ g) : e.hom.left ≫ e.inv.left = 𝟙 _ := by
rw [← comp_left, e.hom_inv_id, id_left]
-@[reassoc (attr := simp)]
+@[to_dual (attr := reassoc (attr := simp))]
lemma inv_hom_id_left (e : f ≅ g) : e.inv.left ≫ e.hom.left = 𝟙 _ := by
rw [← comp_left, e.inv_hom_id, id_left]
-@[reassoc (attr := simp)]
-lemma hom_inv_id_right (e : f ≅ g) : e.hom.right ≫ e.inv.right = 𝟙 _ := by
- rw [← comp_right, e.hom_inv_id, id_right]
-
-@[reassoc (attr := simp)]
-lemma inv_hom_id_right (e : f ≅ g) : e.inv.right ≫ e.hom.right = 𝟙 _ := by
- rw [← comp_right, e.inv_hom_id, id_right]
-
end
/-- Given a square from an arrow `i` to an isomorphism `p`, express the source part of `sq`
@@ -338,15 +340,10 @@ def squareToSnd {X Y Z : C} {i : Arrow C} {f : X ⟶ Y} {g : Y ⟶ Z} (sq : i
Arrow.homMk (sq.left ≫ f) (sq.right) (by simp [w_mk sq])
/-- The functor sending an arrow to its source. -/
-@[simps!]
+@[to_dual (attr := simps!) /-- The functor sending an arrow to its target. -/]
def leftFunc : Arrow C ⥤ C :=
Comma.fst _ _
-/-- The functor sending an arrow to its target. -/
-@[simps!]
-def rightFunc : Arrow C ⥤ C :=
- Comma.snd _ _
-
set_option backward.defeqAttrib.useBackward true in
/-- The natural transformation from `leftFunc` to `rightFunc`, given by the arrow itself. -/
@[simps]
@@ -365,7 +362,9 @@ set_option backward.defeqAttrib.useBackward true in
@[simps]
def mapArrow (F : C ⥤ D) : Arrow C ⥤ Arrow D where
obj a := Arrow.mk (F.map a.hom)
- map f := Arrow.homMk (F.map f.left) (F.map f.right) (by simp [← Functor.map_comp])
+ map {X Y} f := Arrow.homMk (F.map f.left) (F.map f.right) (by simp [← Functor.map_comp])
+
+attribute [to_dual self (reorder := X Y)] mapArrow_map
variable (C D)
@@ -375,7 +374,9 @@ a functor `F : C ⥤ D` to `F.mapArrow`. -/
@[simps]
def mapArrowFunctor : (C ⥤ D) ⥤ (Arrow C ⥤ Arrow D) where
obj F := F.mapArrow
- map τ := { app f := Arrow.homMk (τ.app _) (τ.app _) }
+ map {X Y} τ := { app f := Arrow.homMk (τ.app _) (τ.app _) }
+
+attribute [to_dual self (reorder := X Y)] mapArrowFunctor_map_app
variable {C D}
@@ -431,6 +432,7 @@ def Arrow.discreteEquiv (S : Type u) : Arrow (Discrete S) ≃ S where
/-- Extensionality lemma for functors `C ⥤ D` which uses as an assumption
that the induced maps `Arrow C → Arrow D` coincide. -/
+@[to_dual self]
lemma Arrow.functor_ext {F G : C ⥤ D} (h : ∀ ⦃X Y : C⦄ (f : X ⟶ Y),
F.mapArrow.obj (Arrow.mk f) = G.mapArrow.obj (Arrow.mk f)) :
F = G :=
From 1b0782d8191b03e0001caac10e1601d17f2cd580 Mon Sep 17 00:00:00 2001
From: Marcelo Lynch
Date: Mon, 22 Jun 2026 20:00:22 +0000
Subject: [PATCH 0267/1300] chore(cache): Stop dual-writing the cache to the
legacy container (#40907)
With this PR, master CI now uploads only to the `master` container, and the `legacy` (bare `mathlib4`) container stays as a read-only endpoint for pre-refactor artifacts.
---
.github/workflows/build_template.yml | 17 ++---------------
Cache/Infra.lean | 9 +++++----
2 files changed, 7 insertions(+), 19 deletions(-)
diff --git a/.github/workflows/build_template.yml b/.github/workflows/build_template.yml
index 511e19544781d5..518274f97c7627 100644
--- a/.github/workflows/build_template.yml
+++ b/.github/workflows/build_template.yml
@@ -694,21 +694,8 @@ jobs:
# $MATHLIB_CACHE_PRIMARY is set by the `Compute cache trust dispatch`
# step above, from the shared composite action that owns the (repo,
# branch) → container mapping for both this job and the read-side
- # jobs (build, post_steps).
- # Dual-write to the legacy `mathlib4` container first, then the
- # primary. Only master CI dual-writes: older cache tools read only
- # `legacy`, so it must stay fresh for them, while forks and nightly
- # never write `legacy` (keeping low-trust artifacts out of what those
- # readers trust). Writing `legacy` first keeps it a superset of
- # `master` for as long as we dual-write: with `set -e`, a failed
- # legacy write aborts the step before `master` gets artifacts that
- # `legacy` lacks. (`put-staged` exits non-zero on real upload
- # failures; already-present 409/412 blobs are not failures.)
- if [ "$MATHLIB_CACHE_PRIMARY" = "master" ]; then
- echo "Dual-writing to legacy container first (keeps legacy a superset of master)..."
- lake env "$CACHE_BIN" put-staged --container=legacy --staging-dir="cache-staging" --repo="$REPO"
- fi
-
+ # jobs (build, post_steps). Each job writes only into its own trust-level
+ # container.
echo "Uploading cache to Azure (container: $MATHLIB_CACHE_PRIMARY)..."
lake env "$CACHE_BIN" put-staged --container="$MATHLIB_CACHE_PRIMARY" --staging-dir="cache-staging" --repo="$REPO"
diff --git a/Cache/Infra.lean b/Cache/Infra.lean
index 47b88f739ea583..99f6fc271f52e0 100644
--- a/Cache/Infra.lean
+++ b/Cache/Infra.lean
@@ -51,10 +51,11 @@ inductive Container where
| nightlyTesting
/-- Container for toolchain-PR test runs. -/
| prToolchainTests
- /-- The bare `mathlib4` container that older cache clients read from. Only
- master CI writes here (mirroring its `mathlib4-master` upload), so those
- clients keep finding master-built artifacts; forks and nightly-testing stay
- out to keep low-trust writes from reaching readers that predate the split. -/
+ /-- The bare `mathlib4` container that older cache clients read from. CI does
+ not upload here; it is a read-only store of the master-built artifacts that
+ were mirrored from `mathlib4-master`, kept reachable so those older clients
+ can resolve them. The `master` container is a self-contained cache, so reads
+ fall back to `legacy` only for artifacts predating the write cutover. -/
| legacy
deriving DecidableEq, Repr, BEq, Inhabited
From cf9c5f705282a1cef9d3db3422b0092dd8ba173e Mon Sep 17 00:00:00 2001
From: Weiyi Wang
Date: Mon, 22 Jun 2026 20:47:05 +0000
Subject: [PATCH 0268/1300] feat(Analysis/Meromorphic): const_smul lemma
(#39833)
This allows more general scalar type than the existing `smul` ones
---
Mathlib/Analysis/Meromorphic/Basic.lean | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/Mathlib/Analysis/Meromorphic/Basic.lean b/Mathlib/Analysis/Meromorphic/Basic.lean
index 76db272de7420a..af4ae3fc44673f 100644
--- a/Mathlib/Analysis/Meromorphic/Basic.lean
+++ b/Mathlib/Analysis/Meromorphic/Basic.lean
@@ -29,6 +29,7 @@ open scoped Topology
variable {𝕜 𝕜' : Type*} [NontriviallyNormedField 𝕜] [NontriviallyNormedField 𝕜']
[NormedAlgebra 𝕜 𝕜'] {E : Type*} [NormedAddCommGroup E] [NormedSpace 𝕜 E]
+variable {R : Type*} [NormedRing R] [Module R E] [IsBoundedSMul R E] [SMulCommClass 𝕜 R E]
/-- Meromorphy of `f` at `x` (more precisely, on a punctured neighbourhood of `x`; the value at
`x` itself is irrelevant). -/
@@ -94,6 +95,12 @@ lemma smul {f : 𝕜 → 𝕜} {g : 𝕜 → E} (hf : MeromorphicAt f x) (hg : M
simp
module
+@[to_fun (attr := fun_prop)]
+lemma const_smul {x : 𝕜} {f : 𝕜 → E} (hf : MeromorphicAt f x) (c : R) :
+ MeromorphicAt (c • f) x := by
+ rcases hf with ⟨m, hf⟩
+ exact ⟨m, by simpa [smul_comm _ c _] using hf.fun_const_smul⟩
+
@[to_fun (attr := fun_prop)]
lemma mul {f g : 𝕜 → 𝕜'} (hf : MeromorphicAt f x) (hg : MeromorphicAt g x) :
MeromorphicAt (f * g) x := by
@@ -550,6 +557,9 @@ include hf in
MeromorphicOn (s • f) U :=
fun x hx ↦ (hs x hx).smul (hf x hx)
+include hf in
+@[to_fun] lemma const_smul (c : R) : MeromorphicOn (c • f) U := fun x hx ↦ (hf x hx).const_smul c
+
include hs ht in
@[to_fun] lemma mul : MeromorphicOn (s * t) U := fun x hx ↦ (hs x hx).mul (ht x hx)
@@ -695,6 +705,10 @@ lemma sub (hf : Meromorphic f) (hg : Meromorphic g) :
lemma smul {f : 𝕜 → 𝕜} (hf : Meromorphic f) (hg : Meromorphic g) :
Meromorphic (f • g) := fun x ↦ (hf x).smul (hg x)
+@[to_fun (attr := fun_prop)]
+lemma const_smul (hf : Meromorphic f) (c : R) :
+ Meromorphic (c • f) := fun x ↦ (hf x).const_smul c
+
@[to_fun (attr := fun_prop)]
lemma mul {f g : 𝕜 → 𝕜'} (hf : Meromorphic f) (hg : Meromorphic g) :
Meromorphic (f * g) := fun x ↦ (hf x).mul (hg x)
From 67b53908ee491b8758b4cb294a88e2b45cade69d Mon Sep 17 00:00:00 2001
From: "Yi.Yuan"
Date: Mon, 22 Jun 2026 21:02:08 +0000
Subject: [PATCH 0269/1300] refactor(Analysis): golf
`Mathlib/Analysis/Convex/BetweenList` (#40071)
- rewrites `SortedLE.wbtw` using `triplewise_iff_getElem` and sorted getElem inequalities instead of nested list induction
Extracted from #37968
[](https://gitpod.io/from-referrer/)
---
Mathlib/Analysis/Convex/BetweenList.lean | 17 +++--------------
1 file changed, 3 insertions(+), 14 deletions(-)
diff --git a/Mathlib/Analysis/Convex/BetweenList.lean b/Mathlib/Analysis/Convex/BetweenList.lean
index abe92b3744fc36..a25c94db1e5dc7 100644
--- a/Mathlib/Analysis/Convex/BetweenList.lean
+++ b/Mathlib/Analysis/Convex/BetweenList.lean
@@ -180,20 +180,9 @@ variable [Field R] [LinearOrder R] [IsStrictOrderedRing R]
variable {R}
lemma SortedLE.wbtw {l : List R} (h : l.SortedLE) : l.Wbtw R := by
- induction l with
- | nil => simp
- | cons head tail ih =>
- rw [wbtw_cons]
- refine ⟨?_, ih h.pairwise.of_cons.sortedLE⟩
- clear ih
- induction tail with
- | nil => simp
- | cons head' tail' ih =>
- rw [pairwise_cons]
- refine ⟨?_, ih (h.pairwise.sublist ?_).sortedLE⟩
- · simp_rw [sortedLE_iff_pairwise, pairwise_cons_cons, pairwise_cons] at h
- exact fun a ha ↦ .of_le_of_le h.1 (h.2.2.1 a ha)
- · simp
+ rw [List.Wbtw, List.triplewise_iff_getElem]
+ intro i j k hij hjk hk
+ exact Wbtw.of_le_of_le (h.getElem_le_getElem_of_le hij.le) (h.getElem_le_getElem_of_le hjk.le)
lemma SortedLT.sbtw {l : List R} (h : l.SortedLT) : l.Sbtw R :=
⟨h.sortedLE.wbtw, h.nodup⟩
From 2c4e038a6d3721dcf3c3cd820c7820284e6b8293 Mon Sep 17 00:00:00 2001
From: Eric Wieser <425260+eric-wieser@users.noreply.github.com>
Date: Tue, 23 Jun 2026 00:12:28 +0000
Subject: [PATCH 0270/1300] refactor: switch from RingQuot to RingCon.Quotient
(#40451)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This PR observes that `RingQuot r` is analogous to `(ringConGen r).Quotient`, and changes all callers to use the latter.
Note that `RingQuot` had some extra irreducibility that has not yet been configured for `RingCon.Quotient`, and so there is a performance drop associated with the switch.
Zulip: [#mathlib4 > Canonical way to quotient a ring @ 💬](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Canonical.20way.20to.20quotient.20a.20ring/near/604966059)
---
Mathlib/Algebra/Lie/UniversalEnveloping.lean | 24 ++---
.../LinearAlgebra/CliffordAlgebra/Basic.lean | 48 +++++-----
.../LinearAlgebra/CliffordAlgebra/Equivs.lean | 10 +-
.../LinearAlgebra/CliffordAlgebra/Even.lean | 10 +-
Mathlib/LinearAlgebra/FreeProduct/Basic.lean | 57 ++++++-----
.../LinearAlgebra/SymmetricAlgebra/Basic.lean | 33 ++++---
.../LinearAlgebra/TensorAlgebra/Basic.lean | 50 +++++-----
Mathlib/RingTheory/Congruence/Basic.lean | 2 -
Mathlib/RingTheory/Congruence/Defs.lean | 7 +-
Mathlib/RingTheory/Congruence/Hom.lean | 11 ++-
.../RingTheory/DividedPowerAlgebra/Init.lean | 94 ++++++++++---------
11 files changed, 193 insertions(+), 153 deletions(-)
diff --git a/Mathlib/Algebra/Lie/UniversalEnveloping.lean b/Mathlib/Algebra/Lie/UniversalEnveloping.lean
index dcdf160ac70a4e..8ebf54ba4ef5fa 100644
--- a/Mathlib/Algebra/Lie/UniversalEnveloping.lean
+++ b/Mathlib/Algebra/Lie/UniversalEnveloping.lean
@@ -6,7 +6,6 @@ Authors: Oliver Nash
module
public import Mathlib.Algebra.Lie.OfAssociative
-public import Mathlib.Algebra.RingQuot
public import Mathlib.LinearAlgebra.TensorAlgebra.Basic
/-!
@@ -57,11 +56,13 @@ so that our construction needs only the semiring structure of the tensor algebra
inductive Rel : TensorAlgebra R L → TensorAlgebra R L → Prop
| lie_compat (x y : L) : Rel (ιₜ ⁅x, y⁆ + ιₜ y * ιₜ x) (ιₜ x * ιₜ y)
+/-- `Rel` as a ring congruence, used to build the quotient. -/
+@[no_expose] def ringCon : RingCon (TensorAlgebra R L) := ringConGen (Rel R L)
+
end UniversalEnvelopingAlgebra
/-- The universal enveloping algebra of a Lie algebra. -/
-def UniversalEnvelopingAlgebra :=
- RingQuot (UniversalEnvelopingAlgebra.Rel R L)
+def UniversalEnvelopingAlgebra := (UniversalEnvelopingAlgebra.ringCon R L).Quotient
deriving Inhabited, Ring, Algebra R
namespace UniversalEnvelopingAlgebra
@@ -69,7 +70,7 @@ namespace UniversalEnvelopingAlgebra
/-- The quotient map from the tensor algebra to the universal enveloping algebra as a morphism of
associative algebras. -/
def mkAlgHom : TensorAlgebra R L →ₐ[R] UniversalEnvelopingAlgebra R L :=
- RingQuot.mkAlgHom R (Rel R L)
+ RingCon.mkₐ R _
variable {L}
attribute [local instance 100] LieRing.ofAssociativeRing
@@ -81,7 +82,7 @@ def ι : L →ₗ⁅R⁆ UniversalEnvelopingAlgebra R L :=
map_lie' := fun {x y} => by
suffices mkAlgHom R L (ιₜ ⁅x, y⁆ + ιₜ y * ιₜ x) = mkAlgHom R L (ιₜ x * ιₜ y) by
rw [map_mul] at this; simp [LieRing.of_associative_ring_bracket, ← this]
- exact RingQuot.mkAlgHom_rel _ (Rel.lie_compat x y) }
+ exact Quotient.sound <| RingCon.le_ringConGen _ _ (Rel.lie_compat x y) }
variable {A : Type u₃} [Ring A] [Algebra R A] (f : L →ₗ⁅R⁆ A)
@@ -90,11 +91,11 @@ set_option backward.isDefEq.respectTransparency false in
associative algebras lift to associative algebra morphisms from the universal enveloping algebra. -/
def lift : (L →ₗ⁅R⁆ A) ≃ (UniversalEnvelopingAlgebra R L →ₐ[R] A) where
toFun f :=
- RingQuot.liftAlgHom R
- ⟨TensorAlgebra.lift R (f : L →ₗ[R] A), by
+ RingCon.liftₐ _
+ (TensorAlgebra.lift R (f : L →ₗ[R] A)) <| by
+ grw [ringCon, RingCon.ringConGen_le]
intro a b h; induction h
- simp only [LieRing.of_associative_ring_bracket, map_add, TensorAlgebra.lift_ι_apply,
- LieHom.coe_toLinearMap, LieHom.map_lie, map_mul, sub_add_cancel]⟩
+ simp [LieRing.of_associative_ring_bracket]
invFun F := (F : UniversalEnvelopingAlgebra R L →ₗ⁅R⁆ A).comp (ι R)
left_inv f := by
ext
@@ -105,10 +106,9 @@ def lift : (L →ₗ⁅R⁆ A) ≃ (UniversalEnvelopingAlgebra R L →ₐ[R] A)
-- RingQuot.liftAlgHom_mkAlgHom_apply]
simp only [LieHom.coe_comp, Function.comp_apply, AlgHom.coe_toLieHom,
UniversalEnvelopingAlgebra.ι_apply, mkAlgHom]
- simp only [UniversalEnvelopingAlgebra, RingQuot.liftAlgHom_mkAlgHom_apply,
- TensorAlgebra.lift_ι_apply, LieHom.coe_toLinearMap]
+ simp [UniversalEnvelopingAlgebra]
right_inv F := by
- apply RingQuot.ringQuot_ext'
+ apply RingCon.Quotient.hom_extₐ
ext
-- Porting note: was
-- simp only [ι, mkAlgHom, TensorAlgebra.lift_ι_apply, LieHom.coe_toLinearMap,
diff --git a/Mathlib/LinearAlgebra/CliffordAlgebra/Basic.lean b/Mathlib/LinearAlgebra/CliffordAlgebra/Basic.lean
index b07967f77a8080..d6eb7a6bedd007 100644
--- a/Mathlib/LinearAlgebra/CliffordAlgebra/Basic.lean
+++ b/Mathlib/LinearAlgebra/CliffordAlgebra/Basic.lean
@@ -5,7 +5,7 @@ Authors: Eric Wieser, Utensil Song
-/
module
-public import Mathlib.Algebra.RingQuot
+public import Mathlib.RingTheory.Congruence.Hom
public import Mathlib.LinearAlgebra.TensorAlgebra.Basic
public import Mathlib.LinearAlgebra.QuadraticForm.Isometry
public import Mathlib.LinearAlgebra.QuadraticForm.IsometryEquiv
@@ -62,12 +62,14 @@ The Clifford algebra of `M` is defined as the quotient modulo this relation.
inductive Rel : TensorAlgebra R M → TensorAlgebra R M → Prop
| of (m : M) : Rel (ι R m * ι R m) (algebraMap R _ (Q m))
+/-- `Rel` as a ring congruence, used to build the quotient. -/
+@[no_expose] def ringCon : RingCon (TensorAlgebra R M) := ringConGen (Rel Q)
+
end CliffordAlgebra
/-- The Clifford algebra of an `R`-module `M` equipped with a `QuadraticForm` `Q`.
-/
-def CliffordAlgebra :=
- RingQuot (CliffordAlgebra.Rel Q)
+def CliffordAlgebra := CliffordAlgebra.ringCon Q |>.Quotient
deriving Inhabited, Ring, Algebra R
namespace CliffordAlgebra
@@ -76,7 +78,7 @@ instance (priority := 900) instAlgebra' {R A M} [CommSemiring R] [AddCommGroup M
[Algebra R A] [Module R M] [Module A M] (Q : QuadraticForm A M)
[IsScalarTower R A M] :
Algebra R (CliffordAlgebra Q) :=
- inferInstanceAs <| Algebra R (RingQuot _)
+ inferInstanceAs <| Algebra R (RingCon.Quotient _)
-- verify there are no diamonds
-- but doesn't work at `reducible_and_instances` https://github.com/leanprover-community/mathlib4/issues/10906
@@ -88,27 +90,25 @@ instance {R S A M} [CommSemiring R] [CommSemiring S] [AddCommGroup M] [CommRing
[Algebra R A] [Algebra S A] [Module R M] [Module S M] [Module A M] (Q : QuadraticForm A M)
[IsScalarTower R A M] [IsScalarTower S A M] :
SMulCommClass R S (CliffordAlgebra Q) :=
- RingQuot.instSMulCommClass _
+ RingCon.instSMulCommClassQuotient _
instance {R S A M} [CommSemiring R] [CommSemiring S] [AddCommGroup M] [CommRing A]
[SMul R S] [Algebra R A] [Algebra S A] [Module R M] [Module S M] [Module A M]
[IsScalarTower R A M] [IsScalarTower S A M] [IsScalarTower R S A] (Q : QuadraticForm A M) :
IsScalarTower R S (CliffordAlgebra Q) :=
- RingQuot.instIsScalarTower _
+ RingCon.instIsScalarTowerQuotient _
/-- The canonical linear map `M →ₗ[R] CliffordAlgebra Q`. -/
def ι : M →ₗ[R] CliffordAlgebra Q :=
- (RingQuot.mkAlgHom R _).toLinearMap.comp (TensorAlgebra.ι R)
+ (RingCon.mkₐ R _).toLinearMap.comp (TensorAlgebra.ι R)
+
+private theorem ι_apply (m : M) :
+ ι Q m = (TensorAlgebra.ι R m : CliffordAlgebra.ringCon Q |>.Quotient) := rfl
/-- As well as being linear, `ι Q` squares to the quadratic form -/
@[simp]
-theorem ι_sq_scalar (m : M) : ι Q m * ι Q m = algebraMap R _ (Q m) := by
- rw [ι]
- erw [LinearMap.comp_apply]
- rw [AlgHom.toLinearMap_apply]
- erw [← map_mul (RingQuot.mkAlgHom R (Rel Q))]
- rw [RingQuot.mkAlgHom_rel R (Rel.of m), AlgHom.commutes]
- rfl
+theorem ι_sq_scalar (m : M) : ι Q m * ι Q m = algebraMap R _ (Q m) :=
+ Quotient.sound <| RingCon.le_ringConGen _ _ (Rel.of m)
variable {Q} {A : Type*} [Semiring A] [Algebra R A]
@@ -126,21 +126,25 @@ from `CliffordAlgebra Q` to `A`.
def lift :
{ f : M →ₗ[R] A // ∀ m, f m * f m = algebraMap _ _ (Q m) } ≃ (CliffordAlgebra Q →ₐ[R] A) where
toFun f :=
- RingQuot.liftAlgHom R
- ⟨TensorAlgebra.lift R (f : M →ₗ[R] A), fun x y (h : Rel Q x y) => by
- induction h
- rw [AlgHom.commutes, map_mul, TensorAlgebra.lift_ι_apply, f.prop]⟩
+ RingCon.liftₐ (CliffordAlgebra.ringCon Q)
+ (TensorAlgebra.lift R (f : M →ₗ[R] A))
+ (by
+ exact RingCon.ringConGen_le.2 fun x y (h : Rel Q x y) => by
+ induction h
+ simp [f.prop])
invFun F :=
⟨F.toLinearMap.comp (ι Q), fun m => by
rw [LinearMap.comp_apply, AlgHom.toLinearMap_apply, comp_ι_sq_scalar]⟩
left_inv f := by
ext x
- exact (RingQuot.liftAlgHom_mkAlgHom_apply _ _ _ _).trans (TensorAlgebra.lift_ι_apply _ x)
+ dsimp
+ exact (RingCon.liftₐ_mk _ _ _ _).trans (TensorAlgebra.lift_ι_apply _ x)
right_inv F :=
- RingQuot.ringQuot_ext' _ _ _ <|
+ RingCon.Quotient.hom_extₐ <|
TensorAlgebra.hom_ext <|
- LinearMap.ext fun x ↦
- (RingQuot.liftAlgHom_mkAlgHom_apply _ _ _ _).trans (TensorAlgebra.lift_ι_apply _ _)
+ LinearMap.ext fun x ↦ by
+ dsimp
+ exact (RingCon.liftₐ_mk _ _ _ _).trans (TensorAlgebra.lift_ι_apply _ _)
@[simp]
theorem ι_comp_lift (f : M →ₗ[R] A) (cond : ∀ m, f m * f m = algebraMap _ _ (Q m)) :
diff --git a/Mathlib/LinearAlgebra/CliffordAlgebra/Equivs.lean b/Mathlib/LinearAlgebra/CliffordAlgebra/Equivs.lean
index df8a439744af44..fedb6cf5ebf965 100644
--- a/Mathlib/LinearAlgebra/CliffordAlgebra/Equivs.lean
+++ b/Mathlib/LinearAlgebra/CliffordAlgebra/Equivs.lean
@@ -355,12 +355,14 @@ protected def equiv : CliffordAlgebra (0 : QuadraticForm R R) ≃ₐ[R] R[ε] :=
(by ext : 1; simp) (by ext : 2; simp)
@[simp]
-theorem equiv_ι (r : R) : CliffordAlgebraDualNumber.equiv (ι (R := R) _ r) = r • ε :=
- (lift_ι_apply _ _ r).trans (inr_eq_smul_eps _)
+theorem equiv_ι (r : R) : CliffordAlgebraDualNumber.equiv (ι (R := R) _ r) = r • ε := by
+ dsimp [CliffordAlgebraDualNumber.equiv, AlgEquiv.ofAlgHom]
+ exact (lift_ι_apply _ _ r).trans (inr_eq_smul_eps _)
@[simp]
theorem equiv_symm_eps :
- CliffordAlgebraDualNumber.equiv.symm (eps : R[ε]) = ι (0 : QuadraticForm R R) 1 :=
- DualNumber.lift_apply_eps _
+ CliffordAlgebraDualNumber.equiv.symm (eps : R[ε]) = ι (0 : QuadraticForm R R) 1 := by
+ dsimp [CliffordAlgebraDualNumber.equiv, AlgEquiv.ofAlgHom]
+ exact DualNumber.lift_apply_eps _
end CliffordAlgebraDualNumber
diff --git a/Mathlib/LinearAlgebra/CliffordAlgebra/Even.lean b/Mathlib/LinearAlgebra/CliffordAlgebra/Even.lean
index f7c84a08497f7d..eb3939dd0375a8 100644
--- a/Mathlib/LinearAlgebra/CliffordAlgebra/Even.lean
+++ b/Mathlib/LinearAlgebra/CliffordAlgebra/Even.lean
@@ -203,11 +203,11 @@ theorem aux_one : aux f 1 = 1 :=
congr_arg Prod.fst (foldr_one _ _ _ _)
@[simp]
-theorem aux_ι (m₁ m₂ : M) : aux f ((even.ι Q).bilin m₁ m₂) = f.bilin m₁ m₂ :=
- (congr_arg Prod.fst (foldr_mul _ _ _ _ _ _)).trans
- (by
- rw [foldr_ι, foldr_ι]
- exact mul_one _)
+theorem aux_ι (m₁ m₂ : M) : aux f ((even.ι Q).bilin m₁ m₂) = f.bilin m₁ m₂ := by
+ rw [CliffordAlgebra.even.lift.aux_apply]
+ refine (congr_arg Prod.fst (foldr_mul Q (fFold f) _ _ _ _)).trans ?_
+ rw [foldr_ι, foldr_ι]
+ exact mul_one _
@[simp]
theorem aux_algebraMap (r) :
diff --git a/Mathlib/LinearAlgebra/FreeProduct/Basic.lean b/Mathlib/LinearAlgebra/FreeProduct/Basic.lean
index af6700f7588803..80a143e85978c8 100644
--- a/Mathlib/LinearAlgebra/FreeProduct/Basic.lean
+++ b/Mathlib/LinearAlgebra/FreeProduct/Basic.lean
@@ -7,6 +7,7 @@ module
public import Mathlib.Algebra.DirectSum.Basic
public import Mathlib.LinearAlgebra.TensorAlgebra.ToTensorPower
+public import Mathlib.RingTheory.Congruence.Hom
/-!
# The free product of $R$-algebras
@@ -100,30 +101,41 @@ inductive rel : FreeTensorAlgebra R A → FreeTensorAlgebra R A → Prop
(tprod R (⨁ i, A i) 2 (fun | 0 => lof R I A i a₁ | 1 => lof R I A i a₂))
(ι R <| lof R I A i (a₁ * a₂))
+/-- `rel` as a ring congruence. -/
+def ringCon : RingCon (FreeTensorAlgebra R A) := ringConGen (rel R A)
+
open scoped Function
/-- The generating equivalence relation for elements of the power algebra
that are identified in the free product -/
@[reducible, simp] def rel' := rel R A on ofDirectSum
+/-- `rel'` as a ring congruence. -/
+def ringCon' : RingCon (PowerAlgebra R A) := ringConGen (rel' R A)
+
theorem rel_id (i : I) : rel R A (ι R <| lof R I A i 1) 1 := rel.id
/-- The free product of the collection of `R`-algebras `A i`, as a quotient of
`FreeTensorAlgebra R A` -/
-@[reducible] def _root_.LinearAlgebra.FreeProduct := RingQuot <| FreeProduct.rel R A
+@[reducible] def _root_.LinearAlgebra.FreeProduct := FreeProduct.ringCon R A |>.Quotient
/-- The free product of the collection of `R`-algebras `A i`,
as a quotient of `PowerAlgebra R A` -/
-@[reducible] def asPowers := RingQuot <| FreeProduct.rel' R A
+@[reducible] def asPowers := FreeProduct.ringCon' R A |>.Quotient
/-- The `R`-algebra equivalence relating `FreeProduct` and `FreeProduct.asPowers`. -/
noncomputable def asPowersEquiv : asPowers R A ≃ₐ[R] FreeProduct R A :=
- RingQuot.algEquivQuotAlgEquiv
- (powerAlgebraEquivFreeTensorAlgebra R A |>.symm) (FreeProduct.rel R A)
+ RingCon.congrₐ _
+ (powerAlgebraEquivFreeTensorAlgebra R A |>.symm) (by
+ rw [ringCon', ringCon, rel']
+ erw [RingCon.comap_ringConGen_ringEquiv]
+ congr
+ ext i x
+ simp [Function.onFun])
|>.symm
-open RingQuot Function
+open Function
local infixr:60 " ∘ₐ " => AlgHom.comp
@@ -133,27 +145,29 @@ instance instAlgebra : Algebra R (FreeProduct R A) := by infer_instance
/-- The canonical quotient map `FreeTensorAlgebra R A →ₐ[R] FreeProduct R A`,
as an `R`-algebra homomorphism -/
abbrev mkAlgHom : FreeTensorAlgebra R A →ₐ[R] FreeProduct R A :=
- RingQuot.mkAlgHom R (rel R A)
+ RingCon.mkₐ _ _
/-- The canonical linear map from the direct sum of the `A i` to the free product -/
-abbrev ι' : (⨁ i, A i) →ₗ[R] FreeProduct R A :=
+def ι' : (⨁ i, A i) →ₗ[R] FreeProduct R A :=
(mkAlgHom R A).toLinearMap ∘ₗ TensorAlgebra.ι R (M := ⨁ i, A i)
@[simp] theorem ι_apply (x : ⨁ i, A i) :
- ⟨Quot.mk (Rel <| rel R A) (TensorAlgebra.ι R x)⟩ = ι' R A x := by
- aesop (add simp [ι', mkAlgHom, RingQuot.mkAlgHom, mkRingHom])
+ ↑(TensorAlgebra.ι R x) = ι' R A x := by
+ aesop (add simp [ι', mkAlgHom])
/-- The injection into the free product of any `1 : A i` is the 1 of the free product. -/
theorem identify_one (i : I) : ι' R A (DirectSum.lof R I A i 1) = 1 := by
- suffices ι' R A (DirectSum.lof R I A i 1) = mkAlgHom R A 1 by simpa
- exact RingQuot.mkAlgHom_rel R <| rel_id R A (i := i)
+ suffices ι' R A (DirectSum.lof R I A i 1) = mkAlgHom R A 1 by simpa [← ι_apply]
+ exact Quotient.sound <| RingCon.le_ringConGen _ _ <| rel_id R A (i := i)
/-- Multiplication in the free product of the injections of any two `aᵢ aᵢ': A i` for
the same `i` is just the injection of multiplication `aᵢ * aᵢ'` in `A i`. -/
theorem mul_injections (a₁ a₂ : A i) :
ι' R A (DirectSum.lof R I A i a₁) * ι' R A (DirectSum.lof R I A i a₂)
= ι' R A (DirectSum.lof R I A i (a₁ * a₂)) := by
- convert! RingQuot.mkAlgHom_rel R <| rel.prod
+ rw [← ι_apply, ← ι_apply, ← RingCon.coe_mul]
+ refine Quotient.sound <| RingCon.le_ringConGen _ _ <| ?_
+ convert! rel.prod
simp
/-- The `i`th canonical injection, from `A i` to the free product, as
@@ -173,34 +187,31 @@ irreducible_def ι (i : I) : A i →ₐ[R] FreeProduct R A :=
/-- The family of canonical injection maps, with `i` left implicit -/
irreducible_def of {i : I} : A i →ₐ[R] FreeProduct R A := ι R A i
-
/-- Universal property of the free product of algebras:
for every `R`-algebra `B`, every family of maps `maps : (i : I) → (A i →ₐ[R] B)` lifts
to a unique arrow `π` from `FreeProduct R A` such that `π ∘ ι i = maps i`. -/
@[simps] def lift : ({i : I} → A i →ₐ[R] B) ≃ (FreeProduct R A →ₐ[R] B) where
toFun maps :=
- RingQuot.liftAlgHom R ⟨
- TensorAlgebra.lift R <|
- DirectSum.toModule R I B <|
- (@maps · |>.toLinearMap),
- fun x y r ↦ by
+ RingCon.liftₐ _
+ (TensorAlgebra.lift R <| DirectSum.toModule R I B <| (@maps · |>.toLinearMap))
+ <| RingCon.ringConGen_le.2 fun x y r ↦ by
cases r with
| id => simp
- | prod => simp⟩
+ | prod => simp
invFun π i := π ∘ₐ ι R A i
left_inv π := by
ext i aᵢ
- aesop (add simp [ι, ι'])
+ simp [ι, ← ι_apply]
right_inv maps := by
ext i a
- aesop (add simp [ι, ι'])
+ simp [ι, ← ι_apply]
/-- Universal property of the free product of algebras, property:
for every `R`-algebra `B`, every family of maps `maps : (i : I) → (A i →ₐ[R] B)` lifts
to a unique arrow `π` from `FreeProduct R A` such that `π ∘ ι i = maps i`. -/
@[simp↓] theorem lift_comp_ι : lift R A maps ∘ₐ ι R A i = maps := by
ext a
- simp [lift_apply, ι]
+ simp [lift_apply, ι, ← ι_apply]
@[simp↓] theorem lift_algebraMap (r : R) : lift R A maps (algebraMap R _ r) = algebraMap R _ r := by
rw [lift_apply, AlgHom.commutes]
@@ -209,6 +220,6 @@ to a unique arrow `π` from `FreeProduct R A` such that `π ∘ ι i = maps i`.
(f : FreeProduct R A →ₐ[R] B) (h : ∀ i, f ∘ₐ ι R A i = maps) :
f = lift R A maps := by
ext i a; simp_rw [AlgHom.ext_iff] at h; specialize h i a
- simp [h.symm, ι]
+ simp [h.symm, ι, ← ι_apply]
end LinearAlgebra.FreeProduct
diff --git a/Mathlib/LinearAlgebra/SymmetricAlgebra/Basic.lean b/Mathlib/LinearAlgebra/SymmetricAlgebra/Basic.lean
index 7ccbac331f11e5..4a1147d6b0fbc8 100644
--- a/Mathlib/LinearAlgebra/SymmetricAlgebra/Basic.lean
+++ b/Mathlib/LinearAlgebra/SymmetricAlgebra/Basic.lean
@@ -40,19 +40,21 @@ quotiented out by. -/
inductive TensorAlgebra.SymRel : TensorAlgebra R M → TensorAlgebra R M → Prop where
| mul_comm (x y : M) : SymRel (ι R x * ι R y) (ι R y * ι R x)
+/-- `SymRel` as a ring congruence, used to build the quotient. -/
+@[no_expose] def TensorAlgebra.symRingCon : RingCon (TensorAlgebra R M) := ringConGen (SymRel R M)
+
open TensorAlgebra
/-- Concrete construction of the symmetric algebra of `M` by quotienting out
the tensor algebra by the commutativity relation. -/
-abbrev SymmetricAlgebra := RingQuot (SymRel R M)
+abbrev SymmetricAlgebra := symRingCon R M |>.Quotient
namespace SymmetricAlgebra
/-- Algebra homomorphism from the tensor algebra over `M` to the symmetric algebra over `M`. -/
-abbrev algHom : TensorAlgebra R M →ₐ[R] SymmetricAlgebra R M := RingQuot.mkAlgHom R (SymRel R M)
+abbrev algHom : TensorAlgebra R M →ₐ[R] SymmetricAlgebra R M := RingCon.mkₐ R _
-lemma algHom_surjective : Function.Surjective (algHom R M) :=
- RingQuot.mkAlgHom_surjective _ _
+lemma algHom_surjective : Function.Surjective (algHom R M) := Quotient.mk_surjective
/-- Canonical inclusion of `M` into the symmetric algebra `SymmetricAlgebra R M`. -/
def ι : M →ₗ[R] SymmetricAlgebra R M := algHom R M ∘ₗ TensorAlgebra.ι R
@@ -78,7 +80,9 @@ instance : CommSemiring (SymmetricAlgebra R M) where
| algebraMap r => exact Algebra.commute_algebraMap_right _ _
| ι x => induction a using SymmetricAlgebra.induction with
| algebraMap r => exact Algebra.commute_algebraMap_left _ _
- | ι y => simp [commute_iff_eq, ι, ← map_mul, RingQuot.mkAlgHom_rel _ (SymRel.mul_comm x y)]
+ | ι y =>
+ have := RingCon.le_ringConGen (r := SymRel R M) _ _ <| SymRel.mul_comm y x
+ simpa [commute_iff_eq, ι, ← RingCon.coe_mul]
| mul a b ha hb => exact ha.mul_left hb
| add a b ha hb => exact ha.add_left hb
| mul b c hb hc => exact hb.mul_right hc
@@ -86,29 +90,32 @@ instance : CommSemiring (SymmetricAlgebra R M) where
instance (R M) [CommRing R] [AddCommMonoid M] [Module R M] : CommRing (SymmetricAlgebra R M) where
__ := (inferInstance : CommSemiring (SymmetricAlgebra R M))
- __ := (inferInstance : Ring (RingQuot (SymRel R M)))
+ __ := (inferInstance : Ring (SymmetricAlgebra R M))
variable {R M} {A : Type*} [CommSemiring A] [Algebra R A]
/-- For any linear map `f : M →ₗ[R] A`, `SymmetricAlgebra.lift f` lifts the linear map to an
R-algebra homomorphism from `SymmetricAlgebra R M` to `A`. -/
-def lift : (M →ₗ[R] A) ≃ (SymmetricAlgebra R M →ₐ[R] A) := by
+def lift : (M →ₗ[R] A) ≃ (SymmetricAlgebra R M →ₐ[R] A) :=
let equiv : (TensorAlgebra R M →ₐ[R] A) ≃
- {f : TensorAlgebra R M →ₐ[R] A // ∀ {x y}, (TensorAlgebra.SymRel R M) x y → f x = f y} := by
- refine (Equiv.subtypeUnivEquiv fun h _ _ h' ↦ ?_).symm
- induction h' with | mul_comm x y => rw [map_mul, map_mul, mul_comm]
- exact (TensorAlgebra.lift R).trans <| equiv.trans <| RingQuot.liftAlgHom R
+ {f : TensorAlgebra R M →ₐ[R] A // TensorAlgebra.symRingCon R M ≤ RingCon.ker f.toRingHom} :=
+ (Equiv.subtypeUnivEquiv fun h _ _ h' ↦ ?_).symm
+ (TensorAlgebra.lift R).trans <| equiv.trans <| RingCon.liftₐEquiv (symRingCon R M)
+where finally
+ refine RingCon.ringConGen_le.2 (fun x y h' => ?_) h'
+ induction h' with | mul_comm x y
+ rw [RingCon.ker_apply, map_mul, map_mul, mul_comm]
variable (f : M →ₗ[R] A)
@[simp]
lemma lift_ι_apply (a : M) : lift f (ι R M a) = f a := by
- simp [lift, ι, algHom]
+ simp [lift, ι, algHom, RingCon.liftₐEquiv]
@[simp]
lemma lift_comp_ι : lift f ∘ₗ ι R M = f := LinearMap.ext <| lift_ι_apply f
-@[ext 1100]
+@[ext 1200]
theorem algHom_ext {F G : SymmetricAlgebra R M →ₐ[R] A}
(h : F ∘ₗ ι R M = (G ∘ₗ ι R M : M →ₗ[R] A)) : F = G := by
ext x
diff --git a/Mathlib/LinearAlgebra/TensorAlgebra/Basic.lean b/Mathlib/LinearAlgebra/TensorAlgebra/Basic.lean
index a8e63217d9d82c..704c34f0748b31 100644
--- a/Mathlib/LinearAlgebra/TensorAlgebra/Basic.lean
+++ b/Mathlib/LinearAlgebra/TensorAlgebra/Basic.lean
@@ -6,10 +6,10 @@ Authors: Adam Topaz
module
public import Mathlib.Algebra.FreeAlgebra
-public import Mathlib.Algebra.RingQuot
public import Mathlib.Algebra.TrivSqZeroExt.Basic
public import Mathlib.Algebra.Algebra.Operations
public import Mathlib.LinearAlgebra.Multilinear.Basic
+public import Mathlib.RingTheory.Congruence.Hom
/-!
# Tensor Algebras
@@ -56,12 +56,14 @@ inductive Rel : FreeAlgebra R M → FreeAlgebra R M → Prop
| smul {r : R} {a : M} :
Rel (FreeAlgebra.ι R (r • a)) (algebraMap R (FreeAlgebra R M) r * FreeAlgebra.ι R a)
+/-- `Rel` as a ring congruence, used to build the quotient. -/
+@[no_expose] def ringCon : RingCon (FreeAlgebra R M) := ringConGen (Rel R M)
+
end TensorAlgebra
/-- The tensor algebra of the module `M` over the commutative semiring `R`.
-/
-def TensorAlgebra :=
- RingQuot (TensorAlgebra.Rel R M)
+def TensorAlgebra := TensorAlgebra.ringCon R M |>.Quotient
deriving Inhabited, Semiring
-- `IsScalarTower` is not needed, but the instance isn't really canonical without it.
@@ -70,7 +72,7 @@ instance instAlgebra {R A M} [CommSemiring R] [AddCommMonoid M] [CommSemiring A]
[Algebra R A] [Module R M] [Module A M]
[IsScalarTower R A M] :
Algebra R (TensorAlgebra A M) :=
- inferInstanceAs <| Algebra R (RingQuot _)
+ inferInstanceAs <| Algebra R (RingCon.Quotient _)
-- verify there is no diamond
-- but doesn't work at `reducible_and_instances` https://github.com/leanprover-community/mathlib4/issues/10906
@@ -80,18 +82,18 @@ instance {R S A M} [CommSemiring R] [CommSemiring S] [AddCommMonoid M] [CommSemi
[Algebra R A] [Algebra S A] [Module R M] [Module S M] [Module A M]
[IsScalarTower R A M] [IsScalarTower S A M] :
SMulCommClass R S (TensorAlgebra A M) :=
- inferInstanceAs <| SMulCommClass R S (RingQuot _)
+ inferInstanceAs <| SMulCommClass R S (RingCon.Quotient _)
instance {R S A M} [CommSemiring R] [CommSemiring S] [AddCommMonoid M] [CommSemiring A]
[SMul R S] [Algebra R A] [Algebra S A] [Module R M] [Module S M] [Module A M]
[IsScalarTower R A M] [IsScalarTower S A M] [IsScalarTower R S A] :
IsScalarTower R S (TensorAlgebra A M) :=
- inferInstanceAs <| IsScalarTower R S (RingQuot _)
+ inferInstanceAs <| IsScalarTower R S (RingCon.Quotient _)
namespace TensorAlgebra
instance {S : Type*} [CommRing S] [Module S M] : Ring (TensorAlgebra S M) :=
- inferInstanceAs <| Ring (RingQuot _)
+ inferInstanceAs <| Ring (RingCon.Quotient _)
-- verify there is no diamond
-- but doesn't work at `reducible_and_instances` https://github.com/leanprover-community/mathlib4/issues/10906
@@ -104,16 +106,16 @@ set_option backward.isDefEq.respectTransparency false in
/-- The canonical linear map `M →ₗ[R] TensorAlgebra R M`.
-/
irreducible_def ι : M →ₗ[R] TensorAlgebra R M :=
- { toFun := fun m => RingQuot.mkAlgHom R _ (FreeAlgebra.ι R m)
+ { toFun := fun m => RingCon.toQuotient (FreeAlgebra.ι R m)
map_add' := fun x y => by
- rw [← map_add (RingQuot.mkAlgHom R (Rel R M))]
- exact RingQuot.mkAlgHom_rel R Rel.add
+ rw [← RingCon.coe_add]
+ exact Quotient.sound <| RingConGen.Rel.of _ _ Rel.add
map_smul' := fun r x => by
- rw [← map_smul (RingQuot.mkAlgHom R (Rel R M))]
- exact RingQuot.mkAlgHom_rel R Rel.smul }
+ rw [← RingCon.coe_smul]
+ exact Quotient.sound <| RingConGen.Rel.of _ _ <| Rel.smul}
theorem ringQuot_mkAlgHom_freeAlgebra_ι_eq_ι (m : M) :
- RingQuot.mkAlgHom R (Rel R M) (FreeAlgebra.ι R m) = ι R m := by
+ RingCon.mkₐ R (ringCon R M) (FreeAlgebra.ι R m) = ι R m := by
rw [ι]
rfl
@@ -122,24 +124,26 @@ of `f` to a morphism of `R`-algebras `TensorAlgebra R M → A`.
-/
@[simps symm_apply]
def lift {A : Type*} [Semiring A] [Algebra R A] : (M →ₗ[R] A) ≃ (TensorAlgebra R M →ₐ[R] A) :=
- { toFun :=
- RingQuot.liftAlgHom R ∘ fun f =>
- ⟨FreeAlgebra.lift R (⇑f), fun x y (h : Rel R M x y) => by
- induction h <;>
- simp only [Algebra.smul_def, FreeAlgebra.lift_ι_apply, map_smulₛₗ, RingHom.id_apply,
- map_mul, AlgHom.commutes, map_add]⟩
+ { toFun f :=
+ RingCon.liftₐ (ringCon R M) (FreeAlgebra.lift R (f)) <| by
+ grw [ringCon, RingCon.ringConGen_le]
+ intro x y h
+ induction h <;>
+ simp [Algebra.smul_def, FreeAlgebra.lift_ι_apply,
+ map_mul, AlgHom.commutes, map_add, RingCon.ker]
invFun := fun F => F.toLinearMap.comp (ι R)
left_inv := fun f => by
rw [ι]
ext1 x
- exact (RingQuot.liftAlgHom_mkAlgHom_apply _ _ _ _).trans (FreeAlgebra.lift_ι_apply f x)
+ dsimp
+ exact (RingCon.liftₐ_mk _ _ _ _).trans (FreeAlgebra.lift_ι_apply f x)
right_inv := fun F =>
- RingQuot.ringQuot_ext' _ _ _ <|
+ RingCon.Quotient.hom_extₐ <|
FreeAlgebra.hom_ext <|
funext fun x => by
rw [ι]
- exact
- (RingQuot.liftAlgHom_mkAlgHom_apply _ _ _ _).trans (FreeAlgebra.lift_ι_apply _ _) }
+ simp
+ rfl }
variable {R}
diff --git a/Mathlib/RingTheory/Congruence/Basic.lean b/Mathlib/RingTheory/Congruence/Basic.lean
index 43628cefaaa913..cbd85e276d1fab 100644
--- a/Mathlib/RingTheory/Congruence/Basic.lean
+++ b/Mathlib/RingTheory/Congruence/Basic.lean
@@ -26,7 +26,6 @@ Most of the time you likely want to use the `Ideal.Quotient` API that is built o
## TODO
-* Use this for `RingQuot` too.
* Copy across more API from `Con` and `AddCon` in `Mathlib/GroupTheory/Congruence/`.
-/
@@ -259,7 +258,6 @@ theorem ringConGen_eq (r : R → R → Prop) :
(fun _ _ h1 h2 c hc => c.mul (h1 c hc) <| h2 c hc))
(sInf_le le_ringConGen)
-
/-- The smallest congruence relation containing a binary relation `r` is contained in any
congruence relation containing `r`. -/
theorem ringConGen_le {r : R → R → Prop} {c : RingCon R} : ringConGen r ≤ c ↔ r ≤ ⇑c :=
diff --git a/Mathlib/RingTheory/Congruence/Defs.lean b/Mathlib/RingTheory/Congruence/Defs.lean
index 1ef19c1e92172e..a8750b1c846e1b 100644
--- a/Mathlib/RingTheory/Congruence/Defs.lean
+++ b/Mathlib/RingTheory/Congruence/Defs.lean
@@ -26,7 +26,6 @@ Most of the time you likely want to use the `Ideal.Quotient` API that is built o
## TODO
-* Use this for `RingQuot` too.
* Copy across more API from `Con` and `AddCon` in `Mathlib/GroupTheory/Congruence/`.
-/
@@ -410,8 +409,10 @@ instance [Add R] [CommMagma R] (c : RingCon R) : CommMagma c.Quotient :=
instance [Add R] [CommSemigroup R] (c : RingCon R) : CommSemigroup c.Quotient :=
inferInstanceAs <| CommSemigroup c.toCon.Quotient
-instance [Add R] [Monoid R] (c : RingCon R) : Monoid c.Quotient :=
- inferInstanceAs <| Monoid c.toCon.Quotient
+instance [Add R] [Monoid R] (c : RingCon R) : Monoid c.Quotient := fast_instance%
+ { __ : Monoid c.toCon.Quotient := inferInstanceAs _
+ -- see https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/inferInstanceAs.20creates.20non-reducible.20diamonds/near/603969174
+ npow n x := x ^ n }
instance [Add R] [CommMonoid R] (c : RingCon R) : CommMonoid c.Quotient :=
inferInstanceAs <| CommMonoid c.toCon.Quotient
diff --git a/Mathlib/RingTheory/Congruence/Hom.lean b/Mathlib/RingTheory/Congruence/Hom.lean
index 5e7a93606ddf80..a3ad9e08b2f32b 100644
--- a/Mathlib/RingTheory/Congruence/Hom.lean
+++ b/Mathlib/RingTheory/Congruence/Hom.lean
@@ -502,11 +502,20 @@ theorem liftₐ_range (H : c ≤ ker f.toRingHom) :
/-- Homomorphisms on the quotient of a ring by a ring congruence relation are
equal if they are equal on elements that are coercions from the ring. -/
-@[ext high] -- This should have higher priority than `AlgHom.ext`
+-- This should have higher priority than `AlgHom.ext`, but lower than any types implemented with
+-- `Quotient`, as `ext` is lax with reducibility.
+@[ext 1100]
theorem Quotient.hom_extₐ {f g : c.Quotient →ₐ[R] P}
(h : f.comp (c.mkₐ R) = g.comp (c.mkₐ R)) : f = g :=
DFunLike.ext _ _ <| c.mk'_surjective.forall.mpr fun x ↦ by exact congr($h x)
+/-- `liftₐ` as an equivalence. -/
+@[simps]
+def liftₐEquiv (c : RingCon M) :
+ { f : M →ₐ[R] P // c ≤ ker (f : M →+* P)} ≃ (c.Quotient →ₐ[R] P) where
+ toFun f := liftₐ c f.1 f.2
+ invFun F := ⟨F.comp (c.mkₐ R), fun x y h => congr(F $(Quotient.sound h))⟩
+
variable (f) in
/-- The homomorphism induced on the quotient of a ring by the kernel of a ring homomorphism. -/
def kerLiftₐ : (ker f.toRingHom).Quotient →ₐ[R] P :=
diff --git a/Mathlib/RingTheory/DividedPowerAlgebra/Init.lean b/Mathlib/RingTheory/DividedPowerAlgebra/Init.lean
index b5da30718aae74..3549203e60bb03 100644
--- a/Mathlib/RingTheory/DividedPowerAlgebra/Init.lean
+++ b/Mathlib/RingTheory/DividedPowerAlgebra/Init.lean
@@ -6,7 +6,8 @@ Authors: Antoine Chambert-Loir, María Inés de Frutos—Fernández
module
public import Mathlib.Algebra.MvPolynomial.Eval
-public import Mathlib.Algebra.RingQuot
+public import Mathlib.RingTheory.Congruence.Hom
+public import Mathlib.RingTheory.Congruence.BigOperators
public import Mathlib.RingTheory.DividedPowers.Basic
/-!
@@ -25,7 +26,7 @@ divided powers (`DividedPowerAlgebra.lift`).
divided power algebra.
* `DividedPowerAlgebra R M`: the universal divided power algebra of the `R`-module `M`,
- defined as `RingQuot` of `DividedPowerAlgebra.Rel R M`.
+ defined as `RingCon.Quotient` of `DividedPowerAlgebra.ringCon R M`.
* `DividedPowerAlgebra.dp R n m`: for `n : ℕ` and `m : M`, this is the equivalence class of
`MvPolynomial.X (⟨n, m⟩)` in `DividedPowerAlgebra R M`.
@@ -68,9 +69,9 @@ divided powers (`DividedPowerAlgebra.lift`).
noncomputable section
-open Finset Ideal MvPolynomial RingQuot
+open Finset Ideal MvPolynomial
-variable (R M : Type*) [CommSemiring R] [AddCommMonoid M] [Module R M]
+variable (α R M : Type*) [CommSemiring R] [AddCommMonoid M] [Module R M]
namespace DividedPowerAlgebra
@@ -87,13 +88,16 @@ inductive Rel : MvPolynomial (ℕ × M) R → MvPolynomial (ℕ × M) R → Prop
/-- The ideal of `MvPolynomial (ℕ × M) R` generated by `Rel`. -/
def RelI : Ideal (MvPolynomial (ℕ × M) R) := ofRel (DividedPowerAlgebra.Rel R M)
+/-- The congruence generated by `Rel`. -/
+def ringCon : RingCon (MvPolynomial (ℕ × M) R) := ringConGen (DividedPowerAlgebra.Rel R M)
+
end DividedPowerAlgebra
/-- The divided power algebra of a module M is defined as the ring quotient of the polynomial ring
in the variables `ℕ × M` by the ring relation defined by `DividedPowerAlgebra.Rel`.
We will later show that that `DividedPowerAlgebra R M` has divided powers.
It satisfies a weak universal property for morphisms to rings with divided powers. -/
-abbrev DividedPowerAlgebra := RingQuot (DividedPowerAlgebra.Rel R M)
+abbrev DividedPowerAlgebra := DividedPowerAlgebra.ringCon R M |>.Quotient
namespace DividedPowerAlgebra
@@ -101,59 +105,58 @@ open MvPolynomial
variable {R M}
-lemma mkAlgHom_surjective : Function.Surjective (mkAlgHom R (Rel R M)) :=
- RingQuot.mkAlgHom_surjective _ _
+lemma mkAlgHom_surjective : Function.Surjective (RingCon.mkₐ R (ringCon R M)) :=
+ Quotient.mk_surjective
+
+@[simp]
+lemma coe_C (a : R) :
+ ↑(C (σ := ℕ × M) a) = algebraMap R (DividedPowerAlgebra R M) a := by
+ rw [← MvPolynomial.algebraMap_eq, RingCon.coe_algebraMap]
+@[deprecated coe_C (since := "2026-06-19")]
lemma mkAlgHom_C (a : R) :
- mkAlgHom R (Rel R M) (C a) = algebraMap R (DividedPowerAlgebra R M) a := by
+ RingCon.mkₐ R (ringCon R M) (C a) = algebraMap R (DividedPowerAlgebra R M) a := by
rw [← MvPolynomial.algebraMap_eq, AlgHom.commutes]
+@[deprecated coe_C (since := "2026-06-19")]
lemma mkRingHom_C (a : R) :
- mkRingHom (Rel R M) (C a) = algebraMap R (DividedPowerAlgebra R M) a := by
- rw [← mkAlgHom_C, mkAlgHom, AlgHom.coe_mk]
+ RingCon.mk' (ringCon R M) (C a) = algebraMap R (DividedPowerAlgebra R M) a :=
+ mkAlgHom_C _
variable (R) in
/-- `dp R n m` is the equivalence class of `X (⟨n, m⟩)` in `DividedPowerAlgebra R M`. -/
-def dp (n : ℕ) (m : M) : DividedPowerAlgebra R M := mkAlgHom R (Rel R M) (X ⟨n, m⟩)
+def dp (n : ℕ) (m : M) : DividedPowerAlgebra R M := ↑(X (n, m) : MvPolynomial (ℕ × M) R)
theorem dp_def (n : ℕ) (m : M) :
- dp R n m = mkAlgHom R (Rel R M) (X ⟨n, m⟩) := rfl
+ dp R n m = ↑(X (n, m) : MvPolynomial (ℕ × M) R) := rfl
protected theorem induction_on' {P : DividedPowerAlgebra R M → Prop} (f : DividedPowerAlgebra R M)
- (h_C : ∀ a, P (mkAlgHom R (Rel R M) (C a))) (h_add : ∀ f g, P f → P g → P (f + g))
+ (h_C : ∀ a, P (C a : MvPolynomial (ℕ × M) R)) (h_add : ∀ f g, P f → P g → P (f + g))
(h_dp : ∀ (f : DividedPowerAlgebra R M) (n : ℕ) (m : M), P f → P (f * dp R n m)) : P f := by
- obtain ⟨F, hf⟩ := RingQuot.mkRingHom_surjective (DividedPowerAlgebra.Rel R M) f
- rw [← hf]
- induction F using MvPolynomial.induction_on generalizing f with
- | C a =>
- convert! h_C a using 1
- rw [mkAlgHom, AlgHom.coe_mk]
+ induction f using Quot.induction_on with | _ F
+ dsimp
+ induction F using MvPolynomial.induction_on with
+ | C a => exact h_C a
| add g1 g2 hg1 hg2 =>
- rw [map_add]
- exact h_add _ _ (hg1 ((mkRingHom (Rel R M)) g1) rfl) (hg2 ((mkRingHom (Rel R M)) g2) rfl)
+ rw [RingCon.coe_add]
+ exact h_add _ _ hg1 hg2
| mul_X g nm h =>
- have h' : (mkRingHom (Rel R M)) (X nm) = dp R nm.1 nm.2 := by
- simp only [dp_def, Prod.mk.eta, mkAlgHom, AlgHom.coe_mk]
- rw [_root_.map_mul, h']
- exact h_dp _ _ _ (h (mkRingHom (Rel R M) g) rfl)
+ rw [RingCon.coe_mul]
+ exact h_dp _ _ _ h
@[elab_as_elim]
protected theorem induction_on {P : DividedPowerAlgebra R M → Prop} (f : DividedPowerAlgebra R M)
(C : ∀ a, P (algebraMap R _ a)) (add : ∀ f g, P f → P g → P (f + g))
(dp : ∀ (f : DividedPowerAlgebra R M) (n : ℕ) (m : M), P f → P (f * dp R n m)) : P f :=
- DividedPowerAlgebra.induction_on' f (fun a ↦ by rw [mkAlgHom_C]; exact C a) add dp
-
-theorem dp_eq_mkRingHom (n : ℕ) (m : M) :
- dp R n m = mkRingHom (Rel R M) (X (⟨n, m⟩)) := by
- simp [dp, mkRingHom, mkAlgHom]
+ DividedPowerAlgebra.induction_on' f C add dp
theorem dp_zero {m : M} : dp R 0 m = 1 := by
- rw [dp_def, ← map_one (mkAlgHom R (Rel R M))]
- exact RingQuot.mkAlgHom_rel R Rel.zero
+ rw [dp_def, ← RingCon.coe_one]
+ exact Quotient.sound <| RingCon.le_ringConGen _ _ Rel.zero
theorem dp_smul {r : R} {n : ℕ} {m : M} : dp R n (r • m) = r ^ n • dp R n m := by
- rw [dp_def, dp_def, ← map_smul]
- exact mkAlgHom_rel R Rel.smul
+ rw [dp_def, dp_def, ← RingCon.coe_smul]
+ exact Quotient.sound <| RingCon.le_ringConGen _ _ Rel.smul
theorem dp_null {n : ℕ} : dp R n (0 : M) = if n = 0 then 1 else 0 := by
cases Nat.eq_zero_or_pos n with
@@ -168,14 +171,13 @@ theorem dp_null_of_ne_zero {n : ℕ} (hn : n ≠ 0) : dp R n (0 : M) = 0 := by
theorem dp_mul {n p : ℕ} {m : M} :
dp R n m * dp R p m = (n + p).choose n • dp R (n + p) m := by
- simp only [dp_def, ← _root_.map_mul, ← map_nsmul]
- exact mkAlgHom_rel R Rel.mul
+ simp only [dp_def, ← RingCon.coe_mul, ← RingCon.coe_nsmul]
+ exact Quotient.sound <| RingCon.le_ringConGen _ _ Rel.mul
theorem dp_add {n : ℕ} {x y : M} :
dp R n (x + y) = (antidiagonal n).sum fun k ↦ dp R k.1 x * dp R k.2 y := by
- simp only [dp_def]
- rw [mkAlgHom_rel (A := MvPolynomial (ℕ × M) R) R Rel.add, map_sum,
- Finset.sum_congr rfl (fun k _ ↦ by rw [_root_.map_mul])]
+ simp_rw [dp_def, ← RingCon.coe_mul, ← RingCon.coe_finsetSum]
+ exact Quotient.sound <| RingCon.le_ringConGen _ _ Rel.add
theorem dp_sum {ι : Type*} [DecidableEq ι] (s : Finset ι) (q : ℕ) (x : ι → M) :
dp R q (s.sum x) =
@@ -317,7 +319,9 @@ def lift' {f : ℕ × M → A} (hf_zero : ∀ m, f (0, m) = 1)
(hf_mul : ∀ n p m, f ⟨n, m⟩ * f ⟨p, m⟩ = (n + p).choose n • f ⟨n + p, m⟩)
(hf_add : ∀ n u v, f ⟨n, u + v⟩ = (antidiagonal n).sum fun (k, l) ↦ f ⟨k, u⟩ * f ⟨l, v⟩) :
DividedPowerAlgebra R M →ₐ[R] A :=
- RingQuot.liftAlgHom R ⟨eval₂AlgHom R f, by exact lift'_imp R M hf_zero hf_smul hf_mul hf_add⟩
+ RingCon.liftₐ _ (eval₂AlgHom R f) <| by
+ grw [ringCon, RingCon.ringConGen_le]
+ exact lift'_imp R M hf_zero hf_smul hf_mul hf_add
@[simp]
theorem lift'_apply {f : ℕ × M → A} (hf_zero : ∀ m, f (0, m) = 1)
@@ -325,7 +329,7 @@ theorem lift'_apply {f : ℕ × M → A} (hf_zero : ∀ m, f (0, m) = 1)
(hf_mul : ∀ n p m, f ⟨n, m⟩ * f ⟨p, m⟩ = (n + p).choose n • f ⟨n + p, m⟩)
(hf_add : ∀ n u v, f ⟨n, u + v⟩ = (antidiagonal n).sum fun (k, l) ↦ f ⟨k, u⟩ * f ⟨l, v⟩)
(p : MvPolynomial (ℕ × M) R) :
- lift' hf_zero hf_smul hf_mul hf_add (mkAlgHom R (Rel R M) p) = aeval f p := by
+ lift' hf_zero hf_smul hf_mul hf_add ↑p = aeval f p := by
simp [lift', aeval_eq_eval₂Hom]
@[simp]
@@ -354,7 +358,7 @@ variable {g}
@[simp]
theorem lift_apply (p : MvPolynomial (ℕ × M) R) :
- lift hI g hg (mkAlgHom R (Rel R M) p) = aeval (fun nm : ℕ × M ↦ hI.dpow nm.1 (g nm.2)) p := by
+ lift hI g hg ↑p = aeval (fun nm : ℕ × M ↦ hI.dpow nm.1 (g nm.2)) p := by
rw [lift, lift'_apply]
@[simp]
@@ -421,7 +425,7 @@ def map : DividedPowerAlgebra R M →ₐ[R] DividedPowerAlgebra S N :=
@[simp]
theorem map_apply {p : MvPolynomial (ℕ × M) R} :
- map S f (mkAlgHom R (Rel R M) p) = aeval (fun nm ↦ dp S nm.fst (f nm.snd)) p := by
+ map S f ↑p = aeval (fun nm ↦ dp S nm.fst (f nm.snd)) p := by
rw [map, lift'_apply]
@[simp]
@@ -439,7 +443,7 @@ theorem lift_comp_embed :
theorem lift_surjective {f : M →ₗ[R] N} (hf : Function.Surjective f) :
Function.Surjective (map R f) := by
rw [← AlgHom.range_eq_top, ← Algebra.map_top (map R f), eq_top_iff,
- ← (AlgHom.range_eq_top (mkAlgHom R (Rel R N))).mpr mkAlgHom_surjective,
+ ← (AlgHom.range_eq_top (RingCon.mkₐ R (ringCon R N))).mpr mkAlgHom_surjective,
← Algebra.map_top, (Subalgebra.gc_map_comap _).le_iff_le, ← MvPolynomial.adjoin_range_X,
Algebra.adjoin_le_iff]
intro
@@ -447,7 +451,7 @@ theorem lift_surjective {f : M →ₗ[R] N} (hf : Function.Surjective f) :
rintro ⟨n, m, rfl⟩
obtain ⟨l, rfl⟩ := hf m
simp only [Algebra.map_top, Subalgebra.coe_comap, AlgHom.coe_range, Set.mem_preimage,
- Set.mem_range]
+ Set.mem_range, RingCon.mkₐ_apply]
use dp R n l
rw [map_apply_dp, dp]
From b4384330e5b9d7268e11d3136e669205994fa74a Mon Sep 17 00:00:00 2001
From: Marcelo Lynch
Date: Tue, 23 Jun 2026 03:43:55 +0000
Subject: [PATCH 0271/1300] fix(cache): carry cache misses across 'get' layers
instead of using file existence as a proxy for 'done' (#40817)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
`lake exe cache get!` on a fork that already has a full local cache re-downloads only from `master` and never falls through to `forks`, so a corrupt fork-only `.ltar` is never refreshed.
Between containers, the next round's file set was recomputed with `filterExists` (is the `.ltar` on disk?). Under force, that proxy is wrong: a file served only by a later container whose `.ltar` is already cached looks "done" after the first round, so `remaining` empties and the chain stops. Carry forward the files a round did **not** serve instead — using the set of hashes it actually fetched — independent of disk state.
Follow-up to #40035.
---
Cache/Requests.lean | 122 ++++++++++++++++++++++++++++----------------
1 file changed, 79 insertions(+), 43 deletions(-)
diff --git a/Cache/Requests.lean b/Cache/Requests.lean
index ddd52c5000ef8d..708ca2515f4c46 100644
--- a/Cache/Requests.lean
+++ b/Cache/Requests.lean
@@ -414,24 +414,54 @@ def mkGetConfigContent (container : Option Container) (repo containerURL : Strin
pure <| acc ++ s!"url = {mkFileURL container repo containerURL fileName scope?}\n\
-o {(IO.CACHEDIR / (fileName ++ ".part")).toString.quote}\n"
+/--
+Whether an HTTP status returned for a single-file read should be treated as a
+cache miss (fall through to the next container in the chain) rather than a
+transfer failure worth reporting.
+
+`404` is always a miss. A `403` is a miss only when `treatForbiddenAsMiss` is
+set, which callers do for the `legacy` container: when its public read access is
+revoked ahead of retirement it answers reads with `403`, and old clients whose
+chain still lists `legacy` should fall through quietly instead of printing a
+per-file transfer failure. Any other status is a real failure.
+-/
+def isCacheMissStatus (httpCode : Nat) (treatForbiddenAsMiss : Bool) : Bool :=
+ httpCode == 404 || (httpCode == 403 && treatForbiddenAsMiss)
+
+/-- Outcome of a single serial download: the file arrived (`served`), the server
+returned a cache miss that should fall through to the next container (`miss`), or
+the transfer failed for another reason that should drive the exit code
+(`failed`). The parallel path draws the same distinction through
+`TransferState.failed` and `isCacheMissStatus`. -/
+inductive DownloadOutcome
+ | served
+ | miss
+ | failed
+
/-- Calls `curl` to download a single file from a specific container to `CACHEDIR`
-(`.cache`). Returns `true` on success, `false` on any error including 404.
-`scope?` is the per-round SHA scope (see `mkGetConfigContent`). -/
+(`.cache`). `scope?` is the per-round SHA scope (see `mkGetConfigContent`).
+`treatForbiddenAsMiss` mirrors the parallel path: a `legacy` `403` (public read
+access revoked ahead of retirement) is a miss, not a failure. -/
def downloadFile (container : Option Container) (repo containerURL : String)
- (hash : UInt64) (scope? : Option String) : IO Bool := do
+ (hash : UInt64) (scope? : Option String) (treatForbiddenAsMiss : Bool := false) :
+ IO DownloadOutcome := do
let fileName := hash.asLTar
let url := mkFileURL container repo containerURL fileName scope?
let path := IO.CACHEDIR / fileName
let partFileName := fileName ++ ".part"
let partPath := IO.CACHEDIR / partFileName
let out ← IO.Process.output
- { cmd := (← IO.getCurl), args := #[url, "--fail", "--silent", "-o", partPath.toString] }
+ { cmd := (← IO.getCurl),
+ args := #[url, "--fail", "--silent", "--write-out", "%{http_code}",
+ "-o", partPath.toString] }
if out.exitCode = 0 then
IO.FS.rename partPath path
- pure true
- else
- IO.FS.removeFile partPath
- pure false
+ return .served
+ IO.FS.removeFile partPath
+ -- `--fail` exits nonzero on any HTTP error; the written-out status tells a 404
+ -- miss apart from a real transfer failure (a connection error reports `000`).
+ let httpCode := out.stdout.trimAscii.toNat?.getD 0
+ return if isCacheMissStatus httpCode treatForbiddenAsMiss then .miss else .failed
/-- Extract hash from filename (e.g., "/path/to/.cache/00012345.ltar" → 0x12345).
Handles both `.ltar` and `.ltar.part` files using `FilePath.fileStem`. -/
@@ -498,20 +528,6 @@ def dispatchDecompBatch (pending : Array (FilePath × Lean.Name)) (config : Deco
let task ← IO.asTask (decompressBatch pending config.force config.isMathlibRoot config.mathlibDepPath)
return some task
-/--
-Whether an HTTP status returned for a single-file read should be treated as a
-cache miss (fall through to the next container in the chain) rather than a
-transfer failure worth reporting.
-
-`404` is always a miss. A `403` is a miss only when `treatForbiddenAsMiss` is
-set, which callers do for the `legacy` container: when its public read access is
-revoked ahead of retirement it answers reads with `403`, and old clients whose
-chain still lists `legacy` should fall through quietly instead of printing a
-per-file transfer failure. Any other status is a real failure.
--/
-def isCacheMissStatus (httpCode : Nat) (treatForbiddenAsMiss : Bool) : Bool :=
- httpCode == 404 || (httpCode == 403 && treatForbiddenAsMiss)
-
/--
Whether an HTTP status is the one Azure returns for a blob that already exists,
which a non-overwrite `put` (`If-None-Match: *`) hits when it declines to
@@ -527,8 +543,11 @@ def monitorCurl (args : Array String) (size : Nat)
(caption : String) (speedVar : String) (removeOnError := false)
(decompConfig : Option DecompConfig := none)
(treatForbiddenAsMiss : Bool := false)
- (treatExistsAsSkip : Bool := false) : IO TransferState := do
+ (treatExistsAsSkip : Bool := false) : IO (TransferState × Std.HashSet UInt64) := do
let useAnsi := (← IO.getEnv "TERM").isSome
+ -- Hashes of the files this pass fetched, used to decide what the next
+ -- container in the chain still needs to retry.
+ let servedRef ← IO.mkRef (∅ : Std.HashSet UInt64)
let mkStatus (s : TransferState) : String := Id.run do
let speedStr :=
if s.speed != 0 then
@@ -561,9 +580,11 @@ def monitorCurl (args : Array String) (size : Nat)
if (← System.FilePath.pathExists fn) && fn.endsWith ".part" then
let finalPath := (fn.dropEnd 5).copy
IO.FS.rename fn finalPath
+ let hash? := hashFromFileName finalPath
+ if let some hash := hash? then servedRef.modify (·.insert hash)
-- Add to decompression queue if enabled
if let some config := decompConfig then
- let some hash := hashFromFileName finalPath | do
+ let some hash := hash? | do
IO.eprintln s!"Warning: Failed to extract hash from filename: {finalPath}"
decompFailed := decompFailed + 1
let some mod := config.hashToMod[hash]? | do
@@ -633,18 +654,19 @@ def monitorCurl (args : Array String) (size : Nat)
if s.done > 0 then
-- to avoid confusingly moving on without finishing the count
IO.eprintln (mkStatus s)
- return s
+ return (s, ← servedRef.get)
/-- Run one container's download pass for the given hash map. Returns the
-`TransferState` produced by `monitorCurl` (or a synthesized empty state in
-serial mode). Side effect: any files successfully fetched are written to
-`CACHEDIR` with their final names. -/
+`TransferState` from `monitorCurl` (synthesized in serial mode, where it carries
+only the transfer-failure count) and the set of hashes it fetched, so the caller
+can carry the rest to the next container. Side effect: any files successfully
+fetched are written to `CACHEDIR` with their final names. -/
private def downloadFilesFromContainer
(container : Option Container) (repo containerURL : String)
(hashMap : IO.ModuleHashMap)
(parallel : Bool) (decompConfig : Option DecompConfig)
(scope? : Option String) :
- IO (Nat × TransferState) := do
+ IO (TransferState × Std.HashSet UInt64) := do
let size := hashMap.size
if parallel then
IO.FS.writeFile IO.CURLCFG (← mkGetConfigContent container repo containerURL hashMap scope?)
@@ -657,16 +679,27 @@ private def downloadFilesFromContainer
-- of retirement; treat that as a miss so the chain stays quiet for clients
-- whose chain still lists it.
let treatForbiddenAsMiss := container == some Container.legacy
- let s ← monitorCurl args size "Downloaded" "speed_download" (removeOnError := true)
+ let (s, served) ← monitorCurl args size "Downloaded" "speed_download" (removeOnError := true)
decompConfig (treatForbiddenAsMiss := treatForbiddenAsMiss)
IO.FS.removeFile IO.CURLCFG
- return (s.failed, s)
+ return (s, served)
else
+ -- Mirror the parallel path's miss/failure split: a `legacy` 403 is a miss.
+ let treatForbiddenAsMiss := container == some Container.legacy
let r ← hashMap.foldM (init := []) fun acc _ hash => do
- pure <| (← IO.asTask do downloadFile container repo containerURL hash scope?) :: acc
- let failed := r.foldl (init := 0) fun f t => if let .ok true := t.get then f else f + 1
- let emptyState : TransferState := ⟨0, 0, 0, 0, 0, #[], none, 0, 0, 0⟩
- return (failed, emptyState)
+ pure <| (hash, ← IO.asTask do
+ downloadFile container repo containerURL hash scope? treatForbiddenAsMiss) :: acc
+ -- Served hashes carry the remaining files to the next container; hard
+ -- failures (anything but a 404/legacy-403 miss, including a task that threw)
+ -- feed `TransferState.failed`, so they drive the exit code exactly as the
+ -- parallel path threads its own `failed` count.
+ let (served, failed) := r.foldl (init := ((∅ : Std.HashSet UInt64), 0))
+ fun (served, failed) (hash, t) =>
+ match t.get with
+ | .ok .served => (served.insert hash, failed)
+ | .ok .miss => (served, failed)
+ | _ => (served, failed + 1)
+ return (⟨0, 0, failed, 0, 0, #[], none, 0, 0, 0⟩, served)
/-- Expand the trust-ordered container list into the concrete download rounds to
run, each carrying the SHA scope to read at. A round is
@@ -749,6 +782,8 @@ def downloadFiles
let unsafeMode := !unsafeScopes.isEmpty
let mut remaining := hashMap
let mut finalState : TransferState := ⟨0, 0, 0, 0, 0, #[], none, 0, 0, 0⟩
+ -- Hard transfer failures (not 404 misses) drive the exit code; misses are
+ -- normal and instead surface as the "not found" hint keyed on `remaining`.
let mut downloadFailed := 0
-- For the `--unsafe` summary: how many files each scoped (forks) round supplied,
-- attributed by the drop in `remaining` across that round.
@@ -758,13 +793,14 @@ def downloadFiles
let scopeNote := match roundScope? with | some s => s!" (scope {s})" | none => ""
IO.println s!"Attempting to download {remaining.size} file(s) from {repo} cache at {url}{scopeNote}"
let before := remaining.size
- let (failed, s) ← downloadFilesFromContainer container? repo url remaining parallel decompConfig roundScope?
- -- Carry forward the decompression-related state across container rounds.
- -- Counter fields (success/failed/done) reflect only the last round; we
- -- aggregate `downloadFailed` separately below.
+ let (s, served) ← downloadFilesFromContainer container? repo url remaining parallel decompConfig roundScope?
+ -- Keep the latest round's pipeline state and transfer-failure count for the
+ -- finalization and exit-code logic below. Drop the files this round served so
+ -- the next container only retries genuine misses, regardless of what is
+ -- already on disk.
finalState := s
- downloadFailed := failed
- remaining ← remaining.filterExists false
+ downloadFailed := s.failed
+ remaining := remaining.filter fun _ hash => !served.contains hash
if unsafeMode then
if let some sha := roundScope? then
scopeServed := scopeServed.push (sha, before - remaining.size)
@@ -782,7 +818,7 @@ def downloadFiles
if remaining.size > 0 then
IO.eprintln s!" {remaining.size} file(s) still missing after all scopes."
- if warnOnMissing && downloadFailed > 0 && parallel then
+ if warnOnMissing && !remaining.isEmpty then
IO.eprintln "Warning: some files were not found in the cache."
IO.eprintln "This usually means that your local checkout of mathlib4 has diverged from upstream."
IO.eprintln ""
@@ -1049,7 +1085,7 @@ def putFilesAbsolute
"-X", "PUT", "--parallel",
"--retry", "5", -- there seem to be some intermittent failures
"--write-out", "%{json}\n", "--config", tempConfigFilePath.toString]
- let s ← monitorCurl args size "Uploaded" "speed_upload" (removeOnError := false)
+ let (s, _) ← monitorCurl args size "Uploaded" "speed_upload" (removeOnError := false)
(decompConfig := none) (treatExistsAsSkip := !overwrite)
IO.FS.removeFile tempConfigFilePath
-- Surface genuine upload failures. Already-present blobs (409/412 on a
From e3c61a827191573a57a3b65edb40923d22a52de0 Mon Sep 17 00:00:00 2001
From: Jovan Gerbscheid <56355248+JovanGerb@users.noreply.github.com>
Date: Tue, 23 Jun 2026 07:34:01 +0000
Subject: [PATCH 0272/1300] chore(Translate): turn off
`existingAttributeWarning` for `to_dual/to_additive existing` (#40357)
The existing attribute warning can often come up in `to_dual existing`/`to_additive existing`, which is kind of annoying. This PR turns off the warning in that case. Note that it was previously already turned off for `to_dual self` and `to_dual none`. This lets us remove all but one of the linter exceptions.
---
Mathlib/Algebra/BigOperators/Group/List/Basic.lean | 1 -
Mathlib/Algebra/BigOperators/Group/List/Defs.lean | 1 -
Mathlib/Algebra/Group/Basic.lean | 11 ++++-------
Mathlib/Algebra/Group/Graph.lean | 2 --
Mathlib/Algebra/Symmetrized.lean | 2 --
Mathlib/CategoryTheory/Equivalence.lean | 1 -
Mathlib/CategoryTheory/Iso.lean | 3 ---
Mathlib/CategoryTheory/Monoidal/Grp.lean | 1 -
Mathlib/CategoryTheory/NatIso.lean | 4 ----
Mathlib/Order/RelClasses.lean | 1 -
Mathlib/Tactic/ToDual.lean | 1 -
Mathlib/Tactic/Translate/Core.lean | 6 +++---
12 files changed, 7 insertions(+), 27 deletions(-)
diff --git a/Mathlib/Algebra/BigOperators/Group/List/Basic.lean b/Mathlib/Algebra/BigOperators/Group/List/Basic.lean
index 1f5a5ef9210ca9..525adfce9ad649 100644
--- a/Mathlib/Algebra/BigOperators/Group/List/Basic.lean
+++ b/Mathlib/Algebra/BigOperators/Group/List/Basic.lean
@@ -249,7 +249,6 @@ lemma prod_map_erase [DecidableEq α] (f : α → M) {a} :
@[to_additive] lemma Perm.prod_eq (h : Perm l₁ l₂) : prod l₁ = prod l₂ := h.foldr_op_eq
-set_option linter.existingAttributeWarning false in
attribute [to_additive existing] prod_reverse
@[to_additive]
diff --git a/Mathlib/Algebra/BigOperators/Group/List/Defs.lean b/Mathlib/Algebra/BigOperators/Group/List/Defs.lean
index dc2a0d7b276cc6..c6417cc1f53bc5 100644
--- a/Mathlib/Algebra/BigOperators/Group/List/Defs.lean
+++ b/Mathlib/Algebra/BigOperators/Group/List/Defs.lean
@@ -23,7 +23,6 @@ variable {ι M N : Type*}
namespace List
section Defs
-set_option linter.existingAttributeWarning false in
attribute [to_additive existing] prod prod_nil prod_cons prod_one_cons prod_append prod_concat
prod_flatten prod_eq_foldl
diff --git a/Mathlib/Algebra/Group/Basic.lean b/Mathlib/Algebra/Group/Basic.lean
index 823ca65355f567..c28a5efa016538 100644
--- a/Mathlib/Algebra/Group/Basic.lean
+++ b/Mathlib/Algebra/Group/Basic.lean
@@ -31,25 +31,22 @@ variable {α β G M : Type*}
section ite
variable [Pow α β]
-@[to_additive (attr := simp) dite_smul]
+@[to_additive (attr := simp, to_additive) dite_smul]
lemma pow_dite (p : Prop) [Decidable p] (a : α) (b : p → β) (c : ¬ p → β) :
a ^ (if h : p then b h else c h) = if h : p then a ^ b h else a ^ c h := by split_ifs <;> rfl
-@[to_additive (attr := simp) smul_dite]
+@[to_additive (attr := simp, to_additive) smul_dite]
lemma dite_pow (p : Prop) [Decidable p] (a : p → α) (b : ¬ p → α) (c : β) :
(if h : p then a h else b h) ^ c = if h : p then a h ^ c else b h ^ c := by split_ifs <;> rfl
-@[to_additive (attr := simp) ite_smul]
+@[to_additive (attr := simp, to_additive) ite_smul]
lemma pow_ite (p : Prop) [Decidable p] (a : α) (b c : β) :
a ^ (if p then b else c) = if p then a ^ b else a ^ c := pow_dite _ _ _ _
-@[to_additive (attr := simp) smul_ite]
+@[to_additive (attr := simp, to_additive) smul_ite]
lemma ite_pow (p : Prop) [Decidable p] (a b : α) (c : β) :
(if p then a else b) ^ c = if p then a ^ c else b ^ c := dite_pow _ _ _ _
-set_option linter.existingAttributeWarning false in
-attribute [to_additive (attr := simp)] dite_smul smul_dite ite_smul smul_ite
-
end ite
section Semigroup
diff --git a/Mathlib/Algebra/Group/Graph.lean b/Mathlib/Algebra/Group/Graph.lean
index afff321eb2af61..4253578e4e5926 100644
--- a/Mathlib/Algebra/Group/Graph.lean
+++ b/Mathlib/Algebra/Group/Graph.lean
@@ -51,7 +51,6 @@ def mgraph (f : G →* H) : Submonoid (G × H) where
-- TODO: Can `to_additive` be smarter about `simps`?
attribute [simps! coe] mgraph
attribute [simps! coe] AddMonoidHom.mgraph
-set_option linter.existingAttributeWarning false in
attribute [to_additive existing] coe_mgraph
@[to_additive (attr := simp)]
@@ -163,7 +162,6 @@ def graph (f : G →* H) : Subgroup (G × H) where
-- TODO: Can `to_additive` be smarter about `simps`?
attribute [simps! coe toSubmonoid] graph
attribute [simps! coe toAddSubmonoid] AddMonoidHom.graph
-set_option linter.existingAttributeWarning false in
attribute [to_additive existing] coe_graph graph_toSubmonoid
@[to_additive]
diff --git a/Mathlib/Algebra/Symmetrized.lean b/Mathlib/Algebra/Symmetrized.lean
index 87503423a78036..d2127d0905153b 100644
--- a/Mathlib/Algebra/Symmetrized.lean
+++ b/Mathlib/Algebra/Symmetrized.lean
@@ -183,12 +183,10 @@ theorem sym_mul_sym [Mul α] [Add α] [One α] [OfNat α 2] [Invertible (2 : α)
sym a * sym b = sym (⅟2 * (a * b + b * a)) :=
rfl
-set_option linter.existingAttributeWarning false in
@[simp, to_additive existing]
theorem sym_inv [Inv α] (a : α) : sym a⁻¹ = (sym a)⁻¹ :=
rfl
-set_option linter.existingAttributeWarning false in
@[simp, to_additive existing]
theorem unsym_inv [Inv α] (a : αˢʸᵐ) : unsym a⁻¹ = (unsym a)⁻¹ :=
rfl
diff --git a/Mathlib/CategoryTheory/Equivalence.lean b/Mathlib/CategoryTheory/Equivalence.lean
index ae7d7d853f69c6..977bf5da0969a4 100644
--- a/Mathlib/CategoryTheory/Equivalence.lean
+++ b/Mathlib/CategoryTheory/Equivalence.lean
@@ -214,7 +214,6 @@ def mkIso {e f : C ≌ D} (η : e.functor ≅ f.functor) : e ≅ f where
hom := mkHom η.hom
inv := mkHom η.inv
-set_option linter.existingAttributeWarning false in
attribute [to_dual existing mkIso_inv] mkIso_hom
variable (C D) in
diff --git a/Mathlib/CategoryTheory/Iso.lean b/Mathlib/CategoryTheory/Iso.lean
index 0807fc2e94279c..c7df4b48b8681a 100644
--- a/Mathlib/CategoryTheory/Iso.lean
+++ b/Mathlib/CategoryTheory/Iso.lean
@@ -117,7 +117,6 @@ def refl (X : C) : X ≅ X where
hom := 𝟙 X
inv := 𝟙 X
-set_option linter.existingAttributeWarning false in
attribute [to_dual existing refl_inv] refl_hom
instance : Inhabited (X ≅ X) := ⟨Iso.refl X⟩
@@ -133,7 +132,6 @@ def trans (α : X ≅ Y) (β : Y ≅ Z) : X ≅ Z where
hom := α.hom ≫ β.hom
inv := β.inv ≫ α.inv
-set_option linter.existingAttributeWarning false in
attribute [to_dual existing trans_inv] trans_hom
@[simps]
@@ -473,7 +471,6 @@ def mapIso (F : C ⥤ D) {X Y : C} (i : X ≅ Y) : F.obj X ≅ F.obj Y where
hom := F.map i.hom
inv := F.map i.inv
-set_option linter.existingAttributeWarning false in
attribute [to_dual existing mapIso_inv] mapIso_hom
@[simp]
diff --git a/Mathlib/CategoryTheory/Monoidal/Grp.lean b/Mathlib/CategoryTheory/Monoidal/Grp.lean
index b532109706244c..c515ce6f7c0a53 100644
--- a/Mathlib/CategoryTheory/Monoidal/Grp.lean
+++ b/Mathlib/CategoryTheory/Monoidal/Grp.lean
@@ -57,7 +57,6 @@ namespace GrpObj
attribute [reassoc (attr := simp)] left_inv right_inv
attribute [reassoc (attr := simp)] AddGrpObj.left_neg AddGrpObj.right_neg
-set_option linter.existingAttributeWarning false in
attribute [to_additive existing] left_inv left_inv_assoc right_inv right_inv_assoc
@[to_additive]
diff --git a/Mathlib/CategoryTheory/NatIso.lean b/Mathlib/CategoryTheory/NatIso.lean
index 6737823ba25dc2..dac61bd6dd46be 100644
--- a/Mathlib/CategoryTheory/NatIso.lean
+++ b/Mathlib/CategoryTheory/NatIso.lean
@@ -55,7 +55,6 @@ def app {F G : C ⥤ D} (α : F ≅ G) (X : C) :
hom := α.hom.app X
inv := α.inv.app X
-set_option linter.existingAttributeWarning false in
attribute [to_dual existing app_inv] app_hom
@[reassoc +to_dual (attr := simp), grind =]
@@ -191,11 +190,9 @@ def ofComponents (app : ∀ X : C, F.obj X ≅ G.obj X)
exact h }
set_option linter.translateOverwrite false in
-set_option linter.existingAttributeWarning false in
attribute [to_dual existing ofComponents'_inv_app] ofComponents_hom_app
set_option linter.translateOverwrite false in
-set_option linter.existingAttributeWarning false in
attribute [to_dual existing ofComponents'_hom_app] ofComponents_inv_app
@[to_dual (attr := simp)]
@@ -214,7 +211,6 @@ def hcomp {F G : C ⥤ D} {H I : D ⥤ E} (α : F ≅ G) (β : H ≅ I) : F ⋙
hom := α.hom ◫ β.hom
inv := α.inv ◫ β.inv
-set_option linter.existingAttributeWarning false in
attribute [to_dual existing hcomp_inv] hcomp_hom
@[to_dual self]
diff --git a/Mathlib/Order/RelClasses.lean b/Mathlib/Order/RelClasses.lean
index 63bc0a93b2ab8d..93485384b10b30 100644
--- a/Mathlib/Order/RelClasses.lean
+++ b/Mathlib/Order/RelClasses.lean
@@ -661,7 +661,6 @@ but after translation `instReflLe` becomes `instReflGe : Std.Refl (· ≥ ·)`.
theorem Std.ge_refl {α : Type*} [LE α] [inst : @Std.Refl α (· ≥ ·)] (a : α) : a ≤ a :=
@Std.Refl.refl α (· ≥ ·) inst a
-set_option linter.existingAttributeWarning false in
attribute [to_dual existing Std.ge_refl] Std.le_refl
@[to_dual instIsTransGe]
diff --git a/Mathlib/Tactic/ToDual.lean b/Mathlib/Tactic/ToDual.lean
index 188b513bd39c32..b0b0650974b835 100644
--- a/Mathlib/Tactic/ToDual.lean
+++ b/Mathlib/Tactic/ToDual.lean
@@ -24,7 +24,6 @@ to_dual_insert_cast_fun DecidableLT := fun inst a b ↦ inst b a, fun inst a b
attribute [to_dual_do_translate] Empty PEmpty Unit PUnit
attribute [to_dual_ignore_args 2] Subtype
-set_option linter.existingAttributeWarning false in
attribute [to_dual self] ge_iff_le gt_iff_lt
attribute [to_dual le_of_eq_of_le''] le_of_eq_of_le
diff --git a/Mathlib/Tactic/Translate/Core.lean b/Mathlib/Tactic/Translate/Core.lean
index 39e16b28bd0583..c59c285f0cbe5f 100644
--- a/Mathlib/Tactic/Translate/Core.lean
+++ b/Mathlib/Tactic/Translate/Core.lean
@@ -917,7 +917,7 @@ def targetName (t : TranslateData) (cfg : Config) (src : Name) : CoreM Name := d
return src
if cfg.none then
if cfg.target != .anonymous then
- logWarning m!"`{t.attrName} private` ignores the provided name {cfg.target}"
+ logWarning m!"`{t.attrName} none` ignores the provided name {cfg.target}"
return ← withDeclNameForAuxNaming src do
mkAuxDeclName <| .mkSimple ("_" ++ t.attrName.toString)
-- When re-tagging an existing translation, simply return that existing translation.
@@ -1149,8 +1149,8 @@ partial def applyAttributes (t : TranslateData) (cfg : Config) (src tgt : Name)
(relevantArg : RelevantArg) : TermElabM (Array Name) := do
-- we only copy the `instance` attribute, since it is nice to directly tag `instance` declarations
copyInstanceAttribute src tgt
- -- Warn users if the original declaration has an attributee
- if !cfg.self && !cfg.none && linter.existingAttributeWarning.get (← getOptions) then
+ -- Warn users if the original declaration has an attribute
+ if !cfg.existing && !cfg.none && linter.existingAttributeWarning.get (← getOptions) then
let appliedAttrs ← getAllSimpAttrs src
if appliedAttrs.size > 0 then
let appliedAttrs := ", ".intercalate (appliedAttrs.toList.map toString)
From 4d0f80f9459e84794a0663e59280e78742331f3b Mon Sep 17 00:00:00 2001
From: Michael Rothgang <10105016+grunweg@users.noreply.github.com>
Date: Tue, 23 Jun 2026 08:28:07 +0000
Subject: [PATCH 0273/1300] feat: properly support inferring a model with
corners on a `Bundle.TotalSpace` (#40047)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Support inferring a model with corners on a `Bundle.TotalSpace`, both as the source and target.
The current logic was wrong and incomplete. Incomplete, since it only supported inferring a model on the codomain of a map, and wrong as the algorithm used for that had a logic bug.
Consider a function `f : N → TotalSpace F V`, from a manifold into the total space of a fibre bundle `V` over a manifold `M`. The correct model with corners to infer on the codomain is `I.prod (𝓘(𝕜, F))` --- where `I` is the model with corners on the base `M` of `V`. Previously, they would always use the model on the domain `N` instead of the model `I` on the bundle's base `M`. This works if `f` were a section of `V`, but is incorrect in general!
Instead, determine the model with corners in a principled way (by finding a model on the base): this works in all cases. It also removes the need for the `baseInfo` parameter.
---
.../Geometry/Manifold/ContMDiffMFDeriv.lean | 3 +-
Mathlib/Geometry/Manifold/MFDeriv/FDeriv.lean | 9 +-
.../Manifold/MFDeriv/UniqueDifferential.lean | 22 +--
Mathlib/Geometry/Manifold/Notation.lean | 102 +++++-----
.../Geometry/Manifold/VectorBundle/Hom.lean | 174 ++++++++----------
.../VectorBundle/MDifferentiable.lean | 46 ++---
.../Manifold/VectorBundle/Riemannian.lean | 40 ++--
.../Notation/PR40447.lean | 96 ++++++++++
8 files changed, 284 insertions(+), 208 deletions(-)
create mode 100644 MathlibTest/DifferentialGeometry/Notation/PR40447.lean
diff --git a/Mathlib/Geometry/Manifold/ContMDiffMFDeriv.lean b/Mathlib/Geometry/Manifold/ContMDiffMFDeriv.lean
index fae86ea50365d1..9109509853bc05 100644
--- a/Mathlib/Geometry/Manifold/ContMDiffMFDeriv.lean
+++ b/Mathlib/Geometry/Manifold/ContMDiffMFDeriv.lean
@@ -278,8 +278,7 @@ theorem ContMDiffOn.contMDiffOn_tangentMapWithin
let s' : Set (TangentBundle I M) := (π E (TangentSpace I) ⁻¹' s)
let b₁ : TangentBundle I M → M := fun p ↦ p.1
let v : Π (y : TangentBundle I M), TangentSpace% (b₁ y) := fun y ↦ y.2
- have hv : ContMDiffWithinAt I.tangent I.tangent m (fun y ↦ (v y : TangentBundle I M)) s' x₀ :=
- contMDiffWithinAt_id
+ have hv : CMDiffAt[s'] m (fun y ↦ (v y : TangentBundle I M)) x₀ := contMDiffWithinAt_id
let b₂ : TangentBundle I M → M' := f ∘ b₁
have hb₂ : CMDiffAt[s'] m b₂ x₀ :=
((hf (b₁ x₀) hx₀).of_le (le_self_add.trans hmn)).comp _
diff --git a/Mathlib/Geometry/Manifold/MFDeriv/FDeriv.lean b/Mathlib/Geometry/Manifold/MFDeriv/FDeriv.lean
index ad87a847c3abef..f0d5d10514ba8c 100644
--- a/Mathlib/Geometry/Manifold/MFDeriv/FDeriv.lean
+++ b/Mathlib/Geometry/Manifold/MFDeriv/FDeriv.lean
@@ -48,17 +48,18 @@ theorem ModelWithCorners.uniqueMDiffOn {H : Type*} [TopologicalSpace H]
theorem writtenInExtChartAt_model_space : writtenInExtChartAt 𝓘(𝕜, E) 𝓘(𝕜, E') x f = f :=
rfl
+variable {f' : TangentSpace 𝓘(𝕜, E) x →L[𝕜] TangentSpace 𝓘(𝕜, E') (f x)}
+
set_option backward.isDefEq.respectTransparency false in
-theorem hasMFDerivWithinAt_iff_hasFDerivWithinAt {f'} :
- HasMFDerivWithinAt 𝓘(𝕜, E) 𝓘(𝕜, E') f s x f' ↔ HasFDerivWithinAt f f' s x := by
+theorem hasMFDerivWithinAt_iff_hasFDerivWithinAt :
+ HasMFDerivAt[s] f x f' ↔ HasFDerivWithinAt f f' s x := by
simpa only [HasMFDerivWithinAt, and_iff_right_iff_imp, mfld_simps] using
HasFDerivWithinAt.continuousWithinAt
alias ⟨HasMFDerivWithinAt.hasFDerivWithinAt, HasFDerivWithinAt.hasMFDerivWithinAt⟩ :=
hasMFDerivWithinAt_iff_hasFDerivWithinAt
-theorem hasMFDerivAt_iff_hasFDerivAt {f'} :
- HasMFDerivAt 𝓘(𝕜, E) 𝓘(𝕜, E') f x f' ↔ HasFDerivAt f f' x := by
+theorem hasMFDerivAt_iff_hasFDerivAt : HasMFDerivAt% f x f' ↔ HasFDerivAt f f' x := by
rw [← hasMFDerivWithinAt_univ, hasMFDerivWithinAt_iff_hasFDerivWithinAt, hasFDerivWithinAt_univ]
alias ⟨HasMFDerivAt.hasFDerivAt, HasFDerivAt.hasMFDerivAt⟩ := hasMFDerivAt_iff_hasFDerivAt
diff --git a/Mathlib/Geometry/Manifold/MFDeriv/UniqueDifferential.lean b/Mathlib/Geometry/Manifold/MFDeriv/UniqueDifferential.lean
index 3e0ad20b32c3a2..f85c3efacc4755 100644
--- a/Mathlib/Geometry/Manifold/MFDeriv/UniqueDifferential.lean
+++ b/Mathlib/Geometry/Manifold/MFDeriv/UniqueDifferential.lean
@@ -139,7 +139,7 @@ variable {F : Type*} [NormedAddCommGroup F] [NormedSpace 𝕜 F] {Z : M → Type
set_option backward.isDefEq.respectTransparency false in
private lemma UniqueMDiffWithinAt.bundle_preimage_aux {p : TotalSpace F Z}
(hs : UniqueMDiffAt[s] p.proj) (h's : s ⊆ (trivializationAt F Z p.proj).baseSet) :
- UniqueMDiffWithinAt (I.prod 𝓘(𝕜, F)) (π F Z ⁻¹' s) p := by
+ UniqueMDiffAt[π F Z ⁻¹' s] p := by
suffices ((extChartAt I p.proj).symm ⁻¹' s ∩ range I) ×ˢ univ ⊆
(extChartAt (I.prod 𝓘(𝕜, F)) p).symm ⁻¹' (TotalSpace.proj ⁻¹' s) ∩ range (I.prod 𝓘(𝕜, F)) by
let w := (extChartAt (I.prod 𝓘(𝕜, F)) p p).2
@@ -169,27 +169,25 @@ private lemma UniqueMDiffWithinAt.bundle_preimage_aux {p : TotalSpace F Z}
/-- In a fiber bundle, the preimage under the projection of a set with unique differentials
in the base has unique differentials in the bundle. -/
-theorem UniqueMDiffWithinAt.bundle_preimage {p : TotalSpace F Z}
- (hs : UniqueMDiffAt[s] p.proj) :
- UniqueMDiffWithinAt (I.prod 𝓘(𝕜, F)) (π F Z ⁻¹' s) p := by
- suffices UniqueMDiffWithinAt (I.prod 𝓘(𝕜, F))
- (π F Z ⁻¹' (s ∩ (trivializationAt F Z p.proj).baseSet)) p from this.mono (by simp)
+theorem UniqueMDiffWithinAt.bundle_preimage {p : TotalSpace F Z} (hs : UniqueMDiffAt[s] p.proj) :
+ UniqueMDiffAt[π F Z ⁻¹' s] p := by
+ suffices UniqueMDiffAt[π F Z ⁻¹' (s ∩ (trivializationAt F Z p.proj).baseSet)] p from
+ this.mono (by simp)
apply UniqueMDiffWithinAt.bundle_preimage_aux (hs.inter _) inter_subset_right
- exact IsOpen.mem_nhds (trivializationAt F Z p.proj).open_baseSet
+ exact (trivializationAt F Z p.proj).open_baseSet.mem_nhds
(FiberBundle.mem_baseSet_trivializationAt' p.proj)
variable (Z)
/-- In a fiber bundle, the preimage under the projection of a set with unique differentials
in the base has unique differentials in the bundle. Version with a point `⟨b, x⟩`. -/
-theorem UniqueMDiffWithinAt.bundle_preimage' {b : M} (hs : UniqueMDiffAt[s] b)
- (x : Z b) : UniqueMDiffWithinAt (I.prod 𝓘(𝕜, F)) (π F Z ⁻¹' s) ⟨b, x⟩ :=
+theorem UniqueMDiffWithinAt.bundle_preimage' {b : M} (hs : UniqueMDiffAt[s] b) (x : Z b) :
+ UniqueMDiffAt[π F Z ⁻¹' s] ⟨b, x⟩ :=
hs.bundle_preimage (p := ⟨b, x⟩)
/-- In a fiber bundle, the preimage under the projection of a set with unique differentials
in the base has unique differentials in the bundle. -/
-theorem UniqueMDiffOn.bundle_preimage (hs : UniqueMDiff[s]) :
- UniqueMDiffOn (I.prod 𝓘(𝕜, F)) (π F Z ⁻¹' s) := fun _p hp ↦
- (hs _ hp).bundle_preimage
+theorem UniqueMDiffOn.bundle_preimage (hs : UniqueMDiff[s]) : UniqueMDiff[π F Z ⁻¹' s] :=
+ fun _p hp ↦ (hs _ hp).bundle_preimage
end UniqueMDiff
diff --git a/Mathlib/Geometry/Manifold/Notation.lean b/Mathlib/Geometry/Manifold/Notation.lean
index 0ed3b4eb3b185e..f5618f953402fb 100644
--- a/Mathlib/Geometry/Manifold/Notation.lean
+++ b/Mathlib/Geometry/Manifold/Notation.lean
@@ -331,6 +331,29 @@ private def tryStrategy (strategyDescr : MessageData) (x : TermElabM FindModelRe
s.restore true
return none
+/-- Given an `Expr`ession `e`, try to find a `NormedSpace` instance on `e` and return the
+underlying base field. Search local instances, before recursing into product and bundled
+continuous linear maps. -/
+partial def guessBaseFieldForNormedSpace (e : Expr) : TermElabM <| Option Expr := do
+ if let some k ← findFromLocalInstance e then return k
+ match_expr e with
+ | Prod E _F =>
+ guessBaseFieldForNormedSpace E
+ | _ =>
+ try
+ let (_k, E, _F) ← isCLMReduciblyDefeqCoefficients e
+ guessBaseFieldForNormedSpace E
+ catch _e =>
+ findFromLocalInstance e
+where findFromLocalInstance (e : Expr) : TermElabM <| Option Expr := do
+ findSomeLocalInstanceOf? ``NormedSpace fun _ type ↦ do
+ match_expr type with
+ | NormedSpace K E _ _ =>
+ if ← withReducible (pureIsDefEq E e) then
+ trace[Elab.DiffGeo.MDiff] "`{e}` is a normed field over `{K}`"; return some K
+ else return none
+ | _ => pure none
+
set_option linter.style.emptyLine false in -- linter false positive
/-- Try to find a `ModelWithCorners` instance on a type (represented by an expression `e`),
using the local context to infer the appropriate instance. This supports the following cases:
@@ -355,11 +378,6 @@ Return an expression describing the found model with corners, together with info
whether the model is the trivial model with corners on a normed space. (This is important for
forming products of models.)
-`baseInfo` is only used for the first case, a model with corners on the total space of the vector
-bundle. In this case, it contains a pair of expressions `(e, i)` describing the type of the base
-and the model with corners on the base: these are required to construct the right model with
-corners.
-
Note that the matching on `e` does not see through reducibility (e.g. we distinguish the `abbrev`
`TangentBundle` from its definition), so `whnfR` should not be run on `e` prior to calling
`findModel` on it.
@@ -368,8 +386,7 @@ This implementation is not maximally robust yet.
-/
-- TODO: better error messages when all strategies fail
-- TODO: consider lowering monad to `MetaM`
-def findModelInner (e : Expr) (baseInfo : Option (Expr × Expr) := none) :
- TermElabM (Option FindModelResult) := do
+partial def findModelInner (e : Expr) : TermElabM (Option FindModelResult) := do
if let some m ← tryStrategy "TotalSpace" fromTotalSpace then return some m
if let some m ← tryStrategy "TangentBundle" fromTangentBundle then return some m
if let some m ← tryStrategy "NormedSpace" fromNormedSpace then return some m
@@ -389,34 +406,36 @@ def findModelInner (e : Expr) (baseInfo : Option (Expr × Expr) := none) :
where
/- Note that errors thrown in the following are caught by `tryStrategy` and converted to trace
messages. -/
- /-- Attempt to find a model from a `TotalSpace` first by attempting to use any provided
- `baseInfo`, then by seeing if it is the total space of a tangent bundle. -/
+ /-- Attempt to find a model from a `TotalSpace` first by seeing if it is the total space of a
+ tangent bundle, and otherwise by finding a model with corners on its base. -/
fromTotalSpace : TermElabM FindModelResult := do
match_expr e with
| Bundle.TotalSpace _ F V => do
if let some m ← tryStrategy m!"TangentSpace" (fromTotalSpace.tangentSpace V) then return m
- if let some m ← tryStrategy m!"From base info" (fromTotalSpace.fromBaseInfo F) then return m
- throwError "Having a TotalSpace as source is not yet supported"
+ trace[Elab.DiffGeo.MDiff]
+ "{e} is the total space of a fiber bundle: trying to find a model on the base of `{V}`"
+ -- `V` should be of type `B → Type*`, where `B` is the base of the vector bundle.
+ -- Then, the desired model with corners is `I.prod (𝓘(𝕜, F))`, where `I` is the model on `B`
+ -- and `𝕜` is the base field for `F`.
+ let vtype ← whnf <| ← instantiateMVars <| ← inferType V
+ trace[Elab.DiffGeo.MDiff] "`{V}` has type `{vtype}`"
+ match vtype with
+ | .forallE _x base _tgt _ =>
+ let baseModel ← withTraceNode `Elab.DiffGeo.MDiff
+ (fun _ ↦ pure m!"searching for a model with corners on the base `{base}`") do
+ let some baseI ← findModelInner base
+ | throwError m!"found no model with corners on the base {base} of `TotalSpace {F} {V}`"
+ return baseI.model
+ -- Very likely, `F` is a normed space over some field: let's see if `F` is a normed space
+ -- on the nose.
+ let some K ← guessBaseFieldForNormedSpace F
+ | throwError "Couldn't find a `NormedSpace` structure on `{F}`"
+ let tgtMod ← mkAppOptM ``modelWithCornersSelf #[K, none, F, none, none]
+ mkAppM ``ModelWithCorners.prod #[baseModel, tgtMod]
+ | _ =>
+ throwError s!"{e} is a TotalSpace {F} {V}, but {V} is not a pi type --- \
+ could not infer base of the bundle"
| _ => throwError "`{e}` is not a `Bundle.TotalSpace`."
- /-- Attempt to use the provided `baseInfo` to find a model. -/
- fromTotalSpace.fromBaseInfo (F : Expr) : TermElabM Expr := do
- if let some (src, srcI) := baseInfo then
- trace[Elab.DiffGeo.MDiff] "Using base info `{src}`, `{srcI}`"
- let some K ← findSomeLocalInstanceOf? ``NormedSpace fun _ type ↦ do
- match_expr type with
- | NormedSpace K E _ _ =>
- if ← withReducible (pureIsDefEq E F) then
- trace[Elab.DiffGeo.MDiff] "`{F}` is a normed field over `{K}`"; return some K
- else return none
- | _ => return none
- | throwError "Couldn't find a `NormedSpace` structure on `{F}` among local instances."
- let kT : Term ← Term.exprToSyntax K
- let srcIT : Term ← Term.exprToSyntax srcI
- let FT : Term ← Term.exprToSyntax F
- let iTerm : Term ← ``(ModelWithCorners.prod $srcIT 𝓘($kT, $FT))
- Term.elabTerm iTerm none
- else
- throwError "No `baseInfo` provided"
/-- Attempt to find a model from the total space of a tangent bundle. -/
fromTotalSpace.tangentSpace (V : Expr) : TermElabM Expr := do
match_expr V with
@@ -719,11 +738,6 @@ Further cases can be added as necessary.
Return an expression describing the found model with corners.
-`baseInfo` is only used for the first case, a model with corners on the total space of the vector
-bundle. In this case, it contains a pair of expressions `(e, i)` describing the type of the base
-and the model with corners on the base: these are required to construct the right model with
-corners.
-
Note that the matching on `e` does not see through reducibility (e.g. we distinguish the `abbrev`
`TangentBundle` from its definition), so `whnfR` should not be run on `e` prior to calling
`findModel` on it.
@@ -737,9 +751,9 @@ This implementation is not maximally robust yet.
-- This should not be an issue in practice.
-- FIXME: can one prove this terminates w.r.t. a suitable measure? This is only recursing into
-- subexpressions (at least, after match_expr), right?
-partial def findModel (e : Expr) (baseInfo : Option (Expr × Expr) := none) : TermElabM Expr := do
+partial def findModel (e : Expr) : TermElabM Expr := do
trace[Elab.DiffGeo.MDiff] "Finding a model with corners for: `{e}`"
- if let some { model .. } ← go e baseInfo then
+ if let some { model .. } ← go e then
return model
else
let tracing := (← isTracingEnabledFor `Elab.DiffGeo.MDiff)
@@ -751,9 +765,9 @@ partial def findModel (e : Expr) (baseInfo : Option (Expr × Expr) := none) : Te
command `set_option trace.Elab.DiffGeo.MDiff true`."
throwError "Could not find a model with corners for `{e}`.{hint}"
where
- go (e : Expr) (baseInfo : Option (Expr × Expr)) : TermElabM (Option FindModelResult) := do
+ go (e : Expr) : TermElabM (Option FindModelResult) := do
-- At first, try finding a model with corners on the space itself.
- if let some m ← findModelInner e baseInfo then return some m
+ if let some m ← findModelInner e then return some m
-- Otherwise, we recurse into the expression,
-- depending whether we have an open subset of a space, a product, or a direct sum of spaces.
match_expr e with
@@ -774,16 +788,16 @@ where
trace[Elab.DiffGeo.MDiff] "`{e}` is an open set of `{M}`, finding a model on `{M}`"
-- `M` is not a open set of another manifold, as `Opens X` is (currently) not a
-- topological space (and this would be strange). Therefore, do not recurse into `M`.
- go M baseInfo
+ go M
| _ => return none
| _ => return none
| _ => return none
| _ => return none
| Prod E F =>
trace[Elab.DiffGeo.MDiff] "Expression `{e}` is a product, recursing into each factor"
- let some { model := srcE, normedSpaceInfo? := normedSpaceE } ← go E baseInfo
+ let some { model := srcE, normedSpaceInfo? := normedSpaceE } ← go E
| throwError "Found no model with corners on first factor `{E}`"
- let some { model := srcF, normedSpaceInfo? := normedSpaceF } ← go F baseInfo
+ let some { model := srcF, normedSpaceInfo? := normedSpaceF } ← go F
| throwError "Found no model with corners on second factor `{F}`"
-- If both E and F are normed spaces, we have ambiguity: warn and exit.
if normedSpaceE.isSome && normedSpaceF.isSome then
@@ -797,7 +811,7 @@ where
| Sum E F =>
trace[Elab.DiffGeo.MDiff] "Expression `{e}` is a direct sum of `{E}` and `{F}`\n\
We assume the models match, and only look into the first summand"
- go E baseInfo
+ go E
| _ => return none
/-- If the type of `e` is a non-dependent function between spaces `src` and `tgt`, try to find a
@@ -823,7 +837,7 @@ def findModels (e : Expr) (es : Option Expr) : TermElabM (Expr × Expr) := do
if !(← isDefEq estype <| ← mkAppM ``Set #[src]) then
throwError "The domain `{src}` of `{e}` is not definitionally equal to the carrier type of \
the set `{es}` : `{estype}`"
- let tgtI ← findModel tgt (src, srcI)
+ let tgtI ← findModel tgt
return (srcI, tgtI)
| _ => throwError "Expected{indentD e}\nof type{indentD etype}\nto be a function"
diff --git a/Mathlib/Geometry/Manifold/VectorBundle/Hom.lean b/Mathlib/Geometry/Manifold/VectorBundle/Hom.lean
index 20f7437e4eace3..654aab97adf6ee 100644
--- a/Mathlib/Geometry/Manifold/VectorBundle/Hom.lean
+++ b/Mathlib/Geometry/Manifold/VectorBundle/Hom.lean
@@ -54,9 +54,8 @@ theorem contMDiffOn_continuousLinearMapCoordChange
[ContMDiffVectorBundle n F₁ E₁ IB] [ContMDiffVectorBundle n F₂ E₂ IB]
[MemTrivializationAtlas e₁] [MemTrivializationAtlas e₁']
[MemTrivializationAtlas e₂] [MemTrivializationAtlas e₂'] :
- ContMDiffOn IB 𝓘(𝕜, (F₁ →L[𝕜] F₂) →L[𝕜] F₁ →L[𝕜] F₂) n
- (continuousLinearMapCoordChange (RingHom.id 𝕜) e₁ e₁' e₂ e₂')
- (e₁.baseSet ∩ e₂.baseSet ∩ (e₁'.baseSet ∩ e₂'.baseSet)) := by
+ CMDiff[e₁.baseSet ∩ e₂.baseSet ∩ (e₁'.baseSet ∩ e₂'.baseSet)] n
+ (continuousLinearMapCoordChange (RingHom.id 𝕜) e₁ e₁' e₂ e₂') := by
have h₁ := contMDiffOn_coordChangeL (IB := IB) e₁' e₁ (n := n)
have h₂ := contMDiffOn_coordChangeL (IB := IB) e₂ e₂' (n := n)
refine (h₁.mono ?_).cle_arrowCongr (h₂.mono ?_) <;> mfld_set_tac
@@ -72,14 +71,14 @@ theorem hom_chart (y₀ y : LE₁E₂) :
hom_trivializationAt_apply]
theorem contMDiffWithinAt_hom_bundle (f : M → LE₁E₂) {s : Set M} {x₀ : M} :
- ContMDiffWithinAt IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂)) n f s x₀ ↔
+ CMDiffAt[s] n f x₀ ↔
CMDiffAt[s] n (fun x ↦ (f x).1) x₀ ∧
CMDiffAt[s] n
(fun x ↦ inCoordinates F₁ E₁ F₂ E₂ (f x₀).1 (f x).1 (f x₀).1 (f x).1 (f x).2) x₀ :=
contMDiffWithinAt_totalSpace
theorem contMDiffAt_hom_bundle (f : M → LE₁E₂) {x₀ : M} :
- ContMDiffAt IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂)) n f x₀ ↔
+ CMDiffAt n f x₀ ↔
CMDiffAt n (fun x ↦ (f x).1) x₀ ∧ CMDiffAt n
(fun x ↦ inCoordinates F₁ E₁ F₂ E₂ (f x₀).1 (f x).1 (f x₀).1 (f x).1 (f x).2) x₀ :=
contMDiffAt_totalSpace
@@ -101,14 +100,14 @@ theorem mdifferentiableOn_continuousLinearMapCoordChange
variable [∀ x, IsTopologicalAddGroup (E₂ x)] [∀ x, ContinuousSMul 𝕜 (E₂ x)]
theorem mdifferentiableWithinAt_hom_bundle (f : M → LE₁E₂) {s : Set M} {x₀ : M} :
- MDifferentiableWithinAt IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂)) f s x₀ ↔
+ MDiffAt[s] f x₀ ↔
MDiffAt[s] (fun x ↦ (f x).1) x₀ ∧
MDiffAt[s]
(fun x ↦ inCoordinates F₁ E₁ F₂ E₂ (f x₀).1 (f x).1 (f x₀).1 (f x).1 (f x).2) x₀ :=
mdifferentiableWithinAt_totalSpace IB ..
theorem mdifferentiableAt_hom_bundle (f : M → LE₁E₂) {x₀ : M} :
- MDifferentiableAt IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂)) f x₀ ↔
+ MDiffAt f x₀ ↔
MDiffAt (fun x ↦ (f x).1) x₀ ∧
MDiffAt (fun x ↦ inCoordinates F₁ E₁ F₂ E₂ (f x₀).1 (f x).1 (f x₀).1 (f x).1 (f x).2) x₀ :=
mdifferentiableAt_totalSpace ..
@@ -178,9 +177,8 @@ For a version with `B₁ = B₂` and `b₁ = b₂`, in which smoothness can be e
lemma ContMDiffWithinAt.clm_apply_of_inCoordinates
(hϕ : CMDiffAt[s] n
(fun m ↦ inCoordinates F₁ E₁ F₂ E₂ (b₁ m₀) (b₁ m) (b₂ m₀) (b₂ m) (ϕ m)) m₀)
- (hv : ContMDiffWithinAt IM (IB₁.prod 𝓘(𝕜, F₁)) n (fun m ↦ (v m : TotalSpace F₁ E₁)) s m₀)
- (hb₂ : CMDiffAt[s] n b₂ m₀) :
- ContMDiffWithinAt IM (IB₂.prod 𝓘(𝕜, F₂)) n (fun m ↦ (ϕ m (v m) : TotalSpace F₂ E₂)) s m₀ := by
+ (hv : CMDiffAt[s] n (fun m ↦ (v m : TotalSpace F₁ E₁)) m₀) (hb₂ : CMDiffAt[s] n b₂ m₀) :
+ CMDiffAt[s] n (fun m ↦ (ϕ m (v m) : TotalSpace F₂ E₂)) m₀ := by
rw [← contMDiffWithinAt_insert_self] at hϕ hv hb₂ ⊢
rw [contMDiffWithinAt_totalSpace] at hv ⊢
refine ⟨hb₂, ?_⟩
@@ -215,9 +213,8 @@ For a version with `B₁ = B₂` and `b₁ = b₂`, in which smoothness can be e
-/
lemma ContMDiffAt.clm_apply_of_inCoordinates
(hϕ : CMDiffAt n (fun m ↦ inCoordinates F₁ E₁ F₂ E₂ (b₁ m₀) (b₁ m) (b₂ m₀) (b₂ m) (ϕ m)) m₀)
- (hv : ContMDiffAt IM (IB₁.prod 𝓘(𝕜, F₁)) n (fun m ↦ (v m : TotalSpace F₁ E₁)) m₀)
- (hb₂ : CMDiffAt n b₂ m₀) :
- ContMDiffAt IM (IB₂.prod 𝓘(𝕜, F₂)) n (fun m ↦ (ϕ m (v m) : TotalSpace F₂ E₂)) m₀ := by
+ (hv : CMDiffAt n (fun m ↦ (v m : TotalSpace F₁ E₁)) m₀) (hb₂ : CMDiffAt n b₂ m₀) :
+ CMDiffAt n (fun m ↦ (ϕ m (v m) : TotalSpace F₂ E₂)) m₀ := by
rw [← contMDiffWithinAt_univ] at hϕ hv hb₂ ⊢
exact ContMDiffWithinAt.clm_apply_of_inCoordinates hϕ hv hb₂
@@ -261,12 +258,10 @@ One can apply `ϕ m` to `v m`, and the resulting map is `C^n`.
We give here a version of this statement within a set at a point. -/
lemma ContMDiffWithinAt.clm_bundle_apply
- (hϕ : ContMDiffWithinAt IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂)) n
- (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x)) (b m) (ϕ m))
- s x)
- (hv : ContMDiffWithinAt IM (IB.prod 𝓘(𝕜, F₁)) n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) s x) :
- ContMDiffWithinAt IM (IB.prod 𝓘(𝕜, F₂)) n
- (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) s x := by
+ (hϕ : CMDiffAt[s] n
+ (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x)) (b m) (ϕ m)) x)
+ (hv : CMDiffAt[s] n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) x) :
+ CMDiffAt[s] n (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) x := by
simp only [contMDiffWithinAt_hom_bundle] at hϕ
exact hϕ.2.clm_apply_of_inCoordinates hv hϕ.1
@@ -276,10 +271,10 @@ One can apply `ϕ m` to `v m`, and the resulting map is `C^n`.
We give here a version of this statement at a point. -/
lemma ContMDiffAt.clm_bundle_apply
- (hϕ : ContMDiffAt IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂)) n
+ (hϕ : CMDiffAt n
(fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x)) (b m) (ϕ m)) x)
- (hv : ContMDiffAt IM (IB.prod 𝓘(𝕜, F₁)) n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) x) :
- ContMDiffAt IM (IB.prod 𝓘(𝕜, F₂)) n (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) x :=
+ (hv : CMDiffAt n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) x) :
+ CMDiffAt n (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) x :=
ContMDiffWithinAt.clm_bundle_apply hϕ hv
/-- Consider a `C^n` map `v : M → E₁` to a vector bundle, over a base map `b : M → B`, and
@@ -288,20 +283,20 @@ One can apply `ϕ m` to `v m`, and the resulting map is `C^n`.
We give here a version of this statement on a set. -/
lemma ContMDiffOn.clm_bundle_apply
- (hϕ : ContMDiffOn IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂)) n
- (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x)) (b m) (ϕ m)) s)
- (hv : ContMDiffOn IM (IB.prod 𝓘(𝕜, F₁)) n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) s) :
- ContMDiffOn IM (IB.prod 𝓘(𝕜, F₂)) n (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) s :=
+ (hϕ : CMDiff[s] n
+ (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x)) (b m) (ϕ m)))
+ (hv : CMDiff[s] n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m))) :
+ CMDiff[s] n (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) :=
fun x hx ↦ (hϕ x hx).clm_bundle_apply (hv x hx)
/-- Consider a `C^n` map `v : M → E₁` to a vector bundle, over a base map `b : M → B`, and
linear maps `ϕ m : E₁ (b m) → E₂ (b m)` depending smoothly on `m`.
One can apply `ϕ m` to `v m`, and the resulting map is `C^n`. -/
lemma ContMDiff.clm_bundle_apply
- (hϕ : ContMDiff IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂)) n
+ (hϕ : CMDiff n
(fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x)) (b m) (ϕ m)))
- (hv : ContMDiff IM (IB.prod 𝓘(𝕜, F₁)) n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m))) :
- ContMDiff IM (IB.prod 𝓘(𝕜, F₂)) n (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) :=
+ (hv : CMDiff n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m))) :
+ CMDiff n (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) :=
fun x ↦ (hϕ x).clm_bundle_apply (hv x)
end OneVariable
@@ -317,13 +312,10 @@ One can apply `ϕ m` to `v m`, and the resulting map is differentiable.
We give here a version of this statement within a set at a point. -/
lemma MDifferentiableWithinAt.clm_bundle_apply
- (hϕ : MDifferentiableWithinAt IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂))
- (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x)) (b m) (ϕ m))
- s x)
- (hv : MDifferentiableWithinAt IM (IB.prod 𝓘(𝕜, F₁))
- (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) s x) :
- MDifferentiableWithinAt IM (IB.prod 𝓘(𝕜, F₂))
- (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) s x := by
+ (hϕ : MDiffAt[s]
+ (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x)) (b m) (ϕ m)) x)
+ (hv : MDiffAt[s] (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) x) :
+ MDiffAt[s] (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) x := by
simp only [mdifferentiableWithinAt_hom_bundle] at hϕ
exact hϕ.2.clm_apply_of_inCoordinates hv hϕ.1
@@ -333,10 +325,10 @@ One can apply `ϕ m` to `v m`, and the resulting map is differentiable.
We give here a version of this statement at a point. -/
lemma MDifferentiableAt.clm_bundle_apply
- (hϕ : MDifferentiableAt IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂))
+ (hϕ : MDiffAt
(fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x)) (b m) (ϕ m)) x)
- (hv : MDifferentiableAt IM (IB.prod 𝓘(𝕜, F₁)) (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) x) :
- MDifferentiableAt IM (IB.prod 𝓘(𝕜, F₂)) (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) x :=
+ (hv : MDiffAt (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) x) :
+ MDiffAt (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) x :=
MDifferentiableWithinAt.clm_bundle_apply hϕ hv
/-- Consider a differentiable map `v : M → E₁` to a vector bundle, over a base map `b : M → B`, and
@@ -345,20 +337,20 @@ One can apply `ϕ m` to `v m`, and the resulting map is differentiable.
We give here a version of this statement on a set. -/
lemma MDifferentiableOn.clm_bundle_apply
- (hϕ : MDifferentiableOn IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂))
- (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x)) (b m) (ϕ m)) s)
- (hv : MDifferentiableOn IM (IB.prod 𝓘(𝕜, F₁)) (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) s) :
- MDifferentiableOn IM (IB.prod 𝓘(𝕜, F₂)) (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) s :=
+ (hϕ : MDiff[s]
+ (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x)) (b m) (ϕ m)))
+ (hv : MDiff[s] (fun m ↦ TotalSpace.mk' F₁ (b m) (v m))) :
+ MDiff[s] (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) :=
fun x hx ↦ (hϕ x hx).clm_bundle_apply (hv x hx)
/-- Consider a differentiable map `v : M → E₁` to a vector bundle, over a base map `b : M → B`, and
linear maps `ϕ m : E₁ (b m) → E₂ (b m)` depending smoothly on `m`.
One can apply `ϕ m` to `v m`, and the resulting map is differentiable. -/
lemma MDifferentiable.clm_bundle_apply
- (hϕ : MDifferentiable IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂))
+ (hϕ : MDiff
(fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x)) (b m) (ϕ m)))
- (hv : MDifferentiable IM (IB.prod 𝓘(𝕜, F₁)) (fun m ↦ TotalSpace.mk' F₁ (b m) (v m))) :
- MDifferentiable IM (IB.prod 𝓘(𝕜, F₂)) (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) :=
+ (hv : MDiff (fun m ↦ TotalSpace.mk' F₁ (b m) (v m))) :
+ MDiff (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) :=
fun x ↦ (hϕ x).clm_bundle_apply (hv x)
end OneVariable'
@@ -374,13 +366,11 @@ One can apply `ψ m` to `v m` and `w m`, and the resulting map is `C^n`.
We give here a version of this statement within a set at a point. -/
lemma ContMDiffWithinAt.clm_bundle_apply₂
- (hψ : ContMDiffWithinAt IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂ →L[𝕜] F₃)) n
- (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃)
- (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x →L[𝕜] E₃ x)) (b m) (ψ m)) s x)
- (hv : ContMDiffWithinAt IM (IB.prod 𝓘(𝕜, F₁)) n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) s x)
- (hw : ContMDiffWithinAt IM (IB.prod 𝓘(𝕜, F₂)) n (fun m ↦ TotalSpace.mk' F₂ (b m) (w m)) s x) :
- ContMDiffWithinAt IM (IB.prod 𝓘(𝕜, F₃)) n
- (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) s x :=
+ (hψ : CMDiffAt[s] n (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃)
+ (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x →L[𝕜] E₃ x)) (b m) (ψ m)) x)
+ (hv : CMDiffAt[s] n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) x)
+ (hw : CMDiffAt[s] n (fun m ↦ TotalSpace.mk' F₂ (b m) (w m)) x) :
+ CMDiffAt[s] n (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) x :=
hψ.clm_bundle_apply hv |>.clm_bundle_apply hw
/-- Consider `C^n` maps `v : M → E₁` and `v : M → E₂` to vector bundles, over a base map
@@ -389,13 +379,11 @@ One can apply `ψ m` to `v m` and `w m`, and the resulting map is `C^n`.
We give here a version of this statement at a point. -/
lemma ContMDiffAt.clm_bundle_apply₂
- (hψ : ContMDiffAt IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂ →L[𝕜] F₃)) n
- (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃)
+ (hψ : CMDiffAt n (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃)
(E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x →L[𝕜] E₃ x)) (b m) (ψ m)) x)
- (hv : ContMDiffAt IM (IB.prod 𝓘(𝕜, F₁)) n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) x)
- (hw : ContMDiffAt IM (IB.prod 𝓘(𝕜, F₂)) n (fun m ↦ TotalSpace.mk' F₂ (b m) (w m)) x) :
- ContMDiffAt IM (IB.prod 𝓘(𝕜, F₃)) n
- (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) x :=
+ (hv : CMDiffAt n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) x)
+ (hw : CMDiffAt n (fun m ↦ TotalSpace.mk' F₂ (b m) (w m)) x) :
+ CMDiffAt n (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) x :=
ContMDiffWithinAt.clm_bundle_apply₂ hψ hv hw
/-- Consider `C^n` maps `v : M → E₁` and `v : M → E₂` to vector bundles, over a base map
@@ -404,26 +392,22 @@ One can apply `ψ m` to `v m` and `w m`, and the resulting map is `C^n`.
We give here a version of this statement on a set. -/
lemma ContMDiffOn.clm_bundle_apply₂
- (hψ : ContMDiffOn IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂ →L[𝕜] F₃)) n
- (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃)
- (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x →L[𝕜] E₃ x)) (b m) (ψ m)) s)
- (hv : ContMDiffOn IM (IB.prod 𝓘(𝕜, F₁)) n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) s)
- (hw : ContMDiffOn IM (IB.prod 𝓘(𝕜, F₂)) n (fun m ↦ TotalSpace.mk' F₂ (b m) (w m)) s) :
- ContMDiffOn IM (IB.prod 𝓘(𝕜, F₃)) n
- (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) s :=
+ (hψ : CMDiff[s] n (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃)
+ (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x →L[𝕜] E₃ x)) (b m) (ψ m)))
+ (hv : CMDiff[s] n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)))
+ (hw : CMDiff[s] n (fun m ↦ TotalSpace.mk' F₂ (b m) (w m))) :
+ CMDiff[s] n (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) :=
fun x hx ↦ (hψ x hx).clm_bundle_apply₂ (hv x hx) (hw x hx)
/-- Consider `C^n` maps `v : M → E₁` and `v : M → E₂` to vector bundles, over a base map
`b : M → B`, and bilinear maps `ψ m : E₁ (b m) → E₂ (b m) → E₃ (b m)` depending smoothly on `m`.
One can apply `ψ m` to `v m` and `w m`, and the resulting map is `C^n`. -/
lemma ContMDiff.clm_bundle_apply₂
- (hψ : ContMDiff IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂ →L[𝕜] F₃)) n
- (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃)
+ (hψ : CMDiff n (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃)
(E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x →L[𝕜] E₃ x)) (b m) (ψ m)))
- (hv : ContMDiff IM (IB.prod 𝓘(𝕜, F₁)) n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)))
- (hw : ContMDiff IM (IB.prod 𝓘(𝕜, F₂)) n (fun m ↦ TotalSpace.mk' F₂ (b m) (w m))) :
- ContMDiff IM (IB.prod 𝓘(𝕜, F₃)) n
- (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) :=
+ (hv : CMDiff n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)))
+ (hw : CMDiff n (fun m ↦ TotalSpace.mk' F₂ (b m) (w m))) :
+ CMDiff n (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) :=
fun x ↦ (hψ x).clm_bundle_apply₂ (hv x) (hw x)
end TwoVariables
@@ -439,15 +423,11 @@ One can apply `ψ m` to `v m` and `w m`, and the resulting map is differentiabl
We give here a version of this statement within a set at a point. -/
lemma MDifferentiableWithinAt.clm_bundle_apply₂
- (hψ : MDifferentiableWithinAt IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂ →L[𝕜] F₃))
- (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃)
- (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x →L[𝕜] E₃ x)) (b m) (ψ m)) s x)
- (hv : MDifferentiableWithinAt IM (IB.prod 𝓘(𝕜, F₁))
- (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) s x)
- (hw : MDifferentiableWithinAt IM (IB.prod 𝓘(𝕜, F₂))
- (fun m ↦ TotalSpace.mk' F₂ (b m) (w m)) s x) :
- MDifferentiableWithinAt IM (IB.prod 𝓘(𝕜, F₃))
- (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) s x :=
+ (hψ : MDiffAt[s] (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃)
+ (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x →L[𝕜] E₃ x)) (b m) (ψ m)) x)
+ (hv : MDiffAt[s] (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) x)
+ (hw : MDiffAt[s] (fun m ↦ TotalSpace.mk' F₂ (b m) (w m)) x) :
+ MDiffAt[s] (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) x :=
hψ.clm_bundle_apply hv |>.clm_bundle_apply hw
/-- Consider differentiable maps `v : M → E₁` and `v : M → E₂` to vector bundles, over a base map
@@ -456,13 +436,11 @@ One can apply `ψ m` to `v m` and `w m`, and the resulting map is differentiabl
We give here a version of this statement at a point. -/
lemma MDifferentiableAt.clm_bundle_apply₂
- (hψ : MDifferentiableAt IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂ →L[𝕜] F₃))
- (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃)
+ (hψ : MDiffAt (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃)
(E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x →L[𝕜] E₃ x)) (b m) (ψ m)) x)
- (hv : MDifferentiableAt IM (IB.prod 𝓘(𝕜, F₁)) (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) x)
- (hw : MDifferentiableAt IM (IB.prod 𝓘(𝕜, F₂)) (fun m ↦ TotalSpace.mk' F₂ (b m) (w m)) x) :
- MDifferentiableAt IM (IB.prod 𝓘(𝕜, F₃))
- (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) x :=
+ (hv : MDiffAt (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) x)
+ (hw : MDiffAt (fun m ↦ TotalSpace.mk' F₂ (b m) (w m)) x) :
+ MDiffAt (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) x :=
MDifferentiableWithinAt.clm_bundle_apply₂ hψ hv hw
/-- Consider differentiable maps `v : M → E₁` and `v : M → E₂` to vector bundles, over a base map
@@ -471,26 +449,22 @@ One can apply `ψ m` to `v m` and `w m`, and the resulting map is differentiabl
We give here a version of this statement on a set. -/
lemma MDifferentiableOn.clm_bundle_apply₂
- (hψ : MDifferentiableOn IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂ →L[𝕜] F₃))
- (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃)
- (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x →L[𝕜] E₃ x)) (b m) (ψ m)) s)
- (hv : MDifferentiableOn IM (IB.prod 𝓘(𝕜, F₁)) (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) s)
- (hw : MDifferentiableOn IM (IB.prod 𝓘(𝕜, F₂)) (fun m ↦ TotalSpace.mk' F₂ (b m) (w m)) s) :
- MDifferentiableOn IM (IB.prod 𝓘(𝕜, F₃))
- (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) s :=
+ (hψ : MDiff[s] (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃)
+ (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x →L[𝕜] E₃ x)) (b m) (ψ m)))
+ (hv : MDiff[s] (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)))
+ (hw : MDiff[s] (fun m ↦ TotalSpace.mk' F₂ (b m) (w m))) :
+ MDiff[s] (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) :=
fun x hx ↦ (hψ x hx).clm_bundle_apply₂ (hv x hx) (hw x hx)
/-- Consider differentiable maps `v : M → E₁` and `v : M → E₂` to vector bundles, over a base map
`b : M → B`, and bilinear maps `ψ m : E₁ (b m) → E₂ (b m) → E₃ (b m)` depending smoothly on `m`.
One can apply `ψ m` to `v m` and `w m`, and the resulting map is differentiable. -/
lemma MDifferentiable.clm_bundle_apply₂
- (hψ : MDifferentiable IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂ →L[𝕜] F₃))
- (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃)
+ (hψ : MDiff (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃)
(E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x →L[𝕜] E₃ x)) (b m) (ψ m)))
- (hv : MDifferentiable IM (IB.prod 𝓘(𝕜, F₁)) (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)))
- (hw : MDifferentiable IM (IB.prod 𝓘(𝕜, F₂)) (fun m ↦ TotalSpace.mk' F₂ (b m) (w m))) :
- MDifferentiable IM (IB.prod 𝓘(𝕜, F₃))
- (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) :=
+ (hv : MDiff (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)))
+ (hw : MDiff (fun m ↦ TotalSpace.mk' F₂ (b m) (w m))) :
+ MDiff (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) :=
fun x ↦ (hψ x).clm_bundle_apply₂ (hv x) (hw x)
end TwoVariables'
diff --git a/Mathlib/Geometry/Manifold/VectorBundle/MDifferentiable.lean b/Mathlib/Geometry/Manifold/VectorBundle/MDifferentiable.lean
index b47e572fc3d9b8..2d426f73f5030c 100644
--- a/Mathlib/Geometry/Manifold/VectorBundle/MDifferentiable.lean
+++ b/Mathlib/Geometry/Manifold/VectorBundle/MDifferentiable.lean
@@ -39,7 +39,7 @@ variable [TopologicalSpace B] [ChartedSpace HB B] [FiberBundle F E]
/-- Characterization of differentiable functions into a vector bundle.
Version at a point within a set -/
theorem mdifferentiableWithinAt_totalSpace (f : M → TotalSpace F E) {s : Set M} {x₀ : M} :
- MDifferentiableWithinAt IM (IB.prod 𝓘(𝕜, F)) f s x₀ ↔
+ MDiffAt[s] f x₀ ↔
MDiffAt[s] (fun x => (f x).proj) x₀ ∧
MDiffAt[s] (fun x ↦ (trivializationAt F E (f x₀).proj (f x)).2) x₀ := by
simp +singlePass only [mdifferentiableWithinAt_iff_target]
@@ -61,7 +61,7 @@ theorem mdifferentiableWithinAt_totalSpace (f : M → TotalSpace F E) {s : Set M
/-- Characterization of differentiable functions into a vector bundle.
Version at a point -/
theorem mdifferentiableAt_totalSpace (f : M → TotalSpace F E) {x₀ : M} :
- MDifferentiableAt IM (IB.prod 𝓘(𝕜, F)) f x₀ ↔
+ MDiffAt f x₀ ↔
MDiffAt (fun x => (f x).proj) x₀ ∧
MDiffAt (fun x ↦ (trivializationAt F E (f x₀).proj (f x)).2) x₀ := by
simpa [← mdifferentiableWithinAt_univ] using mdifferentiableWithinAt_totalSpace _ f
@@ -69,8 +69,7 @@ theorem mdifferentiableAt_totalSpace (f : M → TotalSpace F E) {x₀ : M} :
/-- Characterization of differentiable sections of a vector bundle at a point within a set
in terms of the preferred trivialization at that point. -/
theorem mdifferentiableWithinAt_section (s : Π b, E b) {u : Set B} {b₀ : B} :
- MDifferentiableWithinAt IB (IB.prod 𝓘(𝕜, F)) (T% s) u b₀ ↔
- MDiffAt[u] (fun b ↦ (trivializationAt F E b₀ (s b)).2) b₀ := by
+ MDiffAt[u] (T% s) b₀ ↔ MDiffAt[u] (fun b ↦ (trivializationAt F E b₀ (s b)).2) b₀ := by
rw [mdifferentiableWithinAt_totalSpace]
change MDifferentiableWithinAt _ _ id _ _ ∧ _ ↔ _
simp [mdifferentiableWithinAt_id]
@@ -85,21 +84,19 @@ namespace Bundle
variable (E) {IB}
-theorem mdifferentiable_proj : MDifferentiable (IB.prod 𝓘(𝕜, F)) IB (π F E) := fun x ↦ by
- have : MDifferentiableAt (IB.prod 𝓘(𝕜, F)) (IB.prod 𝓘(𝕜, F)) id x := mdifferentiableAt_id
+theorem mdifferentiable_proj : MDiff (π F E) := fun x ↦ by
+ have : MDiffAt (@id <| TotalSpace F E) x := mdifferentiableAt_id
rw [mdifferentiableAt_totalSpace] at this
exact this.1
-theorem mdifferentiableOn_proj {s : Set (TotalSpace F E)} :
- MDifferentiableOn (IB.prod 𝓘(𝕜, F)) IB (π F E) s :=
+theorem mdifferentiableOn_proj {s : Set (TotalSpace F E)} : MDiff[s] (π F E) :=
(mdifferentiable_proj E).mdifferentiableOn
-theorem mdifferentiableAt_proj {p : TotalSpace F E} :
- MDifferentiableAt (IB.prod 𝓘(𝕜, F)) IB (π F E) p :=
+theorem mdifferentiableAt_proj {p : TotalSpace F E} : MDiffAt (π F E) p :=
(mdifferentiable_proj E).mdifferentiableAt
theorem mdifferentiableWithinAt_proj {s : Set (TotalSpace F E)} {p : TotalSpace F E} :
- MDifferentiableWithinAt (IB.prod 𝓘(𝕜, F)) IB (π F E) s p :=
+ MDiffAt[s] (π F E) p :=
(mdifferentiableAt_proj E).mdifferentiableWithinAt
variable (𝕜) [∀ x, AddCommMonoid (E x)]
@@ -243,8 +240,8 @@ theorem mdifferentiableWithinAt_totalSpace_iff
(e : Trivialization F (TotalSpace.proj : TotalSpace F E → B)) [MemTrivializationAtlas e]
(f : M → TotalSpace F E) {s : Set M} {x₀ : M}
(he : f x₀ ∈ e.source) :
- MDifferentiableWithinAt IM (IB.prod 𝓘(𝕜, F)) f s x₀ ↔
- MDiffAt[s] (fun x => (f x).proj) x₀ ∧ MDiffAt[s] (fun x ↦ (e (f x)).2) x₀ := by
+ MDiffAt[s] f x₀ ↔
+ MDiffAt[s] (fun x ↦ (f x).proj) x₀ ∧ MDiffAt[s] (fun x ↦ (e (f x)).2) x₀ := by
rw [mdifferentiableWithinAt_totalSpace]
apply and_congr_right
intro hf
@@ -257,8 +254,7 @@ theorem mdifferentiableAt_totalSpace_iff
(e : Trivialization F (TotalSpace.proj : TotalSpace F E → B)) [MemTrivializationAtlas e]
(f : M → TotalSpace F E) {x₀ : M}
(he : f x₀ ∈ e.source) :
- MDifferentiableAt IM (IB.prod 𝓘(𝕜, F)) f x₀ ↔
- MDiffAt (fun x => (f x).proj) x₀ ∧ MDiffAt (fun x ↦ (e (f x)).2) x₀ := by
+ MDiffAt f x₀ ↔ MDiffAt (fun x ↦ (f x).proj) x₀ ∧ MDiffAt (fun x ↦ (e (f x)).2) x₀ := by
rw [mdifferentiableAt_totalSpace]
apply and_congr_right
intro hf
@@ -273,7 +269,7 @@ theorem mdifferentiableWithinAt_section_iff
(hex₀ : b₀ ∈ e.baseSet) :
MDiffAt[u] (T% s) b₀ ↔ MDiffAt[u] (fun x ↦ (e (s x)).2) b₀ := by
rw [e.mdifferentiableWithinAt_totalSpace_iff IB]
- · change MDifferentiableWithinAt IB IB id u b₀ ∧ _ ↔ _
+ · change MDiffAt[u] (@id B) b₀ ∧ _ ↔ _
simp [mdifferentiableWithinAt_id]
exact (coe_mem_source e).mpr hex₀
@@ -624,10 +620,9 @@ only makes sense around a point.
-/
lemma MDifferentiableWithinAt.clm_apply_of_inCoordinates
(hϕ : MDiffAt[s] (fun m ↦ inCoordinates F₁ E₁ F₂ E₂ (b₁ m₀) (b₁ m) (b₂ m₀) (b₂ m) (ϕ m)) m₀)
- (hv : MDifferentiableWithinAt IM (IB₁.prod 𝓘(𝕜, F₁)) (fun m ↦ (v m : TotalSpace F₁ E₁)) s m₀)
+ (hv : MDiffAt[s] (fun m ↦ (v m : TotalSpace F₁ E₁)) m₀)
(hb₂ : MDiffAt[s] b₂ m₀) :
- MDifferentiableWithinAt IM (IB₂.prod 𝓘(𝕜, F₂))
- (fun m ↦ (ϕ m (v m) : TotalSpace F₂ E₂)) s m₀ := by
+ MDiffAt[s] (fun m ↦ (ϕ m (v m) : TotalSpace F₂ E₂)) m₀ := by
rw [mdifferentiableWithinAt_totalSpace] at hv ⊢
refine ⟨hb₂, ?_⟩
apply (MDifferentiableWithinAt.clm_apply hϕ hv.2).congr_of_eventuallyEq_insert
@@ -658,9 +653,8 @@ in coordinates, only makes sense around a point.
-/
lemma MDifferentiableAt.clm_apply_of_inCoordinates
(hϕ : MDiffAt (fun m ↦ inCoordinates F₁ E₁ F₂ E₂ (b₁ m₀) (b₁ m) (b₂ m₀) (b₂ m) (ϕ m)) m₀)
- (hv : MDifferentiableAt IM (IB₁.prod 𝓘(𝕜, F₁)) (fun m ↦ (v m : TotalSpace F₁ E₁)) m₀)
- (hb₂ : MDiffAt b₂ m₀) :
- MDifferentiableAt IM (IB₂.prod 𝓘(𝕜, F₂)) (fun m ↦ (ϕ m (v m) : TotalSpace F₂ E₂)) m₀ := by
+ (hv : MDiffAt (fun m ↦ (v m : TotalSpace F₁ E₁)) m₀) (hb₂ : MDiffAt b₂ m₀) :
+ MDiffAt (fun m ↦ (ϕ m (v m) : TotalSpace F₂ E₂)) m₀ := by
rw [← mdifferentiableWithinAt_univ] at hϕ hv hb₂ ⊢
exact MDifferentiableWithinAt.clm_apply_of_inCoordinates hϕ hv hb₂
@@ -680,17 +674,17 @@ variable {𝕜 : Type*} [NontriviallyNormedField 𝕜]
lemma exists_contMDiffOn_extend [(x : M) → Module 𝕜 (V x)] [VectorBundle 𝕜 F V]
[ContMDiffVectorBundle k F V I] {x₀ : M} (σ₀ : V x₀) :
- ∃ s ∈ 𝓝 x₀, ContMDiffOn I (I.prod 𝓘(𝕜, F)) k (T% (extend F σ₀)) s := by
+ ∃ s ∈ 𝓝 x₀, CMDiff[s] k (T% (extend F σ₀)) := by
set t := trivializationAt F V x₀
refine ⟨t.baseSet, ?_, ?_⟩
· refine t.open_baseSet.mem_nhds ?_
exact FiberBundle.mem_baseSet_trivializationAt' x₀
- suffices ContMDiffOn I 𝓘(𝕜, F) k (fun x ↦ (t ⟨x, extend F σ₀ x⟩).2) t.baseSet by
+ suffices CMDiff[t.baseSet] k (fun x ↦ (t ⟨x, extend F σ₀ x⟩).2) by
intro x hx
rw [t.contMDiffWithinAt_section _ hx]
exact this x hx
let w : F := (t ⟨x₀, σ₀⟩).2
- have : ContMDiffOn I 𝓘(𝕜, F) k (fun _x ↦ w) t.baseSet := contMDiffOn_const
+ have : CMDiff[t.baseSet] k (fun (_x : M) ↦ w) := contMDiffOn_const
exact this.congr (fun x hx ↦ by simp [extend, t, w, hx])
lemma contMDiffAt_extend' {x : M} (σ₀ : V x) :
@@ -708,7 +702,7 @@ lemma contMDiffAt_extend' {x : M} (σ₀ : V x) :
lemma exists_mdifferentiableOn_extend [∀ x, Module 𝕜 (V x)] [VectorBundle 𝕜 F V]
[ContMDiffVectorBundle 1 F V I] {x₀ : M} (σ₀ : V x₀) :
- ∃ s ∈ 𝓝 x₀, MDifferentiableOn I (I.prod 𝓘(𝕜, F)) (T% (extend F σ₀)) s := by
+ ∃ s ∈ 𝓝 x₀, MDiff[s] (T% (extend F σ₀)) := by
obtain ⟨s, hs, hsσ⟩ := exists_contMDiffOn_extend (k := 1) I F σ₀
exact ⟨s, hs, hsσ.mdifferentiableOn one_ne_zero⟩
diff --git a/Mathlib/Geometry/Manifold/VectorBundle/Riemannian.lean b/Mathlib/Geometry/Manifold/VectorBundle/Riemannian.lean
index bc90f650272711..b5fc92352e7156 100644
--- a/Mathlib/Geometry/Manifold/VectorBundle/Riemannian.lean
+++ b/Mathlib/Geometry/Manifold/VectorBundle/Riemannian.lean
@@ -119,9 +119,9 @@ variable
/-- Given two smooth maps into the same fibers of a Riemannian bundle,
their scalar product is smooth. -/
lemma ContMDiffWithinAt.inner_bundle
- (hv : ContMDiffWithinAt IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (v m : TotalSpace F E)) s x)
- (hw : ContMDiffWithinAt IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (w m : TotalSpace F E)) s x) :
- ContMDiffWithinAt IM 𝓘(ℝ) n (fun m ↦ ⟪v m, w m⟫) s x := by
+ (hv : CMDiffAt[s] n (fun m ↦ (v m : TotalSpace F E)) x)
+ (hw : CMDiffAt[s] n (fun m ↦ (w m : TotalSpace F E)) x) :
+ CMDiffAt[s] n (fun m ↦ ⟪v m, w m⟫) x := by
rcases h.exists_contMDiff with ⟨g, g_smooth, hg⟩
have hb : CMDiffAt[s] n b x := by
simp only [contMDiffWithinAt_totalSpace] at hv
@@ -139,24 +139,24 @@ lemma ContMDiffWithinAt.inner_bundle
/-- Given two smooth maps into the same fibers of a Riemannian bundle,
their scalar product is smooth. -/
lemma ContMDiffAt.inner_bundle
- (hv : ContMDiffAt IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (v m : TotalSpace F E)) x)
- (hw : ContMDiffAt IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (w m : TotalSpace F E)) x) :
- ContMDiffAt IM 𝓘(ℝ) n (fun b ↦ ⟪v b, w b⟫) x :=
+ (hv : CMDiffAt n (fun m ↦ (v m : TotalSpace F E)) x)
+ (hw : CMDiffAt n (fun m ↦ (w m : TotalSpace F E)) x) :
+ CMDiffAt n (fun b ↦ ⟪v b, w b⟫) x :=
ContMDiffWithinAt.inner_bundle hv hw
/-- Given two smooth maps into the same fibers of a Riemannian bundle,
their scalar product is smooth. -/
lemma ContMDiffOn.inner_bundle
- (hv : ContMDiffOn IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (v m : TotalSpace F E)) s)
- (hw : ContMDiffOn IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (w m : TotalSpace F E)) s) :
- ContMDiffOn IM 𝓘(ℝ) n (fun b ↦ ⟪v b, w b⟫) s :=
+ (hv : CMDiff[s] n (fun m ↦ (v m : TotalSpace F E)))
+ (hw : CMDiff[s] n (fun m ↦ (w m : TotalSpace F E))) :
+ CMDiff[s] n (fun b ↦ ⟪v b, w b⟫) :=
fun x hx ↦ (hv x hx).inner_bundle (hw x hx)
/-- Given two smooth maps into the same fibers of a Riemannian bundle,
their scalar product is smooth. -/
lemma ContMDiff.inner_bundle
- (hv : ContMDiff IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (v m : TotalSpace F E)))
- (hw : ContMDiff IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (w m : TotalSpace F E))) :
+ (hv : CMDiff n (fun m ↦ (v m : TotalSpace F E)))
+ (hw : CMDiff n (fun m ↦ (w m : TotalSpace F E))) :
CMDiff n (fun b ↦ ⟪v b, w b⟫) :=
fun x ↦ (hv x).inner_bundle (hw x)
@@ -174,11 +174,11 @@ variable
/-- Given two differentiable maps into the same fibers of a Riemannian bundle,
their scalar product is differentiable. -/
lemma MDifferentiableWithinAt.inner_bundle
- (hv : MDifferentiableWithinAt IM (IB.prod 𝓘(ℝ, F)) (fun m ↦ (v m : TotalSpace F E)) s x)
- (hw : MDifferentiableWithinAt IM (IB.prod 𝓘(ℝ, F)) (fun m ↦ (w m : TotalSpace F E)) s x) :
+ (hv : MDiffAt[s] (fun m ↦ (v m : TotalSpace F E)) x)
+ (hw : MDiffAt[s] (fun m ↦ (w m : TotalSpace F E)) x) :
MDiffAt[s] (fun m ↦ ⟪v m, w m⟫) x := by
rcases h.exists_contMDiff with ⟨g, g_smooth, hg⟩
- have hb : MDifferentiableWithinAt IM IB b s x := by
+ have hb : MDiffAt[s] b x := by
simp only [mdifferentiableWithinAt_totalSpace] at hv
exact hv.1
simp only [hg]
@@ -195,24 +195,24 @@ lemma MDifferentiableWithinAt.inner_bundle
/-- Given two differentiable maps into the same fibers of a Riemannian bundle,
their scalar product is differentiable. -/
lemma MDifferentiableAt.inner_bundle
- (hv : MDifferentiableAt IM (IB.prod 𝓘(ℝ, F)) (fun m ↦ (v m : TotalSpace F E)) x)
- (hw : MDifferentiableAt IM (IB.prod 𝓘(ℝ, F)) (fun m ↦ (w m : TotalSpace F E)) x) :
+ (hv : MDiffAt (fun m ↦ (v m : TotalSpace F E)) x)
+ (hw : MDiffAt (fun m ↦ (w m : TotalSpace F E)) x) :
MDiffAt (fun b ↦ ⟪v b, w b⟫) x :=
MDifferentiableWithinAt.inner_bundle hv hw
/-- Given two differentiable maps into the same fibers of a Riemannian bundle,
their scalar product is differentiable. -/
lemma MDifferentiableOn.inner_bundle
- (hv : MDifferentiableOn IM (IB.prod 𝓘(ℝ, F)) (fun m ↦ (v m : TotalSpace F E)) s)
- (hw : MDifferentiableOn IM (IB.prod 𝓘(ℝ, F)) (fun m ↦ (w m : TotalSpace F E)) s) :
+ (hv : MDiff[s] (fun m ↦ (v m : TotalSpace F E)))
+ (hw : MDiff[s] (fun m ↦ (w m : TotalSpace F E))) :
MDiff[s] (fun b ↦ ⟪v b, w b⟫) :=
fun x hx ↦ (hv x hx).inner_bundle (hw x hx)
/-- Given two differentiable maps into the same fibers of a Riemannian bundle,
their scalar product is differentiable. -/
lemma MDifferentiable.inner_bundle
- (hv : MDifferentiable IM (IB.prod 𝓘(ℝ, F)) (fun m ↦ (v m : TotalSpace F E)))
- (hw : MDifferentiable IM (IB.prod 𝓘(ℝ, F)) (fun m ↦ (w m : TotalSpace F E))) :
+ (hv : MDiff (fun m ↦ (v m : TotalSpace F E)))
+ (hw : MDiff (fun m ↦ (w m : TotalSpace F E))) :
MDiff (fun b ↦ ⟪v b, w b⟫) :=
fun x ↦ (hv x).inner_bundle (hw x)
diff --git a/MathlibTest/DifferentialGeometry/Notation/PR40447.lean b/MathlibTest/DifferentialGeometry/Notation/PR40447.lean
new file mode 100644
index 00000000000000..8b265aaa9becc2
--- /dev/null
+++ b/MathlibTest/DifferentialGeometry/Notation/PR40447.lean
@@ -0,0 +1,96 @@
+import Mathlib.Geometry.Manifold.Notation
+import Mathlib.Geometry.Manifold.VectorBundle.Basic
+
+set_option pp.unicode.fun true
+
+open Bundle
+open scoped Manifold
+
+-- Let `M` and `N` be smooth manifold. Suppose `V` is a vector bundle over `M` with model fiber `F`.
+variable {𝕜 : Type*} [NontriviallyNormedField 𝕜]
+ {E : Type*} [NormedAddCommGroup E] [NormedSpace 𝕜 E]
+ {H : Type*} [TopologicalSpace H] (I : ModelWithCorners 𝕜 E H)
+ {M : Type*} [TopologicalSpace M] [ChartedSpace H M]
+ {E' : Type*} [NormedAddCommGroup E'] [NormedSpace 𝕜 E']
+ {H' : Type*} [TopologicalSpace H'] (J : ModelWithCorners 𝕜 E' H')
+ {N : Type*} [TopologicalSpace N] [ChartedSpace H' N]
+
+variable (F : Type*) [NormedAddCommGroup F] [NormedSpace 𝕜 F]
+ (n : WithTop ℕ∞)
+ (V : M → Type*) [TopologicalSpace (TotalSpace F V)]
+ [∀ x, AddCommGroup (V x)] [∀ x, Module 𝕜 (V x)]
+ [∀ x : M, TopologicalSpace (V x)] [∀ x, IsTopologicalAddGroup (V x)]
+ [∀ x, ContinuousSMul 𝕜 (V x)]
+ [FiberBundle F V] [VectorBundle 𝕜 F V]
+
+-- Consider a function from `N` into the total space of `V`.
+-- The correct model with corners to infer on the latter is `I.prod (𝓘(𝕜, F))` --- where
+-- `I` is the model with corners on the base `M` of `V`.
+/-- info: mfderiv% f x : TangentSpace J x →L[𝕜] TangentSpace (I.prod 𝓘(𝕜, F)) (f x) -/
+#guard_msgs in
+variable {f : N → TotalSpace F V} {x : N} in
+#check mfderiv J (I.prod 𝓘(𝕜, F)) f x
+
+-- The elaborators used to have a bug: they would always use the model on the domain `N` instead of
+-- the model `I` on the bundle's base `M`. This works if `f` were a section of `V`,
+-- but is incorrect in general!
+/-- info: mfderiv% f x : TangentSpace J x →L[𝕜] TangentSpace (I.prod 𝓘(𝕜, F)) (f x) -/
+#guard_msgs in
+variable {f : N → TotalSpace F V} {x : N} in #check mfderiv% f x
+
+-- Previously, projections like below were not supported: fixing the above bug properly also
+-- addresses this.
+/-- info: mfderiv% f x : TangentSpace (I.prod 𝓘(𝕜, F)) x →L[𝕜] TangentSpace J (f x) -/
+#guard_msgs in
+variable {f : TotalSpace F V → N} {x : TotalSpace F V} in #check mfderiv% f x
+/-- info: mfderiv% f x : TangentSpace (I.prod 𝓘(𝕜, F)) x →L[𝕜] TangentSpace J (f x) -/
+#guard_msgs in
+variable {f : TotalSpace F V → N} {x : TotalSpace F V} in #check mfderiv (I.prod 𝓘(𝕜, F)) J f x
+
+-- Further tests for this feature.
+
+-- For a fiber bundle over a normed space, we still infer a model with corners.
+/-- info: mfderiv% f x : TangentSpace J x →L[𝕜] TangentSpace (𝓘(𝕜, E).prod 𝓘(𝕜, F)) (f x) -/
+#guard_msgs in
+variable {V' : E → Type*} [TopologicalSpace (TotalSpace F V')] [∀ x : E, TopologicalSpace (V' x)]
+ [FiberBundle F V'] {f : N → TotalSpace F V'} {x : N} in #check mfderiv% f x
+
+-- We don't do so for a fiber bundle over a product of normed spaces: there are several possible
+-- choices for its base' model with corners.
+/--
+error: Could not find a model with corners for `TotalSpace F V'`.
+
+Hint: failures to find a model with corners can be debugged with the command `set_option trace.Elab.DiffGeo.MDiff true`.
+-/
+#guard_msgs in
+variable {V' : (E × E')→ Type*} [TopologicalSpace (TotalSpace F V')] [∀ x, TopologicalSpace (V' x)]
+ [FiberBundle F V'] {f : N → TotalSpace F V'} {x : N} in
+#check mfderiv% f x
+
+
+-- Fiber bundles with more complicated fibers, e.g. products of normed spaces.
+
+/-- info: mfderiv% f x : TangentSpace J x →L[𝕜] TangentSpace (𝓘(𝕜, E).prod 𝓘(𝕜, F × F)) (f x) -/
+#guard_msgs in
+variable {V' : E → Type*} [TopologicalSpace (TotalSpace (F × F) V')] [∀ x : E, TopologicalSpace (V' x)]
+ [FiberBundle (F × F) V'] {f : N → TotalSpace (F × F) V'} {x : N} in #check mfderiv% f x
+
+/-- info: mfderiv% f x : TangentSpace J x →L[𝕜] TangentSpace (𝓘(𝕜, E).prod 𝓘(𝕜, F →L[𝕜] F)) (f x) -/
+#guard_msgs in
+variable {V' : E → Type*} [TopologicalSpace (TotalSpace (F →L[𝕜] F) V')] [∀ x : E, TopologicalSpace (V' x)]
+ [FiberBundle (F →L[𝕜] F) V'] {f : N → TotalSpace (F →L[𝕜] F) V'} {x : N} in #check mfderiv% f x
+
+/--
+info: mfderiv% f x : TangentSpace J x →L[𝕜] TangentSpace (𝓘(𝕜, E).prod 𝓘(𝕜, F × (F →L[𝕜] F))) (f x)
+-/
+#guard_msgs in
+variable {V' : E → Type*} [TopologicalSpace (TotalSpace (F × (F →L[𝕜] F)) V')] [∀ x : E, TopologicalSpace (V' x)]
+ [FiberBundle (F × (F →L[𝕜] F)) V'] {f : N → TotalSpace (F × (F →L[𝕜] F)) V'} {x : N} in
+#check mfderiv% f x
+
+/--
+info: mfderiv% f x : TangentSpace J x →L[𝕜] TangentSpace (𝓘(𝕜, E).prod 𝓘(𝕜, F × (F →L[𝕜] F →L[𝕜] F))) (f x)
+-/
+#guard_msgs in
+variable {V' : E → Type*} [TopologicalSpace (TotalSpace (F × (F →L[𝕜] F →L[𝕜] F)) V')] [∀ x : E, TopologicalSpace (V' x)]
+ [FiberBundle (F × (F →L[𝕜] F →L[𝕜] F)) V'] {f : N → TotalSpace (F × (F →L[𝕜] F →L[𝕜] F)) V'} {x : N} in #check mfderiv% f x
From a83a1c306aac0fc15753a4b7590db57c1f56fd40 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=ABl=20Riou?=
<37772949+joelriou@users.noreply.github.com>
Date: Tue, 23 Jun 2026 08:28:09 +0000
Subject: [PATCH 0274/1300] feat(Algebra/Homology): more on the functoriality
of categories of homological complexes (#40895)
---
Mathlib/Algebra/Homology/Additive.lean | 25 ++++++++
.../Algebra/Homology/HomotopyCategory.lean | 60 +++++++++++++++++++
2 files changed, 85 insertions(+)
diff --git a/Mathlib/Algebra/Homology/Additive.lean b/Mathlib/Algebra/Homology/Additive.lean
index 86ab7ca1703cef..56b8a7c83daa15 100644
--- a/Mathlib/Algebra/Homology/Additive.lean
+++ b/Mathlib/Algebra/Homology/Additive.lean
@@ -141,6 +141,21 @@ instance Functor.mapHomologicalComplex_reflects_iso (F : W₁ ⥤ W₂) [F.Prese
haveI := fun n => isIso_of_reflects_iso (f.f n) F
exact HomologicalComplex.Hom.isIso_of_components f⟩
+instance (F : V ⥤ W) [F.Additive] (c : ComplexShape ι) [F.Faithful] :
+ (F.mapHomologicalComplex c).Faithful where
+ map_injective {K L} f₁ f₂ h := by
+ ext
+ exact F.map_injective ((HomologicalComplex.eval W c _).congr_map h)
+
+instance (F : V ⥤ W) [F.Additive] (c : ComplexShape ι) [F.Faithful] [F.Full] :
+ (F.mapHomologicalComplex c).Full where
+ map_surjective {X Y} f := ⟨
+ { f n := F.preimage (f.f n)
+ comm' i j _ := by
+ apply F.map_injective
+ simp only [Functor.map_comp, Functor.map_preimage]
+ exact f.comm i j }, by cat_disch⟩
+
variable {W₁}
set_option backward.defeqAttrib.useBackward true in
@@ -188,6 +203,16 @@ def NatIso.mapHomologicalComplex {F G : W₁ ⥤ W₂} [F.PreservesZeroMorphisms
inv_hom_id := by simp only [← NatTrans.mapHomologicalComplex_comp, α.inv_hom_id,
NatTrans.mapHomologicalComplex_id]
+/-- If additive functors are related by an isomorphism `F ⋙ G ≅ H`, this is
+the corresponding isomorphism for the induced functors on categories
+of homological complexes. -/
+@[simps!]
+def Functor.mapHomologicalComplexCompIso {W' : Type*} [Category W'] [Preadditive W']
+ {F : V ⥤ W} {G : W ⥤ W'} {H : V ⥤ W'} (e : F ⋙ G ≅ H)
+ [F.Additive] [G.Additive] [H.Additive] (c : ComplexShape ι) :
+ F.mapHomologicalComplex c ⋙ G.mapHomologicalComplex c ≅ H.mapHomologicalComplex c :=
+ NatIso.mapHomologicalComplex e c
+
set_option backward.defeqAttrib.useBackward true in
/-- An equivalence of categories induces an equivalences between the respective categories
of homological complex.
diff --git a/Mathlib/Algebra/Homology/HomotopyCategory.lean b/Mathlib/Algebra/Homology/HomotopyCategory.lean
index 099d7b695a351b..398b611bbac818 100644
--- a/Mathlib/Algebra/Homology/HomotopyCategory.lean
+++ b/Mathlib/Algebra/Homology/HomotopyCategory.lean
@@ -285,4 +285,64 @@ instance (F : V ⥤ W) [F.Additive] (c : ComplexShape ι) [Linear R V] [Linear R
have := Functor.linear_of_iso R (F.mapHomotopyCategoryFactors c).symm
(HomotopyCategory.quotient V c).linear_of_full_essSurj_comp (F.mapHomotopyCategory c)
+/-- If additive functors are related by an isomorphism `F ⋙ G ≅ H`, this is
+the corresponding isomorphism for the induced functors on homotopy categories
+of homological complexes. -/
+def Functor.mapHomotopyCategoryCompIso {W' : Type*} [Category W'] [Preadditive W']
+ {F : V ⥤ W} {G : W ⥤ W'} {H : V ⥤ W'} (e : F ⋙ G ≅ H)
+ [F.Additive] [G.Additive] [H.Additive] (c : ComplexShape ι) :
+ F.mapHomotopyCategory c ⋙ G.mapHomotopyCategory c ≅ H.mapHomotopyCategory c :=
+ Quotient.natIsoLift _ (isoWhiskerRight (Functor.mapHomologicalComplexCompIso e c)
+ (HomotopyCategory.quotient W' c))
+
+variable {c} in
+set_option backward.defeqAttrib.useBackward true in
+set_option backward.isDefEq.respectTransparency false in
+/-- The preimage by a fully faithful functor of a homotopy between morphisms
+of homological complexes. -/
+def Functor.preimageHomotopy
+ (F : V ⥤ W) [F.Additive] [F.Full] [F.Faithful]
+ {K L : HomologicalComplex V c} {f₁ f₂ : K ⟶ L}
+ (H : Homotopy ((F.mapHomologicalComplex c).map f₁) ((F.mapHomologicalComplex c).map f₂)) :
+ Homotopy f₁ f₂ where
+ hom i j := F.preimage (H.hom i j)
+ zero i j hij := F.map_injective (by simp only [map_preimage, Functor.map_zero, H.zero i j hij])
+ comm i := F.map_injective (by simp [dsimp% H.comm i, dNext, prevD])
+
+instance (F : V ⥤ W) [F.Full] [F.Faithful] [F.Additive] :
+ (F.mapHomotopyCategory c).Faithful where
+ map_injective := by
+ rintro ⟨K⟩ ⟨L⟩ f₁ f₂ h
+ obtain ⟨f₁, rfl⟩ := (HomotopyCategory.quotient _ _).map_surjective f₁
+ obtain ⟨f₂, rfl⟩ := (HomotopyCategory.quotient _ _).map_surjective f₂
+ exact HomotopyCategory.eq_of_homotopy _ _
+ (F.preimageHomotopy (HomotopyCategory.homotopyOfEq _ _ h))
+
+instance (F : V ⥤ W) [F.Full] [F.Faithful] [F.Additive] :
+ (F.mapHomotopyCategory c).Full where
+ map_surjective := by
+ rintro ⟨K⟩ ⟨L⟩ ⟨f⟩
+ obtain ⟨g : K ⟶ L, rfl⟩ := (F.mapHomologicalComplex c).map_surjective f
+ exact ⟨(HomotopyCategory.quotient V c).map g, rfl⟩
+
end CategoryTheory
+
+namespace HomologicalComplex
+
+variable {ι : Type*} {V : Type u} [Category.{v} V] [Preadditive V] {c : ComplexShape ι}
+
+open HomotopyCategory in
+lemma isIso_quotient_map_iff_homotopyEquivalences
+ {K L : HomologicalComplex V c} (f : K ⟶ L) :
+ IsIso ((quotient _ _).map f) ↔
+ homotopyEquivalences _ _ f := by
+ refine ⟨fun _ ↦ ?_, fun hf ↦ quotient_inverts_homotopyEquivalences V c f hf⟩
+ obtain ⟨g, hg⟩ := (quotient V c).map_surjective (inv ((quotient V c).map f))
+ let e : HomotopyEquiv K L :=
+ { hom := f
+ inv := g
+ homotopyHomInvId := HomotopyCategory.homotopyOfEq _ _ (by cat_disch)
+ homotopyInvHomId := HomotopyCategory.homotopyOfEq _ _ (by cat_disch) }
+ exact ⟨e, rfl⟩
+
+end HomologicalComplex
From 84ecb9b7d4f18f74506eb28d83b3165849446cb9 Mon Sep 17 00:00:00 2001
From: zw810-ctrl <248219010+zw810-ctrl@users.noreply.github.com>
Date: Tue, 23 Jun 2026 08:44:05 +0000
Subject: [PATCH 0275/1300] feat(Algebra): range_prodMap for MonoidHom,
RingHom, AlgHom (#40491)
Co-authored-by: Monica Omar <23701951+themathqueen@users.noreply.github.com>
---
Mathlib/Algebra/Algebra/Subalgebra/Prod.lean | 9 ++++++++-
Mathlib/Algebra/Group/Subgroup/Basic.lean | 5 +++++
Mathlib/Algebra/Group/Submonoid/Operations.lean | 6 ++++++
Mathlib/Algebra/Ring/Subring/Basic.lean | 5 +++++
Mathlib/Algebra/Ring/Subsemiring/Basic.lean | 5 +++++
Mathlib/RingTheory/Ideal/Prod.lean | 5 +++++
6 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/Mathlib/Algebra/Algebra/Subalgebra/Prod.lean b/Mathlib/Algebra/Algebra/Subalgebra/Prod.lean
index c3c9982f336797..69b915e51330eb 100644
--- a/Mathlib/Algebra/Algebra/Subalgebra/Prod.lean
+++ b/Mathlib/Algebra/Algebra/Subalgebra/Prod.lean
@@ -25,7 +25,9 @@ namespace Subalgebra
open Algebra
-variable {R A B : Type*} [CommSemiring R] [Semiring A] [Algebra R A] [Semiring B] [Algebra R B]
+variable {R A B C D : Type*} [CommSemiring R] [Semiring A] [Algebra R A] [Semiring B] [Algebra R B]
+ [Semiring C] [Algebra R C] [Semiring D] [Algebra R D]
+
variable (S : Subalgebra R A) (S₁ : Subalgebra R B)
/-- The product of two subalgebras is a subalgebra. -/
@@ -60,4 +62,9 @@ theorem prod_inf_prod {S T : Subalgebra R A} {S₁ T₁ : Subalgebra R B} :
protected theorem center_prod : center R (A × B) = prod (center R A) (center R B) :=
SetLike.coe_injective Set.center_prod
+@[simp]
+theorem _root_.AlgHom.range_prodMap (f : A →ₐ[R] B) (g : C →ₐ[R] D) :
+ (f.prodMap g).range = f.range.prod g.range :=
+ SetLike.coe_injective Set.range_prodMap
+
end Subalgebra
diff --git a/Mathlib/Algebra/Group/Subgroup/Basic.lean b/Mathlib/Algebra/Group/Subgroup/Basic.lean
index c5a07c6d1114d7..0a94a12c97fafa 100644
--- a/Mathlib/Algebra/Group/Subgroup/Basic.lean
+++ b/Mathlib/Algebra/Group/Subgroup/Basic.lean
@@ -729,6 +729,11 @@ lemma ker_snd : ker (snd G G') = .prod ⊤ ⊥ := SetLike.ext fun _ => (iff_of_e
end Ker
+@[to_additive (attr := simp) range_prodMap]
+lemma range_prodMap {G' N' : Type*} [Group G'] [Group N'] (f : G →* N) (g : G' →* N') :
+ (f.prodMap g).range = f.range.prod g.range :=
+ SetLike.coe_injective Set.range_prodMap
+
end MonoidHom
namespace Subgroup
diff --git a/Mathlib/Algebra/Group/Submonoid/Operations.lean b/Mathlib/Algebra/Group/Submonoid/Operations.lean
index 82fdff1c1df514..d498ccecce2a86 100644
--- a/Mathlib/Algebra/Group/Submonoid/Operations.lean
+++ b/Mathlib/Algebra/Group/Submonoid/Operations.lean
@@ -677,6 +677,12 @@ theorem map_mrange (g : N →* P) (f : M →* N) : (mrange f).map g = mrange (co
theorem mrange_eq_top {f : F} : mrange f = (⊤ : Submonoid N) ↔ Surjective f :=
SetLike.ext'_iff.trans <| Iff.trans (by rw [coe_mrange, coe_top]) Set.range_eq_univ
+@[to_additive (attr := simp) mrange_prodMap]
+lemma mrange_prodMap {M' N' : Type*} [MulOneClass M'] [MulOneClass N'] (f : M →* N)
+ (g : M' →* N') :
+ MonoidHom.mrange (f.prodMap g) = (MonoidHom.mrange f).prod (MonoidHom.mrange g) :=
+ SetLike.coe_injective Set.range_prodMap
+
/-- The range of a surjective monoid hom is the whole of the codomain. -/
@[to_additive (attr := simp)
/-- The range of a surjective `AddMonoid` hom is the whole of the codomain. -/]
diff --git a/Mathlib/Algebra/Ring/Subring/Basic.lean b/Mathlib/Algebra/Ring/Subring/Basic.lean
index 381b16158620ac..26fdd10daf025d 100644
--- a/Mathlib/Algebra/Ring/Subring/Basic.lean
+++ b/Mathlib/Algebra/Ring/Subring/Basic.lean
@@ -842,6 +842,11 @@ theorem domRestrict_comp_rangeRestrict (g : S →+* T) (f : R →+* S) :
(g.domRestrict f.range).comp (f.rangeRestrict) = g.comp f :=
rfl
+@[simp]
+theorem range_prodMap {R' S' : Type*} [Ring R'] [Ring S'] (f : R →+* S) (g : R' →+* S') :
+ (f.prodMap g).range = f.range.prod g.range :=
+ SetLike.coe_injective Set.range_prodMap
+
section eqLocus
variable {S : Type v} [Semiring S]
diff --git a/Mathlib/Algebra/Ring/Subsemiring/Basic.lean b/Mathlib/Algebra/Ring/Subsemiring/Basic.lean
index 815cc588c3d5f9..0a393c2b0c391b 100644
--- a/Mathlib/Algebra/Ring/Subsemiring/Basic.lean
+++ b/Mathlib/Algebra/Ring/Subsemiring/Basic.lean
@@ -662,6 +662,11 @@ theorem top_prod (s : Subsemiring S) : (⊤ : Subsemiring R).prod s = s.comap (R
theorem top_prod_top : (⊤ : Subsemiring R).prod (⊤ : Subsemiring S) = ⊤ :=
(top_prod _).trans <| comap_top _
+@[simp]
+theorem _root_.RingHom.rangeS_prodMap (f : R →+* S) (g : S →+* T) :
+ (f.prodMap g).rangeS = Subsemiring.prod f.rangeS g.rangeS :=
+ SetLike.coe_injective Set.range_prodMap
+
protected theorem center_prod : center (R × S) = prod (center R) (center S) :=
SetLike.coe_injective Set.center_prod
diff --git a/Mathlib/RingTheory/Ideal/Prod.lean b/Mathlib/RingTheory/Ideal/Prod.lean
index a35ddec7659af5..2f9f38b8b60868 100644
--- a/Mathlib/RingTheory/Ideal/Prod.lean
+++ b/Mathlib/RingTheory/Ideal/Prod.lean
@@ -36,6 +36,11 @@ theorem coe_prod (I : Ideal R) (J : Ideal S) : ↑(prod I J) = (I ×ˢ J : Set (
theorem mem_prod {x : R × S} : x ∈ prod I J ↔ x.1 ∈ I ∧ x.2 ∈ J :=
Iff.rfl
+@[simp]
+theorem _root_.RingHom.ker_prodMap {T U : Type*} [Semiring T] [Semiring U] (f : R →+* S)
+ (g : T →+* U) : RingHom.ker (f.prodMap g) = (RingHom.ker f).prod (RingHom.ker g) := by
+ ext ⟨⟩; simp
+
@[simp]
theorem prod_top_top : prod (⊤ : Ideal R) (⊤ : Ideal S) = ⊤ :=
Ideal.ext <| by simp
From 66255bfc74956c7a9fb86c60509cc7bc7f06636d Mon Sep 17 00:00:00 2001
From: Riccardo Brasca
Date: Tue, 23 Jun 2026 08:44:07 +0000
Subject: [PATCH 0276/1300] chore: remove useless lines (#40774)
---
Mathlib/NumberTheory/RamificationInertia/Galois.lean | 2 --
1 file changed, 2 deletions(-)
diff --git a/Mathlib/NumberTheory/RamificationInertia/Galois.lean b/Mathlib/NumberTheory/RamificationInertia/Galois.lean
index 06d4f5a5d2f7c0..ec9ce3731a2995 100644
--- a/Mathlib/NumberTheory/RamificationInertia/Galois.lean
+++ b/Mathlib/NumberTheory/RamificationInertia/Galois.lean
@@ -279,8 +279,6 @@ theorem card_stabilizer_eq_card_inertia_mul_finrank (p : Ideal R) [p.IsPrime]
(P : Ideal S) [P.LiesOver p] [P.IsPrime] [PerfectField p.ResidueField] :
Nat.card (MulAction.stabilizer G P) = Nat.card (inertia G P) * P.inertiaDeg' R := by
let := Localization.AtPrime.algebraOfLiesOver p P
- let : Algebra (R ⧸ p) p.ResidueField := inferInstance
- let : Algebra (S ⧸ P) P.ResidueField := inferInstance
have heq : (algebraMap (S ⧸ P) P.ResidueField).comp (algebraMap (R ⧸ p) (S ⧸ P)) =
(algebraMap p.ResidueField P.ResidueField).comp (algebraMap (R ⧸ p) p.ResidueField) := by
ext
From 41520dac113c0aad66a755d8229c4900f99fd58a Mon Sep 17 00:00:00 2001
From: teorth <199308+teorth@users.noreply.github.com>
Date: Tue, 23 Jun 2026 09:25:06 +0000
Subject: [PATCH 0277/1300] feat: tag the pointwise
Continuous/ContDiff/Measurable operation families with to_fun (#40872)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Several `fun_prop` lemma families about pointwise operations existed only in **eta-expanded** form (`Continuous (fun x => (f x)⁻¹)`), so `fun_prop` could not close the corresponding **point-free** goal (`Continuous f⁻¹`). This PR restates
those families point-free and tags them with `@[to_fun (attr := …)]`, which regenerates the eta twin (`fun_inv`, `fun_sub`, `fun_pow`, …). Both forms then carry `fun_prop`, mirroring the existing `Continuous.mul` / `ContinuousOn.mul`
setup, so `fun_prop` now matches goals in either shape (with one caveat, see below).
Because of the name change of the eta version, many downstream invocations of these theorems have their name changed slightly (e.g., `.inv` becomes `.fun_inv`). In most cases this was a mechanical change; there were a few unusual edge cases, noted below and in the more detailed attached report.
For further discussion, see [this Zulip thread](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Should.20one.20to_fun.20various.20fun_prop.20lemmas.3F). Functionally, this PR is similar to #35306, but now implemented over many further `fun_prop` lemmas.
This PR was prepared by an AI agent, guided by myself. In particular the summary text in this comment was initially generated by AI and then edited by myself.
Co-authored-by: Terence Tao
---
.../NonUnital.lean | 2 +-
.../ContinuousFunctionalCalculus/Unital.lean | 4 +-
.../CStarAlgebra/GelfandNaimarkSegal.lean | 4 +-
.../Calculus/ContDiff/Operations.lean | 38 +++++++++----------
Mathlib/Analysis/Calculus/Deriv/Inv.lean | 23 ++---------
.../Analysis/Calculus/Deriv/MeanValue.lean | 4 +-
.../Analysis/Calculus/FDeriv/Analytic.lean | 2 +-
Mathlib/Analysis/Complex/Basic.lean | 2 +-
.../Analysis/Complex/Harmonic/Poisson.lean | 2 +-
.../Analysis/Complex/LocallyUniformLimit.lean | 7 ++--
.../Fourier/FourierTransformDeriv.lean | 2 +-
Mathlib/Analysis/MellinInversion.lean | 2 +-
Mathlib/Analysis/ODE/Gronwall.lean | 2 +-
.../SpecialFunctions/Log/NegMulLog.lean | 2 +-
.../Trigonometric/Cotangent.lean | 2 +-
.../Trigonometric/EulerSineProd.lean | 6 ++-
Mathlib/MeasureTheory/Group/Arithmetic.lean | 12 +++---
.../Integral/CircleIntegral.lean | 2 +-
.../Integral/CircleTransform.lean | 2 +-
.../IntervalIntegral/FundThmCalculus.lean | 4 +-
.../MeasureTheory/Measure/WithDensity.lean | 10 ++---
.../LSeries/DirichletContinuation.lean | 2 +-
Mathlib/Probability/Moments/SubGaussian.lean | 2 +-
Mathlib/Topology/Algebra/ConstMulAction.lean | 18 ++++-----
Mathlib/Topology/Algebra/Group/Basic.lean | 20 +++++-----
Mathlib/Topology/Algebra/Group/Defs.lean | 32 ++++++++--------
Mathlib/Topology/Algebra/GroupCompletion.lean | 4 +-
Mathlib/Topology/Algebra/GroupWithZero.lean | 15 ++++----
.../Topology/Algebra/LinearMapCompletion.lean | 2 +-
.../Algebra/Module/Spaces/WeakBilin.lean | 2 +-
Mathlib/Topology/Algebra/Monoid.lean | 16 ++++----
Mathlib/Topology/Algebra/MulAction.lean | 18 ++++-----
.../Topology/Algebra/UniformMulAction.lean | 2 +-
Mathlib/Topology/ContinuousMap/Ideals.lean | 2 +-
.../Topology/Instances/ENNReal/Lemmas.lean | 2 +-
35 files changed, 129 insertions(+), 142 deletions(-)
diff --git a/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/NonUnital.lean b/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/NonUnital.lean
index be142dd60e974d..303415eb28ff4d 100644
--- a/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/NonUnital.lean
+++ b/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/NonUnital.lean
@@ -580,7 +580,7 @@ lemma cfcₙ_neg : cfcₙ (fun x ↦ -(f x)) a = -(cfcₙ f a) := by
obtain (ha | hf | h0) := h
· simp [cfcₙ_apply_of_not_predicate a ha]
· rw [cfcₙ_apply_of_not_continuousOn a hf, cfcₙ_apply_of_not_continuousOn, neg_zero]
- exact fun hf_neg ↦ hf <| by simpa using hf_neg.neg
+ exact fun hf_neg ↦ hf <| by simpa using hf_neg.fun_neg
· rw [cfcₙ_apply_of_not_map_zero a h0, cfcₙ_apply_of_not_map_zero, neg_zero]
exact (h0 <| neg_eq_zero.mp ·)
diff --git a/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/Unital.lean b/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/Unital.lean
index b72f01d4f986e4..e1daf6ea7a326d 100644
--- a/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/Unital.lean
+++ b/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/Unital.lean
@@ -770,7 +770,7 @@ noncomputable def cfcUnits (hf' : ∀ x ∈ spectrum R a, f x ≠ 0)
lemma cfcUnits_pow (hf' : ∀ x ∈ spectrum R a, f x ≠ 0) (n : ℕ)
(hf : ContinuousOn f (spectrum R a) := by cfc_cont_tac) (ha : p a := by cfc_tac) :
(cfcUnits f a hf') ^ n =
- cfcUnits _ _ (forall₂_imp (fun _ _ ↦ pow_ne_zero n) hf') (hf := hf.pow n) := by
+ cfcUnits _ _ (forall₂_imp (fun _ _ ↦ pow_ne_zero n) hf') (hf := hf.fun_pow n) := by
ext
cases n with
| zero => simp [cfc_const_one R a]
@@ -882,7 +882,7 @@ lemma cfc_neg : cfc (fun x ↦ -(f x)) a = -(cfc f a) := by
· obtain (ha | hf) := not_and_or.mp h
· simp [cfc_apply_of_not_predicate a ha]
· rw [cfc_apply_of_not_continuousOn a hf, cfc_apply_of_not_continuousOn, neg_zero]
- exact fun hf_neg ↦ hf <| by simpa using hf_neg.neg
+ exact fun hf_neg ↦ hf <| by simpa using hf_neg.fun_neg
lemma cfc_neg' : cfc (-f) = (-cfc f : A → A) := by ext1 a; exact cfc_neg f a
diff --git a/Mathlib/Analysis/CStarAlgebra/GelfandNaimarkSegal.lean b/Mathlib/Analysis/CStarAlgebra/GelfandNaimarkSegal.lean
index 812a16c805f531..9d39ae0363b9a5 100644
--- a/Mathlib/Analysis/CStarAlgebra/GelfandNaimarkSegal.lean
+++ b/Mathlib/Analysis/CStarAlgebra/GelfandNaimarkSegal.lean
@@ -138,7 +138,9 @@ private lemma completion_leftMulMapPreGNS_map_smul (m : ℂ) (x : A) :
(f.leftMulMapPreGNS (m • x)).completion = m • (f.leftMulMapPreGNS x).completion := by
ext a
induction a using induction_on with
- | hp => apply isClosed_eq <;> fun_prop
+ | hp =>
+ exact isClosed_eq (f.leftMulMapPreGNS (m • x)).completion.continuous
+ (m • (f.leftMulMapPreGNS x).completion).continuous
| ih a => simp [smul_mul_assoc]
/--
diff --git a/Mathlib/Analysis/Calculus/ContDiff/Operations.lean b/Mathlib/Analysis/Calculus/ContDiff/Operations.lean
index 6a19277621a564..fcd9c1301e8602 100644
--- a/Mathlib/Analysis/Calculus/ContDiff/Operations.lean
+++ b/Mathlib/Analysis/Calculus/ContDiff/Operations.lean
@@ -793,50 +793,46 @@ theorem contDiffOn_inv {n} : ContDiffOn 𝕜 n (Inv.inv : 𝕜' → 𝕜') {0}
variable {𝕜}
-@[fun_prop]
+@[to_fun (attr := fun_prop)]
theorem ContDiffWithinAt.inv {f : E → 𝕜'} {n} (hf : ContDiffWithinAt 𝕜 n f s x) (hx : f x ≠ 0) :
- ContDiffWithinAt 𝕜 n (fun x => (f x)⁻¹) s x :=
+ ContDiffWithinAt 𝕜 n f⁻¹ s x :=
(contDiffAt_inv 𝕜 hx).comp_contDiffWithinAt x hf
-@[fun_prop]
+@[to_fun (attr := fun_prop)]
theorem ContDiffOn.inv {f : E → 𝕜'} (hf : ContDiffOn 𝕜 n f s) (h : ∀ x ∈ s, f x ≠ 0) :
- ContDiffOn 𝕜 n (fun x => (f x)⁻¹) s := fun x hx => (hf.contDiffWithinAt hx).inv (h x hx)
+ ContDiffOn 𝕜 n f⁻¹ s := fun x hx => (hf.contDiffWithinAt hx).inv (h x hx)
-@[fun_prop]
+@[to_fun (attr := fun_prop)]
nonrec theorem ContDiffAt.inv {f : E → 𝕜'} (hf : ContDiffAt 𝕜 n f x) (hx : f x ≠ 0) :
- ContDiffAt 𝕜 n (fun x => (f x)⁻¹) x :=
+ ContDiffAt 𝕜 n f⁻¹ x :=
hf.inv hx
-@[fun_prop]
+@[to_fun (attr := fun_prop)]
theorem ContDiff.inv {f : E → 𝕜'} (hf : ContDiff 𝕜 n f) (h : ∀ x, f x ≠ 0) :
- ContDiff 𝕜 n fun x => (f x)⁻¹ := by
+ ContDiff 𝕜 n f⁻¹ := by
rw [contDiff_iff_contDiffAt]; exact fun x => hf.contDiffAt.inv (h x)
-- TODO: generalize to `f g : E → 𝕜'`
-@[fun_prop]
+@[to_fun (attr := fun_prop)]
theorem ContDiffWithinAt.div {f g : E → 𝕜} {n} (hf : ContDiffWithinAt 𝕜 n f s x)
(hg : ContDiffWithinAt 𝕜 n g s x) (hx : g x ≠ 0) :
- ContDiffWithinAt 𝕜 n (fun x => f x / g x) s x := by
- simpa only [div_eq_mul_inv] using hf.mul (hg.inv hx)
+ ContDiffWithinAt 𝕜 n (f / g) s x := by
+ change ContDiffWithinAt 𝕜 n (fun x => f x / g x) s x
+ simpa only [div_eq_mul_inv] using hf.mul (hg.fun_inv hx)
-@[fun_prop]
+@[to_fun (attr := fun_prop)]
theorem ContDiffOn.div {f g : E → 𝕜} {n} (hf : ContDiffOn 𝕜 n f s)
(hg : ContDiffOn 𝕜 n g s) (h₀ : ∀ x ∈ s, g x ≠ 0) : ContDiffOn 𝕜 n (f / g) s := fun x hx =>
(hf x hx).div (hg x hx) (h₀ x hx)
-@[fun_prop]
-theorem ContDiffOn.fun_div {f g : E → 𝕜} {n} (hf : ContDiffOn 𝕜 n f s)
- (hg : ContDiffOn 𝕜 n g s) (h₀ : ∀ x ∈ s, g x ≠ 0) : ContDiffOn 𝕜 n (fun x => f x / g x) s :=
- ContDiffOn.div hf hg h₀
-
-@[fun_prop]
+@[to_fun (attr := fun_prop)]
nonrec theorem ContDiffAt.div {f g : E → 𝕜} {n} (hf : ContDiffAt 𝕜 n f x)
- (hg : ContDiffAt 𝕜 n g x) (hx : g x ≠ 0) : ContDiffAt 𝕜 n (fun x => f x / g x) x :=
+ (hg : ContDiffAt 𝕜 n g x) (hx : g x ≠ 0) : ContDiffAt 𝕜 n (f / g) x :=
hf.div hg hx
-@[fun_prop]
+@[to_fun (attr := fun_prop)]
theorem ContDiff.div {f g : E → 𝕜} {n} (hf : ContDiff 𝕜 n f) (hg : ContDiff 𝕜 n g)
- (h0 : ∀ x, g x ≠ 0) : ContDiff 𝕜 n fun x => f x / g x := by
+ (h0 : ∀ x, g x ≠ 0) : ContDiff 𝕜 n (f / g) := by
simp only [contDiff_iff_contDiffAt] at *
exact fun x => (hf x).div (hg x) (h0 x)
diff --git a/Mathlib/Analysis/Calculus/Deriv/Inv.lean b/Mathlib/Analysis/Calculus/Deriv/Inv.lean
index 5222e9c6990361..05ff9f51b4928c 100644
--- a/Mathlib/Analysis/Calculus/Deriv/Inv.lean
+++ b/Mathlib/Analysis/Calculus/Deriv/Inv.lean
@@ -183,32 +183,17 @@ theorem DifferentiableWithinAt.div (hc : DifferentiableWithinAt 𝕜 c s x)
DifferentiableWithinAt 𝕜 (c / d) s x :=
hc.fun_div hd hx
-@[simp, fun_prop]
-theorem DifferentiableAt.fun_div (hc : DifferentiableAt 𝕜 c x) (hd : DifferentiableAt 𝕜 d x)
- (hx : d x ≠ 0) : DifferentiableAt 𝕜 (fun x => c x / d x) x :=
- (hc.hasDerivAt.div hd.hasDerivAt hx).differentiableAt
-
-@[simp, fun_prop]
+@[to_fun (attr := simp, fun_prop)]
theorem DifferentiableAt.div (hc : DifferentiableAt 𝕜 c x) (hd : DifferentiableAt 𝕜 d x)
(hx : d x ≠ 0) : DifferentiableAt 𝕜 (c / d) x :=
- hc.fun_div hd hx
-
-@[fun_prop]
-theorem DifferentiableOn.fun_div (hc : DifferentiableOn 𝕜 c s) (hd : DifferentiableOn 𝕜 d s)
- (hx : ∀ x ∈ s, d x ≠ 0) : DifferentiableOn 𝕜 (fun x => c x / d x) s := fun x h =>
- (hc x h).div (hd x h) (hx x h)
+ (hc.hasDerivAt.div hd.hasDerivAt hx).differentiableAt
-@[fun_prop]
+@[to_fun (attr := fun_prop)]
theorem DifferentiableOn.div (hc : DifferentiableOn 𝕜 c s) (hd : DifferentiableOn 𝕜 d s)
(hx : ∀ x ∈ s, d x ≠ 0) : DifferentiableOn 𝕜 (c / d) s := fun x h =>
(hc x h).div (hd x h) (hx x h)
-@[simp, fun_prop]
-theorem Differentiable.fun_div (hc : Differentiable 𝕜 c) (hd : Differentiable 𝕜 d)
- (hx : ∀ x, d x ≠ 0) :
- Differentiable 𝕜 (fun x => c x / d x) := fun x => (hc x).div (hd x) (hx x)
-
-@[simp, fun_prop]
+@[to_fun (attr := simp, fun_prop)]
theorem Differentiable.div (hc : Differentiable 𝕜 c) (hd : Differentiable 𝕜 d) (hx : ∀ x, d x ≠ 0) :
Differentiable 𝕜 (c / d) := fun x => (hc x).div (hd x) (hx x)
diff --git a/Mathlib/Analysis/Calculus/Deriv/MeanValue.lean b/Mathlib/Analysis/Calculus/Deriv/MeanValue.lean
index 35fec7e28a8c2f..bb2d7794ab3b86 100644
--- a/Mathlib/Analysis/Calculus/Deriv/MeanValue.lean
+++ b/Mathlib/Analysis/Calculus/Deriv/MeanValue.lean
@@ -338,7 +338,7 @@ theorem Convex.image_sub_lt_mul_sub_of_deriv_lt {D : Set ℝ} (hD : Convex ℝ D
have hf'_gt : ∀ x ∈ interior D, -C < deriv (fun y => -f y) x := fun x hx => by
rw [deriv.fun_neg, neg_lt_neg_iff]
exact lt_hf' x hx
- linarith [hD.mul_sub_lt_image_sub_of_lt_deriv hf.neg hf'.neg hf'_gt x hx y hy hxy]
+ linarith [hD.mul_sub_lt_image_sub_of_lt_deriv hf.fun_neg hf'.neg hf'_gt x hx y hy hxy]
/-- Let `f : ℝ → ℝ` be a differentiable function. If `f' < C`, then `f` grows slower than
`C * x` on `D`, i.e., `f y - f x < C * (y - x)` whenever `x < y`. -/
@@ -358,7 +358,7 @@ theorem Convex.image_sub_le_mul_sub_of_deriv_le {D : Set ℝ} (hD : Convex ℝ D
have hf'_ge : ∀ x ∈ interior D, -C ≤ deriv (fun y => -f y) x := fun x hx => by
rw [deriv.fun_neg, neg_le_neg_iff]
exact le_hf' x hx
- linarith [hD.mul_sub_le_image_sub_of_le_deriv hf.neg hf'.neg hf'_ge x hx y hy hxy]
+ linarith [hD.mul_sub_le_image_sub_of_le_deriv hf.fun_neg hf'.neg hf'_ge x hx y hy hxy]
/-- Let `f : ℝ → ℝ` be a differentiable function. If `f' ≤ C`, then `f` grows at most as fast
as `C * x`, i.e., `f y - f x ≤ C * (y - x)` whenever `x ≤ y`. -/
diff --git a/Mathlib/Analysis/Calculus/FDeriv/Analytic.lean b/Mathlib/Analysis/Calculus/FDeriv/Analytic.lean
index e8a1130e401d16..fef5366e1c2606 100644
--- a/Mathlib/Analysis/Calculus/FDeriv/Analytic.lean
+++ b/Mathlib/Analysis/Calculus/FDeriv/Analytic.lean
@@ -88,7 +88,7 @@ theorem HasFPowerSeriesWithinAt.hasStrictFDerivWithinAt (h : HasFPowerSeriesWith
refine h.isBigO_image_sub_norm_mul_norm_sub.trans_isLittleO (IsLittleO.of_norm_right ?_)
refine isLittleO_iff_exists_eq_mul.2 ⟨fun y => ‖y - (x, x)‖, ?_, EventuallyEq.rfl⟩
apply Tendsto.mono_left _ nhdsWithin_le_nhds
- refine (continuous_id.sub continuous_const).norm.tendsto' _ _ ?_
+ refine (continuous_id.fun_sub continuous_const).norm.tendsto' _ _ ?_
rw [_root_.id, sub_self, norm_zero]
theorem HasFPowerSeriesAt.hasStrictFDerivAt (h : HasFPowerSeriesAt f p x) :
diff --git a/Mathlib/Analysis/Complex/Basic.lean b/Mathlib/Analysis/Complex/Basic.lean
index 6e4accd7174eea..be3b9d791a5c2b 100644
--- a/Mathlib/Analysis/Complex/Basic.lean
+++ b/Mathlib/Analysis/Complex/Basic.lean
@@ -93,7 +93,7 @@ instance (priority := 900) _root_.NormedAlgebra.complexToReal {A : Type*} [Semin
@[continuity, fun_prop]
theorem continuous_normSq : Continuous normSq := by
- simpa [← Complex.normSq_eq_norm_sq] using continuous_norm (E := ℂ).pow 2
+ simpa [← Complex.normSq_eq_norm_sq] using continuous_norm (E := ℂ).fun_pow 2
theorem nnnorm_eq_one_of_pow_eq_one {ζ : ℂ} {n : ℕ} (h : ζ ^ n = 1) (hn : n ≠ 0) : ‖ζ‖₊ = 1 :=
(pow_left_inj₀ zero_le zero_le hn).1 <| by rw [← nnnorm_pow, h, nnnorm_one, one_pow]
diff --git a/Mathlib/Analysis/Complex/Harmonic/Poisson.lean b/Mathlib/Analysis/Complex/Harmonic/Poisson.lean
index dff01e6642c121..23f6f29bbb0d2f 100644
--- a/Mathlib/Analysis/Complex/Harmonic/Poisson.lean
+++ b/Mathlib/Analysis/Complex/Harmonic/Poisson.lean
@@ -60,7 +60,7 @@ theorem HarmonicOnNhd.circleAverage_re_herglotzRieszKernel_smul
· apply h₂F
grind [mem_ball]
-- CircleIntegrable (fun z ↦ ((z - c + (w - c)) / (z - c - (w - c))).re • F z) c R
- apply (ContinuousOn.smul _ _).circleIntegrable'
+ apply (ContinuousOn.fun_smul _ _).circleIntegrable'
· apply (continuousOn_herglotz_riesz hw).mono
grind [mem_ball, dist_eq_norm, mem_sphere_iff_norm, (pos_of_mem_ball hw)]
· apply (h₁F.mono _).continuousOn (𝕜 := ℂ)
diff --git a/Mathlib/Analysis/Complex/LocallyUniformLimit.lean b/Mathlib/Analysis/Complex/LocallyUniformLimit.lean
index 85b8521479cb74..dfc0c7acfda663 100644
--- a/Mathlib/Analysis/Complex/LocallyUniformLimit.lean
+++ b/Mathlib/Analysis/Complex/LocallyUniformLimit.lean
@@ -65,13 +65,14 @@ theorem norm_cderiv_le (hr : 0 < r) (hf : ∀ w ∈ sphere z r, ‖f w‖ ≤ M)
theorem cderiv_sub (hr : 0 < r) (hf : ContinuousOn f (sphere z r))
(hg : ContinuousOn g (sphere z r)) : cderiv r (f - g) z = cderiv r f z - cderiv r g z := by
have h1 : ContinuousOn (fun w : ℂ => ((w - z) ^ 2)⁻¹) (sphere z r) := by
- refine ((continuous_id'.sub continuous_const).pow 2).continuousOn.inv₀ fun w hw h => hr.ne ?_
+ refine ((continuous_id'.fun_sub continuous_const).fun_pow 2).continuousOn.inv₀
+ fun w hw h => hr.ne ?_
rwa [mem_sphere_iff_norm, sq_eq_zero_iff.mp h, norm_zero] at hw
simp_rw [cderiv, ← smul_sub]
congr 1
simpa only [Pi.sub_apply, smul_sub] using
- circleIntegral.integral_sub ((h1.smul hf).circleIntegrable hr.le)
- ((h1.smul hg).circleIntegrable hr.le)
+ circleIntegral.integral_sub ((h1.fun_smul hf).circleIntegrable hr.le)
+ ((h1.fun_smul hg).circleIntegrable hr.le)
theorem norm_cderiv_lt (hr : 0 < r) (hfM : ∀ w ∈ sphere z r, ‖f w‖ < M)
(hf : ContinuousOn f (sphere z r)) : ‖cderiv r f z‖ < M / r := by
diff --git a/Mathlib/Analysis/Fourier/FourierTransformDeriv.lean b/Mathlib/Analysis/Fourier/FourierTransformDeriv.lean
index 5cf7150a64f798..3a84c32f77f3f7 100644
--- a/Mathlib/Analysis/Fourier/FourierTransformDeriv.lean
+++ b/Mathlib/Analysis/Fourier/FourierTransformDeriv.lean
@@ -310,7 +310,7 @@ lemma fourierPowSMulRight_eq_comp {f : V → E} {v : V} {n : ℕ} :
lemma _root_.Continuous.fourierPowSMulRight {f : V → E} (hf : Continuous f) (n : ℕ) :
Continuous (fun v ↦ fourierPowSMulRight L f v n) := by
simp_rw [fourierPowSMulRight_eq_comp]
- apply Continuous.const_smul
+ apply Continuous.fun_const_smul
apply (smulRightL ℝ (fun (_ : Fin n) ↦ W) E).continuous₂.comp₂ _ hf
exact Continuous.comp (map_continuous _) (continuous_pi (fun _ ↦ L.continuous))
diff --git a/Mathlib/Analysis/MellinInversion.lean b/Mathlib/Analysis/MellinInversion.lean
index 6fdc84bdf207bc..26d5ba10ed9d78 100644
--- a/Mathlib/Analysis/MellinInversion.lean
+++ b/Mathlib/Analysis/MellinInversion.lean
@@ -112,7 +112,7 @@ theorem mellinInv_mellin_eq (σ : ℝ) (f : ℝ → E) {x : ℝ} (hx : 0 < x) (h
simp_rw [neg_mul_eq_neg_mul] at this
exact this
replace hfx : ContinuousAt g (-Real.log x) := by
- refine ContinuousAt.smul (by fun_prop) (ContinuousAt.comp ?_ (by fun_prop))
+ refine ContinuousAt.fun_smul (by fun_prop) (ContinuousAt.comp ?_ (by fun_prop))
simpa [Real.exp_log hx] using hfx
calc
mellinInv σ (mellin f) x
diff --git a/Mathlib/Analysis/ODE/Gronwall.lean b/Mathlib/Analysis/ODE/Gronwall.lean
index 9529632c1d932e..bedc58a9ea540b 100644
--- a/Mathlib/Analysis/ODE/Gronwall.lean
+++ b/Mathlib/Analysis/ODE/Gronwall.lean
@@ -174,7 +174,7 @@ theorem dist_le_of_approx_trajectories_ODE_of_mem
simp only [dist_eq_norm] at ha ⊢
have h_deriv : ∀ t ∈ Ico a b, HasDerivWithinAt (fun t => f t - g t) (f' t - g' t) (Ici t) t :=
fun t ht => (hf' t ht).sub (hg' t ht)
- apply norm_le_gronwallBound_of_norm_deriv_right_le (hf.sub hg) h_deriv ha
+ apply norm_le_gronwallBound_of_norm_deriv_right_le (hf.fun_sub hg) h_deriv ha
intro t ht
have := dist_triangle4_right (f' t) (g' t) (v t (f t)) (v t (g t))
have := (hv t ht).dist_le_mul _ (hfs t ht) _ (hgs t ht)
diff --git a/Mathlib/Analysis/SpecialFunctions/Log/NegMulLog.lean b/Mathlib/Analysis/SpecialFunctions/Log/NegMulLog.lean
index 72625a7781eea3..913cb41fbbf83a 100644
--- a/Mathlib/Analysis/SpecialFunctions/Log/NegMulLog.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Log/NegMulLog.lean
@@ -184,7 +184,7 @@ lemma negMulLog_mul (x y : ℝ) : negMulLog (x * y) = y * negMulLog x + x * negM
ring
@[fun_prop] lemma continuous_negMulLog : Continuous negMulLog := by
- simpa only [negMulLog_eq_neg] using continuous_mul_log.neg
+ simpa only [negMulLog_eq_neg] using continuous_mul_log.fun_neg
lemma differentiableOn_negMulLog : DifferentiableOn ℝ negMulLog {0}ᶜ := by
simpa only [negMulLog_eq_neg] using! differentiableOn_mul_log.neg
diff --git a/Mathlib/Analysis/SpecialFunctions/Trigonometric/Cotangent.lean b/Mathlib/Analysis/SpecialFunctions/Trigonometric/Cotangent.lean
index 46321ea1bf82e3..d5b7ed3e60971b 100644
--- a/Mathlib/Analysis/SpecialFunctions/Trigonometric/Cotangent.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Trigonometric/Cotangent.lean
@@ -247,7 +247,7 @@ open scoped Nat
variable (k : ℕ)
private lemma contDiffOn_inv_linear (d : ℤ) : ContDiffOn ℂ k (fun z : ℂ ↦ 1 / (z + d)) ℂ_ℤ := by
- simpa using ContDiffOn.inv (by fun_prop) (fun x hx ↦ integerComplement_add_ne_zero hx d)
+ simpa using ContDiffOn.fun_inv (by fun_prop) (fun x hx ↦ integerComplement_add_ne_zero hx d)
lemma eqOn_iteratedDeriv_cotTerm (d : ℕ) :
EqOn (iteratedDeriv k (fun z ↦ cotTerm z d))
diff --git a/Mathlib/Analysis/SpecialFunctions/Trigonometric/EulerSineProd.lean b/Mathlib/Analysis/SpecialFunctions/Trigonometric/EulerSineProd.lean
index 6597cad5fe44f4..a4a808bed3e34d 100644
--- a/Mathlib/Analysis/SpecialFunctions/Trigonometric/EulerSineProd.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Trigonometric/EulerSineProd.lean
@@ -160,8 +160,10 @@ theorem integral_cos_pow_eq (n : ℕ) :
(∫ x in (0 : ℝ)..π / 2, cos x ^ n) = 1 / 2 * ∫ x in (0 : ℝ)..π, sin x ^ n := by
rw [mul_comm (1 / 2 : ℝ), ← div_eq_iff (one_div_ne_zero (two_ne_zero' ℝ)), ← div_mul, div_one,
mul_two]
- have L : IntervalIntegrable _ volume 0 (π / 2) := (continuous_sin.pow n).intervalIntegrable _ _
- have R : IntervalIntegrable _ volume (π / 2) π := (continuous_sin.pow n).intervalIntegrable _ _
+ have L : IntervalIntegrable _ volume 0 (π / 2) :=
+ (continuous_sin.fun_pow n).intervalIntegrable _ _
+ have R : IntervalIntegrable _ volume (π / 2) π :=
+ (continuous_sin.fun_pow n).intervalIntegrable _ _
rw [← integral_add_adjacent_intervals L R]
congr 1
· nth_rw 1 [(by ring : 0 = π / 2 - π / 2)]
diff --git a/Mathlib/MeasureTheory/Group/Arithmetic.lean b/Mathlib/MeasureTheory/Group/Arithmetic.lean
index d5e72ff2db5073..2e6e8412650755 100644
--- a/Mathlib/MeasureTheory/Group/Arithmetic.lean
+++ b/Mathlib/MeasureTheory/Group/Arithmetic.lean
@@ -349,23 +349,23 @@ section Inv
variable {G α : Type*} [Inv G] [MeasurableSpace G] [MeasurableInv G] {m : MeasurableSpace α}
{f : α → G} {μ : Measure α}
-@[to_additive (attr := fun_prop)]
-theorem Measurable.inv (hf : Measurable f) : Measurable fun x => (f x)⁻¹ :=
+@[to_fun (attr := to_additive (attr := fun_prop))]
+theorem Measurable.inv (hf : Measurable f) : Measurable f⁻¹ :=
measurable_inv.comp hf
-@[to_additive (attr := fun_prop)]
-theorem AEMeasurable.inv (hf : AEMeasurable f μ) : AEMeasurable (fun x => (f x)⁻¹) μ :=
+@[to_fun (attr := to_additive (attr := fun_prop))]
+theorem AEMeasurable.inv (hf : AEMeasurable f μ) : AEMeasurable f⁻¹ μ :=
measurable_inv.comp_aemeasurable hf
@[to_additive (attr := simp)]
theorem measurable_inv_iff {G : Type*} [InvolutiveInv G] [MeasurableSpace G] [MeasurableInv G]
{f : α → G} : (Measurable fun x => (f x)⁻¹) ↔ Measurable f :=
- ⟨fun h => by simpa only [inv_inv] using h.inv, fun h => h.inv⟩
+ ⟨fun h => by simpa only [inv_inv] using h.fun_inv, fun h => h.inv⟩
@[to_additive (attr := simp)]
theorem aemeasurable_inv_iff {G : Type*} [InvolutiveInv G] [MeasurableSpace G] [MeasurableInv G]
{f : α → G} : AEMeasurable (fun x => (f x)⁻¹) μ ↔ AEMeasurable f μ :=
- ⟨fun h => by simpa only [inv_inv] using h.inv, fun h => h.inv⟩
+ ⟨fun h => by simpa only [inv_inv] using h.fun_inv, fun h => h.inv⟩
@[to_additive]
instance Pi.measurableInv {ι : Type*} {α : ι → Type*} [∀ i, Inv (α i)]
diff --git a/Mathlib/MeasureTheory/Integral/CircleIntegral.lean b/Mathlib/MeasureTheory/Integral/CircleIntegral.lean
index db48abb1c5e777..e79060068bfc98 100644
--- a/Mathlib/MeasureTheory/Integral/CircleIntegral.lean
+++ b/Mathlib/MeasureTheory/Integral/CircleIntegral.lean
@@ -327,7 +327,7 @@ theorem circleIntegrable_iff [NormedSpace ℂ E] {f : ℂ → E} {c : ℂ} (R :
· have H : ∀ {θ}, circleMap 0 R θ * I ≠ 0 := fun {θ} => by simp [h₀, I_ne_zero]
simpa only [inv_smul_smul₀ H]
using ((continuous_circleMap 0 R).aestronglyMeasurable.mul_const
- I).aemeasurable.inv.aestronglyMeasurable.smul h.aestronglyMeasurable
+ I).aemeasurable.fun_inv.aestronglyMeasurable.smul h.aestronglyMeasurable
· simp [norm_smul, h₀]
theorem ContinuousOn.circleIntegrable' {f : ℂ → E} {c : ℂ} {R : ℝ}
diff --git a/Mathlib/MeasureTheory/Integral/CircleTransform.lean b/Mathlib/MeasureTheory/Integral/CircleTransform.lean
index b26e92516f63da..0cd653c004775c 100644
--- a/Mathlib/MeasureTheory/Integral/CircleTransform.lean
+++ b/Mathlib/MeasureTheory/Integral/CircleTransform.lean
@@ -97,7 +97,7 @@ theorem continuousOn_prod_circle_transform_function {R r : ℝ} (hr : r < R) {z
theorem continuousOn_norm_circleTransformBoundingFunction {R r : ℝ} (hr : r < R) (z : ℂ) :
ContinuousOn ((‖·‖) ∘ circleTransformBoundingFunction R z) (closedBall z r ×ˢ univ) := by
have : ContinuousOn (circleTransformBoundingFunction R z) (closedBall z r ×ˢ univ) := by
- apply_rules [ContinuousOn.smul, continuousOn_const]
+ apply_rules [ContinuousOn.fun_smul, continuousOn_const]
· simp only [deriv_circleMap]
apply_rules [ContinuousOn.mul, (continuous_circleMap 0 R).comp_continuousOn continuousOn_snd,
continuousOn_const]
diff --git a/Mathlib/MeasureTheory/Integral/IntervalIntegral/FundThmCalculus.lean b/Mathlib/MeasureTheory/Integral/IntervalIntegral/FundThmCalculus.lean
index 540bba846097fc..c19eb558728a70 100644
--- a/Mathlib/MeasureTheory/Integral/IntervalIntegral/FundThmCalculus.lean
+++ b/Mathlib/MeasureTheory/Integral/IntervalIntegral/FundThmCalculus.lean
@@ -1098,8 +1098,8 @@ theorem integral_le_sub_of_hasDeriv_right_of_le (hab : a ≤ b) (hcont : Continu
(hφg : ∀ x ∈ Ioo a b, φ x ≤ g' x) : (∫ y in a..b, φ y) ≤ g b - g a := by
rw [← neg_le_neg_iff]
convert!
- sub_le_integral_of_hasDeriv_right_of_le hab hcont.neg (fun x hx => (hderiv x hx).neg) φint.neg
- fun x hx => neg_le_neg (hφg x hx) using 1
+ sub_le_integral_of_hasDeriv_right_of_le hab hcont.fun_neg (fun x hx => (hderiv x hx).neg)
+ φint.neg fun x hx => neg_le_neg (hφg x hx) using 1
· abel
· simp only [← integral_neg]; rfl
diff --git a/Mathlib/MeasureTheory/Measure/WithDensity.lean b/Mathlib/MeasureTheory/Measure/WithDensity.lean
index 2cdfce72212c92..3337ad31fdd514 100644
--- a/Mathlib/MeasureTheory/Measure/WithDensity.lean
+++ b/Mathlib/MeasureTheory/Measure/WithDensity.lean
@@ -208,7 +208,7 @@ theorem withDensity_ofReal_mutuallySingular {f : α → ℝ} (hf : Measurable f)
refine ⟨S, hS, ?_, ?_⟩
· rw [withDensity_apply _ hS, lintegral_eq_zero_iff hf.ennreal_ofReal, EventuallyEq]
exact (ae_restrict_mem hS).mono fun x hx => ENNReal.ofReal_eq_zero.2 (le_of_lt hx)
- · rw [withDensity_apply _ hS.compl, lintegral_eq_zero_iff hf.neg.ennreal_ofReal, EventuallyEq]
+ · rw [withDensity_apply _ hS.compl, lintegral_eq_zero_iff hf.fun_neg.ennreal_ofReal, EventuallyEq]
exact
(ae_restrict_mem hS.compl).mono fun x hx =>
ENNReal.ofReal_eq_zero.2 (not_lt.1 <| mt neg_pos.1 hx)
@@ -486,9 +486,9 @@ theorem lintegral_withDensity_eq_lintegral_mul_non_measurable (μ : Measure α)
dsimp
rw [mul_comm, ← div_eq_mul_inv]
exact div_le_of_le_mul' (hi x)
- refine le_iSup_of_le (fun x => (f x)⁻¹ * i x) (le_iSup_of_le (f_meas.inv.mul i_meas) ?_)
+ refine le_iSup_of_le (fun x => (f x)⁻¹ * i x) (le_iSup_of_le (f_meas.fun_inv.mul i_meas) ?_)
refine le_iSup_of_le A ?_
- rw [lintegral_withDensity_eq_lintegral_mul _ f_meas (f_meas.inv.mul i_meas)]
+ rw [lintegral_withDensity_eq_lintegral_mul _ f_meas (f_meas.fun_inv.mul i_meas)]
apply lintegral_mono_ae
filter_upwards [hf]
intro x h'x
@@ -550,7 +550,7 @@ theorem withDensity_mul (μ : Measure α) {f g : α → ℝ≥0∞} (hf : Measur
lemma withDensity_inv_same_le {μ : Measure α} {f : α → ℝ≥0∞} (hf : AEMeasurable f μ) :
(μ.withDensity f).withDensity f⁻¹ ≤ μ := by
change (μ.withDensity f).withDensity (fun x ↦ (f x)⁻¹) ≤ μ
- rw [← withDensity_mul₀ hf hf.inv]
+ rw [← withDensity_mul₀ hf hf.fun_inv]
suffices (f * fun x ↦ (f x)⁻¹) ≤ᵐ[μ] 1 by
refine (withDensity_mono this).trans ?_
rw [withDensity_one]
@@ -560,7 +560,7 @@ lemma withDensity_inv_same_le {μ : Measure α} {f : α → ℝ≥0∞} (hf : AE
lemma withDensity_inv_same₀ {μ : Measure α} {f : α → ℝ≥0∞}
(hf : AEMeasurable f μ) (hf_ne_zero : ∀ᵐ x ∂μ, f x ≠ 0) (hf_ne_top : ∀ᵐ x ∂μ, f x ≠ ∞) :
(μ.withDensity f).withDensity (fun x ↦ (f x)⁻¹) = μ := by
- rw [← withDensity_mul₀ hf hf.inv]
+ rw [← withDensity_mul₀ hf hf.fun_inv]
suffices (f * fun x ↦ (f x)⁻¹) =ᵐ[μ] 1 by
rw [withDensity_congr_ae this, withDensity_one]
filter_upwards [hf_ne_zero, hf_ne_top] with x hf_ne_zero hf_ne_top
diff --git a/Mathlib/NumberTheory/LSeries/DirichletContinuation.lean b/Mathlib/NumberTheory/LSeries/DirichletContinuation.lean
index 2d095f687b1e24..947ea4773befb0 100644
--- a/Mathlib/NumberTheory/LSeries/DirichletContinuation.lean
+++ b/Mathlib/NumberTheory/LSeries/DirichletContinuation.lean
@@ -390,7 +390,7 @@ lemma continuousOn_neg_logDeriv_LFunction_of_nontriv (hχ : χ ≠ 1) :
ContinuousOn (fun s ↦ -deriv (LFunction χ) s / LFunction χ s) {s | LFunction χ s ≠ 0} := by
have h := differentiable_LFunction hχ
simpa [neg_div] using! ((h.contDiff.continuous_deriv le_rfl).continuousOn.div
- h.continuous.continuousOn fun _ hw ↦ hw).neg
+ h.continuous.continuousOn fun _ hw ↦ hw).fun_neg
end nontrivial
diff --git a/Mathlib/Probability/Moments/SubGaussian.lean b/Mathlib/Probability/Moments/SubGaussian.lean
index 7751694c849b57..0de6c2ca8428f9 100644
--- a/Mathlib/Probability/Moments/SubGaussian.lean
+++ b/Mathlib/Probability/Moments/SubGaussian.lean
@@ -852,7 +852,7 @@ lemma hasSubgaussianMGF_of_mem_Icc_of_integral_eq_zero [IsProbabilityMeasure μ]
_ ≤ exp ((‖-a - -b‖₊ / 2) ^ 2 * (-t) ^ 2 / 2) := by
apply ProbabilityTheory.mgf_le_of_mem_Icc_of_integral_eq_zero (hm.neg)
· filter_upwards [hb] with ω ⟨hl, hr⟩ using ⟨neg_le_neg_iff.2 hr, neg_le_neg_iff.2 hl⟩
- · rw [integral_neg, hc, neg_zero]
+ · simp only [Pi.neg_apply]; rw [integral_neg, hc, neg_zero]
· rwa [Left.neg_pos_iff]
_ = exp (((‖b - a‖₊ / 2) ^ 2) * t ^ 2 / 2) := by ring_nf
diff --git a/Mathlib/Topology/Algebra/ConstMulAction.lean b/Mathlib/Topology/Algebra/ConstMulAction.lean
index 0ebae3e977eda3..69f48dd54a30fe 100644
--- a/Mathlib/Topology/Algebra/ConstMulAction.lean
+++ b/Mathlib/Topology/Algebra/ConstMulAction.lean
@@ -92,22 +92,22 @@ theorem Filter.Tendsto.const_smul {f : β → α} {l : Filter β} {a : α} (hf :
variable [TopologicalSpace β] {g : β → α} {b : β} {s : Set β}
-@[to_additive]
+@[to_fun (attr := to_additive (attr := fun_prop))]
nonrec theorem ContinuousWithinAt.const_smul (hg : ContinuousWithinAt g s b) (c : M) :
- ContinuousWithinAt (fun x => c • g x) s b :=
+ ContinuousWithinAt (c • g) s b :=
hg.const_smul c
-@[to_additive (attr := fun_prop)]
+@[to_fun (attr := to_additive (attr := fun_prop))]
nonrec theorem ContinuousAt.const_smul (hg : ContinuousAt g b) (c : M) :
- ContinuousAt (fun x => c • g x) b :=
+ ContinuousAt (c • g) b :=
hg.const_smul c
-@[to_additive (attr := fun_prop)]
+@[to_fun (attr := to_additive (attr := fun_prop))]
theorem ContinuousOn.const_smul (hg : ContinuousOn g s) (c : M) :
- ContinuousOn (fun x => c • g x) s := fun x hx => (hg x hx).const_smul c
+ ContinuousOn (c • g) s := fun x hx => (hg x hx).const_smul c
-@[to_additive (attr := continuity, fun_prop)]
-theorem Continuous.const_smul (hg : Continuous g) (c : M) : Continuous fun x => c • g x :=
+@[to_fun (attr := to_additive (attr := continuity, fun_prop))]
+theorem Continuous.const_smul (hg : Continuous g) (c : M) : Continuous (c • g) :=
(continuous_const_smul _).comp hg
/-- If a scalar is central, then its right action is continuous when its left action is. -/
@@ -157,7 +157,7 @@ theorem Topology.IsInducing.continuousConstSMul {N β : Type*} [SMul N β] [Topo
{g : β → α} (hg : IsInducing g) (f : N → M) (hf : ∀ {c : N} {x : β}, g (c • x) = f c • g x) :
ContinuousConstSMul N β where
continuous_const_smul c := by
- simpa only [Function.comp_def, hf, hg.continuous_iff] using hg.continuous.const_smul (f c)
+ simpa only [Function.comp_def, hf, hg.continuous_iff] using hg.continuous.fun_const_smul (f c)
@[to_additive]
theorem smul_closure_subset (c : M) (s : Set α) : c • closure s ⊆ closure (c • s) :=
diff --git a/Mathlib/Topology/Algebra/Group/Basic.lean b/Mathlib/Topology/Algebra/Group/Basic.lean
index 62d93826965a48..6f485a3f813c0c 100644
--- a/Mathlib/Topology/Algebra/Group/Basic.lean
+++ b/Mathlib/Topology/Algebra/Group/Basic.lean
@@ -410,7 +410,7 @@ end LatticeOps
theorem Topology.IsInducing.continuousInv {G H : Type*} [Inv G] [Inv H] [TopologicalSpace G]
[TopologicalSpace H] [ContinuousInv H] {f : G → H} (hf : IsInducing f)
(hf_inv : ∀ x, f x⁻¹ = (f x)⁻¹) : ContinuousInv G :=
- ⟨hf.continuous_iff.2 <| by simpa only [Function.comp_def, hf_inv] using hf.continuous.inv⟩
+ ⟨hf.continuous_iff.2 <| by simpa only [Function.comp_def, hf_inv] using hf.continuous.fun_inv⟩
section IsTopologicalGroup
@@ -465,7 +465,7 @@ section ZPow
@[to_additive (attr := continuity, fun_prop)]
theorem continuous_zpow : ∀ z : ℤ, Continuous fun a : G => a ^ z
| Int.ofNat n => by simpa using continuous_pow n
- | Int.negSucc n => by simpa using (continuous_pow (n + 1)).inv
+ | Int.negSucc n => by simpa using (continuous_pow (n + 1)).fun_inv
instance AddGroup.continuousConstSMul_int {A} [AddGroup A] [TopologicalSpace A]
[IsTopologicalAddGroup A] : ContinuousConstSMul ℤ A :=
@@ -475,8 +475,8 @@ instance AddGroup.continuousSMul_int {A} [AddGroup A] [TopologicalSpace A]
[IsTopologicalAddGroup A] : ContinuousSMul ℤ A :=
⟨continuous_prod_of_discrete_left.mpr continuous_zsmul⟩
-@[to_additive (attr := continuity, fun_prop)]
-theorem Continuous.zpow {f : α → G} (h : Continuous f) (z : ℤ) : Continuous fun b => f b ^ z :=
+@[to_fun (attr := to_additive (attr := continuity, fun_prop))]
+theorem Continuous.zpow {f : α → G} (h : Continuous f) (z : ℤ) : Continuous (f ^ z) :=
(continuous_zpow z).comp h
@[to_additive]
@@ -492,19 +492,19 @@ theorem Filter.Tendsto.zpow {α} {l : Filter α} {f : α → G} {x : G} (hf : Te
(z : ℤ) : Tendsto (fun x => f x ^ z) l (𝓝 (x ^ z)) :=
(continuousAt_zpow _ _).tendsto.comp hf
-@[to_additive]
+@[to_fun (attr := to_additive (attr := fun_prop))]
theorem ContinuousWithinAt.zpow {f : α → G} {x : α} {s : Set α} (hf : ContinuousWithinAt f s x)
- (z : ℤ) : ContinuousWithinAt (fun x => f x ^ z) s x :=
+ (z : ℤ) : ContinuousWithinAt (f ^ z) s x :=
Filter.Tendsto.zpow hf z
-@[to_additive (attr := fun_prop)]
+@[to_fun (attr := to_additive (attr := fun_prop))]
theorem ContinuousAt.zpow {f : α → G} {x : α} (hf : ContinuousAt f x) (z : ℤ) :
- ContinuousAt (fun x => f x ^ z) x :=
+ ContinuousAt (f ^ z) x :=
Filter.Tendsto.zpow hf z
-@[to_additive (attr := fun_prop)]
+@[to_fun (attr := to_additive (attr := fun_prop))]
theorem ContinuousOn.zpow {f : α → G} {s : Set α} (hf : ContinuousOn f s) (z : ℤ) :
- ContinuousOn (fun x => f x ^ z) s := fun x hx => (hf x hx).zpow z
+ ContinuousOn (f ^ z) s := fun x hx => (hf x hx).zpow z
end ZPow
diff --git a/Mathlib/Topology/Algebra/Group/Defs.lean b/Mathlib/Topology/Algebra/Group/Defs.lean
index 6a769265aea264..714d597f316ab4 100644
--- a/Mathlib/Topology/Algebra/Group/Defs.lean
+++ b/Mathlib/Topology/Algebra/Group/Defs.lean
@@ -72,21 +72,21 @@ theorem Filter.Tendsto.inv {f : α → G} {l : Filter α} {y : G} (h : Tendsto f
variable {f : X → G} {s : Set X} {x : X}
-@[to_additive (attr := continuity, fun_prop)]
-theorem Continuous.inv (hf : Continuous f) : Continuous fun x => (f x)⁻¹ :=
+@[to_fun (attr := to_additive (attr := continuity, fun_prop))]
+theorem Continuous.inv (hf : Continuous f) : Continuous f⁻¹ :=
continuous_inv.comp hf
-@[to_additive]
+@[to_fun (attr := to_additive (attr := fun_prop))]
nonrec theorem ContinuousWithinAt.inv (hf : ContinuousWithinAt f s x) :
- ContinuousWithinAt (fun x => (f x)⁻¹) s x :=
+ ContinuousWithinAt f⁻¹ s x :=
hf.inv
-@[to_additive (attr := fun_prop)]
-nonrec theorem ContinuousAt.inv (hf : ContinuousAt f x) : ContinuousAt (fun x => (f x)⁻¹) x :=
+@[to_fun (attr := to_additive (attr := fun_prop))]
+nonrec theorem ContinuousAt.inv (hf : ContinuousAt f x) : ContinuousAt f⁻¹ x :=
hf.inv
-@[to_additive (attr := fun_prop)]
-theorem ContinuousOn.inv (hf : ContinuousOn f s) : ContinuousOn (fun x => (f x)⁻¹) s := fun x hx ↦
+@[to_fun (attr := to_additive (attr := fun_prop))]
+theorem ContinuousOn.inv (hf : ContinuousOn f s) : ContinuousOn f⁻¹ s := fun x hx ↦
(hf x hx).inv
end ContinuousInv
@@ -144,22 +144,22 @@ theorem Filter.Tendsto.div' {f g : α → G} {l : Filter α} {a b : G} (hf : Ten
variable {f g : X → G} {s : Set X} {x : X}
-@[to_additive (attr := fun_prop) sub]
+@[to_additive (attr := to_fun (attr := fun_prop)) sub]
nonrec theorem ContinuousAt.div' (hf : ContinuousAt f x) (hg : ContinuousAt g x) :
- ContinuousAt (fun x => f x / g x) x :=
+ ContinuousAt (f / g) x :=
hf.div' hg
-@[to_additive sub]
+@[to_additive (attr := to_fun (attr := fun_prop)) sub]
theorem ContinuousWithinAt.div' (hf : ContinuousWithinAt f s x) (hg : ContinuousWithinAt g s x) :
- ContinuousWithinAt (fun x => f x / g x) s x :=
+ ContinuousWithinAt (f / g) s x :=
Filter.Tendsto.div' hf hg
-@[to_additive (attr := fun_prop) sub]
+@[to_additive (attr := to_fun (attr := fun_prop)) sub]
theorem ContinuousOn.div' (hf : ContinuousOn f s) (hg : ContinuousOn g s) :
- ContinuousOn (fun x => f x / g x) s := fun x hx => (hf x hx).div' (hg x hx)
+ ContinuousOn (f / g) s := fun x hx => (hf x hx).div' (hg x hx)
-@[to_additive (attr := continuity, fun_prop) sub]
-theorem Continuous.div' (hf : Continuous f) (hg : Continuous g) : Continuous fun x => f x / g x :=
+@[to_additive (attr := to_fun (attr := continuity, fun_prop)) sub]
+theorem Continuous.div' (hf : Continuous f) (hg : Continuous g) : Continuous (f / g) :=
continuous_div'.comp₂ hf hg
end ContinuousDiv
diff --git a/Mathlib/Topology/Algebra/GroupCompletion.lean b/Mathlib/Topology/Algebra/GroupCompletion.lean
index 6f0c55b83a8665..9c96a32278eae8 100644
--- a/Mathlib/Topology/Algebra/GroupCompletion.lean
+++ b/Mathlib/Topology/Algebra/GroupCompletion.lean
@@ -167,8 +167,8 @@ instance {M} [Monoid M] [DistribMulAction M α] [UniformContinuousConstSMul M α
{ (inferInstance : MulAction M <| Completion α) with
smul_add := fun r x y ↦
induction_on₂ x y
- (isClosed_eq ((continuous_fst.add continuous_snd).const_smul _)
- ((continuous_fst.const_smul _).add (continuous_snd.const_smul _)))
+ (isClosed_eq ((continuous_fst.fun_add continuous_snd).fun_const_smul _)
+ ((continuous_fst.fun_const_smul _).fun_add (continuous_snd.fun_const_smul _)))
fun a b ↦ by simp only [← coe_add, ← coe_smul, smul_add]
smul_zero := fun r ↦ by rw [← coe_zero, ← coe_smul, smul_zero r] }
diff --git a/Mathlib/Topology/Algebra/GroupWithZero.lean b/Mathlib/Topology/Algebra/GroupWithZero.lean
index 08fe633b384d6e..f3e4162725415f 100644
--- a/Mathlib/Topology/Algebra/GroupWithZero.lean
+++ b/Mathlib/Topology/Algebra/GroupWithZero.lean
@@ -112,22 +112,23 @@ theorem Filter.Tendsto.inv₀ {a : G₀} (hf : Tendsto f l (𝓝 a)) (ha : a ≠
variable [TopologicalSpace α]
+@[to_fun (attr := fun_prop)]
nonrec theorem ContinuousWithinAt.inv₀ (hf : ContinuousWithinAt f s a) (ha : f a ≠ 0) :
- ContinuousWithinAt (fun x => (f x)⁻¹) s a :=
+ ContinuousWithinAt f⁻¹ s a :=
hf.inv₀ ha
-@[fun_prop]
+@[to_fun (attr := fun_prop)]
nonrec theorem ContinuousAt.inv₀ (hf : ContinuousAt f a) (ha : f a ≠ 0) :
- ContinuousAt (fun x => (f x)⁻¹) a :=
+ ContinuousAt f⁻¹ a :=
hf.inv₀ ha
-@[continuity, fun_prop]
-theorem Continuous.inv₀ (hf : Continuous f) (h0 : ∀ x, f x ≠ 0) : Continuous fun x => (f x)⁻¹ :=
+@[to_fun (attr := continuity, fun_prop)]
+theorem Continuous.inv₀ (hf : Continuous f) (h0 : ∀ x, f x ≠ 0) : Continuous f⁻¹ :=
continuous_iff_continuousAt.2 fun x => (hf.tendsto x).inv₀ (h0 x)
-@[fun_prop]
+@[to_fun (attr := fun_prop)]
theorem ContinuousOn.inv₀ (hf : ContinuousOn f s) (h0 : ∀ x ∈ s, f x ≠ 0) :
- ContinuousOn (fun x => (f x)⁻¹) s := fun x hx => (hf x hx).inv₀ (h0 x hx)
+ ContinuousOn f⁻¹ s := fun x hx => (hf x hx).inv₀ (h0 x hx)
end Inv₀
diff --git a/Mathlib/Topology/Algebra/LinearMapCompletion.lean b/Mathlib/Topology/Algebra/LinearMapCompletion.lean
index 9506ff449605a0..65520a2f0cada7 100644
--- a/Mathlib/Topology/Algebra/LinearMapCompletion.lean
+++ b/Mathlib/Topology/Algebra/LinearMapCompletion.lean
@@ -42,7 +42,7 @@ noncomputable def completion (f : α →SL[σ] β) : Completion α →SL[σ] Com
induction x using induction_on with
| hp =>
exact isClosed_eq (continuous_map.comp <| continuous_const_smul r)
- (continuous_map.const_smul _)
+ (continuous_map.fun_const_smul _)
| ih x => simp [← Completion.coe_smul]
@[simp]
diff --git a/Mathlib/Topology/Algebra/Module/Spaces/WeakBilin.lean b/Mathlib/Topology/Algebra/Module/Spaces/WeakBilin.lean
index 00a041c4b2d9f4..e75d37c8867b41 100644
--- a/Mathlib/Topology/Algebra/Module/Spaces/WeakBilin.lean
+++ b/Mathlib/Topology/Algebra/Module/Spaces/WeakBilin.lean
@@ -128,7 +128,7 @@ set_option backward.isDefEq.respectTransparency false in
/-- Scalar multiplication by `𝕜` on `WeakBilin B` is continuous. -/
instance instContinuousSMul [ContinuousSMul 𝕜 𝕜] : ContinuousSMul 𝕜 (WeakBilin B) := by
refine ⟨continuous_induced_rng.2 ?_⟩
- refine cast (congr_arg _ ?_) (continuous_fst.smul ((coeFn_continuous B).comp continuous_snd))
+ refine cast (congr_arg _ ?_) (continuous_fst.fun_smul ((coeFn_continuous B).comp continuous_snd))
ext
simp only [Function.comp_apply, Pi.smul_apply, map_smulₛₗ, RingHom.id_apply, LinearMap.smul_apply]
diff --git a/Mathlib/Topology/Algebra/Monoid.lean b/Mathlib/Topology/Algebra/Monoid.lean
index 1f2501628fd2d6..b175611ea822d6 100644
--- a/Mathlib/Topology/Algebra/Monoid.lean
+++ b/Mathlib/Topology/Algebra/Monoid.lean
@@ -809,8 +809,8 @@ instance AddMonoid.continuousSMul_nat {A} [AddMonoid A] [TopologicalSpace A]
-- To properly fix this, we should make sure that `continuity` applies its
-- lemmas with reducible transparency, preventing the unfolding of `^`. But this
-- is quite an invasive change.
-@[to_additive (attr := aesop safe -100 (rule_sets := [Continuous]), fun_prop)]
-theorem Continuous.pow {f : X → M} (h : Continuous f) (n : ℕ) : Continuous fun b => f b ^ n :=
+@[to_fun (attr := to_additive (attr := aesop safe -100 (rule_sets := [Continuous]), fun_prop))]
+theorem Continuous.pow {f : X → M} (h : Continuous f) (n : ℕ) : Continuous (f ^ n) :=
(continuous_pow n).comp h
@[to_additive]
@@ -826,19 +826,19 @@ theorem Filter.Tendsto.pow {l : Filter α} {f : α → M} {x : M} (hf : Tendsto
Tendsto (fun x => f x ^ n) l (𝓝 (x ^ n)) :=
(continuousAt_pow _ _).tendsto.comp hf
-@[to_additive]
+@[to_fun (attr := to_additive (attr := fun_prop))]
theorem ContinuousWithinAt.pow {f : X → M} {x : X} {s : Set X} (hf : ContinuousWithinAt f s x)
- (n : ℕ) : ContinuousWithinAt (fun x => f x ^ n) s x :=
+ (n : ℕ) : ContinuousWithinAt (f ^ n) s x :=
Filter.Tendsto.pow hf n
-@[to_additive (attr := fun_prop)]
+@[to_fun (attr := to_additive (attr := fun_prop))]
theorem ContinuousAt.pow {f : X → M} {x : X} (hf : ContinuousAt f x) (n : ℕ) :
- ContinuousAt (fun x => f x ^ n) x :=
+ ContinuousAt (f ^ n) x :=
Filter.Tendsto.pow hf n
-@[to_additive (attr := fun_prop)]
+@[to_fun (attr := to_additive (attr := fun_prop))]
theorem ContinuousOn.pow {f : X → M} {s : Set X} (hf : ContinuousOn f s) (n : ℕ) :
- ContinuousOn (fun x => f x ^ n) s := fun x hx => (hf x hx).pow n
+ ContinuousOn (f ^ n) s := fun x hx => (hf x hx).pow n
/-- If `R` acts on `A` via `A`, then continuous multiplication implies continuous scalar
multiplication by constants.
diff --git a/Mathlib/Topology/Algebra/MulAction.lean b/Mathlib/Topology/Algebra/MulAction.lean
index 04f549af6779c9..0ec40fe17fabd7 100644
--- a/Mathlib/Topology/Algebra/MulAction.lean
+++ b/Mathlib/Topology/Algebra/MulAction.lean
@@ -117,22 +117,22 @@ theorem Filter.Tendsto.smul_const {f : α → M} {l : Filter α} {c : M} (hf : T
variable {f : Y → M} {g : Y → X} {b : Y} {s : Set Y}
-@[to_additive (attr := fun_prop)]
+@[to_fun (attr := to_additive (attr := fun_prop))]
theorem ContinuousWithinAt.smul (hf : ContinuousWithinAt f s b) (hg : ContinuousWithinAt g s b) :
- ContinuousWithinAt (fun x => f x • g x) s b :=
+ ContinuousWithinAt (f • g) s b :=
Filter.Tendsto.smul hf hg
-@[to_additive (attr := fun_prop)]
+@[to_fun (attr := to_additive (attr := fun_prop))]
theorem ContinuousAt.smul (hf : ContinuousAt f b) (hg : ContinuousAt g b) :
- ContinuousAt (fun x => f x • g x) b :=
+ ContinuousAt (f • g) b :=
Filter.Tendsto.smul hf hg
-@[to_additive (attr := fun_prop)]
+@[to_fun (attr := to_additive (attr := fun_prop))]
theorem ContinuousOn.smul (hf : ContinuousOn f s) (hg : ContinuousOn g s) :
- ContinuousOn (fun x => f x • g x) s := fun x hx => (hf x hx).smul (hg x hx)
+ ContinuousOn (f • g) s := fun x hx => (hf x hx).smul (hg x hx)
-@[to_additive (attr := continuity, fun_prop)]
-theorem Continuous.smul (hf : Continuous f) (hg : Continuous g) : Continuous fun x => f x • g x :=
+@[to_fun (attr := to_additive (attr := continuity, fun_prop))]
+theorem Continuous.smul (hf : Continuous f) (hg : Continuous g) : Continuous (f • g) :=
continuous_smul.comp (hf.prodMk hg)
/-- If a scalar action is central, then its right action is continuous when its left action is. -/
@@ -191,7 +191,7 @@ lemma Topology.IsInducing.continuousSMul {N : Type*} [SMul N Y] [TopologicalSpac
ContinuousSMul N Y where
continuous_smul := by
simpa only [hg.continuous_iff, Function.comp_def, hsmul]
- using (hf.comp continuous_fst).smul <| hg.continuous.comp continuous_snd
+ using (hf.comp continuous_fst).fun_smul <| hg.continuous.comp continuous_snd
@[to_additive]
instance SMulMemClass.continuousSMul {S : Type*} [SetLike S X] [SMulMemClass S M X] (s : S) :
diff --git a/Mathlib/Topology/Algebra/UniformMulAction.lean b/Mathlib/Topology/Algebra/UniformMulAction.lean
index 99c5fecdd7ece9..a5969d94f8b291 100644
--- a/Mathlib/Topology/Algebra/UniformMulAction.lean
+++ b/Mathlib/Topology/Algebra/UniformMulAction.lean
@@ -238,7 +238,7 @@ noncomputable instance [Monoid M] [MulAction M X] [UniformContinuousConstSMul M
MulAction M (Completion X) where
one_smul := ext' (continuous_const_smul _) continuous_id fun a => by rw [← coe_smul, one_smul]
mul_smul x y :=
- ext' (continuous_const_smul _) ((continuous_const_smul _).const_smul _) fun a => by
+ ext' (continuous_const_smul _) ((continuous_const_smul _).fun_const_smul _) fun a => by
simp only [← coe_smul, mul_smul]
end Completion
diff --git a/Mathlib/Topology/ContinuousMap/Ideals.lean b/Mathlib/Topology/ContinuousMap/Ideals.lean
index b13c306a8ecc46..7ab99056669275 100644
--- a/Mathlib/Topology/ContinuousMap/Ideals.lean
+++ b/Mathlib/Topology/ContinuousMap/Ideals.lean
@@ -264,7 +264,7 @@ theorem idealOfSet_ofIdeal_eq_closure (I : Ideal C(X, 𝕜)) :
refine
⟨{y : X | g y ≠ 0} ∩ t,
mem_nhdsWithin_iff_exists_mem_nhds_inter.mpr ⟨_, this, Set.Subset.rfl⟩,
- ⟨⟨fun x => ‖g x‖₊ ^ 2, (map_continuous g).nnnorm.pow 2⟩, ?_, fun x hx =>
+ ⟨⟨fun x => ‖g x‖₊ ^ 2, (map_continuous g).nnnorm.fun_pow 2⟩, ?_, fun x hx =>
pow_pos (norm_pos_iff.mpr hx.1) 2⟩⟩
convert! I.mul_mem_left (star g) hI
ext
diff --git a/Mathlib/Topology/Instances/ENNReal/Lemmas.lean b/Mathlib/Topology/Instances/ENNReal/Lemmas.lean
index 1e3ffeaae4adad..06a8d14917dfe5 100644
--- a/Mathlib/Topology/Instances/ENNReal/Lemmas.lean
+++ b/Mathlib/Topology/Instances/ENNReal/Lemmas.lean
@@ -464,7 +464,7 @@ theorem inv_liminf {ι : Sort _} {x : ι → ℝ≥0∞} {l : Filter ι} :
@[fun_prop]
protected theorem continuous_zpow : ∀ n : ℤ, Continuous (· ^ n : ℝ≥0∞ → ℝ≥0∞)
| (n : ℕ) => mod_cast ENNReal.continuous_pow n
- | .negSucc n => by simpa using (ENNReal.continuous_pow _).inv
+ | .negSucc n => by simpa using (ENNReal.continuous_pow _).fun_inv
@[deprecated (since := "2026-01-15")] protected alias tendsto_inv_iff := tendsto_inv_iff
From 059f3b6cd2acee40e6db108d5c9946ae2d0efa29 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=ABl=20Riou?=
<37772949+joelriou@users.noreply.github.com>
Date: Tue, 23 Jun 2026 09:25:09 +0000
Subject: [PATCH 0278/1300] feat(Algebra/Homology): existence of injective
resolutions for cochain complexes (#40887)
---
.../Homology/DerivedCategory/TStructure.lean | 2 +-
.../Homology/Embedding/CochainComplex.lean | 20 +++++++--
.../Algebra/Homology/Factorizations/CM5a.lean | 43 ++++++++++++++++++-
3 files changed, 59 insertions(+), 6 deletions(-)
diff --git a/Mathlib/Algebra/Homology/DerivedCategory/TStructure.lean b/Mathlib/Algebra/Homology/DerivedCategory/TStructure.lean
index ae92cb4c519546..085d7772a0baca 100644
--- a/Mathlib/Algebra/Homology/DerivedCategory/TStructure.lean
+++ b/Mathlib/Algebra/Homology/DerivedCategory/TStructure.lean
@@ -76,7 +76,7 @@ noncomputable def TStructure.t : TStructure (DerivedCategory C) where
rw [id_comp]
rfl
· dsimp
- rw [← Q.map_comp, CochainComplex.g_shortComplexTruncLEX₃ToTruncGE,
+ rw [← Q.map_comp, CochainComplex.g_shortComplexTruncLEX₃ToTruncGE ..,
Iso.hom_inv_id_assoc]
/-- Given `X : DerivedCategory C` and `n : ℤ`, this property means
diff --git a/Mathlib/Algebra/Homology/Embedding/CochainComplex.lean b/Mathlib/Algebra/Homology/Embedding/CochainComplex.lean
index 800a7708629da9..ae06df80b4d34b 100644
--- a/Mathlib/Algebra/Homology/Embedding/CochainComplex.lean
+++ b/Mathlib/Algebra/Homology/Embedding/CochainComplex.lean
@@ -431,19 +431,33 @@ lemma shortComplexTruncLE_shortExact (n : ℤ) :
(K.shortComplexTruncLE n).ShortExact := by
apply HomologicalComplex.shortComplexTruncLE_shortExact
-variable (n₀ n₁ : ℤ) (h : n₀ + 1 = n₁)
+variable (n₀ n₁ : ℤ)
/-- The canonical morphism `(K.shortComplexTruncLE n₀).X₃ ⟶ K.truncGE n₁`. -/
-noncomputable abbrev shortComplexTruncLEX₃ToTruncGE :
+noncomputable abbrev shortComplexTruncLEX₃ToTruncGE (h : n₀ + 1 = n₁ := by lia) :
(K.shortComplexTruncLE n₀).X₃ ⟶ K.truncGE n₁ :=
HomologicalComplex.shortComplexTruncLEX₃ToTruncGE K
(Embedding.embeddingUpInt_areComplementary n₀ n₁ h)
@[reassoc]
-lemma g_shortComplexTruncLEX₃ToTruncGE :
+lemma g_shortComplexTruncLEX₃ToTruncGE (h : n₀ + 1 = n₁ := by lia) :
(K.shortComplexTruncLE n₀).g ≫ K.shortComplexTruncLEX₃ToTruncGE n₀ n₁ h = K.πTruncGE n₁ := by
apply HomologicalComplex.g_shortComplexTruncLEX₃ToTruncGE
+lemma injective_opcycles [Injective (K.X n₀)] [Injective (K.X n₁)]
+ [K.IsStrictlyGE n₀] (hK : K.ExactAt n₀) (h : n₀ + 1 = n₁ := by lia) :
+ Injective (K.opcycles n₁) := by
+ let S : ShortComplex C := ShortComplex.mk (K.d n₀ n₁) (K.pOpcycles n₁) (by simp)
+ have : Mono S.f := by
+ let T := K.sc' (n₀ - 1) n₀ n₁
+ have hT : T.Exact := by
+ rwa [← K.exactAt_iff' (n₀ - 1) n₀ n₁ (by simp) (by simpa)]
+ exact hT.mono_g ((K.isZero_of_isStrictlyGE n₀ _).eq_of_src ..)
+ have hS : S.ShortExact :=
+ { exact := S.exact_of_g_is_cokernel (K.opcyclesIsCokernel n₀ n₁ (by simp [← h])) }
+ exact Retract.injective
+ { i := _, r := _, retract := (hS.splittingOfInjective).s_g }
+
end Abelian
end CochainComplex
diff --git a/Mathlib/Algebra/Homology/Factorizations/CM5a.lean b/Mathlib/Algebra/Homology/Factorizations/CM5a.lean
index 30242a2ebf1749..42fd1f3e186362 100644
--- a/Mathlib/Algebra/Homology/Factorizations/CM5a.lean
+++ b/Mathlib/Algebra/Homology/Factorizations/CM5a.lean
@@ -5,10 +5,9 @@ Authors: Joël Riou
-/
module
-public import Mathlib.Algebra.Homology.DerivedCategory.HomologySequence
+public import Mathlib.Algebra.Homology.DerivedCategory.TStructure
public import Mathlib.Algebra.Homology.Factorizations.CM5b
public import Mathlib.Algebra.Homology.HomologicalComplexLimitsEventuallyConstant
-public import Mathlib.Algebra.Homology.Refinements
public import Mathlib.Algebra.Homology.SingleHomology
public import Mathlib.CategoryTheory.Category.Factorisation
public import Mathlib.CategoryTheory.Functor.OfSequence
@@ -49,6 +48,7 @@ lemma `cm5a_cof`.
-/
+
open CategoryTheory Limits Opposite Abelian HomologicalComplex Pretriangulated
variable {C : Type*} [Category* C] [Abelian C]
@@ -652,4 +652,43 @@ public lemma cm5a (n : ℤ) [K.IsStrictlyGE (n + 1)] [L.IsStrictlyGE n] :
exact ⟨K', inferInstance, ι, π ≫ p, inferInstance, inferInstance,
MorphismProperty.comp_mem _ _ _ hπ hp, by simp⟩
+open ZeroObject
+
+variable (K)
+
+public lemma exists_mono_quasiIso_injective (n₀ n₁ : ℤ) (h : n₀ + 1 = n₁ := by lia)
+ [K.IsStrictlyGE n₁] :
+ ∃ (L : CochainComplex C ℤ) (i : K ⟶ L) (_hi : Mono i) (_hi' : QuasiIso i)
+ (_ : ∀ (n : ℤ), Injective (L.X n)), L.IsStrictlyGE n₀ := by
+ have : K.IsStrictlyGE (n₀ + 1) := by rw [h]; infer_instance
+ obtain ⟨L, hL, i, p, hi, hi', hp, _⟩ := cm5a (0 : K ⟶ 0) n₀
+ exact ⟨L, i, hi, hi', (degreewiseEpiWithInjectiveKernel_iff_of_isZero p
+ (Limits.isZero_zero _)).1 hp, hL⟩
+
+public lemma exists_quasiIso_injective (n : ℤ) [K.IsStrictlyGE n] :
+ ∃ (L : CochainComplex C ℤ) (i : K ⟶ L) (_hi' : QuasiIso i)
+ (_hL : ∀ (n : ℤ), Injective (L.X n)), L.IsStrictlyGE n := by
+ /- The proof proceeds by first applying `exists_mono_quasiIso_injective` in order to
+ obtain a monomorphism `K ⟶ L` that is also a quasi-isomorphism
+ with `L` consisting of injective objects and `L` lying in degrees `≥ n - 1`.
+ Then, as it is quasi-isomorphic to `K`, the cochain complex `L` is cohomologically
+ in degrees `≥ n`, so that the composition `K ⟶ L ⟶ L.truncGE n` is a quasi-isomorphism.
+ In order to conclude, one needs to show that `(L.truncGE n).X n` is injective,
+ i.e. that `L.opcycles n` is injective. -/
+ have : HasDerivedCategory C := MorphismProperty.HasLocalization.standard _
+ obtain ⟨L, i, _, _, hL, _⟩ := exists_mono_quasiIso_injective K (n - 1) n (by simp)
+ have : L.IsGE n := by
+ have hK : K.IsGE n := inferInstance
+ rw [← DerivedCategory.isGE_Q_obj_iff] at hK ⊢
+ exact DerivedCategory.TStructure.t.isGE_of_iso (asIso (DerivedCategory.Q.map i)) n
+ have : QuasiIso (L.πTruncGE n) := (L.quasiIso_πTruncGE_iff n).mpr inferInstance
+ have : Injective (L.opcycles n) :=
+ L.injective_opcycles (n - 1) n (L.exactAt_of_isGE n (n - 1))
+ -- note: this `i ≫ L.πTruncGE n` is a mono in degrees > n, but it may not be in degree n
+ refine ⟨L.truncGE n, i ≫ L.πTruncGE n, inferInstance, fun q ↦ ?_, inferInstance⟩
+ obtain h | rfl | h := lt_trichotomy q n
+ · exact (isZero_of_isStrictlyGE _ n _ h).injective
+ · exact Injective.of_iso (L.truncGEXIsoOpcycles q).symm inferInstance
+ · exact Injective.of_iso (L.truncGEXIso n q h).symm (hL q)
+
end CochainComplex.Plus.modelCategoryQuillen
From e833f296fdce138502a72d3cddb2b45984448558 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=ABl=20Riou?=
<37772949+joelriou@users.noreply.github.com>
Date: Tue, 23 Jun 2026 09:54:05 +0000
Subject: [PATCH 0279/1300] feat(Algebra/Homology): homological complexes in
full subcategories (#40892)
---
Mathlib.lean | 1 +
Mathlib/Algebra/Homology/FullSubcategory.lean | 45 +++++++++++++++++++
2 files changed, 46 insertions(+)
create mode 100644 Mathlib/Algebra/Homology/FullSubcategory.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index e172db56965dbd..7566842d733330 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -618,6 +618,7 @@ public import Mathlib.Algebra.Homology.ExactSequenceFour
public import Mathlib.Algebra.Homology.Factorizations.Basic
public import Mathlib.Algebra.Homology.Factorizations.CM5a
public import Mathlib.Algebra.Homology.Factorizations.CM5b
+public import Mathlib.Algebra.Homology.FullSubcategory
public import Mathlib.Algebra.Homology.Functor
public import Mathlib.Algebra.Homology.GrothendieckAbelian
public import Mathlib.Algebra.Homology.HasNoLoop
diff --git a/Mathlib/Algebra/Homology/FullSubcategory.lean b/Mathlib/Algebra/Homology/FullSubcategory.lean
new file mode 100644
index 00000000000000..ad1d853e76f99d
--- /dev/null
+++ b/Mathlib/Algebra/Homology/FullSubcategory.lean
@@ -0,0 +1,45 @@
+/-
+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.Homology.HomologicalComplex
+
+/-!
+# Homological complexes in full subcategories
+
+-/
+
+@[expose] public section
+
+open CategoryTheory
+
+namespace HomologicalComplex
+
+/-- Given `P : ObjectProperty V` and `K : HomologicalComplex V c`,
+this is a lift of `K` in `HomologicalComplex P.FullSubcategory c`
+when `K.X n` satisfies `P` for all `n`. -/
+@[simps X d]
+def liftObjectProperty {ι : Type*} {c : ComplexShape ι}
+ {V : Type*} [Category* V] [Preadditive V] (P : ObjectProperty V)
+ (K : HomologicalComplex V c) (hK : ∀ (n : ι), P (K.X n)) :
+ HomologicalComplex P.FullSubcategory c where
+ X n := ⟨_, hK n⟩
+ d i j := ObjectProperty.homMk (K.d i j)
+
+set_option backward.defeqAttrib.useBackward true in
+/-- The functor `D ⥤ HomologicalComplex P.FullSubcategory c`
+which is obtained by lifting a functor `D ⥤ HomologicalComplex V c`
+when for any `X : D` and `n`, the object `(F.obj X).X n`
+satisfies a property `P : ObjectProperty V`. -/
+@[simps]
+def liftFunctorObjectProperty {D : Type*} [Category* D] {ι : Type*} {c : ComplexShape ι}
+ {V : Type*} [Category* V] [Preadditive V] (P : ObjectProperty V)
+ (F : D ⥤ HomologicalComplex V c) (hF : ∀ (X : D) (n : ι), P ((F.obj X).X n)) :
+ D ⥤ HomologicalComplex P.FullSubcategory c where
+ obj X := liftObjectProperty _ (F.obj X) (hF X)
+ map f := { f n := ObjectProperty.homMk ((F.map f).f n) }
+
+end HomologicalComplex
From b1630e12590032cf0c4389a5499001644e8959c9 Mon Sep 17 00:00:00 2001
From: Hannah Scholz <70071345+scholzhannah@users.noreply.github.com>
Date: Tue, 23 Jun 2026 09:54:08 +0000
Subject: [PATCH 0280/1300] feat: add delaborators for `mfderivWithin`,
`HasMFDerivWithinAt` and `HasMFDerivAt` (#40898)
I also added a section in the test file for the cases that include `T%`.
---
Mathlib/Geometry/Manifold/Notation.lean | 69 ++++++++++-
.../Notation/Delaborators.lean | 116 +++++++++++++++++-
2 files changed, 182 insertions(+), 3 deletions(-)
diff --git a/Mathlib/Geometry/Manifold/Notation.lean b/Mathlib/Geometry/Manifold/Notation.lean
index f5618f953402fb..4165465c5cf227 100644
--- a/Mathlib/Geometry/Manifold/Notation.lean
+++ b/Mathlib/Geometry/Manifold/Notation.lean
@@ -1102,7 +1102,25 @@ arguments that can use the `T%` elaborator. -/
let fs ← withAppArg delab
`(mfderiv% $fs) >>= annotateGoToSyntaxDef
--- TODO: add a delaborator for mfderivWithin (with a test)
+/-- Delaborator for `mfderivWithin` using the custom elaborator, and special-casing
+arguments that can use the `T%` elaborator. -/
+@[app_delab mfderivWithin] meta def delabMFDerivWithin : Delab := do
+ whenPPOption getPPNotation do
+ withOverApp 22 do
+ let ss ← withAppArg delab
+ try
+ let fe := (← getExpr).getAppArgs[20]!
+ let .lam n _ b _ := fe | failure
+ guard <| b.isAppOf ``Bundle.TotalSpace.mk'
+ let σe := b.getAppArgs[4]!.getAppFn
+ guard <| σe.isFVar
+ let Tσs ← withNaryArg 20 do
+ let σs ← withBindingBody n <| withNaryArg 4 <| withNaryFn delab
+ `(T% $σs) >>= annotateGoToSyntaxDef
+ `(mfderiv[$ss] ($Tσs)) >>= annotateGoToSyntaxDef
+ catch _ =>
+ let fs ← withNaryArg 20 delab
+ `(mfderiv[$ss] $fs) >>= annotateGoToSyntaxDef
/-- Delaborator for `MDifferentiable` using the custom elaborator, and special-casing
arguments that can use the `T%` elaborator. -/
@@ -1182,6 +1200,53 @@ arguments that can use the `T%` elaborator. -/
let fs ← withNaryArg 20 <| delab
`(MDiffAt[$ss] $fs) >>= annotateGoToSyntaxDef
+/-- Delaborator for `HasMFDerivWithinAt` using the custom elaborator, and special-casing
+arguments that can use the `T%` elaborator. -/
+@[app_delab HasMFDerivWithinAt] meta def delabHasMFDerivWithinAt : Delab := do
+ whenPPOption getPPNotation do
+ withOverApp 24 do
+ let ss ← withNaryArg 21 delab
+ let xs ← withNaryArg 22 delab
+ let f' ← withNaryArg 23 delab
+ try
+ let f := (← getExpr).getAppArgs[20]!
+ let .lam n _ b _ := f | failure
+ guard <| b.isAppOf ``Bundle.TotalSpace.mk'
+ let s := b.getAppArgs[4]!.getAppFn
+ guard <| s.isFVar
+ let σe := b.getAppArgs[4]!.getAppFn
+ guard <| σe.isFVar
+ let Tσs ← withNaryArg 20 do
+ let σs ← withBindingBody n <| withNaryArg 4 <| withNaryFn delab
+ `((T% $σs)) >>= annotateGoToSyntaxDef
+ `(HasMFDerivAt[$ss] $Tσs $xs $f') >>= annotateGoToSyntaxDef
+ catch _ =>
+ let fs ← withNaryArg 20 delab
+ `(HasMFDerivAt[$ss] $fs $xs $f') >>= annotateGoToSyntaxDef
+
+/-- Delaborator for `HasMFDerivWithinAt` using the custom elaborator, and special-casing
+arguments that can use the `T%` elaborator. -/
+@[app_delab HasMFDerivAt] meta def delabHasMFDerivAt : Delab := do
+ whenPPOption getPPNotation do
+ withOverApp 23 do
+ let xs ← withNaryArg 21 delab
+ let f' ← withNaryArg 22 delab
+ try
+ let f := (← getExpr).getAppArgs[20]!
+ let .lam n _ b _ := f | failure
+ guard <| b.isAppOf ``Bundle.TotalSpace.mk'
+ let s := b.getAppArgs[4]!.getAppFn
+ guard <| s.isFVar
+ let σe := b.getAppArgs[4]!.getAppFn
+ guard <| σe.isFVar
+ let Tσs ← withNaryArg 20 do
+ let σs ← withBindingBody n <| withNaryArg 4 <| withNaryFn delab
+ `((T% $σs)) >>= annotateGoToSyntaxDef
+ `(HasMFDerivAt% $Tσs $xs $f') >>= annotateGoToSyntaxDef
+ catch _ =>
+ let fs ← withNaryArg 20 delab
+ `(HasMFDerivAt% $fs $xs $f') >>= annotateGoToSyntaxDef
+
/-- Delaborator for `UniqueMDiffOn` using the custom elaborator. -/
@[app_delab UniqueMDiffOn] meta def delabUniqueMDiffOn : Delab := do
whenPPOption getPPNotation do
@@ -1197,7 +1262,7 @@ arguments that can use the `T%` elaborator. -/
`(UniqueMDiffAt[$ss]) >>= annotateGoToSyntaxDef
-- TODO: add more delaborators (and tests) for
--- ContMDiff, ContMDiffOn, ContMDiffAt, ContMDiffWithinAt, HasMFDerivAt, HasMFDerivWithinAt
+-- ContMDiff, ContMDiffOn, ContMDiffAt, ContMDiffWithinAt
-- TODO: when adding more elaborators, also add the corresponding delaborators
diff --git a/MathlibTest/DifferentialGeometry/Notation/Delaborators.lean b/MathlibTest/DifferentialGeometry/Notation/Delaborators.lean
index be21a824b56d34..6c43114e28e058 100644
--- a/MathlibTest/DifferentialGeometry/Notation/Delaborators.lean
+++ b/MathlibTest/DifferentialGeometry/Notation/Delaborators.lean
@@ -18,7 +18,7 @@ variable
{E : Type*} [NormedAddCommGroup E] [NormedSpace ℝ E]
{H : Type*} [TopologicalSpace H] {I : ModelWithCorners ℝ E H} {M : Type*} [TopologicalSpace M]
[ChartedSpace H M] [IsManifold I ∞ M]
- (f : M → M) (x : M) (s : Set M)
+ (f : M → M) (x : M) (s : Set M) (f' : TangentSpace I x →L[ℝ] TangentSpace I (f x))
(v : (x : M) → TangentSpace I x)
/-- info: MDiff f : Prop -/
@@ -74,6 +74,14 @@ variable
#guard_msgs in
#check mfderiv% (T% v) x
+/-- info: mfderiv[s] f x : TangentSpace I x →L[ℝ] TangentSpace I (f x) -/
+#guard_msgs in
+#check mfderivWithin I I f s x
+
+/-- info: mfderiv[s] f x : TangentSpace I x →L[ℝ] TangentSpace I (f x) -/
+#guard_msgs in
+#check mfderiv[s] f x
+
/-- info: ⟨x, v x⟩ : TotalSpace E (TangentSpace I) -/
#guard_msgs in
#check TotalSpace.mk' E x (v x)
@@ -98,6 +106,111 @@ variable
#guard_msgs in
#check UniqueMDiffWithinAt (𝕜 := ℝ) I s
+/-- info: HasMFDerivAt[s] f x f' : Prop -/
+#guard_msgs in
+#check HasMFDerivWithinAt I I f s x f'
+
+/-- info: HasMFDerivAt[s] f x f' : Prop -/
+#guard_msgs in
+#check HasMFDerivAt[s] f x f'
+
+/-- info: HasMFDerivAt% f x f' : Prop -/
+#guard_msgs in
+#check HasMFDerivAt I I f x f'
+
+/-- info: HasMFDerivAt% f x f' : Prop -/
+#guard_msgs in
+#check HasMFDerivAt% f x f'
+
+section TotalSpace
+
+variable {𝕜 B : Type*} {E : B → Type*}
+
+variable
+ -- Let `E` be a fiber bundle with base `B` and fiber `F` (a vector space over `𝕜`)
+ [TopologicalSpace B] [TopologicalSpace (TotalSpace F E)] [∀ x, TopologicalSpace (E x)]
+ [NormedAddCommGroup F] [NontriviallyNormedField 𝕜] [NormedSpace 𝕜 F] [FiberBundle F E]
+ -- Moreover let `E` be a vector bundle
+ [(x : B) → AddCommGroup (E x)] [(x : B) → Module 𝕜 (E x)] [VectorBundle 𝕜 F E]
+ -- Let the base `B` be charted over a fixed model space `HB`
+ {HB : Type*} [TopologicalSpace HB] [ChartedSpace HB B]
+ -- Moreover let `HB` be modelled on a normed space `EB` so that `B` (and hence `E`) have
+ -- differentiable structures
+ {EB : Type*} [NormedAddCommGroup EB] [NormedSpace 𝕜 EB] {I : ModelWithCorners 𝕜 EB HB}
+
+variable {f : B → 𝕜} {a : 𝕜} {s : Π x : B, E x} {u : Set B} {x₀ : B}
+ {f' : TangentSpace I x₀ →L[𝕜] TangentSpace (I.prod 𝓘(𝕜, F)) (⟨x₀, s x₀⟩ : TotalSpace F E)}
+
+/--
+info: mfderiv% (T% s) : (x : B) → TangentSpace I x →L[𝕜] TangentSpace (I.prod 𝓘(𝕜, F)) ⟨x, s x⟩
+-/
+#guard_msgs in
+#check mfderiv I (I.prod 𝓘(𝕜, F)) (T% s)
+
+/--
+info: mfderiv% (T% s) : (x : B) → TangentSpace I x →L[𝕜] TangentSpace (I.prod 𝓘(𝕜, F)) ⟨x, s x⟩
+-/
+#guard_msgs in
+#check mfderiv% (T% s)
+
+/-- info: mfderiv[u] (T% s) x₀ : TangentSpace I x₀ →L[𝕜] TangentSpace (I.prod 𝓘(𝕜, F)) ⟨x₀, s x₀⟩ -/
+#guard_msgs in
+#check mfderivWithin I (I.prod 𝓘(𝕜, F)) (T% s) u x₀
+
+/-- info: mfderiv[u] (T% s) x₀ : TangentSpace I x₀ →L[𝕜] TangentSpace (I.prod 𝓘(𝕜, F)) ⟨x₀, s x₀⟩ -/
+#guard_msgs in
+#check mfderiv[u] (T% s) x₀
+
+/-- info: MDiff (T% s) : Prop -/
+#guard_msgs in
+#check MDifferentiable I (I.prod 𝓘(𝕜, F)) (T% s)
+
+/-- info: MDiff (T% s) : Prop -/
+#guard_msgs in
+#check MDiff (T% s)
+
+/-- info: MDiffAt (T% s) x₀ : Prop -/
+#guard_msgs in
+#check MDifferentiableAt I (I.prod 𝓘(𝕜, F)) (T% s) x₀
+
+/-- info: MDiffAt (T% s) x₀ : Prop -/
+#guard_msgs in
+#check MDiffAt (T% s) x₀
+
+/-- info: MDiff[u] (T% s) : Prop -/
+#guard_msgs in
+#check MDifferentiableOn I (I.prod 𝓘(𝕜, F)) (T% s) u
+
+/-- info: MDiff[u] (T% s) : Prop -/
+#guard_msgs in
+#check MDiff[u] (T% s)
+
+/-- info: MDiffAt[u] (T% s) x₀ : Prop -/
+#guard_msgs in
+#check MDifferentiableWithinAt I (I.prod 𝓘(𝕜, F)) (T% s) u x₀
+
+/-- info: MDiffAt[u] (T% s) x₀ : Prop -/
+#guard_msgs in
+#check MDiffAt[u] (T% s) x₀
+
+/-- info: HasMFDerivAt[u] (T% s) x₀ f' : Prop -/
+#guard_msgs in
+#check HasMFDerivWithinAt I (I.prod 𝓘(𝕜, F)) (T% s) u x₀ f'
+
+/-- info: HasMFDerivAt[u] (T% s) x₀ f' : Prop -/
+#guard_msgs in
+#check HasMFDerivAt[u] (T% s) x₀ f'
+
+/-- info: HasMFDerivAt% (T% s) x₀ f' : Prop -/
+#guard_msgs in
+#check HasMFDerivAt I (I.prod 𝓘(𝕜, F)) (T% s) x₀ f'
+
+/-- info: HasMFDerivAt% (T% s) x₀ f' : Prop -/
+#guard_msgs in
+#check HasMFDerivAt% (T% s) x₀ f'
+
+end TotalSpace
+
section ambiguity
variable {g : E × E → M} in
@@ -142,6 +255,7 @@ inst✝ : IsManifold I ∞ M
f : M → M
x : M
s : Set M
+f' : TangentSpace I x →L[ℝ] TangentSpace I (f x)
v : (x : M) → TangentSpace I x
g✝ g : E × E → E × E
⊢ MDiff id
From 569b039f070436077c28d4080f66299e0995e91d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=ABl=20Riou?=
<37772949+joelriou@users.noreply.github.com>
Date: Tue, 23 Jun 2026 10:09:20 +0000
Subject: [PATCH 0281/1300] feat(Algebra/Homology): bounded derived categories
(#40923)
---
.../Homology/DerivedCategory/TStructure.lean | 25 +++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/Mathlib/Algebra/Homology/DerivedCategory/TStructure.lean b/Mathlib/Algebra/Homology/DerivedCategory/TStructure.lean
index 085d7772a0baca..8ee57cc096ea31 100644
--- a/Mathlib/Algebra/Homology/DerivedCategory/TStructure.lean
+++ b/Mathlib/Algebra/Homology/DerivedCategory/TStructure.lean
@@ -193,4 +193,29 @@ lemma exists_iso_singleFunctor_obj_of_isGE_of_isLE
obtain ⟨Y, ⟨e'⟩⟩ := CochainComplex.exists_iso_single K n
exact ⟨Y, ⟨e ≪≫ Q.mapIso e'⟩⟩
+
+open DerivedCategory.TStructure
+
+variable (C)
+
+/-- The bounded above derived category of an abelian category. -/
+abbrev Minus : Type max u v := (t : TStructure (DerivedCategory C)).minus.FullSubcategory
+
+/-- The bounded below derived category of an abelian category. -/
+abbrev Plus : Type max u v := (t : TStructure (DerivedCategory C)).plus.FullSubcategory
+
+/-- The bounded derived category of an abelian category. -/
+abbrev Bounded : Type max u v := (t : TStructure (DerivedCategory C)).bounded.FullSubcategory
+
+variable {C}
+
+/-- The inclusion of the bounded above derived category. -/
+noncomputable abbrev Minus.ι : Minus C ⥤ DerivedCategory C := t.minus.ι
+
+/-- The inclusion of the bounded below derived category. -/
+noncomputable abbrev Plus.ι : Plus C ⥤ DerivedCategory C := t.plus.ι
+
+/-- The inclusion of the bounded derived category. -/
+noncomputable abbrev Bounded.ι : Bounded C ⥤ DerivedCategory C := t.bounded.ι
+
end DerivedCategory
From fe35a4e191178bab40bd634844b7237425b7ab30 Mon Sep 17 00:00:00 2001
From: Christian Merten <136261474+chrisflav@users.noreply.github.com>
Date: Tue, 23 Jun 2026 10:09:23 +0000
Subject: [PATCH 0282/1300] chore(CategoryTheory/Sites/Spaces): relate
`IsOpenCover` and `GrothendieckTopology.CoversTop` (#40936)
We also add a lemma about `TopologicalSpace.Opens.IsBasis`, which is not directly related but also used in #40052.
---
Mathlib/CategoryTheory/Sites/Spaces.lean | 16 ++++++++++++++--
Mathlib/Topology/Sets/Opens.lean | 9 +++++++++
2 files changed, 23 insertions(+), 2 deletions(-)
diff --git a/Mathlib/CategoryTheory/Sites/Spaces.lean b/Mathlib/CategoryTheory/Sites/Spaces.lean
index 364e392890809d..637268ca43dd1b 100644
--- a/Mathlib/CategoryTheory/Sites/Spaces.lean
+++ b/Mathlib/CategoryTheory/Sites/Spaces.lean
@@ -5,10 +5,10 @@ Authors: Bhavik Mehta
-/
module
-public import Mathlib.CategoryTheory.Sites.Grothendieck
+public import Mathlib.CategoryTheory.Sites.CoversTop.Basic
public import Mathlib.CategoryTheory.Sites.Pretopology
public import Mathlib.CategoryTheory.Limits.Lattice
-public import Mathlib.Topology.Sets.Opens
+public import Mathlib.Topology.Sets.OpenCover
/-!
# Grothendieck topology on a topological space
@@ -96,4 +96,16 @@ theorem pretopology_toGrothendieck :
rw [← toPretopology_grothendieckTopology]
apply (Pretopology.gi (Opens T)).l_u_eq
+lemma coversTop_iff {ι : Type*} (U : ι → Opens T) :
+ (grothendieckTopology T).CoversTop U ↔ IsOpenCover U := by
+ rw [GrothendieckTopology.coversTop_iff_of_isTerminal _ ⊤ isTerminalTop]
+ dsimp [Opens.grothendieckTopology]
+ simp only [IsOpenCover, eq_top_iff, SetLike.le_def, exists_and_right, Opens.mem_top,
+ Opens.mem_iSup, forall_const]
+ refine ⟨fun h x ↦ ?_, fun hU x hx ↦ ?_⟩
+ · obtain ⟨V, ⟨u, ⟨i, ⟨hi⟩⟩⟩, hx⟩ := h x trivial
+ use i, leOfHom hi hx
+ · obtain ⟨i, hi⟩ := hU (x := x)
+ exact ⟨U i, ⟨homOfLE le_top, ⟨i, ⟨𝟙 _⟩⟩⟩, hi⟩
+
end Opens
diff --git a/Mathlib/Topology/Sets/Opens.lean b/Mathlib/Topology/Sets/Opens.lean
index 13e02d1439fc4c..7c3bb782828e04 100644
--- a/Mathlib/Topology/Sets/Opens.lean
+++ b/Mathlib/Topology/Sets/Opens.lean
@@ -49,6 +49,7 @@ We define order structures on both `Opens α` (`CompleteLattice`, `Frame`) and `
@[expose] public section
+universe u
open Filter Function Order Set
@@ -345,6 +346,14 @@ theorem isBasis_iff_cover {B : Set (Opens α)} :
rcases mem_sSup.1 hx with ⟨U, Us, xU⟩
exact ⟨U, hUs Us, xU, le_sSup Us⟩
+lemma IsBasis.exists_iSup_eq {X : Type u} [TopologicalSpace X] {ι : Type*}
+ {U : ι → TopologicalSpace.Opens X} (hU : TopologicalSpace.Opens.IsBasis (Set.range U))
+ (W : TopologicalSpace.Opens X) : ∃ (κ : Type u) (a : κ → ι), W = ⨆ (k : κ), U (a k) := by
+ obtain ⟨Us, hsub, hUs⟩ := Opens.isBasis_iff_cover.mp hU W
+ choose a ha using hsub
+ use Us, fun i ↦ a i.2
+ simp [hUs, ha, sSup_eq_iSup' Us]
+
/-- If `α` has a basis consisting of compact opens, then an open set in `α` is compact open iff
it is a finite union of some elements in the basis -/
theorem IsBasis.isCompact_open_iff_eq_finite_iUnion {ι : Type*} (b : ι → Opens α)
From 10874878a53987127ef9f6e12d597b7d7fea4a42 Mon Sep 17 00:00:00 2001
From: Thomas Browning <13339017+tb65536@users.noreply.github.com>
Date: Tue, 23 Jun 2026 10:39:03 +0000
Subject: [PATCH 0283/1300] chore(RingTheory/Invariant/Basic): split file by
imports (#40928)
The basic file `RingTheory/Invariant/Basic.lean` proving existence of Frobenius elements has surprisingly heavy imports (e.g., all of Galois theory). This PR splits off the heaver material into a separate file `RingTheory/Invariant/Galois.lean`. This leads to some nontrivial import reductions in downstream files.
Co-authored-by: tb65536
---
Mathlib.lean | 1 +
.../RamificationInertia/Galois.lean | 1 +
Mathlib/RingTheory/Invariant/Basic.lean | 130 +--------------
Mathlib/RingTheory/Invariant/Galois.lean | 152 ++++++++++++++++++
4 files changed, 160 insertions(+), 124 deletions(-)
create mode 100644 Mathlib/RingTheory/Invariant/Galois.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 7566842d733330..d9fda0a91062b6 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -6673,6 +6673,7 @@ public import Mathlib.RingTheory.IntegralClosure.IsIntegralClosure.Defs
public import Mathlib.RingTheory.IntegralDomain
public import Mathlib.RingTheory.Invariant.Basic
public import Mathlib.RingTheory.Invariant.Defs
+public import Mathlib.RingTheory.Invariant.Galois
public import Mathlib.RingTheory.Invariant.Profinite
public import Mathlib.RingTheory.IsAdjoinRoot
public import Mathlib.RingTheory.IsPrimary
diff --git a/Mathlib/NumberTheory/RamificationInertia/Galois.lean b/Mathlib/NumberTheory/RamificationInertia/Galois.lean
index ec9ce3731a2995..bc63cb37041191 100644
--- a/Mathlib/NumberTheory/RamificationInertia/Galois.lean
+++ b/Mathlib/NumberTheory/RamificationInertia/Galois.lean
@@ -5,6 +5,7 @@ Authors: Yongle Hu, Jiedong Jiang
-/
module
+public import Mathlib.RingTheory.Invariant.Galois
public import Mathlib.RingTheory.RamificationInertia.Basic
/-!
diff --git a/Mathlib/RingTheory/Invariant/Basic.lean b/Mathlib/RingTheory/Invariant/Basic.lean
index 83972a5c02b355..e06ded1554491a 100644
--- a/Mathlib/RingTheory/Invariant/Basic.lean
+++ b/Mathlib/RingTheory/Invariant/Basic.lean
@@ -5,9 +5,9 @@ Authors: Thomas Browning
-/
module
+public import Mathlib.FieldTheory.Fixed
+public import Mathlib.RingTheory.Ideal.GoingUp
public import Mathlib.RingTheory.Invariant.Defs
-public import Mathlib.RingTheory.IntegralClosure.IntegralRestrict
-public import Mathlib.RingTheory.LocalRing.ResidueField.Ideal
/-!
# Invariant Extensions of Rings
@@ -41,54 +41,11 @@ If `Q` is a prime ideal of `B` lying over a prime ideal `P` of `A`, then
@[expose] public section
-open scoped Pointwise
+-- this file should not import any field theory beyond the contents of `FieldTheory/Fixed.lean`
+-- material involving Galois theory should be placed in `RingTheory/Invariant/Galois.lean`
+assert_not_exists IntermediateField.adjoin
-section Galois
-
-variable (A K L B : Type*) [CommRing A] [CommRing B] [Field K] [Field L]
- [Algebra A K] [Algebra B L] [IsFractionRing A K] [IsFractionRing B L]
- [Algebra A B] [Algebra K L] [Algebra A L] [IsScalarTower A K L] [IsScalarTower A B L]
- [IsIntegrallyClosed A] [IsIntegralClosure B A L]
-
-/-- In the AKLB setup, the Galois group of `L/K` acts on `B`. -/
-@[implicit_reducible]
-noncomputable def IsIntegralClosure.MulSemiringAction [Algebra.IsAlgebraic K L] :
- MulSemiringAction Gal(L/K) B :=
- MulSemiringAction.compHom B (galRestrict A K L B).toMonoidHom
-
-instance [Algebra.IsAlgebraic K L] : let := IsIntegralClosure.MulSemiringAction A K L B
- SMulDistribClass Gal(L/K) B L :=
- let := IsIntegralClosure.MulSemiringAction A K L B
- ⟨fun g b l ↦ by
- simp only [Algebra.smul_def, smul_mul', mul_eq_mul_right_iff]
- exact Or.inl (algebraMap_galRestrictHom_apply A K L B g b).symm⟩
-
-/-- In the AKLB setup, every fixed point of `B` lies in the image of `A`. -/
-theorem Algebra.isInvariant_of_isGalois [FiniteDimensional K L] [h : IsGalois K L] :
- letI := IsIntegralClosure.MulSemiringAction A K L B
- Algebra.IsInvariant A B Gal(L/K) := by
- replace h := ((IsGalois.tfae (F := K) (E := L)).out 0 1).mp h
- letI := IsIntegralClosure.MulSemiringAction A K L B
- refine ⟨fun b hb ↦ ?_⟩
- replace hb : algebraMap B L b ∈ IntermediateField.fixedField (⊤ : Subgroup Gal(L/K)) := by
- rintro ⟨g, -⟩
- exact (algebraMap_galRestrict_apply A g b).symm.trans (congrArg (algebraMap B L) (hb g))
- rw [h, IntermediateField.mem_bot] at hb
- obtain ⟨k, hk⟩ := hb
- have hb : IsIntegral A b := IsIntegralClosure.isIntegral A L b
- rw [← isIntegral_algebraMap_iff (FaithfulSMul.algebraMap_injective B L), ← hk,
- isIntegral_algebraMap_iff (FaithfulSMul.algebraMap_injective K L)] at hb
- obtain ⟨a, rfl⟩ := IsIntegrallyClosed.algebraMap_eq_of_integral hb
- rw [← IsScalarTower.algebraMap_apply, IsScalarTower.algebraMap_apply A B L,
- (FaithfulSMul.algebraMap_injective B L).eq_iff] at hk
- exact ⟨a, hk⟩
-
-/-- A variant of `Algebra.isInvariant_of_isGalois`, replacing `Gal(L/K)` by `Aut(B/A)`. -/
-theorem Algebra.isInvariant_of_isGalois' [FiniteDimensional K L] [IsGalois K L] :
- Algebra.IsInvariant A B (B ≃ₐ[A] B) :=
- ⟨fun b h ↦ (isInvariant_of_isGalois A K L B).1 b (fun g ↦ h (galRestrict A K L B g))⟩
-
-end Galois
+open scoped Pointwise
section Quotient
@@ -515,81 +472,6 @@ lemma Ideal.Quotient.exists_algEquiv_fixedPoint_quotient_under
refine .trans ?_ (σ.apply_symm_apply _)
rw [← h₂, ← e, h₁]
-namespace Ideal.IsFractionRing
-
-variable [P.IsPrime] [Q.IsPrime] (K L : Type*) [Field K] [Field L] [Algebra K L]
- [Algebra (A ⧸ P) K] [IsFractionRing (A ⧸ P) K] [Algebra (B ⧸ Q) L] [IsFractionRing (B ⧸ Q) L]
- [Algebra (A ⧸ P) L] [IsScalarTower (A ⧸ P) (B ⧸ Q) L] [IsScalarTower (A ⧸ P) K L]
-
-open Polynomial in
-include P Q G in
-lemma normal : Normal K L := by
- have := Algebra.IsInvariant.isIntegral A B G
- have := isAlgebraic_of_isFractionRing (A ⧸ P) (B ⧸ Q) K L
- constructor
- intro x
- obtain ⟨x, y, hy, rfl⟩ := IsFractionRing.div_surjective (B ⧸ Q) x
- obtain ⟨b, a, ha, h⟩ := (Algebra.IsAlgebraic.isAlgebraic (R := A ⧸ P) y).exists_smul_eq_mul x hy
- obtain ⟨a, rfl⟩ := Quotient.mk_surjective a
- obtain ⟨b, rfl⟩ := Quotient.mk_surjective b
- simp_rw [← Quotient.algebraMap_eq] at *
- cases nonempty_fintype G
- obtain ⟨p, hp, -, h_monic⟩ := lifts_and_natDegree_eq_and_monic
- (Algebra.IsInvariant.charpoly_mem_lifts A B G b) (MulSemiringAction.monic_charpoly ..)
- have h_eval : p.aeval b = 0 := by
- rw [← eval_map_algebraMap, hp, MulSemiringAction.eval_charpoly]
- let q := p.comp (C a * X)
- let d := (algebraMap (B ⧸ Q) L) x / (algebraMap (B ⧸ Q) L) y
- have comm₁ : (algebraMap K L).comp (algebraMap (A ⧸ P) K) =
- (algebraMap (B ⧸ Q) L).comp (algebraMap (A ⧸ P) (B ⧸ Q)) := by
- simp_rw [← IsScalarTower.algebraMap_eq]
- have comm₂ : (algebraMap (A ⧸ P) (B ⧸ Q)).comp (algebraMap A (A ⧸ P)) =
- (algebraMap B (B ⧸ Q)).comp (algebraMap A B) := by
- simp_rw [← IsScalarTower.algebraMap_eq]
- replace h_eval : ((q.map (algebraMap A (A ⧸ P))).map (algebraMap (A ⧸ P) K)).aeval d = 0 := by
- simp_rw [q, map_comp, Polynomial.map_mul, map_C, map_X, aeval_comp, aeval_mul, aeval_C, aeval_X,
- ← RingHom.comp_apply, ← RingHom.comp_assoc, comm₁, RingHom.comp_apply, d, mul_div, ← map_mul]
- rw [← Algebra.smul_def, h, map_mul, mul_div_cancel_left₀ _ (by simpa using hy),
- aeval_map_algebraMap, aeval_algebraMap_apply, aeval_map_algebraMap, aeval_algebraMap_apply,
- h_eval, map_zero, map_zero]
- replace h_splits : (p.map (algebraMap A B)).Splits := by
- rw [hp]
- exact MulSemiringAction.splits_charpoly G b
- refine .of_dvd ?_ ?_ (map_dvd (algebraMap K L) (minpoly.dvd K d h_eval))
- · simp_rw [q, map_comp, Polynomial.map_mul, map_C, map_X]
- refine .comp_of_degree_le_one ?_ (degree_C_mul_X_le _)
- rw [Polynomial.map_map, Polynomial.map_map, comm₁, RingHom.comp_assoc, comm₂,
- ← RingHom.comp_assoc, ← Polynomial.map_map]
- apply h_splits.map
- · simp_rw [q, map_comp, Polynomial.map_mul, map_C, map_X, Polynomial.map_map]
- exact mt (comp_C_mul_X_eq_zero_iff (by simpa)).mp (map_monic_ne_zero h_monic)
-
-include P Q in
-lemma finite_of_isInvariant [SMulCommClass G A B] [Algebra.IsSeparable K L] :
- Module.Finite K L := by
- have : IsGalois K L := { __ := normal G P Q K L }
- have := Finite.of_surjective _ (IsFractionRing.stabilizerHom_surjective G P Q K L)
- apply IsGalois.finiteDimensional_of_finite
-
-end Ideal.IsFractionRing
-
-attribute [local instance] Ideal.Quotient.field in
-include G in
-/--
-For any domain `k` containing `B ⧸ Q`,
-any endomorphism of `k` can be restricted to an endomorphism of `B ⧸ Q`. -/
-lemma Ideal.Quotient.normal [P.IsMaximal] [Q.IsMaximal] :
- Normal (A ⧸ P) (B ⧸ Q) :=
- IsFractionRing.normal G P Q (A ⧸ P) (B ⧸ Q)
-
-attribute [local instance] Ideal.Quotient.field in
-include G in
-/-- If the extension `B/Q` over `A/P` is separable, then it is finite dimensional. -/
-lemma Ideal.Quotient.finite_of_isInvariant [P.IsMaximal] [Q.IsMaximal]
- [SMulCommClass G A B] [Algebra.IsSeparable (A ⧸ P) (B ⧸ Q)] :
- Module.Finite (A ⧸ P) (B ⧸ Q) :=
- IsFractionRing.finite_of_isInvariant G P Q (A ⧸ P) (B ⧸ Q)
-
end normal
namespace IsFractionRing
diff --git a/Mathlib/RingTheory/Invariant/Galois.lean b/Mathlib/RingTheory/Invariant/Galois.lean
new file mode 100644
index 00000000000000..c86f93056fb6d5
--- /dev/null
+++ b/Mathlib/RingTheory/Invariant/Galois.lean
@@ -0,0 +1,152 @@
+/-
+Copyright (c) 2024 Thomas Browning. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Thomas Browning
+-/
+module
+
+public import Mathlib.RingTheory.Invariant.Basic
+public import Mathlib.RingTheory.IntegralClosure.IntegralRestrict
+
+/-!
+# Invariant Extensions of Rings and Galois Theory
+
+Given an extension of rings `B/A` and an action of `G` on `B`, the predicate
+`Algebra.IsInvariant A B G` states that every fixed point of `B` lies in the image of `A`.
+
+This file relates this predicate `Algebra.IsInvariant` to Galois theory.
+-/
+
+@[expose] public section
+
+open scoped Pointwise
+
+section Galois
+
+variable (A K L B : Type*) [CommRing A] [CommRing B] [Field K] [Field L]
+ [Algebra A K] [Algebra B L] [IsFractionRing A K] [IsFractionRing B L]
+ [Algebra A B] [Algebra K L] [Algebra A L] [IsScalarTower A K L] [IsScalarTower A B L]
+ [IsIntegrallyClosed A] [IsIntegralClosure B A L]
+
+/-- In the AKLB setup, the Galois group of `L/K` acts on `B`. -/
+@[implicit_reducible]
+noncomputable def IsIntegralClosure.MulSemiringAction [Algebra.IsAlgebraic K L] :
+ MulSemiringAction Gal(L/K) B :=
+ MulSemiringAction.compHom B (galRestrict A K L B).toMonoidHom
+
+instance [Algebra.IsAlgebraic K L] : let := IsIntegralClosure.MulSemiringAction A K L B
+ SMulDistribClass Gal(L/K) B L :=
+ let := IsIntegralClosure.MulSemiringAction A K L B
+ ⟨fun g b l ↦ by
+ simp only [Algebra.smul_def, smul_mul', mul_eq_mul_right_iff]
+ exact Or.inl (algebraMap_galRestrictHom_apply A K L B g b).symm⟩
+
+/-- In the AKLB setup, every fixed point of `B` lies in the image of `A`. -/
+theorem Algebra.isInvariant_of_isGalois [FiniteDimensional K L] [h : IsGalois K L] :
+ letI := IsIntegralClosure.MulSemiringAction A K L B
+ Algebra.IsInvariant A B Gal(L/K) := by
+ replace h := ((IsGalois.tfae (F := K) (E := L)).out 0 1).mp h
+ letI := IsIntegralClosure.MulSemiringAction A K L B
+ refine ⟨fun b hb ↦ ?_⟩
+ replace hb : algebraMap B L b ∈ IntermediateField.fixedField (⊤ : Subgroup Gal(L/K)) := by
+ rintro ⟨g, -⟩
+ exact (algebraMap_galRestrict_apply A g b).symm.trans (congrArg (algebraMap B L) (hb g))
+ rw [h, IntermediateField.mem_bot] at hb
+ obtain ⟨k, hk⟩ := hb
+ have hb : IsIntegral A b := IsIntegralClosure.isIntegral A L b
+ rw [← isIntegral_algebraMap_iff (FaithfulSMul.algebraMap_injective B L), ← hk,
+ isIntegral_algebraMap_iff (FaithfulSMul.algebraMap_injective K L)] at hb
+ obtain ⟨a, rfl⟩ := IsIntegrallyClosed.algebraMap_eq_of_integral hb
+ rw [← IsScalarTower.algebraMap_apply, IsScalarTower.algebraMap_apply A B L,
+ (FaithfulSMul.algebraMap_injective B L).eq_iff] at hk
+ exact ⟨a, hk⟩
+
+/-- A variant of `Algebra.isInvariant_of_isGalois`, replacing `Gal(L/K)` by `Aut(B/A)`. -/
+theorem Algebra.isInvariant_of_isGalois' [FiniteDimensional K L] [IsGalois K L] :
+ Algebra.IsInvariant A B (B ≃ₐ[A] B) :=
+ ⟨fun b h ↦ (isInvariant_of_isGalois A K L B).1 b (fun g ↦ h (galRestrict A K L B g))⟩
+
+end Galois
+
+section normal
+
+variable {A B : Type*} [CommRing A] [CommRing B] [Algebra A B]
+ (G : Type*) [Finite G] [Group G] [MulSemiringAction G B] [Algebra.IsInvariant A B G]
+ (P : Ideal A) (Q : Ideal B) [Q.LiesOver P]
+
+namespace Ideal.IsFractionRing
+
+variable [P.IsPrime] [Q.IsPrime] (K L : Type*) [Field K] [Field L] [Algebra K L]
+ [Algebra (A ⧸ P) K] [IsFractionRing (A ⧸ P) K] [Algebra (B ⧸ Q) L] [IsFractionRing (B ⧸ Q) L]
+ [Algebra (A ⧸ P) L] [IsScalarTower (A ⧸ P) (B ⧸ Q) L] [IsScalarTower (A ⧸ P) K L]
+
+open Polynomial in
+include P Q G in
+lemma normal : Normal K L := by
+ have := Algebra.IsInvariant.isIntegral A B G
+ have := isAlgebraic_of_isFractionRing (A ⧸ P) (B ⧸ Q) K L
+ constructor
+ intro x
+ obtain ⟨x, y, hy, rfl⟩ := IsFractionRing.div_surjective (B ⧸ Q) x
+ obtain ⟨b, a, ha, h⟩ := (Algebra.IsAlgebraic.isAlgebraic (R := A ⧸ P) y).exists_smul_eq_mul x hy
+ obtain ⟨a, rfl⟩ := Quotient.mk_surjective a
+ obtain ⟨b, rfl⟩ := Quotient.mk_surjective b
+ simp_rw [← Quotient.algebraMap_eq] at *
+ cases nonempty_fintype G
+ obtain ⟨p, hp, -, h_monic⟩ := lifts_and_natDegree_eq_and_monic
+ (Algebra.IsInvariant.charpoly_mem_lifts A B G b) (MulSemiringAction.monic_charpoly ..)
+ have h_eval : p.aeval b = 0 := by
+ rw [← eval_map_algebraMap, hp, MulSemiringAction.eval_charpoly]
+ let q := p.comp (C a * X)
+ let d := (algebraMap (B ⧸ Q) L) x / (algebraMap (B ⧸ Q) L) y
+ have comm₁ : (algebraMap K L).comp (algebraMap (A ⧸ P) K) =
+ (algebraMap (B ⧸ Q) L).comp (algebraMap (A ⧸ P) (B ⧸ Q)) := by
+ simp_rw [← IsScalarTower.algebraMap_eq]
+ have comm₂ : (algebraMap (A ⧸ P) (B ⧸ Q)).comp (algebraMap A (A ⧸ P)) =
+ (algebraMap B (B ⧸ Q)).comp (algebraMap A B) := by
+ simp_rw [← IsScalarTower.algebraMap_eq]
+ replace h_eval : ((q.map (algebraMap A (A ⧸ P))).map (algebraMap (A ⧸ P) K)).aeval d = 0 := by
+ simp_rw [q, map_comp, Polynomial.map_mul, map_C, map_X, aeval_comp, aeval_mul, aeval_C, aeval_X,
+ ← RingHom.comp_apply, ← RingHom.comp_assoc, comm₁, RingHom.comp_apply, d, mul_div, ← map_mul]
+ rw [← Algebra.smul_def, h, map_mul, mul_div_cancel_left₀ _ (by simpa using hy),
+ aeval_map_algebraMap, aeval_algebraMap_apply, aeval_map_algebraMap, aeval_algebraMap_apply,
+ h_eval, map_zero, map_zero]
+ replace h_splits : (p.map (algebraMap A B)).Splits := by
+ rw [hp]
+ exact MulSemiringAction.splits_charpoly G b
+ refine .of_dvd ?_ ?_ (map_dvd (algebraMap K L) (minpoly.dvd K d h_eval))
+ · simp_rw [q, map_comp, Polynomial.map_mul, map_C, map_X]
+ refine .comp_of_degree_le_one ?_ (degree_C_mul_X_le _)
+ rw [Polynomial.map_map, Polynomial.map_map, comm₁, RingHom.comp_assoc, comm₂,
+ ← RingHom.comp_assoc, ← Polynomial.map_map]
+ apply h_splits.map
+ · simp_rw [q, map_comp, Polynomial.map_mul, map_C, map_X, Polynomial.map_map]
+ exact mt (comp_C_mul_X_eq_zero_iff (by simpa)).mp (map_monic_ne_zero h_monic)
+
+include P Q in
+lemma finite_of_isInvariant [SMulCommClass G A B] [Algebra.IsSeparable K L] :
+ Module.Finite K L := by
+ have : IsGalois K L := { __ := normal G P Q K L }
+ have := Finite.of_surjective _ (IsFractionRing.stabilizerHom_surjective G P Q K L)
+ apply IsGalois.finiteDimensional_of_finite
+
+end Ideal.IsFractionRing
+
+attribute [local instance] Ideal.Quotient.field in
+include G in
+/--
+For any domain `k` containing `B ⧸ Q`,
+any endomorphism of `k` can be restricted to an endomorphism of `B ⧸ Q`. -/
+lemma Ideal.Quotient.normal [P.IsMaximal] [Q.IsMaximal] :
+ Normal (A ⧸ P) (B ⧸ Q) :=
+ IsFractionRing.normal G P Q (A ⧸ P) (B ⧸ Q)
+
+attribute [local instance] Ideal.Quotient.field in
+include G in
+/-- If the extension `B/Q` over `A/P` is separable, then it is finite dimensional. -/
+lemma Ideal.Quotient.finite_of_isInvariant [P.IsMaximal] [Q.IsMaximal]
+ [SMulCommClass G A B] [Algebra.IsSeparable (A ⧸ P) (B ⧸ Q)] :
+ Module.Finite (A ⧸ P) (B ⧸ Q) :=
+ IsFractionRing.finite_of_isInvariant G P Q (A ⧸ P) (B ⧸ Q)
+
+end normal
From 2a63917e6adf6223eb965f95e5efa57eade46fbb Mon Sep 17 00:00:00 2001
From: Ben Eltschig <43812953+peabrainiac@users.noreply.github.com>
Date: Tue, 23 Jun 2026 10:53:02 +0000
Subject: [PATCH 0284/1300] feat(Topology): generalise `Trivialization.symm`
(#40903)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Change `Bundle.Trivialisation.symm` to get its junk values via `Classical.arbitrary` from a `Nonempty` instance, instead of requiring using `0` as the junk value and requiring `Zero` instances for that. This in particular allows us to generalise `FiberBundle.pullback` to bundles with nonempty fibres, which previously also required the bundle fibres to have zeroes. This is motivated by future work on principal bundles, whose fibres are always nonempty but have no preferred elements and hence no instances like `Zero` or `Inhabited`.
`Bundle.Trivialisation.symmₗ` and `Bundle.Trivialisation.symmL` use `0` as the junk value as before; so while their definition got slightly more complicated and their underlying function no longer definitionally equal to `.symm`, all statements that were true about them previously continue to be true now. In particular, I've tested this PR against the sphere eversion project and ran into minimal breakage there.
---
.../Topology/FiberBundle/Constructions.lean | 2 +-
.../Topology/FiberBundle/Trivialization.lean | 44 +++++++----
Mathlib/Topology/VectorBundle/Basic.lean | 73 +++++++++++++------
.../Topology/VectorBundle/Constructions.lean | 2 +-
.../ContinuousAlternatingMap.lean | 5 +-
Mathlib/Topology/VectorBundle/Hom.lean | 12 +--
Mathlib/Topology/VectorBundle/Riemannian.lean | 13 ++--
7 files changed, 97 insertions(+), 54 deletions(-)
diff --git a/Mathlib/Topology/FiberBundle/Constructions.lean b/Mathlib/Topology/FiberBundle/Constructions.lean
index 0e44b40be93d8c..98b4083007ac0f 100644
--- a/Mathlib/Topology/FiberBundle/Constructions.lean
+++ b/Mathlib/Topology/FiberBundle/Constructions.lean
@@ -301,7 +301,7 @@ theorem Pullback.continuous_totalSpaceMk [∀ x, TopologicalSpace (E x)] [FiberB
exact (FiberBundle.totalSpaceMk_isInducing F E (f x)).eq_induced.le
variable {E F}
-variable [∀ _b, Zero (E _b)] {K : Type U} [FunLike K B' B] [ContinuousMapClass K B' B]
+variable [∀ _b, Nonempty (E _b)] {K : Type U} [FunLike K B' B] [ContinuousMapClass K B' B]
/-- A fiber bundle trivialization can be pulled back to a trivialization on the pullback bundle. -/
@[simps]
diff --git a/Mathlib/Topology/FiberBundle/Trivialization.lean b/Mathlib/Topology/FiberBundle/Trivialization.lean
index 38133c78000fab..2177900ac23584 100644
--- a/Mathlib/Topology/FiberBundle/Trivialization.lean
+++ b/Mathlib/Topology/FiberBundle/Trivialization.lean
@@ -223,29 +223,38 @@ theorem symm_coe_proj {x : B} {y : F} (e' : Pretrivialization F (π F E)) (h : x
(e'.toPartialEquiv.symm (x, y)).1 = x :=
e'.proj_symm_apply' h
-section Zero
+section Nonempty
-variable [∀ x, Zero (E x)]
+variable [∀ x, Nonempty (E x)]
open Classical in
/-- A fiberwise inverse to `e`. This is the function `F → E b` that induces a local inverse
-`B × F → TotalSpace F E` of `e` on `e.baseSet`. It is defined to be `0` outside `e.baseSet`. -/
+`B × F → TotalSpace F E` of `e` on `e.baseSet`. Outside of `e.baseSet` it takes on arbitrarily
+chosen junk values. -/
protected noncomputable def symm (e : Pretrivialization F (π F E)) (b : B) (y : F) : E b :=
if hb : b ∈ e.baseSet then
cast (congr_arg E (e.proj_symm_apply' hb)) (e.toPartialEquiv.symm (b, y)).2
- else 0
+ else Classical.arbitrary _
theorem symm_apply (e : Pretrivialization F (π F E)) {b : B} (hb : b ∈ e.baseSet) (y : F) :
e.symm b y = cast (congr_arg E (e.symm_coe_proj hb)) (e.toPartialEquiv.symm (b, y)).2 :=
dif_pos hb
+@[deprecated "The junk values of `Pretrivialization.symm` were changed from `0` to
+`Classical.arbitrary` and should not be relied on; this lemma will be removed soon. Note that this
+change does not affect the linear versions `symmₗ` and `symmL`, which still retain `0` as the junk
+values." (since := "2026-06-23")]
theorem symm_apply_of_notMem (e : Pretrivialization F (π F E)) {b : B} (hb : b ∉ e.baseSet)
- (y : F) : e.symm b y = 0 :=
- dif_neg hb
+ (y : F) : e.symm b y = Classical.arbitrary _ := by
+ simp [Pretrivialization.symm, hb]
+@[deprecated "The junk values of `Pretrivialization.symm` were changed from `0` to
+`Classical.arbitrary` and should not be relied on; this lemma will be removed soon. Note that this
+change does not affect the linear versions `symmₗ` and `symmL`, which still retain `0` as the junk
+values." (since := "2026-06-23")]
theorem coe_symm_of_notMem (e : Pretrivialization F (π F E)) {b : B} (hb : b ∉ e.baseSet) :
- (e.symm b : F → E b) = 0 :=
- funext fun _ => dif_neg hb
+ e.symm b = fun _ ↦ Classical.arbitrary _ := by
+ ext; exact symm_apply_of_notMem e hb _
theorem mk_symm (e : Pretrivialization F (π F E)) {b : B} (hb : b ∈ e.baseSet) (y : F) :
TotalSpace.mk b (e.symm b y) = e.toPartialEquiv.symm (b, y) := by
@@ -266,7 +275,7 @@ theorem apply_mk_symm (e : Pretrivialization F (π F E)) {b : B} (hb : b ∈ e.b
e ⟨b, e.symm b y⟩ = (b, y) := by
rw [e.mk_symm hb, e.apply_symm_apply (e.mk_mem_target.mpr hb)]
-end Zero
+end Nonempty
/-- The restriction of a pretrivialization to a subset of the base. -/
@[simps toFun source target baseSet]
@@ -668,12 +677,13 @@ theorem symm_coe_proj {x : B} {y : F} (e : Trivialization F (π F E)) (h : x ∈
(e.toOpenPartialHomeomorph.symm (x, y)).1 = x :=
e.proj_symm_apply' h
-section Zero
+section Nonempty
-variable [∀ x, Zero (E x)]
+variable [∀ x, Nonempty (E x)]
/-- A fiberwise inverse to `e'`. The function `F → E x` that induces a local inverse
-`B × F → TotalSpace F E` of `e'` on `e'.baseSet`. It is defined to be `0` outside `e'.baseSet`. -/
+`B × F → TotalSpace F E` of `e'` on `e'.baseSet`. It takes on junk values chosen using
+`Classical.arbitrary` outside `e'.baseSet`. -/
protected noncomputable def symm (e : Trivialization F (π F E)) (b : B) (y : F) : E b :=
e.toPretrivialization.symm b y
@@ -682,9 +692,13 @@ theorem symm_apply (e : Trivialization F (π F E)) {b : B} (hb : b ∈ e.baseSet
cast (congr_arg E (e.symm_coe_proj hb)) (e.toOpenPartialHomeomorph.symm (b, y)).2 :=
dif_pos hb
+@[deprecated "The junk values of `Trivialization.symm` were changed from `0` to
+`Classical.arbitrary` and should not be relied on; this lemma will be removed soon. Note that this
+change does not affect the linear versions `symmₗ` and `symmL`, which still retain `0` as the junk
+values." (since := "2026-06-23")]
theorem symm_apply_of_notMem (e : Trivialization F (π F E)) {b : B} (hb : b ∉ e.baseSet) (y : F) :
- e.symm b y = 0 :=
- dif_neg hb
+ e.symm b y = Classical.arbitrary _ :=
+ e.toPretrivialization.symm_apply_of_notMem hb y
theorem mk_symm (e : Trivialization F (π F E)) {b : B} (hb : b ∈ e.baseSet) (y : F) :
TotalSpace.mk b (e.symm b y) = e.toOpenPartialHomeomorph.symm (b, y) :=
@@ -715,7 +729,7 @@ theorem continuousOn_symm (e : Trivialization F (π F E)) :
rw [← e.target_eq]
exact e.toOpenPartialHomeomorph.continuousOn_symm
-end Zero
+end Nonempty
/-- If `e` is a `Trivialization` of `proj : Z → B` with fiber `F` and `h` is a homeomorphism
`F ≃ₜ F'`, then `e.trans_fiber_homeomorph h` is the trivialization of `proj` with the fiber `F'`
diff --git a/Mathlib/Topology/VectorBundle/Basic.lean b/Mathlib/Topology/VectorBundle/Basic.lean
index 21c11a5ea2ad57..ada696545834d0 100644
--- a/Mathlib/Topology/VectorBundle/Basic.lean
+++ b/Mathlib/Topology/VectorBundle/Basic.lean
@@ -86,15 +86,22 @@ theorem linear [AddCommMonoid F] [Module R F] [∀ x, AddCommMonoid (E x)] [∀
variable [AddCommMonoid F] [Module R F] [∀ x, AddCommMonoid (E x)] [∀ x, Module R (E x)]
+open Classical in
/-- A fiberwise linear inverse to `e`. -/
-@[simps!]
protected def symmₗ (e : Pretrivialization F (π F E)) [e.IsLinear R] (b : B) : F →ₗ[R] E b := by
- refine IsLinearMap.mk' (e.symm b) ?_
- by_cases hb : b ∈ e.baseSet
- · exact (((e.linear R hb).mk' _).inverse (e.symm b) (e.symm_apply_apply_mk hb) fun v ↦
- congr_arg Prod.snd <| e.apply_mk_symm hb v).isLinear
- · rw [e.coe_symm_of_notMem hb]
- exact (0 : F →ₗ[R] E b).isLinear
+ refine if hb : b ∈ e.baseSet then IsLinearMap.mk' (e.symm b) ?_ else 0
+ exact (((e.linear R hb).mk' _).inverse (e.symm b) (e.symm_apply_apply_mk hb) fun v ↦
+ congr_arg Prod.snd <| e.apply_mk_symm hb v).isLinear
+
+@[simp]
+lemma symmₗ_apply (e : Pretrivialization F (π F E)) [e.IsLinear R] {b : B} (hb : b ∈ e.baseSet)
+ (y : F) : e.symmₗ R b y = e.symm b y := by
+ simp [Pretrivialization.symmₗ, hb]
+
+@[simp]
+lemma symmₗ_apply_of_notMem (e : Pretrivialization F (π F E)) [e.IsLinear R] {b : B}
+ (hb : b ∉ e.baseSet) (y : F) : e.symmₗ R b y = 0 := by
+ simp [Pretrivialization.symmₗ, hb]
/-- A pretrivialization for a vector bundle defines linear equivalences between the
fibers and the model space. -/
@@ -197,9 +204,19 @@ variable (R) in
protected def symmₗ (e : Trivialization F (π F E)) [e.IsLinear R] (b : B) : F →ₗ[R] E b :=
e.toPretrivialization.symmₗ R b
-theorem coe_symmₗ (e : Trivialization F (π F E)) [e.IsLinear R] (b : B) :
- ⇑(e.symmₗ R b) = e.symm b :=
- rfl
+theorem coe_symmₗ (e : Trivialization F (π F E)) [e.IsLinear R] {b : B} (hb : b ∈ e.baseSet) :
+ ⇑(e.symmₗ R b) = e.symm b := by
+ ext y; exact e.toPretrivialization.symmₗ_apply R hb y
+
+@[simp]
+theorem symmₗ_apply (e : Trivialization F (π F E)) [e.IsLinear R] {b : B} (hb : b ∈ e.baseSet)
+ (y : F) : e.symmₗ R b y = e.symm b y :=
+ e.toPretrivialization.symmₗ_apply R hb y
+
+@[simp]
+theorem symmₗ_apply_of_notMem (e : Trivialization F (π F E)) [e.IsLinear R] {b : B}
+ (hb : b ∉ e.baseSet) (y : F) : e.symmₗ R b y = 0 :=
+ e.toPretrivialization.symmₗ_apply_of_notMem R hb y
variable (R) in
/-- A fiberwise linear map equal to `e` on `e.baseSet`. -/
@@ -230,8 +247,8 @@ theorem linearMapAt_def_of_notMem (e : Trivialization F (π F E)) [e.IsLinear R]
dif_neg hb
theorem symm_linearMapAt (e : Trivialization F (π F E)) [e.IsLinear R] {b : B} (hb : b ∈ e.baseSet)
- (y : E b) : e.symm b (e.linearMapAt R b y) = y :=
- e.toPretrivialization.symmₗ_linearMapAt hb y
+ (y : E b) : e.symm b (e.linearMapAt R b y) = y := by
+ simp [hb]
theorem symmₗ_linearMapAt (e : Trivialization F (π F E)) [e.IsLinear R] {b : B} (hb : b ∈ e.baseSet)
(y : E b) : e.symmₗ R b (e.linearMapAt R b y) = y :=
@@ -239,8 +256,8 @@ theorem symmₗ_linearMapAt (e : Trivialization F (π F E)) [e.IsLinear R] {b :
@[simp]
theorem linearMapAt_symm (e : Trivialization F (π F E)) [e.IsLinear R] {b : B} (hb : b ∈ e.baseSet)
- (y : F) : e.linearMapAt R b (e.symm b y) = y :=
- e.toPretrivialization.linearMapAt_symmₗ hb y
+ (y : F) : e.linearMapAt R b (e.symm b y) = y := by
+ simp [hb]
theorem linearMapAt_symmₗ (e : Trivialization F (π F E)) [e.IsLinear R] {b : B} (hb : b ∈ e.baseSet)
(y : F) : e.linearMapAt R b (e.symmₗ R b y) = y :=
@@ -395,19 +412,28 @@ lemma continuousLinearMapAt_apply_of_mem (e : Trivialization F TotalSpace.proj)
simp [coe_linearMapAt_of_mem e hb]
/-- Backwards map of `Bundle.Trivialization.continuousLinearEquivAt`, defined everywhere. -/
-@[simps -fullyApplied apply]
def symmL (e : Trivialization F (π F E)) [e.IsLinear R] (b : B) : F →L[R] E b :=
{ e.symmₗ R b with
- toFun := e.symm b -- given explicitly to help `simps`
cont := by
by_cases hb : b ∈ e.baseSet
· rw [(FiberBundle.totalSpaceMk_isInducing F E b).continuous_iff]
+ refine .congr (f := TotalSpace.mk b ∘ e.symm b) ?_ (by simp [hb])
exact e.continuousOn_symm.comp_continuous (.prodMk_right _) fun x ↦
mk_mem_prod hb (mem_univ x)
- · refine continuous_zero.congr fun x => (e.symm_apply_of_notMem hb x).symm }
+ · exact continuous_zero.congr fun x => (e.symmₗ_apply_of_notMem hb x).symm }
variable {R}
+@[simp]
+theorem symmL_apply (e : Trivialization F (π F E)) [e.IsLinear R] {b : B} (hb : b ∈ e.baseSet)
+ (y : F) : e.symmL R b y = e.symm b y :=
+ e.toPretrivialization.symmₗ_apply R hb y
+
+@[simp]
+lemma symmL_apply_of_notMem (e : Trivialization F (π F E)) [e.IsLinear R] {b : B}
+ (hb : b ∉ e.baseSet) (y : F) : e.symmL R b y = 0 :=
+ e.toPretrivialization.symmₗ_apply_of_notMem _ hb _
+
theorem symmL_continuousLinearMapAt (e : Trivialization F (π F E)) [e.IsLinear R] {b : B}
(hb : b ∈ e.baseSet) (y : E b) : e.symmL R b (e.continuousLinearMapAt R b y) = y :=
e.symmₗ_linearMapAt hb y
@@ -427,7 +453,7 @@ def continuousLinearEquivAt (e : Trivialization F (π F E)) [e.IsLinear R] (b :
invFun := e.symm b -- given explicitly to help `simps`
continuous_toFun := (e.continuousOn.comp_continuous
(FiberBundle.totalSpaceMk_isInducing F E b).continuous fun _ => e.mem_source.mpr hb).snd
- continuous_invFun := (e.symmL R b).continuous }
+ continuous_invFun := by convert (e.symmL R b).continuous; ext; simp [hb] }
theorem coe_continuousLinearEquivAt_eq (e : Trivialization F (π F E)) [e.IsLinear R] {b : B}
(hb : b ∈ e.baseSet) :
@@ -440,12 +466,13 @@ theorem coe_continuousLinearEquivAt_eq' (e : Trivialization F (π F E)) [e.IsLin
DFunLike.coe_injective (e.coe_linearMapAt_of_mem hb).symm
theorem symm_continuousLinearEquivAt_eq (e : Trivialization F (π F E)) [e.IsLinear R] {b : B}
- (hb : b ∈ e.baseSet) : ((e.continuousLinearEquivAt R b hb).symm : F → E b) = e.symmL R b :=
- rfl
+ (hb : b ∈ e.baseSet) : ((e.continuousLinearEquivAt R b hb).symm : F → E b) = e.symmL R b := by
+ ext; simp [hb]
theorem symm_continuousLinearEquivAt_eq' (e : Trivialization F (π F E)) [e.IsLinear R] {b : B}
- (hb : b ∈ e.baseSet) : ((e.continuousLinearEquivAt R b hb).symm : F →L[R] E b) = e.symmL R b :=
- rfl
+ (hb : b ∈ e.baseSet) :
+ ((e.continuousLinearEquivAt R b hb).symm : F →L[R] E b) = e.symmL R b := by
+ ext; simp [hb]
@[simp]
theorem continuousLinearEquivAt_apply' (e : Trivialization F (π F E)) [e.IsLinear R]
@@ -745,7 +772,7 @@ theorem trivializationAt_continuousLinearMapAt {b₀ b : B}
theorem localTriv_symmL {b : B} (hb : b ∈ (Z.localTriv i).baseSet) :
(Z.localTriv i).symmL R b = Z.coordChange i (Z.indexAt b) b := by
ext1 v
- rw [(Z.localTriv i).symmL_apply R, (Z.localTriv i).symm_apply]
+ rw [(Z.localTriv i).symmL_apply hb, (Z.localTriv i).symm_apply]
exacts [rfl, hb]
@[simp, mfld_simps]
diff --git a/Mathlib/Topology/VectorBundle/Constructions.lean b/Mathlib/Topology/VectorBundle/Constructions.lean
index 1ea7d3026627dc..6851c1994c7c01 100644
--- a/Mathlib/Topology/VectorBundle/Constructions.lean
+++ b/Mathlib/Topology/VectorBundle/Constructions.lean
@@ -74,7 +74,7 @@ instance vectorBundle : VectorBundle 𝕜 F (Bundle.Trivial B F) where
@[simp] lemma symmₗ_trivialization (x : B) :
(trivialization B F).symmₗ 𝕜 x = LinearMap.id := by
- ext; simp [Trivialization.coe_symmₗ, trivialization_symm_apply B F]
+ ext; simp [trivialization_symm_apply B F]
@[simp] lemma symmL_trivialization (x : B) :
(trivialization B F).symmL 𝕜 x = ContinuousLinearMap.id 𝕜 F := by
diff --git a/Mathlib/Topology/VectorBundle/ContinuousAlternatingMap.lean b/Mathlib/Topology/VectorBundle/ContinuousAlternatingMap.lean
index 8a1fb98ff57e10..ffce633b4ed0d1 100644
--- a/Mathlib/Topology/VectorBundle/ContinuousAlternatingMap.lean
+++ b/Mathlib/Topology/VectorBundle/ContinuousAlternatingMap.lean
@@ -87,7 +87,7 @@ theorem inCoordinates_eq {x₀ x : B₁} {y₀ y : B₂} {ϕ : E₁ x [⋀^ι]
|>.compContinuousAlternatingMap ϕ |>.compContinuousLinearMap
(((trivializationAt F₁ E₁ x₀).continuousLinearEquivAt 𝕜 x hx).symm : F₁ →L[𝕜] E₁ x)) := by
ext
- simp [inCoordinates, *]
+ simp [inCoordinates, *, Function.comp_def]
end ContinuousAlternatingMap
@@ -277,7 +277,8 @@ def vectorPrebundle :
(mem_baseSet_trivializationAt _ _ _)
convert! (L₁.continuousAlternatingMapCongr L₂).toHomeomorph.isInducing
ext f
- simp [Trivialization.linearMapAt_def_of_mem _ (mem_baseSet_trivializationAt _ _ _), L₁, L₂]
+ simp [Trivialization.linearMapAt_def_of_mem _ (mem_baseSet_trivializationAt _ _ _), L₁, L₂,
+ Function.comp_def, mem_baseSet_trivializationAt]
/-- Topology on the total space of the continuous `σ`-semilinear maps between two "normable" vector
bundles over the same base. -/
diff --git a/Mathlib/Topology/VectorBundle/Hom.lean b/Mathlib/Topology/VectorBundle/Hom.lean
index 2a2a8778a17f73..5162b27a435c26 100644
--- a/Mathlib/Topology/VectorBundle/Hom.lean
+++ b/Mathlib/Topology/VectorBundle/Hom.lean
@@ -154,10 +154,9 @@ theorem continuousLinearMapCoordChange_apply (b : B)
simp_rw [continuousLinearMapCoordChange, ContinuousLinearEquiv.coe_coe,
ContinuousLinearEquiv.arrowCongrSL_apply, continuousLinearMap_apply,
continuousLinearMap_symm_apply' σ e₁ e₂ hb.1, comp_apply, ContinuousLinearEquiv.coe_coe,
- ContinuousLinearEquiv.symm_symm, Trivialization.continuousLinearMapAt_apply,
- Trivialization.symmL_apply]
- rw [e₂.coordChangeL_apply e₂', e₁'.coordChangeL_apply e₁, e₁.coe_linearMapAt_of_mem hb.1.1,
- e₂'.coe_linearMapAt_of_mem hb.2.2]
+ ContinuousLinearEquiv.symm_symm, Trivialization.continuousLinearMapAt_apply]
+ rw [e₂.symmL_apply hb.1.2, e₁'.symmL_apply hb.2.1, e₂.coordChangeL_apply e₂',
+ e₁'.coordChangeL_apply e₁, e₁.coe_linearMapAt_of_mem hb.1.1, e₂'.coe_linearMapAt_of_mem hb.2.2]
exacts [⟨hb.2.1, hb.1.1⟩, ⟨hb.1.2, hb.2.2⟩]
end Bundle.Pretrivialization
@@ -208,7 +207,8 @@ def Bundle.ContinuousLinearMap.vectorPrebundle :
convert! this
ext f
dsimp [Pretrivialization.continuousLinearMap_apply]
- rw [Trivialization.linearMapAt_def_of_mem _ (mem_baseSet_trivializationAt _ _ _)]
+ simp only [Trivialization.symmL_apply, mem_baseSet_trivializationAt,
+ Trivialization.linearMapAt_def_of_mem]
rfl
/-- Topology on the total space of the continuous `σ`-semilinear maps between two "normable" vector
@@ -520,7 +520,7 @@ theorem inCoordinates_apply_eq₂
(trivializationAt F₃ E₃ x₀).linearMapAt 𝕜 x
(ϕ ((trivializationAt F₁ E₁ x₀).symm x v) ((trivializationAt F₂ E₂ x₀).symm x w)) := by
rw [inCoordinates_eq h₁x (by simp [h₂x, h₃x])]
- simp [hom_trivializationAt, Trivialization.continuousLinearMap_apply]
+ simp [hom_trivializationAt, Trivialization.continuousLinearMap_apply, h₂x]
end TwoVariables
diff --git a/Mathlib/Topology/VectorBundle/Riemannian.lean b/Mathlib/Topology/VectorBundle/Riemannian.lean
index cf108f20289169..a5ae4263dd779a 100644
--- a/Mathlib/Topology/VectorBundle/Riemannian.lean
+++ b/Mathlib/Topology/VectorBundle/Riemannian.lean
@@ -183,7 +183,8 @@ lemma eventually_norm_symmL_trivializationAt_self_comp_lt (x : B) {r : ℝ} (hr
let w := (trivializationAt F E x).continuousLinearMapAt ℝ y v
suffices ‖((trivializationAt F E x).symmL ℝ x) w‖ ^ 2 ≤ r' ^ 2 * ‖v‖ ^ 2 from
le_of_sq_le_sq (by simpa [mul_pow]) (by positivity)
- simp only [Trivialization.symmL_apply, ← real_inner_self_eq_norm_sq, hg]
+ simp only [Trivialization.symmL_apply, mem_baseSet_trivializationAt,
+ ← real_inner_self_eq_norm_sq, hg]
have hgy : g y v v = g' y w w := by
rw [inCoordinates_apply_eq₂ h'y h'y (Set.mem_univ _)]
have A : ((trivializationAt F E x).symm y)
@@ -228,8 +229,8 @@ lemma eventually_norm_trivializationAt_lt (x : B) :
((trivializationAt F E x).symmL ℝ x) = ContinuousLinearMap.id _ _ := by
ext v
have h'x : x ∈ (trivializationAt F E x).baseSet := FiberBundle.mem_baseSet_trivializationAt' x
- simp only [Trivialization.continuousLinearMapAt_apply, Trivialization.symmL_apply, comp_apply,
- id_apply]
+ simp only [Trivialization.continuousLinearMapAt_apply, Trivialization.symmL_apply,
+ mem_baseSet_trivializationAt, comp_apply, id_apply]
convert! ((trivializationAt F E x).continuousLinearEquivAt ℝ _ h'x).apply_symm_apply v
simp [Trivialization.coe_continuousLinearEquivAt_eq _ h'x]
have : (trivializationAt F E x).continuousLinearMapAt ℝ y =
@@ -286,7 +287,7 @@ lemma eventually_norm_symmL_trivializationAt_comp_self_lt (x : B) {r : ℝ} (hr
let w := (trivializationAt F E x).continuousLinearMapAt ℝ x v
suffices ‖((trivializationAt F E x).symmL ℝ y) w‖ ^ 2 ≤ r' ^ 2 * ‖v‖ ^ 2 from
le_of_sq_le_sq (by simpa [mul_pow]) (by positivity)
- simp only [Trivialization.symmL_apply, ← real_inner_self_eq_norm_sq, hg]
+ simp only [Trivialization.symmL_apply, h'y, ← real_inner_self_eq_norm_sq, hg]
have hgx : g x v v = g' x w w := by
rw [inCoordinates_apply_eq₂ h'x h'x (Set.mem_univ _)]
have A : ((trivializationAt F E x).symm x)
@@ -333,8 +334,8 @@ lemma eventually_norm_symmL_trivializationAt_lt (x : B) :
((trivializationAt F E x).symmL ℝ x) = ContinuousLinearMap.id _ _ := by
ext v
have h'x : x ∈ (trivializationAt F E x).baseSet := FiberBundle.mem_baseSet_trivializationAt' x
- simp only [Trivialization.continuousLinearMapAt_apply, Trivialization.symmL_apply, comp_apply,
- id_apply]
+ simp only [Trivialization.continuousLinearMapAt_apply, Trivialization.symmL_apply,
+ mem_baseSet_trivializationAt, comp_apply, id_apply]
convert! ((trivializationAt F E x).continuousLinearEquivAt ℝ _ h'x).apply_symm_apply v
simp [Trivialization.coe_continuousLinearEquivAt_eq _ h'x]
have : (trivializationAt F E x).symmL ℝ y =
From 1531e8c38272ca00718134635350fbbafbbf8c67 Mon Sep 17 00:00:00 2001
From: Christian Merten <136261474+chrisflav@users.noreply.github.com>
Date: Tue, 23 Jun 2026 12:01:30 +0000
Subject: [PATCH 0285/1300] feat(AlgebraicGeometry/Modules): compatibilities of
`Over` and restriction along open immersions (#40935)
We construct natural isomorphisms relating `Scheme.Modules.overEquiv` with `Scheme.Modules.restrictFunctor`.
---
Mathlib/Algebra/Category/ModuleCat/Basic.lean | 27 +++++++
.../Category/ModuleCat/ChangeOfRings.lean | 11 +++
.../Algebra/Category/ModuleCat/Presheaf.lean | 7 ++
.../ModuleCat/Presheaf/Pushforward.lean | 9 +++
.../Sheaf/PushforwardContinuous.lean | 19 ++++-
Mathlib/AlgebraicGeometry/Modules/Sheaf.lean | 77 +++++++++++++++++++
Mathlib/AlgebraicGeometry/Restrict.lean | 13 ++++
7 files changed, 160 insertions(+), 3 deletions(-)
diff --git a/Mathlib/Algebra/Category/ModuleCat/Basic.lean b/Mathlib/Algebra/Category/ModuleCat/Basic.lean
index 4c29f9d53dd76d..8f8a97ee01b9ad 100644
--- a/Mathlib/Algebra/Category/ModuleCat/Basic.lean
+++ b/Mathlib/Algebra/Category/ModuleCat/Basic.lean
@@ -585,6 +585,33 @@ def homMk : M ⟶ N where
lemma forget₂_map_homMk :
(forget₂ (ModuleCat R) AddCommGrpCat).map (homMk φ hφ) = φ := rfl
+/-- Constructor for isomorphisms in `ModuleCat R` taking an isomorphism in `AddCommGrpCat`
+and a compatibility condition. -/
+def isoMk (φ : (forget₂ (ModuleCat R) Ab).obj M ≅ (forget₂ _ _).obj N)
+ (hφ : ∀ r, φ.hom ≫ N.smul r = M.smul r ≫ φ.hom) :
+ M ≅ N :=
+ LinearEquiv.toModuleIso
+ { __ := φ.addCommGroupIsoToAddEquiv
+ map_smul' r x := congr($(hφ r) x).symm }
+
+@[simp]
+lemma isoMk_hom (φ : (forget₂ (ModuleCat R) Ab).obj M ≅ (forget₂ _ _).obj N)
+ (hφ : ∀ r, φ.hom ≫ N.smul r = M.smul r ≫ φ.hom) :
+ (isoMk φ hφ).hom = homMk φ.hom hφ :=
+ rfl
+
+@[simp]
+lemma isoMk_inv (φ : (forget₂ (ModuleCat R) Ab).obj M ≅ (forget₂ _ _).obj N)
+ (hφ : ∀ r, φ.hom ≫ N.smul r = M.smul r ≫ φ.hom) :
+ (isoMk φ hφ).inv = homMk φ.inv (ModuleCat.smul_naturality (isoMk φ hφ).inv) :=
+ rfl
+
+@[simp]
+lemma isoMk_symm (φ : (forget₂ (ModuleCat R) Ab).obj M ≅ (forget₂ _ _).obj N)
+ (hφ : ∀ r, φ.hom ≫ N.smul r = M.smul r ≫ φ.hom) :
+ (isoMk φ hφ).symm = isoMk φ.symm (ModuleCat.smul_naturality (isoMk φ hφ).inv) :=
+ rfl
+
end
instance : (forget (ModuleCat.{v} R)).ReflectsIsomorphisms where
diff --git a/Mathlib/Algebra/Category/ModuleCat/ChangeOfRings.lean b/Mathlib/Algebra/Category/ModuleCat/ChangeOfRings.lean
index 0f4781fb157094..b162815dd0a236 100644
--- a/Mathlib/Algebra/Category/ModuleCat/ChangeOfRings.lean
+++ b/Mathlib/Algebra/Category/ModuleCat/ChangeOfRings.lean
@@ -87,6 +87,17 @@ def restrictScalars {R : Type u₁} {S : Type u₂} [Ring R] [Ring S] (f : R →
obj := RestrictScalars.obj' f
map := RestrictScalars.map' f
+@[simp]
+lemma smul_restrictScalars {R : Type u₁} {S : Type u₂} [Ring R] [Ring S] (f : R →+* S) (r : R)
+ (M : ModuleCat S) :
+ dsimp% ((ModuleCat.restrictScalars f).obj M).smul r = M.smul (f r) :=
+ rfl
+
+lemma forget₂_map_restrictScalars {R : Type u₁} {S : Type u₂} [Ring R] [Ring S] (f : R →+* S)
+ {M N : ModuleCat S} (g : M ⟶ N) :
+ (forget₂ _ Ab).map ((ModuleCat.restrictScalars f).map g) = (forget₂ _ Ab).map g :=
+ rfl
+
instance {R : Type u₁} {S : Type u₂} [Ring R] [Ring S] (f : R →+* S) :
(restrictScalars.{v} f).Faithful where
map_injective h := by
diff --git a/Mathlib/Algebra/Category/ModuleCat/Presheaf.lean b/Mathlib/Algebra/Category/ModuleCat/Presheaf.lean
index b2d94c8c704d37..1428f49d38f65b 100644
--- a/Mathlib/Algebra/Category/ModuleCat/Presheaf.lean
+++ b/Mathlib/Algebra/Category/ModuleCat/Presheaf.lean
@@ -140,6 +140,13 @@ lemma presheaf_obj_coe (X : Cᵒᵖ) :
lemma presheaf_map_apply_coe {X Y : Cᵒᵖ} (f : X ⟶ Y) (x : M.obj X) :
DFunLike.coe (α := M.obj X) (β := fun _ ↦ M.obj Y) (M.presheaf.map f).hom x = M.map f x := rfl
+@[reassoc]
+lemma smul_map {U V : Cᵒᵖ} (f : U ⟶ V) (r : R.obj U) :
+ dsimp% ModuleCat.smul _ r ≫ M.presheaf.map f =
+ M.presheaf.map f ≫ ModuleCat.smul _ (R.map f r) := by
+ ext x
+ exact (M.map f).hom.map_smul r x
+
instance (M : PresheafOfModules R) (X : Cᵒᵖ) :
Module (R.obj X) (M.presheaf.obj X) :=
inferInstanceAs (Module (R.obj X) (M.obj X))
diff --git a/Mathlib/Algebra/Category/ModuleCat/Presheaf/Pushforward.lean b/Mathlib/Algebra/Category/ModuleCat/Presheaf/Pushforward.lean
index db8c9a6831aa72..1b537629bacb2a 100644
--- a/Mathlib/Algebra/Category/ModuleCat/Presheaf/Pushforward.lean
+++ b/Mathlib/Algebra/Category/ModuleCat/Presheaf/Pushforward.lean
@@ -89,6 +89,15 @@ a morphism of presheaves of rings `S ⟶ F.op ⋙ R`. -/
noncomputable def pushforward : PresheafOfModules.{v} R ⥤ PresheafOfModules.{v} S :=
pushforward₀ F R ⋙ restrictScalars φ
+lemma forget₂_map_pushforward_obj_map {U V : Cᵒᵖ} (f : U ⟶ V) (M : PresheafOfModules R) :
+ (forget₂ _ Ab).map (((PresheafOfModules.pushforward φ).obj M).map f) =
+ M.presheaf.map (F.map f.unop).op :=
+ rfl
+
+lemma forget₂_map_pushforward_map_app {U : Cᵒᵖ} {M N : PresheafOfModules _} (g : M ⟶ N) :
+ (forget₂ _ Ab).map (((pushforward φ).map g).app U) = (forget₂ _ Ab).map (g.app _) :=
+ rfl
+
/-- The pushforward of presheaves of modules commutes with the forgetful functor
to presheaves of abelian groups. -/
noncomputable def pushforwardCompToPresheaf :
diff --git a/Mathlib/Algebra/Category/ModuleCat/Sheaf/PushforwardContinuous.lean b/Mathlib/Algebra/Category/ModuleCat/Sheaf/PushforwardContinuous.lean
index ba6bd327e13c56..6ff62bcb90c789 100644
--- a/Mathlib/Algebra/Category/ModuleCat/Sheaf/PushforwardContinuous.lean
+++ b/Mathlib/Algebra/Category/ModuleCat/Sheaf/PushforwardContinuous.lean
@@ -48,6 +48,11 @@ noncomputable def pushforward : SheafOfModules.{v} R ⥤ SheafOfModules.{v} S wh
map f :=
{ val := (PresheafOfModules.pushforward φ.hom).map f.val }
+lemma forget₂_map_pushforward_obj_val_map {U V : Cᵒᵖ} (f : U ⟶ V) (M) :
+ (forget₂ _ Ab).map (((pushforward.{v} φ).obj M).val.map f) =
+ M.val.presheaf.map (F.map f.unop).op :=
+ rfl
+
variable (R) in
/-- The restriction functor from sheaves of `R`-modules to sheaves of `R.over X`-modules
for some `X : D`. -/
@@ -128,12 +133,13 @@ section
variable {K' : GrothendieckTopology D'} {K'' : GrothendieckTopology D''}
{G : D ⥤ D'} {R' : Sheaf K' RingCat.{u}}
- [Functor.IsContinuous G K K'] [Functor.IsContinuous (F ⋙ G) J K']
+ [Functor.IsContinuous G K K']
(ψ : R ⟶ (G.sheafPushforwardContinuous RingCat.{u} K K').obj R')
/-- The composition of two pushforward functors on categories of sheaves of modules
identify to the pushforward for the composition. -/
noncomputable def pushforwardComp :
+ haveI : Functor.IsContinuous (F ⋙ G) J K' := Functor.isContinuous_comp _ _ _ K _
pushforward.{v} ψ ⋙ pushforward.{v} φ ≅
pushforward.{v} (F := F ⋙ G) (φ ≫ (F.sheafPushforwardContinuous RingCat.{u} J K).map ψ) :=
Iso.refl _
@@ -149,8 +155,7 @@ lemma pushforwardComp_inv_app_val_app (M U x) :
variable {G' : D' ⥤ D''} {R'' : Sheaf K'' RingCat.{u}}
[Functor.IsContinuous G' K' K'']
[Functor.IsContinuous (G ⋙ G') K K'']
- [Functor.IsContinuous ((F ⋙ G) ⋙ G') J K'']
- [Functor.IsContinuous (F ⋙ G ⋙ G') J K'']
+ [(F ⋙ G).IsContinuous J K']
(ψ' : R' ⟶ (G'.sheafPushforwardContinuous RingCat.{u} K' K'').obj R'')
lemma pushforward_assoc :
@@ -240,6 +245,14 @@ noncomputable def pushforwardNatIso (α : F ≅ G) :
simp only [← Functor.map_comp, ← op_comp,
Iso.hom_inv_id_app, op_id, CategoryTheory.Functor.map_id]
+/-- More flexible variant of `SheafOfModules.pushforwardNatIso`. -/
+@[simps!]
+noncomputable
+def pushforwardCongr₂ {ψ : T ⟶ (F.sheafPushforwardContinuous RingCat J K).obj S} (e : F ≅ G)
+ (he : φ ≫ (Functor.sheafPushforwardContinuousNatTrans e.hom _ _ _).app S = ψ) :
+ pushforward.{v} φ ≅ pushforward.{v} ψ :=
+ pushforwardNatIso _ e ≪≫ pushforwardCongr he
+
end NatTrans
section Adjunction
diff --git a/Mathlib/AlgebraicGeometry/Modules/Sheaf.lean b/Mathlib/AlgebraicGeometry/Modules/Sheaf.lean
index b415ed001bbaa5..32ec8d0dc0624f 100644
--- a/Mathlib/AlgebraicGeometry/Modules/Sheaf.lean
+++ b/Mathlib/AlgebraicGeometry/Modules/Sheaf.lean
@@ -10,9 +10,11 @@ public import Mathlib.Algebra.Category.ModuleCat.Sheaf.Colimits
public import Mathlib.Algebra.Category.ModuleCat.Sheaf.PullbackContinuous
public import Mathlib.AlgebraicGeometry.Modules.Presheaf
public import Mathlib.AlgebraicGeometry.OpenImmersion
+public import Mathlib.AlgebraicGeometry.AffineScheme
public import Mathlib.CategoryTheory.Bicategory.Adjunction.Adj
public import Mathlib.CategoryTheory.Bicategory.Adjunction.Cat
public import Mathlib.CategoryTheory.Bicategory.Functor.LocallyDiscrete
+public import Mathlib.Topology.Sheaves.Module
/-!
# The category of sheaves of modules over a scheme
@@ -381,6 +383,23 @@ instead. -/
lemma restrict_map (M : Y.Modules) (f : X ⟶ Y) [IsOpenImmersion f] {U V} (i : U ⟶ V) :
(M.restrict f).presheaf.map i.op = M.presheaf.map (f.opensFunctor.map i).op := rfl
+/-- `Scheme.Modules.restrict` along an open immersion `X ⟶ Y` sends `𝒪_Y` to `𝒪_X`. -/
+def restrictUnitIso (f : X ⟶ Y) [IsOpenImmersion f] :
+ restrict (.unit <| Y.ringCatSheaf) f ≅ .unit X.ringCatSheaf := by
+ refine (fullyFaithfulForget _).preimageIso <| PresheafOfModules.isoMk (fun U ↦ ?_) ?_
+ · refine ModuleCat.isoMk
+ ((forget₂ CommRingCat RingCat ⋙ forget₂ _ Ab).mapIso (f.appIso U.unop)) ?_
+ intro (r : Γ(X, U.unop))
+ ext (x : Γ(Y, f ''ᵁ U.unop))
+ change r * (f.appIso U.unop).hom x = (f.appIso U.unop).hom ((f.appIso U.unop).inv r * x)
+ simp
+ · intro U V g
+ have : Y.presheaf.map (homOfLE (by grw [leOfHom g.unop])).op ≫
+ (f.appIso _).hom = (f.appIso U.unop).hom ≫ X.presheaf.map g := by
+ simp [Hom.appIso_hom']
+ ext x
+ exact congr($(this) x)
+
/-- The restriction of a module along an open immersion. -/
def restrictFunctorAdjCounitIso : pushforward f ⋙ restrictFunctor f ≅ 𝟭 _ :=
letI := CategoryTheory.Functor.isContinuous_comp.{u} f.opensFunctor (Opens.map f.base)
@@ -521,4 +540,62 @@ noncomputable def sheafComposePushforwardComp {R S : CommRingCat.{u}} (φ : R
· cat_disch
· cat_disch
+/-- Sheaves of modules on `𝒪_X` restricted to `U` are equivalent to sheaves of `𝒪_U`-modules. -/
+noncomputable
+def overEquiv {X : Scheme.{u}} (U : X.Opens) :
+ SheafOfModules (X.ringCatSheaf.over U) ≌ (U : Scheme.{u}).Modules :=
+ TopologicalSpace.Opens.sheafOfModulesEquivOver _ _
+
+set_option backward.isDefEq.respectTransparency false in
+/-- Up to `Scheme.Modules.overEquiv`, `SheafOfModules.overMap` is isomorphic to
+`Scheme.Modules.restrictFunctor`. -/
+noncomputable
+def overMapCompOverEquiv {X : Scheme.{u}} {U V : X.Opens} (f : V ⟶ U) :
+ overMap X.ringCatSheaf f ⋙ (overEquiv V).functor ≅
+ (overEquiv U).functor ⋙ restrictFunctor (X.homOfLE <| leOfHom f) := by
+ haveI : (Hom.opensFunctor (X.homOfLE <| leOfHom f)).IsContinuous
+ (Opens.grothendieckTopology V.toScheme) (Opens.grothendieckTopology U.carrier) :=
+ inferInstanceAs <|
+ (Hom.opensFunctor (X.homOfLE <| leOfHom f)).IsContinuous _
+ (Opens.grothendieckTopology U.toScheme)
+ haveI := U.instIsDenseSubsiteSubtypeMemOverGrothendieckTopologyOverInverseOverEquivalence
+ haveI : (Hom.opensFunctor (X.homOfLE <| leOfHom f)).IsContinuous
+ (Opens.grothendieckTopology ↥V) (Opens.grothendieckTopology U.toScheme) :=
+ inferInstanceAs <| (X.homOfLE <| leOfHom f).opensFunctor.IsContinuous
+ (Opens.grothendieckTopology V.toScheme) (Opens.grothendieckTopology U.toScheme)
+ haveI : ((Opens.overEquivalence V).symm.functor ⋙ Over.map f).IsContinuous
+ (Opens.grothendieckTopology ↥V) ((Opens.grothendieckTopology X).over U) :=
+ Functor.isContinuous_comp _ _ _ (.over (Opens.grothendieckTopology _) _) _
+ haveI : (Opens.overEquivalence U).symm.functor.IsContinuous (Opens.grothendieckTopology U)
+ ((Opens.grothendieckTopology X).over U) :=
+ inferInstanceAs <| U.overEquivalence.inverse.IsContinuous (Opens.grothendieckTopology U.carrier)
+ ((Opens.grothendieckTopology X).over U)
+ haveI : ((X.homOfLE (leOfHom f)).opensFunctor ⋙
+ (Opens.overEquivalence U).symm.functor).IsContinuous (Opens.grothendieckTopology ↥V)
+ ((Opens.grothendieckTopology ↥X).over U) :=
+ Functor.isContinuous_comp _ _ _ (Opens.grothendieckTopology _) _
+ refine (SheafOfModules.pushforwardComp _ _) ≪≫ ?_ ≪≫ (SheafOfModules.pushforwardComp _ _).symm
+ refine SheafOfModules.pushforwardCongr₂ _ ?_ ?_
+ · refine NatIso.ofComponents (fun W ↦ Over.isoMk (eqToIso ?_) ?_) ?_
+ · suffices U.ι ''ᵁ ((X.homOfLE (leOfHom f)) ''ᵁ W) = V.ι ''ᵁ W by simpa
+ simp [← Scheme.Hom.comp_image]
+ · cat_disch
+ · cat_disch
+ · ext W x
+ suffices X.presheaf.map _ x = ((X.homOfLE <| leOfHom f).appIso _).inv x by simpa
+ rw [Scheme.Hom.appIso_homOfLE_inv]
+ rfl
+
+/-- Up to `Scheme.Modules.overEquiv`, `SheafOfModules.overFunctor` is isomorphic to
+`Scheme.Modules.restrictFunctor`. -/
+noncomputable
+def overFunctorEquiv {X : Scheme.{u}} (U : X.Opens) :
+ overFunctor X.ringCatSheaf U ⋙ (overEquiv U).functor ≅ restrictFunctor U.ι := by
+ have : ((Opens.overEquivalence U).symm.functor ⋙ Over.forget U).IsContinuous
+ (Opens.grothendieckTopology ↥U) (Opens.grothendieckTopology ↥X) :=
+ Functor.isContinuous_comp _ _ _ (.over (Opens.grothendieckTopology _) U) _
+ refine SheafOfModules.pushforwardComp _ _ ≪≫ SheafOfModules.pushforwardCongr ?_
+ simp only [CategoryTheory.Functor.map_id, Opposite.op_unop, Opens.ι_appIso, Iso.refl_inv]
+ rfl
+
end AlgebraicGeometry.Scheme.Modules
diff --git a/Mathlib/AlgebraicGeometry/Restrict.lean b/Mathlib/AlgebraicGeometry/Restrict.lean
index a8ccc83681a2f8..2ac6b65fe87143 100644
--- a/Mathlib/AlgebraicGeometry/Restrict.lean
+++ b/Mathlib/AlgebraicGeometry/Restrict.lean
@@ -307,6 +307,19 @@ instance (X : Scheme.{u}) {U V : X.Opens} (e : U ≤ V) : IsOpenImmersion (X.hom
delta Scheme.homOfLE
infer_instance
+set_option backward.isDefEq.respectTransparency false in
+lemma Scheme.Hom.appIso_homOfLE_inv {X : Scheme.{u}} {U V : X.Opens} (h : U ≤ V)
+ (W : (U : Scheme.{u}).Opens) :
+ ((X.homOfLE h).appIso W).inv =
+ X.presheaf.map (.op <| homOfLE <| by
+ suffices V.ι ''ᵁ _ ≤ U.ι ''ᵁ W by simpa
+ simp [← Scheme.Hom.comp_image]) := by
+ rw [eq_comm, ← Iso.hom_comp_eq_id]
+ dsimp
+ simp only [appIso_hom, homOfLE_app, homOfLE_leOfHom, eqToHom_op, Opens.toScheme_presheaf_map,
+ eqToHom_unop, ← X.presheaf.map_comp, Category.assoc, ← X.presheaf.map_id]
+ rfl
+
@[simp]
lemma Scheme.opensRange_homOfLE {U V : X.Opens} (e : U ≤ V) :
(X.homOfLE e).opensRange = V.ι ⁻¹ᵁ U :=
From f4001a93bf4e707c723b07dfe3bbee1a588a6bf2 Mon Sep 17 00:00:00 2001
From: Jovan Gerbscheid <56355248+JovanGerb@users.noreply.github.com>
Date: Tue, 23 Jun 2026 12:20:09 +0000
Subject: [PATCH 0286/1300] feat(CategoryTheory/Limits/IsLimit): use `to_dual`
on `IsLimit` (#36572)
This PR makes a start at translating `IsLimit` using `to_dual`.
---
.../Algebra/Category/Grp/LargeColimits.lean | 2 +-
.../Category/ModuleCat/FilteredColimits.lean | 3 +-
.../Abelian/GrothendieckAxioms/Colim.lean | 1 -
.../Abelian/GrothendieckAxioms/Connected.lean | 1 +
Mathlib/CategoryTheory/Limits/Cones.lean | 4 +-
Mathlib/CategoryTheory/Limits/IsLimit.lean | 601 +++++-------------
Mathlib/CategoryTheory/Limits/MonoCoprod.lean | 1 +
.../Limits/MorphismProperty.lean | 2 +-
.../Limits/Preserves/Basic.lean | 2 +-
.../Limits/Preserves/Bifunctor.lean | 6 +-
.../Limits/Shapes/Grothendieck.lean | 2 +-
.../CategoryTheory/Limits/Types/Colimits.lean | 2 +-
Mathlib/CategoryTheory/Monad/Limits.lean | 4 +-
.../MorphismProperty/Limits.lean | 3 +
.../Presentable/ColimitPresentation.lean | 2 +-
Mathlib/Condensed/Discrete/Colimit.lean | 3 +
16 files changed, 196 insertions(+), 443 deletions(-)
diff --git a/Mathlib/Algebra/Category/Grp/LargeColimits.lean b/Mathlib/Algebra/Category/Grp/LargeColimits.lean
index 9f169824b42e8d..bc79879e2627e0 100644
--- a/Mathlib/Algebra/Category/Grp/LargeColimits.lean
+++ b/Mathlib/Algebra/Category/Grp/LargeColimits.lean
@@ -46,7 +46,7 @@ lemma isColimit_iff_bijective_desc [DecidableEq J] :
apply ofHom_injective
refine hc.hom_ext (fun j ↦ ?_)
ext x
- rw [ConcreteCategory.comp_apply, ConcreteCategory.comp_apply, ← Quot.ι_desc _ c j x]
+ erw [ConcreteCategory.comp_apply, ConcreteCategory.comp_apply, ← Quot.ι_desc _ c j x]
exact DFunLike.congr_fun eq (Quot.ι F j x)
· set c' : Cocone F :=
{ pt := AddCommGrpCat.of (ULift (AddCircle (1 : ℚ)))
diff --git a/Mathlib/Algebra/Category/ModuleCat/FilteredColimits.lean b/Mathlib/Algebra/Category/ModuleCat/FilteredColimits.lean
index 1ba7a0e47b858a..0f8b07457d348c 100644
--- a/Mathlib/Algebra/Category/ModuleCat/FilteredColimits.lean
+++ b/Mathlib/Algebra/Category/ModuleCat/FilteredColimits.lean
@@ -175,9 +175,10 @@ def colimitDesc (t : Cocone F) : colimit F ⟶ t.pt :=
obtain ⟨j, x, rfl⟩ := M.mk_surjective F x
simp [hf] }
+set_option backward.defeqAttrib.useBackward true in
@[reassoc (attr := simp)]
lemma ι_colimitDesc (t : Cocone F) (j : J) :
- (colimitCocone F).ι.app j ≫ colimitDesc F t = t.ι.app j :=
+ dsimp% (colimitCocone F).ι.app j ≫ colimitDesc F t = t.ι.app j :=
(forget₂ _ AddCommGrpCat).map_injective
((AddCommGrpCat.FilteredColimits.colimitCoconeIsColimit (F ⋙ forget₂ _ _)).fac _ _)
diff --git a/Mathlib/CategoryTheory/Abelian/GrothendieckAxioms/Colim.lean b/Mathlib/CategoryTheory/Abelian/GrothendieckAxioms/Colim.lean
index 86d80f40428592..47521a2a24aab7 100644
--- a/Mathlib/CategoryTheory/Abelian/GrothendieckAxioms/Colim.lean
+++ b/Mathlib/CategoryTheory/Abelian/GrothendieckAxioms/Colim.lean
@@ -109,7 +109,6 @@ short complex `c₁.pt ⟶ c₂.pt ⟶ c₃.pt`. -/
@[simps]
def colim.mapShortComplex : ShortComplex C :=
ShortComplex.mk f g (hc₁.hom_ext (fun j ↦ by
- dsimp
rw [reassoc_of% (hf j), hg j, comp_zero, ← NatTrans.comp_app_assoc, S.zero,
zero_app, zero_comp]))
diff --git a/Mathlib/CategoryTheory/Abelian/GrothendieckAxioms/Connected.lean b/Mathlib/CategoryTheory/Abelian/GrothendieckAxioms/Connected.lean
index 80c296ebe1f07c..2d124b5e493a0d 100644
--- a/Mathlib/CategoryTheory/Abelian/GrothendieckAxioms/Connected.lean
+++ b/Mathlib/CategoryTheory/Abelian/GrothendieckAxioms/Connected.lean
@@ -51,6 +51,7 @@ noncomputable def IsColimit.pullbackOfHasExactColimitsOfShape [HasPullbacks C]
have := hc.isIso_colimMap_ι
apply hpull.isIso_snd_of_isIso
+set_option backward.isDefEq.respectTransparency false in
/-- Detecting equality of morphisms factoring through a connected colimit by pulling back along
the inclusions of the colimit. -/
theorem IsColimit.pullback_hom_ext [HasPullbacks C] [HasColimitsOfShape J C]
diff --git a/Mathlib/CategoryTheory/Limits/Cones.lean b/Mathlib/CategoryTheory/Limits/Cones.lean
index 5913294e8a5ba4..0d406a66ca30b3 100644
--- a/Mathlib/CategoryTheory/Limits/Cones.lean
+++ b/Mathlib/CategoryTheory/Limits/Cones.lean
@@ -151,13 +151,11 @@ instance inhabitedCone (F : Discrete PUnit ⥤ C) : Inhabited (Cone F) :=
}⟩
set_option backward.defeqAttrib.useBackward true in
-@[to_dual (attr := reassoc (attr := simp))]
+@[to_dual (attr := reassoc (attr := simp), elementwise)]
theorem Cone.w {F : J ⥤ C} (c : Cone F) {j j' : J} (f : j ⟶ j') :
dsimp% c.π.app j ≫ F.map f = c.π.app j' := by
simpa using (c.π.naturality f).symm
-attribute [elementwise] Cocone.w Cone.w
-
end
variable {F : J ⥤ C}
diff --git a/Mathlib/CategoryTheory/Limits/IsLimit.lean b/Mathlib/CategoryTheory/Limits/IsLimit.lean
index 3045f689aa7f55..57c4bedb521de0 100644
--- a/Mathlib/CategoryTheory/Limits/IsLimit.lean
+++ b/Mathlib/CategoryTheory/Limits/IsLimit.lean
@@ -61,43 +61,69 @@ structure IsLimit (t : Cone F) where
uniq : ∀ (s : Cone F) (m : s.pt ⟶ t.pt) (_ : ∀ j : J, m ≫ t.π.app j = s.π.app j), m = lift s := by
cat_disch
-attribute [reassoc (attr := simp)] IsLimit.fac
+set_option backward.defeqAttrib.useBackward true in
+/-- A cocone `t` on `F` is a colimit cocone if each cocone on `F` admits a unique
+cocone morphism from `t`. -/
+@[stacks 002F, to_dual]
+structure IsColimit (t : Cocone F) where
+ /-- `t.pt` maps to all other cocone covertices -/
+ desc : ∀ s : Cocone F, t.pt ⟶ s.pt
+ /-- The map `desc` makes the diagram with the natural transformations commute -/
+ fac : ∀ (s : Cocone F) (j : J), dsimp% t.ι.app j ≫ desc s = s.ι.app j := by cat_disch
+ /-- `desc` is the unique such map -/
+ uniq :
+ ∀ (s : Cocone F) (m : t.pt ⟶ s.pt) (_ : ∀ j : J, t.ι.app j ≫ m = s.ι.app j), m = desc s := by
+ cat_disch
+
+attribute [reassoc (attr := simp)] IsLimit.fac IsColimit.fac
+
+to_dual_name_hint Lift Desc, Left Right
namespace IsLimit
+@[to_dual]
instance subsingleton {t : Cone F} : Subsingleton (IsLimit t) :=
⟨by intro P Q; cases P; cases Q; congr; cat_disch⟩
/-- Given a natural transformation `α : F ⟶ G`, we give a morphism from the cone point
of any cone over `F` to the cone point of a limit cone over `G`. -/
+@[to_dual (reorder := s P t)
+/-- Given a natural transformation `α : F ⟶ G`, we give a morphism from the cocone point
+of a colimit cocone over `F` to the cocone point of any cocone over `G`. -/]
def map {F G : J ⥤ C} (s : Cone F) {t : Cone G} (P : IsLimit t) (α : F ⟶ G) : s.pt ⟶ t.pt :=
P.lift ((Cone.postcompose α).obj s)
-@[reassoc (attr := simp)]
+-- The `set_option` is needed to make reassoc generate the right theorem
+set_option backward.isDefEq.respectTransparency false in
+@[to_dual (attr := reassoc (attr := simp)) (reorder := c hd d) ι_map]
theorem map_π {F G : J ⥤ C} (c : Cone F) {d : Cone G} (hd : IsLimit d) (α : F ⟶ G) (j : J) :
hd.map c α ≫ d.π.app j = c.π.app j ≫ α.app j :=
fac _ _ _
-@[simp]
+@[to_dual (attr := simp)]
theorem lift_self {c : Cone F} (t : IsLimit c) : t.lift c = 𝟙 c.pt :=
(t.uniq _ _ fun _ => id_comp _).symm
-- Repackaging the definition in terms of cone morphisms.
/-- The universal morphism from any other cone to a limit cone. -/
-@[simps]
+@[to_dual (attr := simps)
+/-- The universal morphism from a colimit cocone to any other cocone. -/]
def liftConeMorphism {t : Cone F} (h : IsLimit t) (s : Cone F) : s ⟶ t where hom := h.lift s
+@[to_dual]
theorem uniq_cone_morphism {s t : Cone F} (h : IsLimit t) {f f' : s ⟶ t} : f = f' :=
have : ∀ {g : s ⟶ t}, g = h.liftConeMorphism s := by
intro g; apply ConeMorphism.ext; exact h.uniq _ _ g.w
this.trans this.symm
/-- Restating the definition of a limit cone in terms of the ∃! operator. -/
+@[to_dual /-- Restating the definition of a colimit cocone in terms of the ∃! operator. -/]
theorem existsUnique {t : Cone F} (h : IsLimit t) (s : Cone F) :
∃! l : s.pt ⟶ t.pt, ∀ j, l ≫ t.π.app j = s.π.app j :=
⟨h.lift s, h.fac s, h.uniq s⟩
/-- Noncomputably make a limit cone from the existence of unique factorizations. -/
+@[to_dual /-- Noncomputably make a colimit cocone from the existence of unique factorizations. -/]
def ofExistsUnique {t : Cone F}
(ht : ∀ s : Cone F, ∃! l : s.pt ⟶ t.pt, ∀ j, l ≫ t.π.app j = s.π.app j) : IsLimit t := by
choose s hs hs' using ht
@@ -107,7 +133,11 @@ def ofExistsUnique {t : Cone F}
providing a morphism of cones rather than a morphism between the cone points
and separately the factorisation condition.
-/
-@[simps]
+@[to_dual (attr := simps)
+/-- Alternative constructor for `IsColimit`,
+providing a morphism of cocones rather than a morphism between the cocone points
+and separately the factorisation condition.
+-/]
def mkConeMorphism {t : Cone F} (lift : ∀ s : Cone F, s ⟶ t)
(uniq : ∀ (s : Cone F) (m : s ⟶ t), m = lift s) : IsLimit t where
lift s := (lift s).hom
@@ -116,7 +146,7 @@ def mkConeMorphism {t : Cone F} (lift : ∀ s : Cone F, s ⟶ t)
congrArg ConeMorphism.hom this
/-- Limit cones on `F` are unique up to isomorphism. -/
-@[simps]
+@[to_dual (attr := simps) /-- Colimit cocones on `F` are unique up to isomorphism. -/]
def uniqueUpToIso {s t : Cone F} (P : IsLimit s) (Q : IsLimit t) : s ≅ t where
hom := Q.liftConeMorphism s
inv := P.liftConeMorphism t
@@ -124,56 +154,61 @@ def uniqueUpToIso {s t : Cone F} (P : IsLimit s) (Q : IsLimit t) : s ≅ t where
inv_hom_id := Q.uniq_cone_morphism
/-- Any cone morphism between limit cones is an isomorphism. -/
+@[to_dual (reorder := P Q) /-- Any cocone morphism between colimit cocones is an isomorphism. -/]
theorem hom_isIso {s t : Cone F} (P : IsLimit s) (Q : IsLimit t) (f : s ⟶ t) : IsIso f :=
⟨⟨P.liftConeMorphism t, ⟨P.uniq_cone_morphism, Q.uniq_cone_morphism⟩⟩⟩
/-- Limits of `F` are unique up to isomorphism. -/
+@[to_dual /-- Colimits of `F` are unique up to isomorphism. -/]
def conePointUniqueUpToIso {s t : Cone F} (P : IsLimit s) (Q : IsLimit t) : s.pt ≅ t.pt :=
(Cone.forget F).mapIso (uniqueUpToIso P Q)
-@[reassoc (attr := simp)]
+@[to_dual (attr := reassoc (attr := simp)) comp_coconePointUniqueUpToIso_inv]
theorem conePointUniqueUpToIso_hom_comp {s t : Cone F} (P : IsLimit s) (Q : IsLimit t) (j : J) :
(conePointUniqueUpToIso P Q).hom ≫ t.π.app j = s.π.app j :=
(uniqueUpToIso P Q).hom.w _
-@[reassoc (attr := simp)]
+@[to_dual (attr := reassoc (attr := simp)) comp_coconePointUniqueUpToIso_hom]
theorem conePointUniqueUpToIso_inv_comp {s t : Cone F} (P : IsLimit s) (Q : IsLimit t) (j : J) :
(conePointUniqueUpToIso P Q).inv ≫ s.π.app j = t.π.app j :=
(uniqueUpToIso P Q).inv.w _
-@[reassoc (attr := simp)]
+@[to_dual (attr := reassoc (attr := simp)) coconePointUniqueUpToIso_inv_desc]
theorem lift_comp_conePointUniqueUpToIso_hom {r s t : Cone F} (P : IsLimit s) (Q : IsLimit t) :
P.lift r ≫ (conePointUniqueUpToIso P Q).hom = Q.lift r :=
Q.uniq _ _ (by simp)
-@[reassoc (attr := simp)]
+@[to_dual (attr := reassoc (attr := simp)) coconePointUniqueUpToIso_hom_desc]
theorem lift_comp_conePointUniqueUpToIso_inv {r s t : Cone F} (P : IsLimit s) (Q : IsLimit t) :
Q.lift r ≫ (conePointUniqueUpToIso P Q).inv = P.lift r :=
P.uniq _ _ (by simp)
/-- Transport evidence that a cone is a limit cone across an isomorphism of cones. -/
+@[to_dual
+/-- Transport evidence that a cocone is a colimit cocone across an isomorphism of cocones. -/]
def ofIsoLimit {r t : Cone F} (P : IsLimit r) (i : r ≅ t) : IsLimit t :=
IsLimit.mkConeMorphism (fun s => P.liftConeMorphism s ≫ i.hom) fun s m => by
rw [← i.comp_inv_eq]; apply P.uniq_cone_morphism
-@[simp]
+@[to_dual (attr := simp)]
theorem ofIsoLimit_lift {r t : Cone F} (P : IsLimit r) (i : r ≅ t) (s) :
(P.ofIsoLimit i).lift s = P.lift s ≫ i.hom.hom :=
rfl
/-- Isomorphism of cones preserves whether or not they are limiting cones. -/
+@[to_dual /-- Isomorphism of cocones preserves whether or not they are colimiting cocones. -/]
def equivIsoLimit {r t : Cone F} (i : r ≅ t) : IsLimit r ≃ IsLimit t where
toFun h := h.ofIsoLimit i
invFun h := h.ofIsoLimit i.symm
left_inv := by cat_disch
right_inv := by cat_disch
-@[simp]
+@[to_dual (attr := simp)]
theorem equivIsoLimit_apply {r t : Cone F} (i : r ≅ t) (P : IsLimit r) :
equivIsoLimit i P = P.ofIsoLimit i :=
rfl
-@[simp]
+@[to_dual (attr := simp)]
theorem equivIsoLimit_symm_apply {r t : Cone F} (i : r ≅ t) (P : IsLimit t) :
(equivIsoLimit i).symm P = P.ofIsoLimit i.symm :=
rfl
@@ -181,6 +216,10 @@ theorem equivIsoLimit_symm_apply {r t : Cone F} (i : r ≅ t) (P : IsLimit t) :
/-- If the canonical morphism from a cone point to a limiting cone point is an iso, then the
first cone was limiting also.
-/
+@[to_dual
+/-- If the canonical morphism to a cocone point from a colimiting cocone point is an iso, then the
+first cocone was colimiting also.
+-/]
def ofPointIso {r t : Cone F} (P : IsLimit r) [i : IsIso (P.lift t)] : IsLimit t :=
ofIsoLimit P (by
haveI : IsIso (P.liftConeMorphism t).hom := i
@@ -191,17 +230,21 @@ def ofPointIso {r t : Cone F} (P : IsLimit r) [i : IsIso (P.lift t)] : IsLimit t
variable {t : Cone F}
set_option backward.defeqAttrib.useBackward true in
+@[to_dual]
theorem hom_lift (h : IsLimit t) {W : C} (m : W ⟶ t.pt) :
m = h.lift { pt := W, π := { app := fun b => m ≫ t.π.app b } } :=
h.uniq { pt := W, π := { app := fun b => m ≫ t.π.app b } } m fun _ => rfl
/-- Two morphisms into a limit are equal if their compositions with
- each cone morphism are equal. -/
+each cone morphism are equal. -/
+@[to_dual /-- Two morphisms out of a colimit are equal if their compositions with
+each cocone morphism are equal. -/]
theorem hom_ext (h : IsLimit t) {W : C} {f f' : W ⟶ t.pt}
(w : ∀ j, f ≫ t.π.app j = f' ≫ t.π.app j) :
f = f' := by
rw [h.hom_lift f, h.hom_lift f']; congr; exact funext w
+@[to_dual]
lemma nonempty_isLimit_iff_isIso_lift {s t : Cone F} (hs : IsLimit s) :
Nonempty (IsLimit t) ↔ IsIso (hs.lift t) :=
⟨fun ⟨ht⟩ ↦ ⟨ht.lift s, ht.hom_ext (by simp), hs.hom_ext (by simp)⟩, fun h ↦ ⟨hs.ofPointIso⟩⟩
@@ -209,6 +252,10 @@ lemma nonempty_isLimit_iff_isIso_lift {s t : Cone F} (hs : IsLimit s) :
/-- Given a right adjoint functor between categories of cones,
the image of a limit cone is a limit cone.
-/
+@[to_dual
+/-- Given a left adjoint functor between categories of cocones,
+the image of a colimit cocone is a colimit cocone.
+-/]
def ofRightAdjoint {D : Type u₄} [Category.{v₄} D] {G : K ⥤ D} {left : Cone F ⥤ Cone G}
{right : Cone G ⥤ Cone F}
(adj : left ⊣ right) {c : Cone G} (t : IsLimit c) : IsLimit (right.obj c) :=
@@ -225,31 +272,56 @@ def ofConeEquiv {D : Type u₄} [Category.{v₄} D] {G : K ⥤ D} (h : Cone G
left_inv := by cat_disch
right_inv := by cat_disch
-@[simp]
-theorem ofConeEquiv_apply_desc {D : Type u₄} [Category.{v₄} D] {G : K ⥤ D} (h : Cone G ≌ Cone F)
+/-- Given two functors which have equivalent categories of cocones,
+we can transport a colimiting cocone across the equivalence.
+-/
+@[to_dual existing]
+def _root_.CategoryTheory.Limits.IsColimit.ofCoconeEquiv {D : Type u₄} [Category.{v₄} D]
+ {G : K ⥤ D} (h : Cocone G ≌ Cocone F) {c : Cocone G} :
+ IsColimit (h.functor.obj c) ≃ IsColimit c where
+ toFun P := IsColimit.ofIsoColimit (IsColimit.ofLeftAdjoint h.symm.toAdjunction P)
+ (h.unitIso.symm.app c)
+ invFun := IsColimit.ofLeftAdjoint h.toAdjunction
+ left_inv := by cat_disch
+ right_inv := by cat_disch
+
+@[to_dual (attr := simp)]
+theorem ofConeEquiv_apply_lift {D : Type u₄} [Category.{v₄} D] {G : K ⥤ D} (h : Cone G ≌ Cone F)
{c : Cone G} (P : IsLimit (h.functor.obj c)) (s) :
(ofConeEquiv h P).lift s =
((h.unitIso.hom.app s).hom ≫ (h.inverse.map (P.liftConeMorphism (h.functor.obj s))).hom) ≫
(h.unitIso.inv.app c).hom :=
rfl
-@[simp]
-theorem ofConeEquiv_symm_apply_desc {D : Type u₄} [Category.{v₄} D] {G : K ⥤ D}
+@[to_dual (attr := simp)]
+theorem ofConeEquiv_symm_apply_lift {D : Type u₄} [Category.{v₄} D] {G : K ⥤ D}
(h : Cone G ≌ Cone F) {c : Cone G} (P : IsLimit c) (s) :
((ofConeEquiv h).symm P).lift s =
(h.counitIso.inv.app s).hom ≫ (h.functor.map (P.liftConeMorphism (h.inverse.obj s))).hom :=
rfl
-/--
-A cone postcomposed with a natural isomorphism is a limit cone if and only if the original cone is.
+@[deprecated (since := "2026-06-21")] alias ofConeEquiv_apply_desc := ofConeEquiv_apply_lift
+@[deprecated (since := "2026-06-21")]
+alias ofConeEquiv_symm_apply_desc := ofConeEquiv_symm_apply_lift
+
+/-- A cone postcomposed with a natural isomorphism is a limit cone
+if and only if the original cone is.
-/
+@[to_dual precomposeInvEquiv
+/-- A cocone precomposed with the inverse of a natural isomorphism is a colimit cocone
+if and only if the original cocone is.
+-/]
def postcomposeHomEquiv {F G : J ⥤ C} (α : F ≅ G) (c : Cone F) :
IsLimit ((Cone.postcompose α.hom).obj c) ≃ IsLimit c :=
ofConeEquiv (Cone.postcomposeEquivalence α)
-/-- A cone postcomposed with the inverse of a natural isomorphism is a limit cone if and only if
-the original cone is.
+/-- A cone postcomposed with the inverse of a natural isomorphism is a limit cone
+if and only if the original cone is.
-/
+@[to_dual precomposeHomEquiv
+/-- A cocone precomposed with a natural isomorphism is a colimit cocone
+if and only if the original cocone is.
+-/]
def postcomposeInvEquiv {F G : J ⥤ C} (α : F ≅ G) (c : Cone G) :
IsLimit ((Cone.postcompose α.inv).obj c) ≃ IsLimit c :=
postcomposeHomEquiv α.symm c
@@ -257,6 +329,10 @@ def postcomposeInvEquiv {F G : J ⥤ C} (α : F ≅ G) (c : Cone G) :
/-- Constructing an equivalence `IsLimit c ≃ IsLimit d` from a natural isomorphism
between the underlying functors, and then an isomorphism between `c` transported along this and `d`.
-/
+@[to_dual
+/-- Constructing an equivalence `isColimit c ≃ isColimit d` from a natural isomorphism
+between the underlying functors, and then an isomorphism between `c` transported along this and `d`.
+-/]
def equivOfNatIsoOfIso {F G : J ⥤ C} (α : F ≅ G) (c : Cone F) (d : Cone G)
(w : (Cone.postcompose α.hom).obj c ≅ d) : IsLimit c ≃ IsLimit d :=
(postcomposeHomEquiv α _).symm.trans (equivIsoLimit w)
@@ -265,7 +341,10 @@ set_option backward.defeqAttrib.useBackward true in
/-- The cone points of two limit cones for naturally isomorphic functors
are themselves isomorphic.
-/
-@[simps]
+@[to_dual (attr := simps)
+/-- The cocone points of two colimit cocones for naturally isomorphic functors
+are themselves isomorphic.
+-/]
def conePointsIsoOfNatIso {F G : J ⥤ C} {s : Cone F} {t : Cone G} (P : IsLimit s) (Q : IsLimit t)
(w : F ≅ G) : s.pt ≅ t.pt where
hom := Q.map s w.hom
@@ -273,25 +352,30 @@ def conePointsIsoOfNatIso {F G : J ⥤ C} {s : Cone F} {t : Cone G} (P : IsLimit
hom_inv_id := P.hom_ext (by simp)
inv_hom_id := Q.hom_ext (by simp)
-@[reassoc]
+set_option linter.translateOverwrite false in
+attribute [to_dual existing IsColimit.coconePointsIsoOfNatIso_inv] conePointsIsoOfNatIso_hom
+set_option linter.translateOverwrite false in
+attribute [to_dual existing IsColimit.coconePointsIsoOfNatIso_hom] conePointsIsoOfNatIso_inv
+
+@[to_dual (attr := reassoc) comp_coconePointsIsoOfNatIso_inv]
theorem conePointsIsoOfNatIso_hom_comp {F G : J ⥤ C} {s : Cone F} {t : Cone G} (P : IsLimit s)
(Q : IsLimit t) (w : F ≅ G) (j : J) :
(conePointsIsoOfNatIso P Q w).hom ≫ t.π.app j = s.π.app j ≫ w.hom.app j := by simp
-@[reassoc]
+@[to_dual (attr := reassoc) comp_coconePointsIsoOfNatIso_hom]
theorem conePointsIsoOfNatIso_inv_comp {F G : J ⥤ C} {s : Cone F} {t : Cone G} (P : IsLimit s)
(Q : IsLimit t) (w : F ≅ G) (j : J) :
(conePointsIsoOfNatIso P Q w).inv ≫ s.π.app j = t.π.app j ≫ w.inv.app j := by simp
set_option backward.defeqAttrib.useBackward true in
-@[reassoc]
+@[to_dual (attr := reassoc) coconePointsIsoOfNatIso_inv_desc]
theorem lift_comp_conePointsIsoOfNatIso_hom {F G : J ⥤ C} {r s : Cone F} {t : Cone G}
(P : IsLimit s) (Q : IsLimit t) (w : F ≅ G) :
P.lift r ≫ (conePointsIsoOfNatIso P Q w).hom = Q.map r w.hom :=
Q.hom_ext (by simp)
set_option backward.defeqAttrib.useBackward true in
-@[reassoc]
+@[to_dual (attr := reassoc) coconePointsIsoOfNatIso_hom_desc]
theorem lift_comp_conePointsIsoOfNatIso_inv {F G : J ⥤ C} {r s : Cone G} {t : Cone F}
(P : IsLimit t) (Q : IsLimit s) (w : F ≅ G) :
Q.lift r ≫ (conePointsIsoOfNatIso P Q w).inv = P.map r w.inv :=
@@ -301,33 +385,48 @@ section Equivalence
open CategoryTheory.Equivalence
-/-- If `s : Cone F` is a limit cone, so is `s` whiskered by an equivalence `e`.
--/
+/-- If `s : Cone F` is a limit cone, so is `s` whiskered by an equivalence `e`. -/
def whiskerEquivalence {s : Cone F} (P : IsLimit s) (e : K ≌ J) : IsLimit (s.whisker e.functor) :=
ofRightAdjoint (Cone.whiskeringEquivalence e).symm.toAdjunction P
-/-- If `s : Cone F` whiskered by an equivalence `e` is a limit cone, so is `s`.
--/
+/-- If `s : Cocone F` is a colimit cocone, so is `s` whiskered by an equivalence `e`. -/
+@[to_dual existing]
+def _root_.CategoryTheory.Limits.IsColimit.whiskerEquivalence {s : Cocone F}
+ (P : IsColimit s) (e : K ≌ J) : IsColimit (s.whisker e.functor) :=
+ IsColimit.ofLeftAdjoint (Cocone.whiskeringEquivalence e).toAdjunction P
+
+/-- If `s : Cone F` whiskered by an equivalence `e` is a limit cone, so is `s`. -/
def ofWhiskerEquivalence {s : Cone F} (e : K ≌ J) (P : IsLimit (s.whisker e.functor)) : IsLimit s :=
equivIsoLimit ((Cone.whiskeringEquivalence e).unitIso.app s).symm
(ofRightAdjoint (Cone.whiskeringEquivalence e).toAdjunction P)
-/-- Given an equivalence of diagrams `e`, `s` is a limit cone iff `s.whisker e.functor` is.
--/
+/-- If `s : Cocone F` whiskered by an equivalence `e` is a colimit cocone, so is `s`. -/
+@[to_dual existing]
+def _root_.CategoryTheory.Limits.IsColimit.ofWhiskerEquivalence {s : Cocone F} (e : K ≌ J)
+ (P : IsColimit (s.whisker e.functor)) : IsColimit s :=
+ IsColimit.equivIsoColimit ((Cocone.whiskeringEquivalence e).unitIso.app s).symm
+ (IsColimit.ofLeftAdjoint (Cocone.whiskeringEquivalence e).symm.toAdjunction P)
+
+/-- Given an equivalence of diagrams `e`, `s` is a limit cone iff `s.whisker e.functor` is. -/
+@[to_dual
+/-- Given an equivalence of diagrams `e`, `s` is a colimit cocone iff `s.whisker e.functor` is. -/]
def whiskerEquivalenceEquiv {s : Cone F} (e : K ≌ J) : IsLimit s ≃ IsLimit (s.whisker e.functor) :=
⟨fun h => h.whiskerEquivalence e, ofWhiskerEquivalence e, by cat_disch, by cat_disch⟩
/-- A limit cone extended by an isomorphism is a limit cone. -/
+@[to_dual /-- A colimit cocone extended by an isomorphism is a colimit cocone. -/]
def extendIso {s : Cone F} {X : C} (i : X ⟶ s.pt) [IsIso i] (hs : IsLimit s) :
IsLimit (s.extend i) :=
IsLimit.ofIsoLimit hs (Cone.extendIso s (asIso' i))
/-- A cone is a limit cone if its extension by an isomorphism is. -/
+@[to_dual /-- A cocone is a colimit cocone if its extension by an isomorphism is. -/]
def ofExtendIso {s : Cone F} {X : C} (i : X ⟶ s.pt) [IsIso i] (hs : IsLimit (s.extend i)) :
IsLimit s :=
IsLimit.ofIsoLimit hs (Cone.extendIso s (asIso' i)).symm
/-- A cone is a limit cone iff its extension by an isomorphism is. -/
+@[to_dual /-- A cocone is a colimit cocone iff its extension by an isomorphism is. -/]
def extendIsoEquiv {s : Cone F} {X : C} (i : X ⟶ s.pt) [IsIso i] :
IsLimit s ≃ IsLimit (s.extend i) :=
equivOfSubsingletonOfSubsingleton (extendIso i) (ofExtendIso i)
@@ -343,7 +442,16 @@ This is the most general form of uniqueness of cone points,
allowing relabelling of both the indexing category (up to equivalence)
and the functor (up to natural isomorphism).
-/
-@[simps]
+@[to_dual (attr := simps)
+/-- We can prove two cocone points `(s : Cocone F).pt` and `(t : Cocone G).pt` are isomorphic if
+* both cocones are colimit cocones
+* their indexing categories are equivalent via some `e : J ≌ K`,
+* the triangle of functors commutes up to a natural isomorphism: `e.functor ⋙ G ≅ F`.
+
+This is the most general form of uniqueness of cocone points,
+allowing relabelling of both the indexing category (up to equivalence)
+and the functor (up to natural isomorphism).
+-/]
def conePointsIsoOfEquivalence {F : J ⥤ C} {s : Cone F} {G : K ⥤ C} {t : Cone G} (P : IsLimit s)
(Q : IsLimit t) (e : J ≌ K) (w : e.functor ⋙ G ≅ F) : s.pt ≅ t.pt :=
let w' : e.inverse ⋙ F ≅ G := (isoWhiskerLeft e.inverse w).symm ≪≫ invFunIdAssoc e G
@@ -354,45 +462,55 @@ def conePointsIsoOfEquivalence {F : J ⥤ C} {s : Cone F} {G : K ⥤ C} {t : Con
dsimp [w']
simp only [Limits.Cone.whisker_π, Limits.Cone.postcompose_obj_π, fac, whiskerLeft_app,
assoc, id_comp, invFunIdAssoc_hom_app, fac_assoc, NatTrans.comp_app]
- rw [counit_app_functor, ← Functor.comp_map]
- have l :
- NatTrans.app w.hom j = NatTrans.app w.hom ((𝟭 J).obj j) := by dsimp
- rw [l, w.hom.naturality]
+ rw [counit_app_functor, ← Functor.comp_map, ← w.inv.naturality_assoc]
simp
inv_hom_id := by
apply hom_ext Q
cat_disch }
+set_option linter.translateOverwrite false in
+attribute [to_dual existing IsColimit.coconePointsIsoOfEquivalence_inv]
+ conePointsIsoOfEquivalence_hom
+set_option linter.translateOverwrite false in
+attribute [to_dual existing IsColimit.coconePointsIsoOfEquivalence_hom]
+ conePointsIsoOfEquivalence_inv
+
end Equivalence
set_option backward.defeqAttrib.useBackward true in
-/-- The universal property of a limit cone: a wap `W ⟶ t.pt` is the same as
- a cone on `F` with cone point `W`. -/
-@[simps apply]
+/-- The universal property of a limit cone: a map `W ⟶ t.pt` is the same as
+a cone on `F` with cone point `W`. -/
+@[to_dual (attr := simps apply)
+/-- The universal property of a colimit cocone: a map `X ⟶ W` is the same as
+a cocone on `F` with cone point `W`. -/]
def homEquiv (h : IsLimit t) {W : C} : (W ⟶ t.pt) ≃ ((Functor.const J).obj W ⟶ F) where
toFun f := (t.extend f).π
invFun π := h.lift (Cone.mk _ π)
left_inv f := h.hom_ext (by simp)
right_inv π := by cat_disch
-@[reassoc (attr := simp)]
+@[to_dual (attr := reassoc (attr := simp)) ι_app_homEquiv_symm]
lemma homEquiv_symm_π_app (h : IsLimit t) {W : C}
(f : (const J).obj W ⟶ F) (j : J) :
h.homEquiv.symm f ≫ t.π.app j = f.app j := by
simp [homEquiv]
set_option backward.defeqAttrib.useBackward true in
+@[to_dual]
lemma homEquiv_symm_naturality (h : IsLimit t) {W W' : C}
(f : (const J).obj W ⟶ F) (g : W' ⟶ W) :
h.homEquiv.symm ((Functor.const _).map g ≫ f) = g ≫ h.homEquiv.symm f :=
h.homEquiv.injective (by aesop)
/-- The universal property of a limit cone: a map `W ⟶ X` is the same as
- a cone on `F` with cone point `W`. -/
-def homIso (h : IsLimit t) (W : C) :
- (ULift.{u₁} (W ⟶ t.pt : Type v₃)) ≅ ((const J).obj W ⟶ F) :=
+a cone on `F` with cone point `W`. -/
+@[to_dual
+/-- The universal property of a colimit cocone: a map `X ⟶ W` is the same as
+a cocone on `F` with cone point `W`. -/]
+def homIso (h : IsLimit t) (W : C) : ULift.{u₁} (W ⟶ t.pt : Type v₃) ≅ (const J).obj W ⟶ F :=
Equiv.toIso (Equiv.ulift.trans h.homEquiv)
+-- TODO: `to_dual` doesn't yet know that it shouldn't translate the category on `Type _`.
@[simp]
theorem homIso_hom (h : IsLimit t) {W : C} :
(IsLimit.homIso h W).hom = ↾fun f ↦ (t.extend f.down).π :=
@@ -417,9 +535,12 @@ def homIso' (h : IsLimit t) (W : C) :
{ app := fun j => p.1 j
naturality := fun j j' f => by dsimp; rw [id_comp]; exact (p.2 f).symm } }
-/-- If G : C → D is a faithful functor which sends t to a limit cone,
- then it suffices to check that the induced maps for the image of t
- can be lifted to maps of C. -/
+/-- If `G : C → D` is a faithful functor which sends t to a limit cone,
+then it suffices to check that the induced maps for the image of t
+can be lifted to maps of `C`. -/
+@[to_dual /-- If `G : C → D` is a faithful functor which sends t to a colimit cocone,
+then it suffices to check that the induced maps for the image of t
+can be lifted to maps of `C`. -/]
def ofFaithful {t : Cone F} {D : Type u₄} [Category.{v₄} D] (G : C ⥤ D) [G.Faithful]
(ht : IsLimit (mapCone G t)) (lift : ∀ s : Cone F, s.pt ⟶ t.pt)
(h : ∀ s, G.map (lift s) = ht.lift (mapCone G s)) : IsLimit t :=
@@ -434,11 +555,16 @@ def ofFaithful {t : Cone F} {D : Type u₄} [Category.{v₄} D] (G : C ⥤ D) [G
/-- If `F` and `G` are naturally isomorphic, then `F.mapCone c` being a limit implies
`G.mapCone c` is also a limit.
-/
+@[to_dual
+/-- If `F` and `G` are naturally isomorphic, then `F.mapCocone c` being a colimit implies
+`G.mapCocone c` is also a colimit.
+-/]
def mapConeEquiv {D : Type u₄} [Category.{v₄} D] {K : J ⥤ C} {F G : C ⥤ D} (h : F ≅ G) {c : Cone K}
(t : IsLimit (mapCone F c)) : IsLimit (mapCone G c) := by
apply postcomposeInvEquiv (isoWhiskerLeft K h :) (mapCone G c) _
apply t.ofIsoLimit (postcomposeWhiskerLeftMapCone h.symm c).symm
+-- TODO: `to_dual` doesn't yet know that it shouldn't translate the category on `Type _`.
/-- A cone is a limit cone exactly if
there is a unique cone morphism from any other cone.
-/
@@ -533,369 +659,12 @@ end
end IsLimit
-/-- A cocone `t` on `F` is a colimit cocone if each cocone on `F` admits a unique
-cocone morphism from `t`. -/
-@[stacks 002F]
-structure IsColimit (t : Cocone F) where
- /-- `t.pt` maps to all other cocone covertices -/
- desc : ∀ s : Cocone F, t.pt ⟶ s.pt
- /-- The map `desc` makes the diagram with the natural transformations commute -/
- fac : ∀ (s : Cocone F) (j : J), t.ι.app j ≫ desc s = s.ι.app j := by cat_disch
- /-- `desc` is the unique such map -/
- uniq :
- ∀ (s : Cocone F) (m : t.pt ⟶ s.pt) (_ : ∀ j : J, t.ι.app j ≫ m = s.ι.app j), m = desc s := by
- cat_disch
-
-attribute [reassoc (attr := simp)] IsColimit.fac
namespace IsColimit
-instance subsingleton {t : Cocone F} : Subsingleton (IsColimit t) :=
- ⟨by intro P Q; cases P; cases Q; congr; cat_disch⟩
-
-/-- Given a natural transformation `α : F ⟶ G`, we give a morphism from the cocone point
-of a colimit cocone over `F` to the cocone point of any cocone over `G`. -/
-def map {F G : J ⥤ C} {s : Cocone F} (P : IsColimit s) (t : Cocone G) (α : F ⟶ G) : s.pt ⟶ t.pt :=
- P.desc ((Cocone.precompose α).obj t)
-
-set_option backward.isDefEq.respectTransparency false in -- This is needed in CategoryTheory/Limits/Shapes/Biproducts.lean
-@[reassoc (attr := simp)]
-theorem ι_map {F G : J ⥤ C} {c : Cocone F} (hc : IsColimit c) (d : Cocone G) (α : F ⟶ G) (j : J) :
- c.ι.app j ≫ IsColimit.map hc d α = α.app j ≫ d.ι.app j :=
- fac _ _ _
-
-@[simp]
-theorem desc_self {t : Cocone F} (h : IsColimit t) : h.desc t = 𝟙 t.pt :=
- (h.uniq _ _ fun _ => comp_id _).symm
-
-set_option backward.isDefEq.respectTransparency false in
--- Repackaging the definition in terms of cocone morphisms.
-/-- The universal morphism from a colimit cocone to any other cocone. -/
-@[simps]
-def descCoconeMorphism {t : Cocone F} (h : IsColimit t) (s : Cocone F) : t ⟶ s where hom := h.desc s
-
-theorem uniq_cocone_morphism {s t : Cocone F} (h : IsColimit t) {f f' : t ⟶ s} : f = f' :=
- have : ∀ {g : t ⟶ s}, g = h.descCoconeMorphism s := by
- intro g; ext; exact h.uniq _ _ g.w
- this.trans this.symm
-
-/-- Restating the definition of a colimit cocone in terms of the ∃! operator. -/
-theorem existsUnique {t : Cocone F} (h : IsColimit t) (s : Cocone F) :
- ∃! d : t.pt ⟶ s.pt, ∀ j, t.ι.app j ≫ d = s.ι.app j :=
- ⟨h.desc s, h.fac s, h.uniq s⟩
-
-/-- Noncomputably make a colimit cocone from the existence of unique factorizations. -/
-def ofExistsUnique {t : Cocone F}
- (ht : ∀ s : Cocone F, ∃! d : t.pt ⟶ s.pt, ∀ j, t.ι.app j ≫ d = s.ι.app j) : IsColimit t := by
- choose s hs hs' using ht
- exact ⟨s, hs, hs'⟩
-
-set_option backward.defeqAttrib.useBackward true in
-/-- Alternative constructor for `IsColimit`,
-providing a morphism of cocones rather than a morphism between the cocone points
-and separately the factorisation condition.
--/
-@[simps]
-def mkCoconeMorphism {t : Cocone F} (desc : ∀ s : Cocone F, t ⟶ s)
- (uniq' : ∀ (s : Cocone F) (m : t ⟶ s), m = desc s) : IsColimit t where
- desc s := (desc s).hom
- uniq s m w :=
- have : CoconeMorphism.mk m w = desc s := by apply uniq'
- congrArg CoconeMorphism.hom this
-
-/-- Colimit cocones on `F` are unique up to isomorphism. -/
-@[simps]
-def uniqueUpToIso {s t : Cocone F} (P : IsColimit s) (Q : IsColimit t) : s ≅ t where
- hom := P.descCoconeMorphism t
- inv := Q.descCoconeMorphism s
- hom_inv_id := P.uniq_cocone_morphism
- inv_hom_id := Q.uniq_cocone_morphism
-
-/-- Any cocone morphism between colimit cocones is an isomorphism. -/
-theorem hom_isIso {s t : Cocone F} (P : IsColimit s) (Q : IsColimit t) (f : s ⟶ t) : IsIso f :=
- ⟨⟨Q.descCoconeMorphism s, ⟨P.uniq_cocone_morphism, Q.uniq_cocone_morphism⟩⟩⟩
-
-/-- Colimits of `F` are unique up to isomorphism. -/
-def coconePointUniqueUpToIso {s t : Cocone F} (P : IsColimit s) (Q : IsColimit t) : s.pt ≅ t.pt :=
- (Cocone.forget F).mapIso (uniqueUpToIso P Q)
-
-@[reassoc (attr := simp)]
-theorem comp_coconePointUniqueUpToIso_hom {s t : Cocone F} (P : IsColimit s) (Q : IsColimit t)
- (j : J) : s.ι.app j ≫ (coconePointUniqueUpToIso P Q).hom = t.ι.app j :=
- (uniqueUpToIso P Q).hom.w _
-
-@[reassoc (attr := simp)]
-theorem comp_coconePointUniqueUpToIso_inv {s t : Cocone F} (P : IsColimit s) (Q : IsColimit t)
- (j : J) : t.ι.app j ≫ (coconePointUniqueUpToIso P Q).inv = s.ι.app j :=
- (uniqueUpToIso P Q).inv.w _
-
-set_option backward.isDefEq.respectTransparency false in
-@[reassoc (attr := simp)]
-theorem coconePointUniqueUpToIso_hom_desc {r s t : Cocone F} (P : IsColimit s) (Q : IsColimit t) :
- (coconePointUniqueUpToIso P Q).hom ≫ Q.desc r = P.desc r :=
- P.uniq _ _ (by simp)
-
-set_option backward.isDefEq.respectTransparency false in
-@[reassoc (attr := simp)]
-theorem coconePointUniqueUpToIso_inv_desc {r s t : Cocone F} (P : IsColimit s) (Q : IsColimit t) :
- (coconePointUniqueUpToIso P Q).inv ≫ P.desc r = Q.desc r :=
- Q.uniq _ _ (by simp)
-
-/-- Transport evidence that a cocone is a colimit cocone across an isomorphism of cocones. -/
-def ofIsoColimit {r t : Cocone F} (P : IsColimit r) (i : r ≅ t) : IsColimit t :=
- IsColimit.mkCoconeMorphism (fun s => i.inv ≫ P.descCoconeMorphism s) fun s m => by
- rw [i.eq_inv_comp]; apply P.uniq_cocone_morphism
-
-@[simp]
-theorem ofIsoColimit_desc {r t : Cocone F} (P : IsColimit r) (i : r ≅ t) (s) :
- (P.ofIsoColimit i).desc s = i.inv.hom ≫ P.desc s :=
- rfl
-
-/-- Isomorphism of cocones preserves whether or not they are colimiting cocones. -/
-def equivIsoColimit {r t : Cocone F} (i : r ≅ t) : IsColimit r ≃ IsColimit t where
- toFun h := h.ofIsoColimit i
- invFun h := h.ofIsoColimit i.symm
- left_inv := by cat_disch
- right_inv := by cat_disch
-
-@[simp]
-theorem equivIsoColimit_apply {r t : Cocone F} (i : r ≅ t) (P : IsColimit r) :
- equivIsoColimit i P = P.ofIsoColimit i :=
- rfl
-
-@[simp]
-theorem equivIsoColimit_symm_apply {r t : Cocone F} (i : r ≅ t) (P : IsColimit t) :
- (equivIsoColimit i).symm P = P.ofIsoColimit i.symm :=
- rfl
-
-/-- If the canonical morphism to a cocone point from a colimiting cocone point is an iso, then the
-first cocone was colimiting also.
--/
-def ofPointIso {r t : Cocone F} (P : IsColimit r) [i : IsIso (P.desc t)] : IsColimit t :=
- ofIsoColimit P (by
- haveI : IsIso (P.descCoconeMorphism t).hom := i
- haveI : IsIso (P.descCoconeMorphism t) := Cocone.cocone_iso_of_hom_iso _
- apply asIso (P.descCoconeMorphism t))
variable {t : Cocone F}
-set_option backward.defeqAttrib.useBackward true in
-theorem hom_desc (h : IsColimit t) {W : C} (m : t.pt ⟶ W) :
- m =
- h.desc
- { pt := W
- ι := { app := fun b => t.ι.app b ≫ m } } :=
- h.uniq
- { pt := W
- ι := { app := fun b => t.ι.app b ≫ m } }
- m fun _ => rfl
-
-/-- Two morphisms out of a colimit are equal if their compositions with
- each cocone morphism are equal. -/
-theorem hom_ext (h : IsColimit t) {W : C} {f f' : t.pt ⟶ W}
- (w : ∀ j, t.ι.app j ≫ f = t.ι.app j ≫ f') : f = f' := by
- rw [h.hom_desc f, h.hom_desc f']; congr; exact funext w
-
-set_option backward.defeqAttrib.useBackward true in
-set_option backward.isDefEq.respectTransparency false in
-lemma nonempty_isColimit_iff_isIso_desc {s t : Cocone F} (hs : IsColimit s) :
- Nonempty (IsColimit t) ↔ IsIso (hs.desc t) :=
- ⟨fun ⟨ht⟩ ↦ ⟨ht.desc s, hs.hom_ext (by simp), ht.hom_ext (by simp)⟩, fun h ↦ ⟨hs.ofPointIso⟩⟩
-
-/-- Given a left adjoint functor between categories of cocones,
-the image of a colimit cocone is a colimit cocone.
--/
-def ofLeftAdjoint {D : Type u₄} [Category.{v₄} D] {G : K ⥤ D} {left : Cocone G ⥤ Cocone F}
- {right : Cocone F ⥤ Cocone G} (adj : left ⊣ right) {c : Cocone G} (t : IsColimit c) :
- IsColimit (left.obj c) :=
- mkCoconeMorphism
- (fun s => (adj.homEquiv c s).symm (t.descCoconeMorphism _)) fun _ _ =>
- (Adjunction.homEquiv_apply_eq _ _ _).1 t.uniq_cocone_morphism
-
-/-- Given two functors which have equivalent categories of cocones,
-we can transport a colimiting cocone across the equivalence.
--/
-def ofCoconeEquiv {D : Type u₄} [Category.{v₄} D] {G : K ⥤ D} (h : Cocone G ≌ Cocone F)
- {c : Cocone G} : IsColimit (h.functor.obj c) ≃ IsColimit c where
- toFun P := ofIsoColimit (ofLeftAdjoint h.symm.toAdjunction P) (h.unitIso.symm.app c)
- invFun := ofLeftAdjoint h.toAdjunction
- left_inv := by cat_disch
- right_inv := by cat_disch
-
-@[simp]
-theorem ofCoconeEquiv_apply_desc {D : Type u₄} [Category.{v₄} D] {G : K ⥤ D}
- (h : Cocone G ≌ Cocone F) {c : Cocone G} (P : IsColimit (h.functor.obj c)) (s) :
- (ofCoconeEquiv h P).desc s =
- (h.unit.app c).hom ≫
- (h.inverse.map (P.descCoconeMorphism (h.functor.obj s))).hom ≫ (h.unitInv.app s).hom :=
- rfl
-
-@[simp]
-theorem ofCoconeEquiv_symm_apply_desc {D : Type u₄} [Category.{v₄} D] {G : K ⥤ D}
- (h : Cocone G ≌ Cocone F) {c : Cocone G} (P : IsColimit c) (s) :
- ((ofCoconeEquiv h).symm P).desc s =
- (h.functor.map (P.descCoconeMorphism (h.inverse.obj s))).hom ≫ (h.counit.app s).hom :=
- rfl
-
-/-- A cocone precomposed with the inverse of a natural isomorphism is a colimit cocone
-if and only if the original cocone is.
--/
-def precomposeInvEquiv {F G : J ⥤ C} (α : F ≅ G) (c : Cocone F) :
- IsColimit ((Cocone.precompose α.inv).obj c) ≃ IsColimit c :=
- ofCoconeEquiv (Cocone.precomposeEquivalence α)
-
-/-- A cocone precomposed with a natural isomorphism is a colimit cocone
-if and only if the original cocone is.
--/
-def precomposeHomEquiv {F G : J ⥤ C} (α : F ≅ G) (c : Cocone G) :
- IsColimit ((Cocone.precompose α.hom).obj c) ≃ IsColimit c :=
- precomposeInvEquiv α.symm c
-
-/-- Constructing an equivalence `is_colimit c ≃ is_colimit d` from a natural isomorphism
-between the underlying functors, and then an isomorphism between `c` transported along this and `d`.
--/
-def equivOfNatIsoOfIso {F G : J ⥤ C} (α : F ≅ G) (c : Cocone F) (d : Cocone G)
- (w : (Cocone.precompose α.inv).obj c ≅ d) : IsColimit c ≃ IsColimit d :=
- (precomposeInvEquiv α _).symm.trans (equivIsoColimit w)
-
-set_option backward.defeqAttrib.useBackward true in
-set_option backward.isDefEq.respectTransparency false in
-/-- The cocone points of two colimit cocones for naturally isomorphic functors
-are themselves isomorphic.
--/
-@[simps]
-def coconePointsIsoOfNatIso {F G : J ⥤ C} {s : Cocone F} {t : Cocone G} (P : IsColimit s)
- (Q : IsColimit t) (w : F ≅ G) : s.pt ≅ t.pt where
- hom := P.map t w.hom
- inv := Q.map s w.inv
- hom_inv_id := P.hom_ext (by simp)
- inv_hom_id := Q.hom_ext (by simp)
-
-@[reassoc]
-theorem comp_coconePointsIsoOfNatIso_hom {F G : J ⥤ C} {s : Cocone F} {t : Cocone G}
- (P : IsColimit s) (Q : IsColimit t) (w : F ≅ G) (j : J) :
- s.ι.app j ≫ (coconePointsIsoOfNatIso P Q w).hom = w.hom.app j ≫ t.ι.app j := by simp
-
-@[reassoc]
-theorem comp_coconePointsIsoOfNatIso_inv {F G : J ⥤ C} {s : Cocone F} {t : Cocone G}
- (P : IsColimit s) (Q : IsColimit t) (w : F ≅ G) (j : J) :
- t.ι.app j ≫ (coconePointsIsoOfNatIso P Q w).inv = w.inv.app j ≫ s.ι.app j := by simp
-
-set_option backward.isDefEq.respectTransparency false in
-@[reassoc]
-theorem coconePointsIsoOfNatIso_hom_desc {F G : J ⥤ C} {s : Cocone F} {r t : Cocone G}
- (P : IsColimit s) (Q : IsColimit t) (w : F ≅ G) :
- (coconePointsIsoOfNatIso P Q w).hom ≫ Q.desc r = P.map _ w.hom :=
- P.hom_ext (by simp)
-
-set_option backward.isDefEq.respectTransparency false in
-@[reassoc]
-theorem coconePointsIsoOfNatIso_inv_desc {F G : J ⥤ C} {s : Cocone G} {r t : Cocone F}
- (P : IsColimit t) (Q : IsColimit s) (w : F ≅ G) :
- (coconePointsIsoOfNatIso P Q w).inv ≫ P.desc r = Q.map _ w.inv :=
- Q.hom_ext (by simp)
-
-section Equivalence
-
-open CategoryTheory.Equivalence
-
-/-- If `s : Cocone F` is a colimit cocone, so is `s` whiskered by an equivalence `e`.
--/
-def whiskerEquivalence {s : Cocone F} (P : IsColimit s) (e : K ≌ J) :
- IsColimit (s.whisker e.functor) :=
- ofLeftAdjoint (Cocone.whiskeringEquivalence e).toAdjunction P
-
-/-- If `s : Cocone F` whiskered by an equivalence `e` is a colimit cocone, so is `s`.
--/
-def ofWhiskerEquivalence {s : Cocone F} (e : K ≌ J) (P : IsColimit (s.whisker e.functor)) :
- IsColimit s :=
- equivIsoColimit ((Cocone.whiskeringEquivalence e).unitIso.app s).symm
- (ofLeftAdjoint (Cocone.whiskeringEquivalence e).symm.toAdjunction P)
-
-/-- Given an equivalence of diagrams `e`, `s` is a colimit cocone iff `s.whisker e.functor` is.
--/
-def whiskerEquivalenceEquiv {s : Cocone F} (e : K ≌ J) :
- IsColimit s ≃ IsColimit (s.whisker e.functor) :=
- ⟨fun h => h.whiskerEquivalence e, ofWhiskerEquivalence e, by cat_disch, by cat_disch⟩
-
-/-- A colimit cocone extended by an isomorphism is a colimit cocone. -/
-def extendIso {s : Cocone F} {X : C} (i : s.pt ⟶ X) [IsIso i] (hs : IsColimit s) :
- IsColimit (s.extend i) :=
- IsColimit.ofIsoColimit hs (Cocone.extendIso s (asIso i))
-
-/-- A cocone is a colimit cocone if its extension by an isomorphism is. -/
-def ofExtendIso {s : Cocone F} {X : C} (i : s.pt ⟶ X) [IsIso i] (hs : IsColimit (s.extend i)) :
- IsColimit s :=
- IsColimit.ofIsoColimit hs (Cocone.extendIso s (asIso i)).symm
-
-/-- A cocone is a colimit cocone iff its extension by an isomorphism is. -/
-def extendIsoEquiv {s : Cocone F} {X : C} (i : s.pt ⟶ X) [IsIso i] :
- IsColimit s ≃ IsColimit (s.extend i) :=
- equivOfSubsingletonOfSubsingleton (extendIso i) (ofExtendIso i)
-
-set_option backward.defeqAttrib.useBackward true in
-set_option backward.isDefEq.respectTransparency false in
-/-- We can prove two cocone points `(s : Cocone F).pt` and `(t : Cocone G).pt` are isomorphic if
-* both cocones are colimit cocones
-* their indexing categories are equivalent via some `e : J ≌ K`,
-* the triangle of functors commutes up to a natural isomorphism: `e.functor ⋙ G ≅ F`.
-
-This is the most general form of uniqueness of cocone points,
-allowing relabelling of both the indexing category (up to equivalence)
-and the functor (up to natural isomorphism).
--/
-@[simps]
-def coconePointsIsoOfEquivalence {F : J ⥤ C} {s : Cocone F} {G : K ⥤ C} {t : Cocone G}
- (P : IsColimit s) (Q : IsColimit t) (e : J ≌ K) (w : e.functor ⋙ G ≅ F) : s.pt ≅ t.pt :=
- let w' : e.inverse ⋙ F ≅ G := (isoWhiskerLeft e.inverse w).symm ≪≫ invFunIdAssoc e G
- { hom := P.desc ((Cocone.equivalenceOfReindexing e w).functor.obj t)
- inv := Q.desc ((Cocone.equivalenceOfReindexing e.symm w').functor.obj s)
- hom_inv_id := by
- apply hom_ext P; intro j
- dsimp [w']
- simp only [Limits.Cocone.whisker_ι, fac, invFunIdAssoc_inv_app, whiskerLeft_app, assoc,
- comp_id, Limits.Cocone.precompose_obj_ι, fac_assoc, NatTrans.comp_app]
- rw [counitInv_app_functor, ← Functor.comp_map, ← w.inv.naturality_assoc]
- simp
- inv_hom_id := by
- apply hom_ext Q
- cat_disch }
-
-end Equivalence
-
-set_option backward.isDefEq.respectTransparency false in
-/-- The universal property of a colimit cocone: a map `X ⟶ W` is the same as
- a cocone on `F` with cone point `W`. -/
-def homEquiv (h : IsColimit t) {W : C} : (t.pt ⟶ W) ≃ (F ⟶ (const J).obj W) where
- toFun f := (t.extend f).ι
- invFun ι := h.desc
- { pt := W
- ι }
- left_inv f := h.hom_ext (by simp)
- right_inv ι := by cat_disch
-
-@[simp]
-lemma homEquiv_apply (h : IsColimit t) {W : C} (f : t.pt ⟶ W) :
- h.homEquiv f = (t.extend f).ι := rfl
-
-@[reassoc (attr := simp)]
-lemma ι_app_homEquiv_symm (h : IsColimit t) {W : C}
- (f : F ⟶ (const J).obj W) (j : J) :
- t.ι.app j ≫ h.homEquiv.symm f = f.app j := by
- simp [homEquiv]
-
-set_option backward.defeqAttrib.useBackward true in
-set_option backward.isDefEq.respectTransparency false in
-lemma homEquiv_symm_naturality (h : IsColimit t) {W W' : C}
- (f : F ⟶ (const J).obj W) (g : W ⟶ W') :
- h.homEquiv.symm (f ≫ (Functor.const _).map g) = h.homEquiv.symm f ≫ g :=
- h.homEquiv.injective (by aesop)
-
-/-- The universal property of a colimit cocone: a map `X ⟶ W` is the same as
- a cocone on `F` with cone point `W`. -/
-def homIso (h : IsColimit t) (W : C) :
- ULift.{u₁} (t.pt ⟶ W : Type v₃) ≅ (F ⟶ (const J).obj W) :=
- Equiv.toIso (Equiv.ulift.trans h.homEquiv)
@[simp]
theorem homIso_hom (h : IsColimit t) {W : C} :
@@ -921,28 +690,6 @@ def homIso' (h : IsColimit t) (W : C) :
{ app := fun j => p.1 j
naturality := fun j j' f => by dsimp; rw [comp_id]; exact p.2 f } }
-set_option backward.isDefEq.respectTransparency false in
-/-- If G : C → D is a faithful functor which sends t to a colimit cocone,
- then it suffices to check that the induced maps for the image of t
- can be lifted to maps of C. -/
-def ofFaithful {t : Cocone F} {D : Type u₄} [Category.{v₄} D] (G : C ⥤ D) [G.Faithful]
- (ht : IsColimit (mapCocone G t)) (desc : ∀ s : Cocone F, t.pt ⟶ s.pt)
- (h : ∀ s, G.map (desc s) = ht.desc (mapCocone G s)) : IsColimit t :=
- { desc
- fac := fun s j => by apply G.map_injective; rw [G.map_comp, h]; apply ht.fac
- uniq := fun s m w => by
- apply G.map_injective; rw [h]
- refine ht.uniq (mapCocone G s) _ fun j => ?_
- convert! ← congrArg (fun f => G.map f) (w j)
- apply G.map_comp }
-
-/-- If `F` and `G` are naturally isomorphic, then `F.mapCocone c` being a colimit implies
-`G.mapCocone c` is also a colimit.
--/
-def mapCoconeEquiv {D : Type u₄} [Category.{v₄} D] {K : J ⥤ C} {F G : C ⥤ D} (h : F ≅ G)
- {c : Cocone K} (t : IsColimit (mapCocone F c)) : IsColimit (mapCocone G c) := by
- apply IsColimit.ofIsoColimit _ (precomposeWhiskerLeftMapCocone h c)
- apply (precomposeInvEquiv (isoWhiskerLeft K h :) _).symm t
set_option backward.defeqAttrib.useBackward true in
/-- A cocone is a colimit cocone exactly if
diff --git a/Mathlib/CategoryTheory/Limits/MonoCoprod.lean b/Mathlib/CategoryTheory/Limits/MonoCoprod.lean
index 3cf2f524ae1b5e..8f6fd6777fbe73 100644
--- a/Mathlib/CategoryTheory/Limits/MonoCoprod.lean
+++ b/Mathlib/CategoryTheory/Limits/MonoCoprod.lean
@@ -76,6 +76,7 @@ instance {A B : C} [MonoCoprod C] [HasBinaryCoproduct A B] : Mono (coprod.inl :
instance {A B : C} [MonoCoprod C] [HasBinaryCoproduct A B] : Mono (coprod.inr : B ⟶ A ⨿ B) :=
binaryCofan_inr _ (colimit.isColimit _)
+set_option backward.isDefEq.respectTransparency false in
theorem mono_inl_iff {A B : C} {c₁ c₂ : BinaryCofan A B} (hc₁ : IsColimit c₁) (hc₂ : IsColimit c₂) :
Mono c₁.inl ↔ Mono c₂.inl := by
suffices
diff --git a/Mathlib/CategoryTheory/Limits/MorphismProperty.lean b/Mathlib/CategoryTheory/Limits/MorphismProperty.lean
index 469c6f3a5e85bd..66f8eb57f06f89 100644
--- a/Mathlib/CategoryTheory/Limits/MorphismProperty.lean
+++ b/Mathlib/CategoryTheory/Limits/MorphismProperty.lean
@@ -121,7 +121,7 @@ lemma CostructuredArrow.isClosedUnderColimitsOfShape {J : Type*} [Category* J]
isColimitOfPreserves _ d.isColimit
have heq : Y.hom = hd.desc { pt := X, ι := { app j := (d.diag.obj j).hom } } := by
refine hd.hom_ext fun j ↦ ?_
- simp only [Functor.const_obj_obj, IsColimit.fac]
+ simp only [IsColimit.fac]
simp
rw [P.costructuredArrowObj_iff, heq, ← hd.coconePointUniqueUpToIso_hom_desc (hc _),
P.cancel_left_of_respectsIso]
diff --git a/Mathlib/CategoryTheory/Limits/Preserves/Basic.lean b/Mathlib/CategoryTheory/Limits/Preserves/Basic.lean
index d9bbd2bac9030c..36706e0f7f9b90 100644
--- a/Mathlib/CategoryTheory/Limits/Preserves/Basic.lean
+++ b/Mathlib/CategoryTheory/Limits/Preserves/Basic.lean
@@ -778,7 +778,7 @@ lemma isIso_app_coconePt_of_preservesColimit
(isColimitOfPreserves L hc) (isColimitOfPreserves L' hc) (asIso (whiskerLeft K α))
convert! (inferInstance : IsIso e.hom)
apply (isColimitOfPreserves L hc).hom_ext fun j ↦ ?_
- simp only [Functor.comp_obj, Functor.mapCocone_pt, Functor.const_obj_obj, Functor.mapCocone_ι_app,
+ simp only [Functor.comp_obj, Functor.mapCocone_pt, Functor.mapCocone_ι_app,
NatTrans.naturality, IsColimit.coconePointsIsoOfNatIso_hom, asIso_hom, e]
refine (((isColimitOfPreserves L hc).ι_map (L'.mapCocone c) (whiskerLeft K α) j).trans ?_).symm
simp
diff --git a/Mathlib/CategoryTheory/Limits/Preserves/Bifunctor.lean b/Mathlib/CategoryTheory/Limits/Preserves/Bifunctor.lean
index 0c96c6d0d6de04..5dbce868ff5ce4 100644
--- a/Mathlib/CategoryTheory/Limits/Preserves/Bifunctor.lean
+++ b/Mathlib/CategoryTheory/Limits/Preserves/Bifunctor.lean
@@ -146,6 +146,7 @@ variable {c₁ : Cocone K₁} (hc₁ : IsColimit c₁)
{c₃ : Cocone <| uncurry.obj (whiskeringLeft₂ C |>.obj K₁ |>.obj K₂ |>.obj G)}
(hc₃ : IsColimit c₃)
+set_option backward.isDefEq.respectTransparency false in
/-- Characterize the inverse direction of the isomorphism
`PreservesColimit₂.isoObjCoconePointsOfIsColimit` w.r.t. the canonical maps to the colimit. -/
@[reassoc (attr := simp)]
@@ -231,10 +232,9 @@ instance of_preservesColimits_in_each_variable
apply (P j₁).hom_ext
intro j₂
haveI := (P j₁).fac s j₂
+ simp only [Functor.mapCocone_pt, Functor.mapCocone_ι_app, Q₀, s] at this
simp only [Functor.mapCocone_pt,
- Functor.const_obj_obj, Functor.mapCocone_ι_app, Q₀, s] at this
- simp only [Functor.mapCocone_pt,
- Functor.const_obj_obj, Functor.mapCocone_ι_app, NatTrans.naturality, this, Q₀, s])
+ Functor.mapCocone_ι_app, NatTrans.naturality, this, Q₀, s])
⟨IsColimit.ofCoconeUncurry P <| IsColimit.precomposeHomEquiv E₀ _ <|
IsColimit.ofIsoColimit (isColimitOfPreserves _ hc₁) E₁.symm⟩
diff --git a/Mathlib/CategoryTheory/Limits/Shapes/Grothendieck.lean b/Mathlib/CategoryTheory/Limits/Shapes/Grothendieck.lean
index d81f695d3dd635..55a57cc5349fce 100644
--- a/Mathlib/CategoryTheory/Limits/Shapes/Grothendieck.lean
+++ b/Mathlib/CategoryTheory/Limits/Shapes/Grothendieck.lean
@@ -178,7 +178,7 @@ def isColimitCoconeOfFiberwiseCocone {c : Cocone (fiberwiseColimit G)} (hc : IsC
desc s := hc.desc <| Cocone.mk s.pt <|
{ app := fun X => colimit.desc (Grothendieck.ι F X ⋙ G) (s.whisker _) }
uniq s m hm := hc.hom_ext <| fun X => by
- simp only [fiberwiseColimit_obj, Functor.const_obj_obj, IsColimit.fac]
+ simp only [fiberwiseColimit_obj, IsColimit.fac]
simp only [coconeOfCoconeFiberwiseColimit_pt, Functor.const_obj_obj,
coconeOfCoconeFiberwiseColimit_ι_app, Category.assoc] at hm
ext d
diff --git a/Mathlib/CategoryTheory/Limits/Types/Colimits.lean b/Mathlib/CategoryTheory/Limits/Types/Colimits.lean
index 544a0ffbce3a6b..ae469221a059ea 100644
--- a/Mathlib/CategoryTheory/Limits/Types/Colimits.lean
+++ b/Mathlib/CategoryTheory/Limits/Types/Colimits.lean
@@ -227,7 +227,7 @@ theorem jointly_surjective_of_isColimit {F : J ⥤ Type u} {t : Cocone F} (h : I
(↾fun y ↦ ULift.up (y ≠ x)))
· refine h.hom_ext fun j ↦ ?_
ext y
- simp only [Functor.const_obj_obj, TypeCat.Fun.toFun_apply, comp_apply, hom_ofHom,
+ simp only [TypeCat.Fun.toFun_apply, comp_apply, hom_ofHom,
TypeCat.Fun.coe_mk, ne_eq, true_iff]
exact hx j y
· intro he
diff --git a/Mathlib/CategoryTheory/Monad/Limits.lean b/Mathlib/CategoryTheory/Monad/Limits.lean
index cb06848c5cea2d..e74d2fc24dc126 100644
--- a/Mathlib/CategoryTheory/Monad/Limits.lean
+++ b/Mathlib/CategoryTheory/Monad/Limits.lean
@@ -413,11 +413,11 @@ def coconePoint : Coalgebra T where
A := c.pt
a := t.desc (newCocone D c)
counit := t.hom_ext fun j ↦ by
- simp only [Functor.comp_obj, forget_obj, Functor.id_obj, Functor.const_obj_obj,
+ simp only [Functor.comp_obj, forget_obj, Functor.id_obj,
IsColimit.fac_assoc, newCocone_ι_app, assoc, NatTrans.naturality, Functor.id_map, comp_id]
rw [← Category.assoc, (D.obj j).counit, Category.id_comp]
coassoc := t.hom_ext fun j ↦ by
- simp only [Functor.comp_obj, forget_obj, Functor.const_obj_obj, IsColimit.fac_assoc,
+ simp only [Functor.comp_obj, forget_obj, IsColimit.fac_assoc,
newCocone_ι_app, assoc, NatTrans.naturality, Functor.comp_map]
rw [← Category.assoc, (D.obj j).coassoc, ← Functor.map_comp, t.fac (newCocone D c) j,
newCocone_ι_app, Functor.map_comp, assoc]
diff --git a/Mathlib/CategoryTheory/MorphismProperty/Limits.lean b/Mathlib/CategoryTheory/MorphismProperty/Limits.lean
index e23d976bc8ad8a..88affa6db43406 100644
--- a/Mathlib/CategoryTheory/MorphismProperty/Limits.lean
+++ b/Mathlib/CategoryTheory/MorphismProperty/Limits.lean
@@ -517,6 +517,7 @@ inductive colimitsOfShape : MorphismProperty C
(h₁ : IsColimit c₁) (h₂ : IsColimit c₂) (f : X₁ ⟶ X₂) (_ : W.functorCategory J f) :
colimitsOfShape (h₁.desc (Cocone.mk _ (f ≫ c₂.ι)))
+set_option backward.isDefEq.respectTransparency false in
variable {W J} in
lemma colimitsOfShape.mk' (X₁ X₂ : J ⥤ C) (c₁ : Cocone X₁) (c₂ : Cocone X₂)
(h₁ : IsColimit c₁) (h₂ : IsColimit c₂) (f : X₁ ⟶ X₂) (hf : W.functorCategory J f)
@@ -599,6 +600,7 @@ class IsStableUnderColimitsOfShape : Prop where
(h₁ : IsColimit c₁) (h₁ : IsColimit c₂) (f : X₁ ⟶ X₂) (_ : W.functorCategory J f)
(φ : c₁.pt ⟶ c₂.pt) (hφ : ∀ j, c₁.ι.app j ≫ φ = f.app j ≫ c₂.ι.app j) : W φ
+set_option backward.isDefEq.respectTransparency false in
lemma isStableUnderColimitsOfShape_iff_colimitsOfShape_le :
W.IsStableUnderColimitsOfShape J ↔ W.colimitsOfShape J ≤ W := by
constructor
@@ -668,6 +670,7 @@ lemma coproducts_of_small {X Y : C} (f : X ⟶ Y) {J : Type w'}
rwa [← W.colimitsOfShape_eq_of_equivalence (Discrete.equivalence (equivShrink.{w} J))]
set_option backward.defeqAttrib.useBackward true in
+set_option backward.isDefEq.respectTransparency false in
lemma le_colimitsOfShape_punit : W ≤ W.colimitsOfShape (Discrete PUnit.{w + 1}) := by
intro X₁ X₂ f hf
have h := initialIsInitial (C := Discrete (PUnit.{w + 1}))
diff --git a/Mathlib/CategoryTheory/Presentable/ColimitPresentation.lean b/Mathlib/CategoryTheory/Presentable/ColimitPresentation.lean
index 3ff5e8e417ea26..cb0d056b5e23e0 100644
--- a/Mathlib/CategoryTheory/Presentable/ColimitPresentation.lean
+++ b/Mathlib/CategoryTheory/Presentable/ColimitPresentation.lean
@@ -151,7 +151,7 @@ def bind {X : C} (P : ColimitPresentation J X) (Q : ∀ j, ColimitPresentation (
isColimit.fac := fun c ⟨j, i⟩ ↦ by simp [P.isColimit.fac, (Q j).isColimit.fac]
isColimit.uniq c m hm := by
refine P.isColimit.hom_ext fun j ↦ ?_
- simp only [Functor.const_obj_obj, P.isColimit.fac]
+ simp only [P.isColimit.fac]
refine (Q j).isColimit.hom_ext fun i ↦ ?_
simpa [(Q j).isColimit.fac] using hm (.mk _ j i)
diff --git a/Mathlib/Condensed/Discrete/Colimit.lean b/Mathlib/Condensed/Discrete/Colimit.lean
index 3644a930d60a4c..789a1ec6e5c50d 100644
--- a/Mathlib/Condensed/Discrete/Colimit.lean
+++ b/Mathlib/Condensed/Discrete/Colimit.lean
@@ -62,6 +62,7 @@ noncomputable def isColimitLocallyConstantPresheaf (hc : IsLimit c) [∀ i, Epi
dsimp
rwa [dsimp% c.w, dsimp% c.w]
+set_option backward.isDefEq.respectTransparency false in
@[simp]
lemma isColimitLocallyConstantPresheaf_desc_apply (hc : IsLimit c) [∀ i, Epi (c.π.app i)]
(s : Cocone ((F ⋙ toProfinite).op ⋙ locallyConstantPresheaf X))
@@ -349,6 +350,7 @@ noncomputable def isColimitLocallyConstantPresheaf (hc : IsLimit c) [∀ i, Epi
dsimp
rwa [dsimp% c.w, dsimp% c.w]
+set_option backward.isDefEq.respectTransparency false in
@[simp]
lemma isColimitLocallyConstantPresheaf_desc_apply (hc : IsLimit c) [∀ i, Epi (c.π.app i)]
(s : Cocone ((F ⋙ toLightProfinite).op ⋙ locallyConstantPresheaf X))
@@ -366,6 +368,7 @@ noncomputable def isColimitLocallyConstantPresheafDiagram (S : LightProfinite) :
(Functor.Final.isColimitWhiskerEquiv (opOpEquivalence ℕ).inverse _).symm
(isColimitLocallyConstantPresheaf _ _ S.asLimit)
+set_option backward.isDefEq.respectTransparency false in
@[simp]
lemma isColimitLocallyConstantPresheafDiagram_desc_apply (S : LightProfinite)
(s : Cocone (S.diagram.rightOp ⋙ locallyConstantPresheaf X))
From eac05c611ebca3eca12571cba5d29b7c32049208 Mon Sep 17 00:00:00 2001
From: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com>
Date: Tue, 23 Jun 2026 12:20:11 +0000
Subject: [PATCH 0287/1300] feat(Combinatorics/SimpleGraph/Finite): some
`minDegree`/`maxDegree` lemmas (#40622)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- `G.minDegree = 0 ∧ G.maxDegree = 0` given `Subsingleton V` (we have these for `IsEmpty V`)
- `G.maxDegree = 0 ↔ G = ⊥`
- `G.minDegree = 0 ↔ ∃ v, G.IsIsolated v`
- `G.minDegree = 0 ↔ G.support ≠ .univ`
- For a set `s` that contains the support we have
- `G.minDegree ≤ (G.induce s).minDegree`
- `(G.induce s).maxDegree = G.maxDegree`
---
Mathlib/Combinatorics/SimpleGraph/Copy.lean | 12 +++-
Mathlib/Combinatorics/SimpleGraph/Finite.lean | 56 +++++++++++++++----
2 files changed, 57 insertions(+), 11 deletions(-)
diff --git a/Mathlib/Combinatorics/SimpleGraph/Copy.lean b/Mathlib/Combinatorics/SimpleGraph/Copy.lean
index 3bc1aeb011955b..79ec4ae0442ef6 100644
--- a/Mathlib/Combinatorics/SimpleGraph/Copy.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/Copy.lean
@@ -341,7 +341,8 @@ lemma maxDegree_mono {H : SimpleGraph V} [Fintype V] [DecidableRel G.Adj] [Decid
theorem Copy.minDegree_mono [Fintype V] [Fintype W] [DecidableRel G.Adj] [DecidableRel H.Adj]
{f : Copy G H} (hf : Function.Surjective f) : G.minDegree ≤ H.minDegree := by
cases isEmpty_or_nonempty W
- · simp [Function.isEmpty f]
+ · have := Function.isEmpty f
+ simp
refine H.le_minDegree_of_forall_le_degree _ fun w ↦ ?_
obtain ⟨v, rfl⟩ := hf w
grw [← f.degree_le, ← minDegree_le_degree]
@@ -354,6 +355,15 @@ theorem Hom.minDegree_mono [Fintype V] [Fintype W] [DecidableRel G.Adj] [Decidab
@[deprecated (since := "2026-05-20")] alias Hom.minDegree_le := Hom.minDegree_mono
+theorem maxDegree_induce_of_support_subset [Fintype V] [DecidableRel G.Adj] {s : Set V}
+ [DecidablePred (· ∈ s)] (h : G.support ⊆ s) : (G.induce s).maxDegree = G.maxDegree := by
+ apply le_antisymm <| Copy.maxDegree_mono <| Embedding.induce s |>.toCopy
+ refine G.maxDegree_le_of_forall_degree_le _ fun v ↦ ?_
+ by_cases hv : G.IsIsolated v
+ · simp [hv]
+ grw [← degree_le_maxDegree _ ⟨v, h <| G.mem_support_iff_not_isIsolated.mpr hv⟩,
+ degree_induce_of_neighborSet_subset <| G.neighborSet_subset_support v |>.trans h]
+
end IsContained
section Free
diff --git a/Mathlib/Combinatorics/SimpleGraph/Finite.lean b/Mathlib/Combinatorics/SimpleGraph/Finite.lean
index c5f2e356c5f83c..a3f9f29851e7b6 100644
--- a/Mathlib/Combinatorics/SimpleGraph/Finite.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/Finite.lean
@@ -385,10 +385,8 @@ theorem exists_minimal_degree_vertex [DecidableRel G.Adj] [Nonempty V] :
grind [minDegree, WithTop.untopD_coe, min_mem_image_coe <| univ_nonempty.image (G.degree ·)]
/-- The minimum degree in the graph is at most the degree of any particular vertex. -/
-theorem minDegree_le_degree [DecidableRel G.Adj] (v : V) : G.minDegree ≤ G.degree v := by
- obtain ⟨t, ht⟩ := Finset.min_of_mem (mem_image_of_mem (fun v => G.degree v) (mem_univ v))
- have := Finset.min_le_of_eq (mem_image_of_mem _ (mem_univ v)) ht
- rwa [minDegree, ht]
+theorem minDegree_le_degree [DecidableRel G.Adj] (v : V) : G.minDegree ≤ G.degree v :=
+ WithTop.untopD_le <| Finset.min_le <| mem_image_of_mem (G.degree ·) <| mem_univ v
/-- In a nonempty graph, if `k` is at most the degree of every vertex, it is at most the minimum
degree. Note the assumption that the graph is nonempty is necessary as long as `G.minDegree` is
@@ -399,14 +397,16 @@ theorem le_minDegree_of_forall_le_degree [DecidableRel G.Adj] [Nonempty V] (k :
rw [hv]
apply h
-/-- If there are no vertices then the `minDegree` is zero. -/
@[simp]
-lemma minDegree_of_isEmpty [DecidableRel G.Adj] [IsEmpty V] : G.minDegree = 0 := by
- rw [minDegree, WithTop.untopD_eq_self_iff]
- simp
+lemma minDegree_of_subsingleton [DecidableRel G.Adj] [Subsingleton V] : G.minDegree = 0 := by
+ cases isEmpty_or_nonempty V <;>
+ simp [minDegree, Finset.image_const]
+
+@[deprecated (since := "2026-06-15")] alias minDegree_of_isEmpty := minDegree_of_subsingleton
variable {G} in
/-- If `G` is a subgraph of `H` then `G.minDegree ≤ H.minDegree`. -/
+@[gcongr]
lemma minDegree_le_minDegree {H : SimpleGraph V} [DecidableRel G.Adj] [DecidableRel H.Adj]
(hle : G ≤ H) : G.minDegree ≤ H.minDegree := by
cases isEmpty_or_nonempty V
@@ -438,8 +438,11 @@ theorem degree_le_maxDegree [DecidableRel G.Adj] (v : V) : G.degree v ≤ G.maxD
WithBot.le_unbotD <| Finset.le_max <| mem_image_of_mem (G.degree ·) <| mem_univ v
@[simp]
-lemma maxDegree_of_isEmpty [DecidableRel G.Adj] [IsEmpty V] : G.maxDegree = 0 := by
- rw [maxDegree, univ_eq_empty, image_empty, max_empty, WithBot.unbotD_bot]
+lemma maxDegree_of_subsingleton [DecidableRel G.Adj] [Subsingleton V] : G.maxDegree = 0 := by
+ cases isEmpty_or_nonempty V <;>
+ simp [maxDegree, Finset.image_const]
+
+@[deprecated (since := "2026-06-15")] alias maxDegree_of_isEmpty := maxDegree_of_subsingleton
/-- In a graph, if `k` is at least the degree of every vertex, then it is at least the maximum
degree. -/
@@ -458,6 +461,16 @@ theorem IsRegularOfDegree.maxDegree_eq [Nonempty V] [DecidableRel G.Adj] {d :
lemma maxDegree_bot_eq_zero : (⊥ : SimpleGraph V).maxDegree = 0 :=
Nat.le_zero.1 <| maxDegree_le_of_forall_degree_le _ _ (by simp)
+variable {G} in
+@[simp]
+theorem maxDegree_eq_zero_iff [DecidableRel G.Adj] : G.maxDegree = 0 ↔ G = ⊥ := by
+ refine ⟨fun h ↦ ?_, fun h ↦ ?_⟩
+ · rw [eq_bot_iff_isIsolated]
+ intro v
+ grind [degree_eq_zero, G.degree_le_maxDegree v]
+ · convert maxDegree_bot_eq_zero
+ assumption
+
@[simp]
lemma maxDegree_top [DecidableEq V] : (⊤ : SimpleGraph V).maxDegree = Fintype.card V - 1 := by
cases isEmpty_or_nonempty V
@@ -478,6 +491,18 @@ theorem IsRegularOfDegree.minDegree_eq [Nonempty V] [DecidableRel G.Adj] {d :
lemma minDegree_bot_eq_zero : (⊥ : SimpleGraph V).minDegree = 0 :=
Nat.le_zero.1 <| (minDegree_le_maxDegree _).trans (by simp)
+variable {G} in
+theorem minDegree_eq_zero_iff [DecidableRel G.Adj] [Nonempty V] :
+ G.minDegree = 0 ↔ ∃ v, G.IsIsolated v := by
+ refine ⟨fun h ↦ ?_, fun ⟨v, hv⟩ ↦ ?_⟩
+ · grind [G.exists_minimal_degree_vertex, degree_eq_zero]
+ · grind [G.minDegree_le_degree v, degree_eq_zero]
+
+variable {G} in
+theorem minDegree_eq_zero_iff_support_ne [DecidableRel G.Adj] [Nonempty V] :
+ G.minDegree = 0 ↔ G.support ≠ .univ := by
+ simp [Set.ne_univ_iff_exists_notMem, minDegree_eq_zero_iff]
+
@[simp]
lemma minDegree_top [DecidableEq V] : (⊤ : SimpleGraph V).minDegree = Fintype.card V - 1 := by
cases isEmpty_or_nonempty V
@@ -613,6 +638,17 @@ theorem degree_induce_support (v : G.support) :
(G.induce G.support).degree v = G.degree v :=
degree_induce_of_support_subset subset_rfl v
+theorem le_minDegree_induce_of_support_subset (h : G.support ⊆ s) :
+ G.minDegree ≤ (G.induce s).minDegree := by
+ cases isEmpty_or_nonempty V
+ · simp
+ rcases s.eq_empty_or_nonempty with (rfl | hs)
+ · simp [minDegree_eq_zero_iff_support_ne, Set.subset_empty_iff.mp h, Set.empty_ne_univ]
+ have := hs.to_subtype
+ refine le_minDegree_of_forall_le_degree _ _ fun v ↦ ?_
+ grw [G.minDegree_le_degree v, degree_induce_of_neighborSet_subset]
+ grw [neighborSet_subset_support, h]
+
end Support
section Map
From 7ff2d88e920dfd955813615527b82ed3b8f45550 Mon Sep 17 00:00:00 2001
From: Thomas Browning <13339017+tb65536@users.noreply.github.com>
Date: Tue, 23 Jun 2026 12:44:44 +0000
Subject: [PATCH 0288/1300] feat(RingTheory/Ideal/Defs): add
`Ideal.coe_mem_inertia` (#40383)
This PR adds a lemma `coe_mem_inertia` for the situation when a coercion from a subgroup lies in an inertia subgroup. I added both an `AddSubgroup` version and an `Ideal` version to allow for better rewriting.
Co-authored-by: tb65536
---
Mathlib/Algebra/Group/Subgroup/Basic.lean | 28 +++++++++++++++--------
Mathlib/RingTheory/Ideal/Defs.lean | 17 ++++++++++----
2 files changed, 30 insertions(+), 15 deletions(-)
diff --git a/Mathlib/Algebra/Group/Subgroup/Basic.lean b/Mathlib/Algebra/Group/Subgroup/Basic.lean
index 0a94a12c97fafa..0799e6710ca3cd 100644
--- a/Mathlib/Algebra/Group/Subgroup/Basic.lean
+++ b/Mathlib/Algebra/Group/Subgroup/Basic.lean
@@ -1088,26 +1088,34 @@ def noncenter (G : Type*) [Monoid G] : Set (ConjClasses G) :=
end ConjClasses
+namespace AddSubgroup
+
+variable {M : Type*} [AddGroup M] (I : AddSubgroup M) (G : Type*)
+ [Group G] [MulAction G M]
+
/-- Suppose `G` acts on `M` and `I` is a subgroup of `M`.
The inertia subgroup of `I` is the subgroup of `G` whose action is trivial mod `I`. -/
-def AddSubgroup.inertia {M : Type*} [AddGroup M] (I : AddSubgroup M) (G : Type*)
- [Group G] [MulAction G M] : Subgroup G where
+def inertia : Subgroup G where
carrier := { σ | ∀ x, σ • x - x ∈ I }
mul_mem' {a b} ha hb x := by simpa [mul_smul] using add_mem (ha (b • x)) (hb x)
one_mem' := by simp [zero_mem]
inv_mem' {a} ha x := by simpa using sub_mem_comm_iff.mp (ha (a⁻¹ • x))
-@[simp] lemma AddSubgroup.mem_inertia {M : Type*} [AddGroup M] {I : AddSubgroup M} {G : Type*}
- [Group G] [MulAction G M] {σ : G} : σ ∈ I.inertia G ↔ ∀ x, σ • x - x ∈ I := .rfl
+variable {I G} in
+@[simp]
+lemma mem_inertia {σ : G} : σ ∈ I.inertia G ↔ ∀ x, σ • x - x ∈ I := .rfl
+variable {G} in
@[simp]
-lemma AddSubgroup.subgroupOf_inertia {M : Type*} [AddGroup M] (I : AddSubgroup M)
- {G : Type*} [Group G] [MulAction G M] (H : Subgroup G) :
- (I.inertia G).subgroupOf H = I.inertia H :=
+lemma subgroupOf_inertia (H : Subgroup G) : (I.inertia G).subgroupOf H = I.inertia H :=
rfl
+variable {I G} in
+lemma coe_mem_inertia {H : Subgroup G} {σ : H} : ↑σ ∈ I.inertia G ↔ σ ∈ I.inertia H := .rfl
+
+variable {G} in
@[simp]
-lemma AddSubgroup.inertia_map_subtype {M : Type*} [AddGroup M] (I : AddSubgroup M)
- {G : Type*} [Group G] [MulAction G M] (H : Subgroup G) :
- (I.inertia H).map H.subtype = I.inertia G ⊓ H := by
+lemma inertia_map_subtype (H : Subgroup G) : (I.inertia H).map H.subtype = I.inertia G ⊓ H := by
rw [← AddSubgroup.subgroupOf_inertia, Subgroup.subgroupOf_map_subtype]
+
+end AddSubgroup
diff --git a/Mathlib/RingTheory/Ideal/Defs.lean b/Mathlib/RingTheory/Ideal/Defs.lean
index f8a8c64474e4b2..aee2dc55cc58e9 100644
--- a/Mathlib/RingTheory/Ideal/Defs.lean
+++ b/Mathlib/RingTheory/Ideal/Defs.lean
@@ -146,11 +146,18 @@ theorem mul_sub_mul_mem [I.IsTwoSided]
rw [show a * c - b * d = (a - b) * c + b * (c - d) by rw [sub_mul, mul_sub]; abel]
exact I.add_mem (I.mul_mem_right _ h1) (I.mul_mem_left _ h2)
-/--
-The subgroup of elements `g` of `G` such that `∀ x, g • x - x ∈ I`.
--/
-abbrev inertia (G : Type*) [Group G] [MulAction G α] (I : Ideal α) :
- Subgroup G := AddSubgroup.inertia I.toAddSubgroup G
+section inertia
+
+variable (G : Type*) [Group G] [MulAction G α] (I : Ideal α)
+
+/-- The subgroup of elements `g` of `G` such that `∀ x, g • x - x ∈ I`. -/
+abbrev inertia : Subgroup G := I.toAddSubgroup.inertia G
+
+variable {I G} in
+theorem coe_mem_inertia {H : Subgroup G} {σ : H} : ↑σ ∈ I.inertia G ↔ σ ∈ I.inertia H :=
+ I.toAddSubgroup.coe_mem_inertia
+
+end inertia
end Ideal
From d5c56094122b41159528a7eea1d85ab706849369 Mon Sep 17 00:00:00 2001
From: Thomas Browning <13339017+tb65536@users.noreply.github.com>
Date: Tue, 23 Jun 2026 14:13:40 +0000
Subject: [PATCH 0289/1300] chore(FieldTheory/IsGaloisGroup): split file by
imports (#40942)
This PR splits off the ring-theoretic part of `FieldTheory/IsGaloisGroup.lean` to the `RingTheory` folder.
Co-authored-by: tb65536
---
Mathlib.lean | 2 +
Mathlib/FieldTheory/Galois/IsGaloisGroup.lean | 321 +-----------------
Mathlib/RingTheory/IsGaloisGroup/Basic.lean | 251 ++++++++++++++
Mathlib/RingTheory/IsGaloisGroup/Defs.lean | 155 +++++++++
4 files changed, 413 insertions(+), 316 deletions(-)
create mode 100644 Mathlib/RingTheory/IsGaloisGroup/Basic.lean
create mode 100644 Mathlib/RingTheory/IsGaloisGroup/Defs.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index d9fda0a91062b6..f0735f278b484c 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -6676,6 +6676,8 @@ public import Mathlib.RingTheory.Invariant.Defs
public import Mathlib.RingTheory.Invariant.Galois
public import Mathlib.RingTheory.Invariant.Profinite
public import Mathlib.RingTheory.IsAdjoinRoot
+public import Mathlib.RingTheory.IsGaloisGroup.Basic
+public import Mathlib.RingTheory.IsGaloisGroup.Defs
public import Mathlib.RingTheory.IsPrimary
public import Mathlib.RingTheory.IsTensorProduct
public import Mathlib.RingTheory.Jacobson.Artinian
diff --git a/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean b/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean
index 2fcbbed0106e1f..431a89fbdd99a2 100644
--- a/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean
+++ b/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean
@@ -7,14 +7,14 @@ module
public import Mathlib.FieldTheory.Galois.Infinite
public import Mathlib.NumberTheory.NumberField.Basic
-public import Mathlib.RingTheory.Invariant.Basic
+public import Mathlib.RingTheory.IsGaloisGroup.Basic
/-!
-# Predicate for Galois Groups
+# Galois Groups of Fields
-Given an action of a group `G` on an extension of fields `L/K`, we introduce a predicate
-`IsGaloisGroup G K L` saying that `G` acts faithfully on `L` with fixed field `K`. In particular,
-we do not assume that `L` is an algebraic extension of `K`.
+Given an action of a group `G` on an extension of fields `L/K`, the predicate `IsGaloisGroup G K L`
+states that `G` acts faithfully on `L` with fixed field `K`. In particular, we do not assume that
+`L` is an algebraic extension of `K`.
## Implementation notes
@@ -40,218 +40,8 @@ extensions of rings `B/A` seems to outweigh these terminological issues.
open Module
-section CommRing
-
-variable (G A A' B : Type*) [Group G] [CommSemiring A] [Semiring B] [Algebra A B]
- [MulSemiringAction G B]
-
-/-- `G` is a Galois group for `L/K` if the action of `G` on `L` is faithful with fixed field `K`.
-In particular, we do not assume that `L` is an algebraic extension of `K`.
-
-See the implementation notes in this file for the meaning of this definition in the case of rings.
--/
-class IsGaloisGroup where
- faithful : FaithfulSMul G B
- commutes : SMulCommClass G A B
- isInvariant : Algebra.IsInvariant A B G
-
-variable {G A B} in
-theorem IsGaloisGroup.of_mulEquiv [hG : IsGaloisGroup G A B] {H : Type*} [Group H]
- [MulSemiringAction H B] (e : H ≃* G) (he : ∀ h (x : B), (e h) • x = h • x) :
- IsGaloisGroup H A B where
- faithful := ⟨fun h ↦ e.injective <| hG.faithful.eq_of_smul_eq_smul <| by simpa only [he]⟩
- commutes := ⟨fun x a b ↦ by simpa [he] using hG.commutes.smul_comm (e x) a b⟩
- isInvariant := ⟨fun b h ↦
- 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 ‹_›
-
-theorem IsGaloisGroup.of_algEquiv [hG : IsGaloisGroup G A B] (B' : Type*) [Semiring B']
- [Algebra A B'] [MulSemiringAction G B'] (e : B ≃ₐ[A] B')
- (he : ∀ (g : G) (x : B), e (g • x) = g • (e x)) :
- IsGaloisGroup G A B' where
- faithful := ⟨fun h ↦ hG.faithful.eq_of_smul_eq_smul fun b ↦ by simpa [← he] using h (e b)⟩
- commutes := ⟨fun g a b' ↦ by
- have h' {x'} : e.symm (g • x') = g • e.symm x' := by
- apply e.injective
- simp [he]
- apply e.symm.injective
- simpa [h', map_smul] using hG.commutes.smul_comm g a (e.symm b')⟩
- isInvariant := ⟨fun x' hx' ↦ by
- obtain ⟨a, ha⟩ := hG.isInvariant.isInvariant (e.symm x') (fun g ↦ by
- apply e.injective
- simp [he, hx'])
- exact ⟨a, by rw [← e.commutes, ha, AlgEquiv.apply_symm_apply]⟩⟩
-
-theorem IsGaloisGroup.of_ringHom_surjective [hG : IsGaloisGroup G A B] [CommSemiring A']
- [Algebra A' B] (e : A →+* A') (he : ∀ a, algebraMap A' B (e a) = algebraMap A B a)
- (he' : Function.Surjective e) : IsGaloisGroup G A' B where
- faithful := hG.faithful
- commutes := ⟨by
- intro g a' b
- obtain ⟨a, rfl⟩ : ∃ a, e a = a' := he' a'
- rw [Algebra.smul_def, Algebra.smul_def, he, ← Algebra.smul_def, ← Algebra.smul_def]
- exact hG.commutes.smul_comm g a b⟩
- isInvariant := ⟨by
- intro b h
- obtain ⟨a, ha⟩ := hG.isInvariant.isInvariant b h
- exact ⟨e a, by rw [he, ha]⟩⟩
-
-theorem IsGaloisGroup.of_ringEquiv [hG : IsGaloisGroup G A B] [CommSemiring A'] [Algebra A' B]
- (e : A ≃+* A') (he : ∀ a, algebraMap A' B (e a) = algebraMap A B a) :
- IsGaloisGroup G A' B :=
- .of_ringHom_surjective G A A' B e he e.surjective
-
-attribute [instance low] IsGaloisGroup.commutes IsGaloisGroup.isInvariant
-
-variable {C : Type*} [CommSemiring C] [Algebra C B]
-
-variable {G} in
-protected theorem Subgroup.smul_algebraMap {H : Subgroup G} [SMulCommClass H C B] {g : G}
- (hg : g ∈ H) (x : C) :
- g • algebraMap C B x = algebraMap C B x :=
- smul_algebraMap (⟨g, hg⟩ : H) x
-
-theorem IsGaloisGroup.smul_mem_of_normal (N : Subgroup G) [hN : N.Normal]
- [hC : IsGaloisGroup N C B] (g : G) (x : C) :
- g • algebraMap C B x ∈ Set.range (algebraMap C B) := by
- apply hC.isInvariant.isInvariant (g • algebraMap C B x)
- intro n
- rw [← inv_smul_eq_iff, Subgroup.smul_def, ← mul_smul, ← mul_smul]
- exact Subgroup.smul_algebraMap B (hN.conj_mem' n n.prop g) x
-
-@[deprecated (since := "2026-05-28")] alias smul_eq_self := Subgroup.smul_algebraMap
-@[deprecated (since := "2026-05-28")] alias smul_mem_of_normal := IsGaloisGroup.smul_mem_of_normal
-
-variable [hA : IsGaloisGroup G A B] [FaithfulSMul A B]
-
-/--
-If `B/A` is Galois with Galois group `G`, then `A` is isomorphic to the subring of elements of `B`
-fixed by `G`.
--/
-@[simps apply_coe]
-noncomputable def IsGaloisGroup.ringEquivFixedPoints :
- A ≃+* FixedPoints.subsemiring B G where
- toFun x := ⟨algebraMap A B x, fun _ ↦ by rw [smul_algebraMap]⟩
- invFun x := (hA.isInvariant.isInvariant x x.prop).choose
- map_mul' _ _ := by simp [Subtype.ext_iff]
- map_add' _ _ := by simp [Subtype.ext_iff]
- left_inv _ := by simp
- right_inv x := by simpa [Subtype.ext_iff] using (hA.isInvariant.isInvariant x x.prop).choose_spec
-
-@[simp]
-theorem IsGaloisGroup.algebraMap_ringEquivFixedPoints_symm_apply (x : FixedPoints.subsemiring B G) :
- algebraMap A B ((ringEquivFixedPoints G A B).symm x) = x :=
- (hA.isInvariant.isInvariant x x.prop).choose_spec
-
-variable [CommSemiring A'] [Algebra A' B] [FaithfulSMul A' B] [hA' : IsGaloisGroup G A' B]
-
-/--
-If `B/A` and `B/A'` are Galois with the same Galois group, then `A ≃+* A'`.
--/
-noncomputable def IsGaloisGroup.ringEquiv :
- A ≃+* A' :=
- (ringEquivFixedPoints G A B).trans (ringEquivFixedPoints G A' B).symm
-
-@[simp]
-theorem IsGaloisGroup.algebraMap_ringEquiv_apply (x : A) :
- algebraMap A' B (IsGaloisGroup.ringEquiv G A A' B x) = algebraMap A B x := by
- simp [ringEquiv]
-
-@[simp]
-theorem IsGaloisGroup.algebraMap_ringEquiv_symm_apply (x : A') :
- algebraMap A B ((IsGaloisGroup.ringEquiv G A A' B).symm x) = algebraMap A' B x := by
- simp [ringEquiv]
-
-end CommRing
-
section Field
-variable (G A B K L : Type*) [Group G] [CommRing A] [CommRing B] [MulSemiringAction G B]
- [Algebra A B] [Field K] [Field L] [Algebra K L] [Algebra A K] [Algebra B L] [Algebra A L]
- [IsFractionRing A K] [IsFractionRing B L] [IsScalarTower A K L] [IsScalarTower A B L]
- [MulSemiringAction G L] [SMulDistribClass G B L]
-
-instance [IsGaloisGroup G A B] : IsGaloisGroup G (algebraMap A B).range B where
- faithful := IsGaloisGroup.faithful A
- commutes := ⟨fun g ⟨a', ⟨a, ha⟩⟩ b ↦ by simp [Subring.smul_def, ← ha]⟩
- isInvariant := ⟨fun b hb ↦ by
- obtain ⟨a, ha⟩ := Algebra.IsInvariant.isInvariant (A := A) b hb
- exact ⟨⟨algebraMap A B a, ⟨a, rfl⟩⟩, ha⟩⟩
-
-/-- `IsGaloisGroup` for rings implies `IsGaloisGroup` for their fraction fields. -/
-theorem IsGaloisGroup.to_isFractionRing_of_isIntegral
- [Algebra.IsIntegral A B] [hGAB : IsGaloisGroup G A B] :
- IsGaloisGroup G K L where
- faithful :=
- have := hGAB.faithful
- IsFractionRing.faithfulSMul G B L
- commutes := IsFractionRing.smulCommClass G A B K L
- isInvariant := IsFractionRing.isInvariant_of_isIntegral G A B K L
-
-/-- `IsGaloisGroup` for rings implies `IsGaloisGroup` for their fraction fields. -/
-theorem IsGaloisGroup.to_isFractionRing [Finite G] [hGAB : IsGaloisGroup G A B] :
- IsGaloisGroup G K L :=
- have := hGAB.isInvariant.isIntegral
- IsGaloisGroup.to_isFractionRing_of_isIntegral G A B K L
-
-/-- If `B` is an integral extension of an integrally closed domain `A`, then `IsGaloisGroup` for
-their fraction fields implies `IsGaloisGroup` for these rings. -/
-theorem IsGaloisGroup.of_isFractionRing [hGKL : IsGaloisGroup G K L]
- [IsIntegrallyClosed A] [Algebra.IsIntegral A B] : IsGaloisGroup G A B := by
- have hc (a : A) : (algebraMap K L) (algebraMap A K a) = (algebraMap B L) (algebraMap A B a) := by
- simp_rw [← IsScalarTower.algebraMap_apply]
- refine ⟨⟨fun h ↦ ?_⟩, ⟨fun g x y ↦ IsFractionRing.injective B L ?_⟩, ⟨fun x h ↦ ?_⟩⟩
- · have := hGKL.faithful
- refine eq_of_smul_eq_smul fun (y : L) ↦ ?_
- obtain ⟨a, b, hb, rfl⟩ := IsFractionRing.div_surjective B y
- simp only [smul_div₀', ← algebraMap.coe_smul', h]
- · simp [Algebra.smul_def, algebraMap.coe_smul', ← hc]
- · obtain ⟨b, hb⟩ := hGKL.isInvariant.isInvariant (algebraMap B L x)
- (by simpa [← algebraMap.coe_smul'])
- have hx : IsIntegral A (algebraMap B L x) := (Algebra.IsIntegral.isIntegral x).algebraMap
- rw [← hb, isIntegral_algebraMap_iff (algebraMap K L).injective,
- IsIntegrallyClosedIn.isIntegral_iff] at hx
- obtain ⟨a, rfl⟩ := hx
- exact ⟨a, by rwa [hc, IsFractionRing.coe_inj] at hb⟩
-
-/-- If `G` is finite and `A` is integrally closed then `IsGaloisGroup G A B` is equivalent to `B/A`
-being integral and the fields of fractions `Frac(B)/Frac(A)` being Galois with Galois group `G`. -/
-theorem IsGaloisGroup.iff_isFractionRing [Finite G] [IsIntegrallyClosed A] :
- IsGaloisGroup G A B ↔ Algebra.IsIntegral A B ∧ IsGaloisGroup G K L :=
- ⟨fun h ↦ ⟨h.isInvariant.isIntegral, h.to_isFractionRing G A B K L⟩,
- fun ⟨_, h⟩ ↦ h.of_isFractionRing G A B K L⟩
-
-@[deprecated (since := "2026-04-20")] alias FractionRing.mulSemiringAction_of_isGaloisGroup :=
- IsFractionRing.mulSemiringAction
-
-/--
-If `G` is finite and `IsGaloisGroup G A B` with `A` and `B` domains, then `G` is also
-a Galois group for `FractionRing B / FractionRing A` for the action defined by
-`IsFractionRing.mulSemiringAction`.
--/
-instance IsGaloisGroup.toFractionRing [IsDomain A] [IsDomain B] [IsTorsionFree A B] [Finite G]
- [IsGaloisGroup G A B] [Algebra (FractionRing A) (FractionRing B)]
- [IsScalarTower A (FractionRing A) (FractionRing B)] :
- letI := IsFractionRing.mulSemiringAction G B (FractionRing B)
- IsGaloisGroup G (FractionRing A) (FractionRing B) := by
- let := IsFractionRing.mulSemiringAction G B (FractionRing B)
- apply IsGaloisGroup.to_isFractionRing G A B _ _
-
open NumberField
instance (K L : Type*) [Field K] [Field L] [NumberField K] [NumberField L] [Algebra K L]
@@ -383,13 +173,6 @@ end IsDomain
variable (H H' : Subgroup G) (F F' : IntermediateField K L)
-instance (R S : Type*) [CommRing R] [CommRing S] [Algebra R S]
- [MulSemiringAction G S] [hGKL : IsGaloisGroup G R S] :
- IsGaloisGroup H (FixedPoints.subalgebra R S H) S where
- faithful := have := hGKL.faithful; inferInstance
- commutes := inferInstance
- isInvariant := ⟨fun x h ↦ ⟨⟨x, h⟩, rfl⟩⟩
-
instance subgroup [hGKL : IsGaloisGroup G K L] :
IsGaloisGroup H (FixedPoints.intermediateField H : IntermediateField K L) L :=
inferInstanceAs (IsGaloisGroup H (FixedPoints.subalgebra K L H) L)
@@ -602,90 +385,6 @@ end GaloisCorrespondence
section Quotient
-section Semiring
-
-variable (A B C : Type*) [CommSemiring A] [Semiring C] [Algebra A C] [MulSemiringAction G C]
-variable (N : Subgroup G) [CommSemiring B] [Algebra B C]
-
-/-- If `N` is a normal subgroup of `G` and `IsGaloisGroup N B C`, then `G` acts on `B`.
-For `g : G` and `x : B`, `g • x` is the unique element of `B` whose image in `C` is
-`g • algebraMap B C x`, see `algebraMap_smulOfNormal`. -/
-@[implicit_reducible]
-noncomputable def smulOfNormal [N.Normal] [IsGaloisGroup N B C] : SMul G B where
- smul g x := (smul_mem_of_normal G C N g x).choose
-
-@[simp]
-theorem algebraMap_smulOfNormal [N.Normal] [IsGaloisGroup N B C] (g : G) (x : B) :
- letI := smulOfNormal G B C
- algebraMap B C (g • x) = g • algebraMap B C x :=
- (smul_mem_of_normal G C N g x).choose_spec
-
-/-- If `N` is normal and `IsGaloisGroup N B C`, the action `smulOfNormal G B C` satisfies
-`SMulDistribClass G B C`. -/
-instance smulDistribClass_smulOfNormal [N.Normal] [IsGaloisGroup N B C] :
- letI := smulOfNormal G B C
- SMulDistribClass G B C :=
- let := smulOfNormal G B C
- ⟨fun g b c ↦ by simp [Algebra.smul_def]⟩
-
-variable [FaithfulSMul B C]
-
-/-- If `N` is a normal subgroup of `G` and `IsGaloisGroup N B C`, then `G` acts on `B` as a
-`MulSemiringAction`, via the action defined in `smulOfNormal`. -/
-@[implicit_reducible]
-noncomputable def mulSemiringActionOfNormal [IsGaloisGroup N B C] [N.Normal] :
- MulSemiringAction G B := by
- let : SMul G B := smulOfNormal G B C N
- have : SMulDistribClass G B C := smulDistribClass_smulOfNormal G B C N
- exact mulSemiringActionOfSmulDistribClass B C G
-
-/-- If `N` is a normal subgroup of `G` and `IsGaloisGroup N B C`, then the quotient group `G ⧸ N`
-acts on `B` by `(g : G ⧸ N) • x = g • x`. -/
-@[implicit_reducible]
-noncomputable def mulSemiringActionQuotient [IsGaloisGroup N B C] [N.Normal] :
- MulSemiringAction (G ⧸ N) B :=
- letI := mulSemiringActionOfNormal G B C N
- { smul q x :=
- Quotient.liftOn' q (· • x) fun g₁ g₂ h ↦ by
- apply FaithfulSMul.algebraMap_injective B C
- rw [algebraMap.smul', algebraMap.smul', smul_eq_iff_eq_inv_smul, ← smul_assoc, smul_eq_mul,
- Subgroup.smul_algebraMap C (by rwa [← QuotientGroup.leftRel_apply])]
- one_smul x := one_smul G x
- mul_smul q₁ q₂ x := Quotient.inductionOn₂' q₁ q₂ fun g h ↦ mul_smul g h x
- smul_add q x y := Quotient.inductionOn' q fun g ↦ smul_add g x y
- smul_zero q := Quotient.inductionOn' q fun g ↦ smul_zero g
- smul_one q := Quotient.inductionOn' q fun g ↦ smul_one g
- smul_mul q x y := Quotient.inductionOn' q fun g ↦ smul_mul' g x y }
-
-theorem mulSemiringActionQuotient_smul_def [MulSemiringAction G B] [SMulDistribClass G B C]
- [IsGaloisGroup N B C] [N.Normal] (g : G) (b : B) :
- letI := mulSemiringActionQuotient G B C N
- (g : G ⧸ N) • b = g • b := by
- let := mulSemiringActionOfNormal G B C N
- refine (Quotient.liftOn'_mk'' (· • b) _ g).trans (FaithfulSMul.algebraMap_injective B C ?_)
- rw [algebraMap.smul', algebraMap.smul']
-
-instance isScalarTower_mulSemiringActionQuotient [MulSemiringAction G B] [SMulDistribClass G B C]
- [IsGaloisGroup N B C] [N.Normal] :
- letI := mulSemiringActionQuotient G B C N
- IsScalarTower G (G ⧸ N) B :=
- let := mulSemiringActionQuotient G B C N
- ⟨fun g q b ↦ Quotient.inductionOn' q fun h ↦ by
- simp [mul_smul, mulSemiringActionQuotient_smul_def]⟩
-
-set_option linter.defProp false in
-/-- If `G` acts on `C` commuting with `A`, then the action of `G ⧸ N` on `B` commutes with `A`. -/
-@[implicit_reducible]
-def smulCommClassQuotient [N.Normal] [Algebra A B] [IsScalarTower A B C] [SMulCommClass G A C]
- [MulSemiringAction G B] [MulAction (G ⧸ N) B] [SMulDistribClass G B C]
- [IsScalarTower G (G ⧸ N) B] :
- SMulCommClass (G ⧸ N) A B :=
- ⟨fun g k x ↦ Quotient.inductionOn' g fun g ↦
- FaithfulSMul.algebraMap_injective B C (by
- simp [algebraMap.smul, algebraMap.smul', smul_comm])⟩
-
-end Semiring
-
section Domain
variable (A B C : Type*) [CommRing A] [CommRing B] [CommRing C] [IsDomain C] [Algebra A B]
@@ -796,16 +495,6 @@ noncomputable section IntermediateField
variable (N : Subgroup G) [N.Normal] [IsGaloisGroup N F L]
-instance : MulSemiringAction (G ⧸ N) F :=
- letI := smulOfNormal G F L N
- haveI := smulDistribClass_smulOfNormal G F L N
- letI := mulSemiringActionOfSmulDistribClass F L G
- mulSemiringActionQuotient G F L N
-
-instance [SMulCommClass G K L] [MulSemiringAction G F] [SMulDistribClass G F L]
- [IsScalarTower G (G ⧸ N) F] : SMulCommClass (G ⧸ N) K F :=
- smulCommClassQuotient G K F L N
-
/-- If `G` is a finite Galois group for `L/K` and `N` is a normal subgroup of `G` that is a
Galois group for `L/F`, then the quotient group `G ⧸ N` is a Galois group for `F/K`. -/
instance [Finite G] [IsGaloisGroup G K L] : IsGaloisGroup (G ⧸ N) K F :=
diff --git a/Mathlib/RingTheory/IsGaloisGroup/Basic.lean b/Mathlib/RingTheory/IsGaloisGroup/Basic.lean
new file mode 100644
index 00000000000000..19bd8b54b351a6
--- /dev/null
+++ b/Mathlib/RingTheory/IsGaloisGroup/Basic.lean
@@ -0,0 +1,251 @@
+/-
+Copyright (c) 2025 Thomas Browning. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Thomas Browning
+-/
+module
+
+public import Mathlib.RingTheory.IntegralClosure.IntegrallyClosed
+public import Mathlib.RingTheory.Invariant.Basic
+public import Mathlib.RingTheory.IsGaloisGroup.Defs
+
+/-!
+# Galois Groups of Rings
+
+Given an action of a group `G` on an extension of rings `B/A`, the predicate `IsGaloisGroup G A B`
+states that `G` acts faithfully on `B` with fixed ring `A`. This file develops some of the theory
+of this predicate without assuming Galois theory for fields.
+-/
+
+@[expose] public section
+
+-- this file should not import any field theory beyond the contents of `FieldTheory/Fixed.lean`
+-- material involving Galois theory should be placed in `FieldTheory/IsGaloisGroup.lean`
+assert_not_exists IntermediateField.adjoin
+
+open Module
+
+section CommRing
+
+variable (G A B : Type*) [Group G] [CommSemiring A] [Semiring B] [Algebra A B]
+ [MulSemiringAction G B]
+
+variable {C : Type*} [CommSemiring C] [Algebra C B]
+
+variable {G} in
+protected theorem Subgroup.smul_algebraMap {H : Subgroup G} [SMulCommClass H C B] {g : G}
+ (hg : g ∈ H) (x : C) :
+ g • algebraMap C B x = algebraMap C B x :=
+ smul_algebraMap (⟨g, hg⟩ : H) x
+
+theorem IsGaloisGroup.smul_mem_of_normal (N : Subgroup G) [hN : N.Normal]
+ [hC : IsGaloisGroup N C B] (g : G) (x : C) :
+ g • algebraMap C B x ∈ Set.range (algebraMap C B) := by
+ apply hC.isInvariant.isInvariant (g • algebraMap C B x)
+ intro n
+ rw [← inv_smul_eq_iff, Subgroup.smul_def, ← mul_smul, ← mul_smul]
+ exact Subgroup.smul_algebraMap B (hN.conj_mem' n n.prop g) x
+
+@[deprecated (since := "2026-05-28")] alias smul_eq_self := Subgroup.smul_algebraMap
+@[deprecated (since := "2026-05-28")] alias smul_mem_of_normal := IsGaloisGroup.smul_mem_of_normal
+
+end CommRing
+
+section Field
+
+variable (G A B K L : Type*) [Group G] [CommRing A] [CommRing B] [MulSemiringAction G B]
+ [Algebra A B] [Field K] [Field L] [Algebra K L] [Algebra A K] [Algebra B L] [Algebra A L]
+ [IsFractionRing A K] [IsFractionRing B L] [IsScalarTower A K L] [IsScalarTower A B L]
+ [MulSemiringAction G L] [SMulDistribClass G B L]
+
+instance [IsGaloisGroup G A B] : IsGaloisGroup G (algebraMap A B).range B where
+ faithful := IsGaloisGroup.faithful A
+ commutes := ⟨fun g ⟨a', ⟨a, ha⟩⟩ b ↦ by simp [Subring.smul_def, ← ha]⟩
+ isInvariant := ⟨fun b hb ↦ by
+ obtain ⟨a, ha⟩ := Algebra.IsInvariant.isInvariant (A := A) b hb
+ exact ⟨⟨algebraMap A B a, ⟨a, rfl⟩⟩, ha⟩⟩
+
+/-- `IsGaloisGroup` for rings implies `IsGaloisGroup` for their fraction fields. -/
+theorem IsGaloisGroup.to_isFractionRing_of_isIntegral
+ [Algebra.IsIntegral A B] [hGAB : IsGaloisGroup G A B] :
+ IsGaloisGroup G K L where
+ faithful :=
+ have := hGAB.faithful
+ IsFractionRing.faithfulSMul G B L
+ commutes := IsFractionRing.smulCommClass G A B K L
+ isInvariant := IsFractionRing.isInvariant_of_isIntegral G A B K L
+
+/-- `IsGaloisGroup` for rings implies `IsGaloisGroup` for their fraction fields. -/
+theorem IsGaloisGroup.to_isFractionRing [Finite G] [hGAB : IsGaloisGroup G A B] :
+ IsGaloisGroup G K L :=
+ have := hGAB.isInvariant.isIntegral
+ IsGaloisGroup.to_isFractionRing_of_isIntegral G A B K L
+
+/-- If `B` is an integral extension of an integrally closed domain `A`, then `IsGaloisGroup` for
+their fraction fields implies `IsGaloisGroup` for these rings. -/
+theorem IsGaloisGroup.of_isFractionRing [hGKL : IsGaloisGroup G K L]
+ [IsIntegrallyClosed A] [Algebra.IsIntegral A B] : IsGaloisGroup G A B := by
+ have hc (a : A) : (algebraMap K L) (algebraMap A K a) = (algebraMap B L) (algebraMap A B a) := by
+ simp_rw [← IsScalarTower.algebraMap_apply]
+ refine ⟨⟨fun h ↦ ?_⟩, ⟨fun g x y ↦ IsFractionRing.injective B L ?_⟩, ⟨fun x h ↦ ?_⟩⟩
+ · have := hGKL.faithful
+ refine eq_of_smul_eq_smul fun (y : L) ↦ ?_
+ obtain ⟨a, b, hb, rfl⟩ := IsFractionRing.div_surjective B y
+ simp only [smul_div₀', ← algebraMap.coe_smul', h]
+ · simp [Algebra.smul_def, algebraMap.coe_smul', ← hc]
+ · obtain ⟨b, hb⟩ := hGKL.isInvariant.isInvariant (algebraMap B L x)
+ (by simpa [← algebraMap.coe_smul'])
+ have hx : IsIntegral A (algebraMap B L x) := (Algebra.IsIntegral.isIntegral x).algebraMap
+ rw [← hb, isIntegral_algebraMap_iff (algebraMap K L).injective,
+ IsIntegrallyClosedIn.isIntegral_iff] at hx
+ obtain ⟨a, rfl⟩ := hx
+ exact ⟨a, by rwa [hc, IsFractionRing.coe_inj] at hb⟩
+
+/-- If `G` is finite and `A` is integrally closed then `IsGaloisGroup G A B` is equivalent to `B/A`
+being integral and the fields of fractions `Frac(B)/Frac(A)` being Galois with Galois group `G`. -/
+theorem IsGaloisGroup.iff_isFractionRing [Finite G] [IsIntegrallyClosed A] :
+ IsGaloisGroup G A B ↔ Algebra.IsIntegral A B ∧ IsGaloisGroup G K L :=
+ ⟨fun h ↦ ⟨h.isInvariant.isIntegral, h.to_isFractionRing G A B K L⟩,
+ fun ⟨_, h⟩ ↦ h.of_isFractionRing G A B K L⟩
+
+@[deprecated (since := "2026-04-20")] alias FractionRing.mulSemiringAction_of_isGaloisGroup :=
+ IsFractionRing.mulSemiringAction
+
+/--
+If `G` is finite and `IsGaloisGroup G A B` with `A` and `B` domains, then `G` is also
+a Galois group for `FractionRing B / FractionRing A` for the action defined by
+`IsFractionRing.mulSemiringAction`.
+-/
+instance IsGaloisGroup.toFractionRing [IsDomain A] [IsDomain B] [IsTorsionFree A B] [Finite G]
+ [IsGaloisGroup G A B] [Algebra (FractionRing A) (FractionRing B)]
+ [IsScalarTower A (FractionRing A) (FractionRing B)] :
+ letI := IsFractionRing.mulSemiringAction G B (FractionRing B)
+ IsGaloisGroup G (FractionRing A) (FractionRing B) := by
+ let := IsFractionRing.mulSemiringAction G B (FractionRing B)
+ apply IsGaloisGroup.to_isFractionRing G A B _ _
+
+end Field
+
+variable (G G' K L : Type*) [Group G] [Group G'] [Field K] [Field L] [Algebra K L]
+ [MulSemiringAction G L] [MulSemiringAction G' L]
+
+namespace IsGaloisGroup
+
+section IsDomain
+
+variable (A B : Type*) [CommRing A] [CommRing B] [IsDomain B] [Algebra A B] [FaithfulSMul A B]
+ [MulSemiringAction G B] [MulSemiringAction G' B] [IsGaloisGroup G A B] [IsGaloisGroup G' A B]
+ [Finite G] [Finite G']
+
+end IsDomain
+
+variable (H : Subgroup G)
+
+instance (R S : Type*) [CommRing R] [CommRing S] [Algebra R S]
+ [MulSemiringAction G S] [hGKL : IsGaloisGroup G R S] :
+ IsGaloisGroup H (FixedPoints.subalgebra R S H) S where
+ faithful := have := hGKL.faithful; inferInstance
+ commutes := inferInstance
+ isInvariant := ⟨fun x h ↦ ⟨⟨x, h⟩, rfl⟩⟩
+
+section Quotient
+
+section Semiring
+
+variable (A B C : Type*) [CommSemiring A] [Semiring C] [Algebra A C] [MulSemiringAction G C]
+variable (N : Subgroup G) [CommSemiring B] [Algebra B C]
+
+/-- If `N` is a normal subgroup of `G` and `IsGaloisGroup N B C`, then `G` acts on `B`.
+For `g : G` and `x : B`, `g • x` is the unique element of `B` whose image in `C` is
+`g • algebraMap B C x`, see `algebraMap_smulOfNormal`. -/
+@[implicit_reducible]
+noncomputable def smulOfNormal [N.Normal] [IsGaloisGroup N B C] : SMul G B where
+ smul g x := (smul_mem_of_normal G C N g x).choose
+
+@[simp]
+theorem algebraMap_smulOfNormal [N.Normal] [IsGaloisGroup N B C] (g : G) (x : B) :
+ letI := smulOfNormal G B C
+ algebraMap B C (g • x) = g • algebraMap B C x :=
+ (smul_mem_of_normal G C N g x).choose_spec
+
+/-- If `N` is normal and `IsGaloisGroup N B C`, the action `smulOfNormal G B C` satisfies
+`SMulDistribClass G B C`. -/
+instance smulDistribClass_smulOfNormal [N.Normal] [IsGaloisGroup N B C] :
+ letI := smulOfNormal G B C
+ SMulDistribClass G B C :=
+ let := smulOfNormal G B C
+ ⟨fun g b c ↦ by simp [Algebra.smul_def]⟩
+
+variable [FaithfulSMul B C]
+
+/-- If `N` is a normal subgroup of `G` and `IsGaloisGroup N B C`, then `G` acts on `B` as a
+`MulSemiringAction`, via the action defined in `smulOfNormal`. -/
+@[implicit_reducible]
+noncomputable def mulSemiringActionOfNormal [IsGaloisGroup N B C] [N.Normal] :
+ MulSemiringAction G B := by
+ let : SMul G B := smulOfNormal G B C N
+ have : SMulDistribClass G B C := smulDistribClass_smulOfNormal G B C N
+ exact mulSemiringActionOfSmulDistribClass B C G
+
+/-- If `N` is a normal subgroup of `G` and `IsGaloisGroup N B C`, then the quotient group `G ⧸ N`
+acts on `B` by `(g : G ⧸ N) • x = g • x`. -/
+@[implicit_reducible]
+noncomputable def mulSemiringActionQuotient [IsGaloisGroup N B C] [N.Normal] :
+ MulSemiringAction (G ⧸ N) B :=
+ letI := mulSemiringActionOfNormal G B C N
+ { smul q x :=
+ Quotient.liftOn' q (· • x) fun g₁ g₂ h ↦ by
+ apply FaithfulSMul.algebraMap_injective B C
+ rw [algebraMap.smul', algebraMap.smul', smul_eq_iff_eq_inv_smul, ← smul_assoc, smul_eq_mul,
+ Subgroup.smul_algebraMap C (by rwa [← QuotientGroup.leftRel_apply])]
+ one_smul x := one_smul G x
+ mul_smul q₁ q₂ x := Quotient.inductionOn₂' q₁ q₂ fun g h ↦ mul_smul g h x
+ smul_add q x y := Quotient.inductionOn' q fun g ↦ smul_add g x y
+ smul_zero q := Quotient.inductionOn' q fun g ↦ smul_zero g
+ smul_one q := Quotient.inductionOn' q fun g ↦ smul_one g
+ smul_mul q x y := Quotient.inductionOn' q fun g ↦ smul_mul' g x y }
+
+theorem mulSemiringActionQuotient_smul_def [MulSemiringAction G B] [SMulDistribClass G B C]
+ [IsGaloisGroup N B C] [N.Normal] (g : G) (b : B) :
+ letI := mulSemiringActionQuotient G B C N
+ (g : G ⧸ N) • b = g • b := by
+ let := mulSemiringActionOfNormal G B C N
+ refine (Quotient.liftOn'_mk'' (· • b) _ g).trans (FaithfulSMul.algebraMap_injective B C ?_)
+ rw [algebraMap.smul', algebraMap.smul']
+
+instance isScalarTower_mulSemiringActionQuotient [MulSemiringAction G B] [SMulDistribClass G B C]
+ [IsGaloisGroup N B C] [N.Normal] :
+ letI := mulSemiringActionQuotient G B C N
+ IsScalarTower G (G ⧸ N) B :=
+ let := mulSemiringActionQuotient G B C N
+ ⟨fun g q b ↦ Quotient.inductionOn' q fun h ↦ by
+ simp [mul_smul, mulSemiringActionQuotient_smul_def]⟩
+
+set_option linter.defProp false in
+/-- If `G` acts on `C` commuting with `A`, then the action of `G ⧸ N` on `B` commutes with `A`. -/
+@[implicit_reducible]
+def smulCommClassQuotient [N.Normal] [Algebra A B] [IsScalarTower A B C] [SMulCommClass G A C]
+ [MulSemiringAction G B] [MulAction (G ⧸ N) B] [SMulDistribClass G B C]
+ [IsScalarTower G (G ⧸ N) B] :
+ SMulCommClass (G ⧸ N) A B :=
+ ⟨fun g k x ↦ Quotient.inductionOn' g fun g ↦
+ FaithfulSMul.algebraMap_injective B C (by
+ simp [algebraMap.smul, algebraMap.smul', smul_comm])⟩
+
+end Semiring
+
+variable (F : IntermediateField K L) (N : Subgroup G) [N.Normal] [IsGaloisGroup N F L]
+
+noncomputable instance : MulSemiringAction (G ⧸ N) F :=
+ letI := smulOfNormal G F L N
+ haveI := smulDistribClass_smulOfNormal G F L N
+ letI := mulSemiringActionOfSmulDistribClass F L G
+ mulSemiringActionQuotient G F L N
+
+instance [SMulCommClass G K L] [MulSemiringAction G F] [SMulDistribClass G F L]
+ [IsScalarTower G (G ⧸ N) F] : SMulCommClass (G ⧸ N) K F :=
+ smulCommClassQuotient G K F L N
+
+end Quotient
+
+end IsGaloisGroup
diff --git a/Mathlib/RingTheory/IsGaloisGroup/Defs.lean b/Mathlib/RingTheory/IsGaloisGroup/Defs.lean
new file mode 100644
index 00000000000000..c0ae57e8a37fce
--- /dev/null
+++ b/Mathlib/RingTheory/IsGaloisGroup/Defs.lean
@@ -0,0 +1,155 @@
+/-
+Copyright (c) 2025 Thomas Browning. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Thomas Browning
+-/
+module
+
+public import Mathlib.Algebra.Algebra.Subalgebra.Operations
+public import Mathlib.RingTheory.Invariant.Defs
+
+/-!
+# Predicate for Galois Groups
+
+Given an action of a group `G` on an extension of fields `L/K`, we introduce a predicate
+`IsGaloisGroup G K L` saying that `G` acts faithfully on `L` with fixed field `K`. In particular,
+we do not assume that `L` is an algebraic extension of `K`.
+
+## Implementation notes
+
+We actually define `IsGaloisGroup G A B` for extensions of rings `B/A`, with the same definition
+(faithful action on `B` with fixed ring `A`). This definition turns out to axiomatize a common
+setup in algebraic number theory where a Galois group `Gal(L/K)` acts on an extension of subrings
+`B/A` (e.g., rings of integers). In particular, there are theorems in algebraic number theory that
+naturally assume `[IsGaloisGroup G A B]` and whose statements would otherwise require assuming
+`(K L : Type*) [Field K] [Field L] [Algebra K L] [IsGalois K L]` (along with predicates relating
+`K` and `L` to the rings `A` and `B`) despite `K` and `L` not appearing in the conclusion.
+
+Unfortunately, this definition of `IsGaloisGroup G A B` for extensions of rings `B/A` is
+nonstandard and clashes with other notions such as the étale fundamental group. In particular, if
+`G` is finite and `A` is integrally closed, then `IsGaloisGroup G A B` is equivalent to `B/A`
+being integral and the fields of fractions `Frac(B)/Frac(A)` being Galois with Galois group `G`
+(see `IsGaloisGroup.iff_isFractionRing`), rather than `B/A` being étale for instance.
+
+But in the absence of a more suitable name, the utility of the predicate `IsGaloisGroup G A B` for
+extensions of rings `B/A` seems to outweigh these terminological issues.
+-/
+
+@[expose] public section
+
+assert_not_exists IsFractionRing
+
+variable (G A A' B : Type*) [Group G] [CommSemiring A] [Semiring B] [Algebra A B]
+ [MulSemiringAction G B]
+
+/-- `G` is a Galois group for `L/K` if the action of `G` on `L` is faithful with fixed field `K`.
+In particular, we do not assume that `L` is an algebraic extension of `K`.
+
+See the implementation notes in this file for the meaning of this definition in the case of rings.
+-/
+class IsGaloisGroup where
+ faithful : FaithfulSMul G B
+ commutes : SMulCommClass G A B
+ isInvariant : Algebra.IsInvariant A B G
+
+namespace IsGaloisGroup
+
+variable {G A B} in
+theorem of_mulEquiv [hG : IsGaloisGroup G A B] {H : Type*} [Group H]
+ [MulSemiringAction H B] (e : H ≃* G) (he : ∀ h (x : B), (e h) • x = h • x) :
+ IsGaloisGroup H A B where
+ faithful := ⟨fun h ↦ e.injective <| hG.faithful.eq_of_smul_eq_smul <| by simpa only [he]⟩
+ commutes := ⟨fun x a b ↦ by simpa [he] using hG.commutes.smul_comm (e x) a b⟩
+ isInvariant := ⟨fun b h ↦
+ 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 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 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 ‹_›
+
+theorem of_algEquiv [hG : IsGaloisGroup G A B] (B' : Type*) [Semiring B']
+ [Algebra A B'] [MulSemiringAction G B'] (e : B ≃ₐ[A] B')
+ (he : ∀ (g : G) (x : B), e (g • x) = g • (e x)) :
+ IsGaloisGroup G A B' where
+ faithful := ⟨fun h ↦ hG.faithful.eq_of_smul_eq_smul fun b ↦ by simpa [← he] using h (e b)⟩
+ commutes := ⟨fun g a b' ↦ by
+ have h' {x'} : e.symm (g • x') = g • e.symm x' := by
+ apply e.injective
+ simp [he]
+ apply e.symm.injective
+ simpa [h', map_smul] using hG.commutes.smul_comm g a (e.symm b')⟩
+ isInvariant := ⟨fun x' hx' ↦ by
+ obtain ⟨a, ha⟩ := hG.isInvariant.isInvariant (e.symm x') (fun g ↦ by
+ apply e.injective
+ simp [he, hx'])
+ exact ⟨a, by rw [← e.commutes, ha, AlgEquiv.apply_symm_apply]⟩⟩
+
+theorem of_ringHom_surjective [hG : IsGaloisGroup G A B] [CommSemiring A']
+ [Algebra A' B] (e : A →+* A') (he : ∀ a, algebraMap A' B (e a) = algebraMap A B a)
+ (he' : Function.Surjective e) : IsGaloisGroup G A' B where
+ faithful := hG.faithful
+ commutes := ⟨by
+ intro g a' b
+ obtain ⟨a, rfl⟩ : ∃ a, e a = a' := he' a'
+ rw [Algebra.smul_def, Algebra.smul_def, he, ← Algebra.smul_def, ← Algebra.smul_def]
+ exact hG.commutes.smul_comm g a b⟩
+ isInvariant := ⟨by
+ intro b h
+ obtain ⟨a, ha⟩ := hG.isInvariant.isInvariant b h
+ exact ⟨e a, by rw [he, ha]⟩⟩
+
+theorem of_ringEquiv [hG : IsGaloisGroup G A B] [CommSemiring A'] [Algebra A' B]
+ (e : A ≃+* A') (he : ∀ a, algebraMap A' B (e a) = algebraMap A B a) :
+ IsGaloisGroup G A' B :=
+ .of_ringHom_surjective G A A' B e he e.surjective
+
+attribute [instance low] IsGaloisGroup.commutes IsGaloisGroup.isInvariant
+
+variable [hA : IsGaloisGroup G A B] [FaithfulSMul A B]
+
+/-- If `B/A` is Galois with Galois group `G`, then `A` is isomorphic to the subring of elements of
+`B` fixed by `G`. -/
+@[simps apply_coe]
+noncomputable def ringEquivFixedPoints :
+ A ≃+* FixedPoints.subsemiring B G where
+ toFun x := ⟨algebraMap A B x, fun _ ↦ by rw [smul_algebraMap]⟩
+ invFun x := (hA.isInvariant.isInvariant x x.prop).choose
+ map_mul' _ _ := by simp [Subtype.ext_iff]
+ map_add' _ _ := by simp [Subtype.ext_iff]
+ left_inv _ := by simp
+ right_inv x := by simpa [Subtype.ext_iff] using (hA.isInvariant.isInvariant x x.prop).choose_spec
+
+@[simp]
+theorem algebraMap_ringEquivFixedPoints_symm_apply (x : FixedPoints.subsemiring B G) :
+ algebraMap A B ((ringEquivFixedPoints G A B).symm x) = x :=
+ (hA.isInvariant.isInvariant x x.prop).choose_spec
+
+variable [CommSemiring A'] [Algebra A' B] [FaithfulSMul A' B] [hA' : IsGaloisGroup G A' B]
+
+/-- If `B/A` and `B/A'` are Galois with the same Galois group, then `A ≃+* A'`. -/
+noncomputable def ringEquiv : A ≃+* A' :=
+ (ringEquivFixedPoints G A B).trans (ringEquivFixedPoints G A' B).symm
+
+@[simp]
+theorem algebraMap_ringEquiv_apply (x : A) :
+ algebraMap A' B (IsGaloisGroup.ringEquiv G A A' B x) = algebraMap A B x := by
+ simp [ringEquiv]
+
+@[simp]
+theorem algebraMap_ringEquiv_symm_apply (x : A') :
+ algebraMap A B ((IsGaloisGroup.ringEquiv G A A' B).symm x) = algebraMap A' B x := by
+ simp [ringEquiv]
+
+end IsGaloisGroup
From a2bff7b898cdaaddae5789dc8e20445d011003ba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=ABl=20Riou?=
<37772949+joelriou@users.noreply.github.com>
Date: Tue, 23 Jun 2026 14:52:42 +0000
Subject: [PATCH 0290/1300] feat(AlgebraicTopology): a nonsingular simplicial
set is the colimit of standard simplices indexed by nondegenerate simplices
(#40254)
---
Mathlib.lean | 1 +
.../NonDegenerateSimplicesColimit.lean | 2 +-
.../SimplicialSet/Nonsingular.lean | 77 ++++++++++++++++++-
.../SimplicialSet/NonsingularColimit.lean | 74 ++++++++++++++++++
.../SimplicialSet/StdSimplex.lean | 60 +++++++++++++++
5 files changed, 212 insertions(+), 2 deletions(-)
create mode 100644 Mathlib/AlgebraicTopology/SimplicialSet/NonsingularColimit.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index f0735f278b484c..001a649d202c66 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -1604,6 +1604,7 @@ public import Mathlib.AlgebraicTopology.SimplicialSet.NonDegenerateSimplicesColi
public import Mathlib.AlgebraicTopology.SimplicialSet.NonDegenerateSimplicesSubcomplex
public import Mathlib.AlgebraicTopology.SimplicialSet.Nonempty
public import Mathlib.AlgebraicTopology.SimplicialSet.Nonsingular
+public import Mathlib.AlgebraicTopology.SimplicialSet.NonsingularColimit
public import Mathlib.AlgebraicTopology.SimplicialSet.Op
public import Mathlib.AlgebraicTopology.SimplicialSet.Path
public import Mathlib.AlgebraicTopology.SimplicialSet.PiZero
diff --git a/Mathlib/AlgebraicTopology/SimplicialSet/NonDegenerateSimplicesColimit.lean b/Mathlib/AlgebraicTopology/SimplicialSet/NonDegenerateSimplicesColimit.lean
index 12e7737e704c5a..f2c473ade6dfe4 100644
--- a/Mathlib/AlgebraicTopology/SimplicialSet/NonDegenerateSimplicesColimit.lean
+++ b/Mathlib/AlgebraicTopology/SimplicialSet/NonDegenerateSimplicesColimit.lean
@@ -29,7 +29,7 @@ variable (X : SSet.{u})
/-- If `X : SSet`, this is the functor `X.N ⥤ SSet` which sends a
nondegenerate simplex of `X` to the subcomplex of `X` that it generates. -/
-@[expose, simps! obj]
+@[expose, simps! obj map]
public def functorN : X.N ⥤ SSet.{u} :=
X.orderEmbeddingN.monotone.functor ⋙ Subcomplex.toSSetFunctor
diff --git a/Mathlib/AlgebraicTopology/SimplicialSet/Nonsingular.lean b/Mathlib/AlgebraicTopology/SimplicialSet/Nonsingular.lean
index 50bb14b9e13fba..0d31bdaaec145b 100644
--- a/Mathlib/AlgebraicTopology/SimplicialSet/Nonsingular.lean
+++ b/Mathlib/AlgebraicTopology/SimplicialSet/Nonsingular.lean
@@ -32,7 +32,7 @@ public section
universe u
-open CategoryTheory MonoidalCategory Simplicial
+open CategoryTheory MonoidalCategory Simplicial Opposite
namespace SSet
@@ -104,4 +104,79 @@ lemma Nonsingular.δ_injective [X.Nonsingular]
have := mono' x hx
exact injective_of_mono ((yonedaEquiv.symm x).app _) hij
+lemma Nonsingular.injective_map
+ [X.Nonsingular] {n : ℕ} (x : X _⦋n⦌) (hx : x ∈ X.nonDegenerate n)
+ {m : SimplexCategory} {f g : m ⟶ ⦋n⦌}
+ (h : X.map f.op x = X.map g.op x) :
+ f = g := by
+ have := Nonsingular.mono' x hx
+ apply stdSimplex.{u}.map_injective
+ rw [← cancel_mono (yonedaEquiv.symm x)]
+ apply yonedaEquiv.injective
+ simpa [yonedaEquiv_comp, yonedaEquiv_map]
+
+lemma Nonsingular.isIso_toOfSimplex [X.Nonsingular]
+ {n : ℕ} (x : X _⦋n⦌) (hx : x ∈ X.nonDegenerate n) :
+ IsIso (Subcomplex.toOfSimplex x) := by
+ rw [Subcomplex.isIso_toOfSimplex_iff]
+ exact Nonsingular.mono' x hx
+
+/-- If `x : X _⦋n⦌` is a nondegenerate simplex of a nonsingular simplcial set,
+this is the isomorphism `Δ[n] ≅ Subcomplex.ofSimplex x` induced by `x`. -/
+@[expose, simps! hom]
+noncomputable def Nonsingular.iso
+ [X.Nonsingular] {n : ℕ} (x : X _⦋n⦌) (hx : x ∈ X.nonDegenerate n) :
+ Δ[n] ≅ Subcomplex.ofSimplex x :=
+ letI := Nonsingular.isIso_toOfSimplex x hx
+ asIso (Subcomplex.toOfSimplex x)
+
+namespace N
+
+variable [X.Nonsingular] {x y z : X.N} (h : x ≤ y)
+
+include h in
+lemma existsUnique_of_le :
+ ∃! (f : ⦋x.dim⦌ ⟶ ⦋y.dim⦌), Mono f ∧ X.map f.op y.1.2 = x.1.2 :=
+ existsUnique_of_exists_of_unique (by
+ obtain ⟨f, _, hf⟩ := le_iff_exists_mono.1 h
+ exact ⟨f, inferInstance, hf⟩) (fun f₁ f₂ ⟨_, hf₁⟩ ⟨_, hf₂⟩ ↦ by
+ exact Nonsingular.injective_map _ y.nonDegenerate (by rw [hf₁, hf₂]))
+
+/-- Given an inequality `x ≤ y` between nondegenerate simplices of a
+nonsingular simplicial set `X`, this is the corresponding morphism
+`⦋x.dim⦌ ⟶ ⦋y.dim⦌` in the simplex category. -/
+noncomputable def monoOfLE : ⦋x.dim⦌ ⟶ ⦋y.dim⦌ :=
+ (existsUnique_of_le h).exists.choose
+
+instance : Mono (monoOfLE h) :=
+ (existsUnique_of_le h).exists.choose_spec.1
+
+@[simp]
+lemma map_monoOfLE : X.map (monoOfLE h).op y.simplex = x.simplex :=
+ (existsUnique_of_le h).exists.choose_spec.2
+
+@[reassoc, simp]
+lemma stdSimplex_map_monoOfLE_yonedaEquiv_symm_simplex :
+ stdSimplex.map (monoOfLE h) ≫ yonedaEquiv.symm y.simplex =
+ yonedaEquiv.symm x.simplex := by
+ rw [yonedaEquiv_symm_naturality_left, map_monoOfLE]
+
+lemma monoOfLE_eq_iff (h : x ≤ y) (g : ⦋x.dim⦌ ⟶ ⦋y.dim⦌) [Mono g] :
+ monoOfLE h = g ↔ X.map g.op y.simplex = x.simplex :=
+ ⟨by rintro rfl; simp,
+ fun h' ↦ (existsUnique_of_le h).unique ⟨inferInstance, by simp⟩ ⟨inferInstance, h'⟩⟩
+
+variable (x) in
+@[simp]
+lemma monoOfLE_refl : monoOfLE (le_refl x) = 𝟙 _ := by
+ simp [monoOfLE_eq_iff]
+
+@[reassoc (attr := simp)]
+lemma monoOfLE_comp (h' : y ≤ z) :
+ monoOfLE h ≫ monoOfLE h' = monoOfLE (h.trans h') := by
+ symm
+ simp [monoOfLE_eq_iff]
+
+end N
+
end SSet
diff --git a/Mathlib/AlgebraicTopology/SimplicialSet/NonsingularColimit.lean b/Mathlib/AlgebraicTopology/SimplicialSet/NonsingularColimit.lean
new file mode 100644
index 00000000000000..15635797ca2175
--- /dev/null
+++ b/Mathlib/AlgebraicTopology/SimplicialSet/NonsingularColimit.lean
@@ -0,0 +1,74 @@
+/-
+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.SemiSimplexCategory
+public import Mathlib.AlgebraicTopology.SimplicialSet.Nonsingular
+public import Mathlib.AlgebraicTopology.SimplicialSet.NonDegenerateSimplicesColimit
+
+/-!
+# Nonsingular simplicial sets, as colimits of standard simplices
+
+In the file `Mathlib/AlgebraicTopology/SimplicialSet/NonDegenerateSimplicesColimit.lean`,
+it was shown that any simplicial set `X` is the colimit (indexed by the type `X.N`
+of nondegenerate simplices) of its monogenous subcomplexes.
+
+In this file, we assume that `X` is nonsingular, in which case its monogenous subcomplexes
+identify to standard simplices. This allows to show that `X` is the colimit
+of `Δ[x.dim]` for `x : X.N`.
+
+-/
+
+@[expose] public section
+
+universe u
+
+open CategoryTheory Simplicial Limits
+
+namespace SSet
+
+variable (X : SSet.{u}) [X.Nonsingular]
+
+namespace N
+
+set_option backward.isDefEq.respectTransparency false in
+/-- If `X` is a nonsingular simplicial set, this is the functor
+`X.N ⥤ SemiSimplexCategory` which sends a nondegenerate
+simplex `s : X : N` to `⦋s.dim⦌ₛ` -/
+@[simps obj map]
+noncomputable def toSemiSimplexCategory : X.N ⥤ SemiSimplexCategory where
+ obj s := ⦋s.dim⦌ₛ
+ map f := SemiSimplexCategory.homOfMono (N.monoOfLE (leOfHom f))
+ map_id _ := SemiSimplexCategory.toSimplexCategory.map_injective (by simp)
+ map_comp _ _ := SemiSimplexCategory.toSimplexCategory.map_injective (by simp)
+
+end N
+
+/-- The functor `X.N ⥤ SSet` which sends `x : X.N` to `Δ[x.dim]`. -/
+noncomputable abbrev functorN' : X.N ⥤ SSet.{u} :=
+ N.toSemiSimplexCategory X ⋙ SemiSimplexCategory.toSimplexCategory ⋙ SSet.stdSimplex
+
+set_option backward.defeqAttrib.useBackward true in
+/-- The isomorphism `X.functorN' ≅ X.functorN` for a nonsingular simplicial set `X`. -/
+noncomputable def functorN'Iso : X.functorN' ≅ X.functorN :=
+ NatIso.ofComponents (fun x ↦ Nonsingular.iso _ x.nonDegenerate) (fun _ ↦ by
+ simp [← cancel_mono (Subcomplex.ι _)])
+
+/-- If `X` is a nonsingular simplicial set, this is the cocone consisting
+of the (mono)morphisms `Δ[x.dim] ⟶ X` for all nondegenerate simplices `x : X.N`. -/
+@[simps]
+noncomputable def coconeN' : Cocone X.functorN' where
+ pt := X
+ ι.app s := yonedaEquiv.symm s.simplex
+ ι.naturality _ _ f := N.stdSimplex_map_monoOfLE_yonedaEquiv_symm_simplex (leOfHom f)
+
+/-- If `X` is a nonsingular simplicial set, `X` is the colimit of `Δ[x.dim]`
+for all nondegenerate simplices `x : X.N`. -/
+noncomputable def isColimitCoconeN' : IsColimit X.coconeN' :=
+ (IsColimit.equivOfNatIsoOfIso
+ X.functorN'Iso.symm _ _ (Cocone.ext (Iso.refl _))).1 X.isColimitCoconeN
+
+end SSet
diff --git a/Mathlib/AlgebraicTopology/SimplicialSet/StdSimplex.lean b/Mathlib/AlgebraicTopology/SimplicialSet/StdSimplex.lean
index b0448158cbc584..329cf1f5e2c2ad 100644
--- a/Mathlib/AlgebraicTopology/SimplicialSet/StdSimplex.lean
+++ b/Mathlib/AlgebraicTopology/SimplicialSet/StdSimplex.lean
@@ -50,6 +50,13 @@ namespace stdSimplex
open Finset Opposite SimplexCategory
+/-- The functor `stdSimplex : SimplexCategory ⥤ SSet` is fully faithful; -/
+abbrev fullyFaithful : stdSimplex.{u}.FullyFaithful :=
+ ULiftYoneda.fullyFaithful SimplexCategory
+
+instance : stdSimplex.{u}.Full := fullyFaithful.full
+instance : stdSimplex.{u}.Faithful := fullyFaithful.faithful
+
@[simp]
lemma map_id (n : SimplexCategory) :
(SSet.stdSimplex.map (SimplexCategory.Hom.mk OrderHom.id : n ⟶ n)) = 𝟙 _ :=
@@ -302,6 +309,17 @@ lemma yonedaEquiv_symm_app_id {X : SSet.{u}} {n : ℕ} (x : X _⦋n⦌) :
(yonedaEquiv.symm x).app _ (yonedaEquiv (𝟙 _)) = x := by
simp
+lemma yonedaEquiv_naturality {X : SSet} {m n : SimplexCategory}
+ (f : m ⟶ n) (g : stdSimplex.obj n ⟶ X) :
+ X.map f.op (yonedaEquiv g) = yonedaEquiv (stdSimplex.map f ≫ g) :=
+ uliftYonedaEquiv_naturality _ _
+
+@[reassoc]
+lemma yonedaEquiv_symm_naturality_left {X : SSet} {m n : SimplexCategory}
+ (f : m ⟶ n) (g : X.obj (Opposite.op n)) :
+ stdSimplex.map f ≫ yonedaEquiv.symm g = yonedaEquiv.symm (X.map f.op g) := by
+ rw [← yonedaEquiv.apply_eq_iff_eq_symm_apply, ← yonedaEquiv_naturality,
+ yonedaEquiv.apply_symm_apply]
namespace Subcomplex
@@ -784,4 +802,46 @@ noncomputable def stdSimplex : SimplexCategory ⥤ SSet.Augmented.{u} where
end Augmented
+namespace Subcomplex
+
+variable {X : SSet.{u}} {n : ℕ} (x : X _⦋n⦌)
+
+/-- Given `x : X _⦋n⦌`, this is the epimorphism from `Δ[n]`
+to the subcomplex of `X` generated by `x`. -/
+def toOfSimplex : Δ[n] ⟶ ofSimplex x :=
+ Subcomplex.lift (yonedaEquiv.symm x) (by simp [range_eq_ofSimplex])
+
+@[reassoc (attr := simp)]
+lemma toOfSimplex_ι :
+ toOfSimplex x ≫ (ofSimplex x).ι = yonedaEquiv.symm x := rfl
+
+@[simp]
+lemma yonedaEquiv_toOfSimplex :
+ yonedaEquiv (toOfSimplex x) = ⟨x, mem_ofSimplex_obj x⟩ :=
+ yonedaEquiv.symm.injective (by cat_disch)
+
+set_option backward.defeqAttrib.useBackward true in
+instance : Epi (toOfSimplex x) := by
+ rw [← range_eq_top_iff]
+ ext m ⟨_, u, rfl⟩
+ simp only [range_eq_ofSimplex, yonedaEquiv_toOfSimplex, Subfunctor.top_obj,
+ Set.top_eq_univ, Set.mem_univ, iff_true]
+ refine ⟨u, ?_⟩
+ dsimp
+ ext
+ rw [← yonedaEquiv.right_inv x]
+ aesop
+
+lemma isIso_toOfSimplex_iff :
+ IsIso (toOfSimplex x) ↔ Mono (yonedaEquiv.symm x) := by
+ constructor
+ · intro
+ rw [← toOfSimplex_ι]
+ infer_instance
+ · intro h
+ have := mono_of_mono_fac (toOfSimplex_ι x)
+ apply isIso_of_mono_of_epi
+
+end Subcomplex
+
end SSet
From 247b7ec2c202a571d3f222c29f41442bba96093e Mon Sep 17 00:00:00 2001
From: "Filippo A. E. Nuccio" <65080144+faenuccio@users.noreply.github.com>
Date: Tue, 23 Jun 2026 15:06:27 +0000
Subject: [PATCH 0291/1300] doc: fix the doc of two files about group actions
(#40138)
Co-authored-by: faenuccio
---
Mathlib/GroupTheory/GroupAction/Quotient.lean | 278 +++++++++---------
Mathlib/GroupTheory/Schreier.lean | 12 +-
2 files changed, 146 insertions(+), 144 deletions(-)
diff --git a/Mathlib/GroupTheory/GroupAction/Quotient.lean b/Mathlib/GroupTheory/GroupAction/Quotient.lean
index e5ac2d4b6cbbaa..5ccc2b03d56143 100644
--- a/Mathlib/GroupTheory/GroupAction/Quotient.lean
+++ b/Mathlib/GroupTheory/GroupAction/Quotient.lean
@@ -32,7 +32,7 @@ assert_not_exists Cardinal
universe u v w
-variable {α : Type u} {β : Type v} {γ : Type w}
+variable {G : Type u} {X : Type v}
open Function
@@ -40,75 +40,75 @@ open scoped commutatorElement
namespace MulAction
-variable [Group α]
+variable [Group G]
section QuotientAction
open Subgroup MulOpposite QuotientGroup
-variable (β) [Monoid β] [MulAction β α] (H : Subgroup α)
+variable (X) [Monoid X] [MulAction X G] (H : Subgroup G)
-/-- A typeclass for when a `MulAction β α` descends to the quotient `α ⧸ H`. -/
+/-- A typeclass for when a `MulAction X G` descends to the quotient `G ⧸ H`. -/
class QuotientAction : Prop where
/-- The action fulfils a normality condition on products that lie in `H`.
- This ensures that the action descends to an action on the quotient `α ⧸ H`. -/
- inv_mul_mem : ∀ (b : β) {a a' : α}, a⁻¹ * a' ∈ H → (b • a)⁻¹ * b • a' ∈ H
+ This ensures that the action descends to an action on the quotient `G ⧸ H`. -/
+ inv_mul_mem : ∀ (b : X) {a a' : G}, a⁻¹ * a' ∈ H → (b • a)⁻¹ * b • a' ∈ H
-/-- A typeclass for when an `AddAction β α` descends to the quotient `α ⧸ H`. -/
-class _root_.AddAction.QuotientAction {α : Type u} (β : Type v) [AddGroup α] [AddMonoid β]
- [AddAction β α] (H : AddSubgroup α) : Prop where
+/-- A typeclass for when an `AddAction X G` descends to the quotient `G ⧸ H`. -/
+class _root_.AddAction.QuotientAction {G : Type u} (X : Type v) [AddGroup G] [AddMonoid X]
+ [AddAction X G] (H : AddSubgroup G) : Prop where
/-- The action fulfils a normality condition on summands that lie in `H`.
- This ensures that the action descends to an action on the quotient `α ⧸ H`. -/
- inv_mul_mem : ∀ (b : β) {a a' : α}, -a + a' ∈ H → -(b +ᵥ a) + (b +ᵥ a') ∈ H
+ This ensures that the action descends to an action on the quotient `G ⧸ H`. -/
+ inv_mul_mem : ∀ (x : X) {g g' : G}, -g + g' ∈ H → -(x +ᵥ g) + (x +ᵥ g') ∈ H
attribute [to_additive] MulAction.QuotientAction
@[to_additive]
-instance left_quotientAction : QuotientAction α H :=
+instance left_quotientAction : QuotientAction G H :=
⟨fun _ _ _ _ => by rwa [smul_eq_mul, smul_eq_mul, mul_inv_rev, mul_assoc, inv_mul_cancel_left]⟩
@[to_additive]
-instance right_quotientAction : QuotientAction (normalizer H : Subgroup α).op H :=
+instance right_quotientAction : QuotientAction (normalizer H : Subgroup G).op H :=
⟨fun b c _ _ => by
rwa [smul_def, smul_def, smul_eq_mul_unop, smul_eq_mul_unop, mul_inv_rev, ← mul_assoc,
mem_normalizer_iff'.mp b.prop, mul_assoc, mul_inv_cancel_left]⟩
@[to_additive]
-instance right_quotientAction' [hH : H.Normal] : QuotientAction αᵐᵒᵖ H :=
+instance right_quotientAction' [hH : H.Normal] : QuotientAction Gᵐᵒᵖ H :=
⟨fun _ _ _ _ => by
rwa [smul_eq_mul_unop, smul_eq_mul_unop, mul_inv_rev, mul_assoc, hH.mem_comm_iff, mul_assoc,
mul_inv_cancel_right]⟩
@[to_additive]
-instance quotient [QuotientAction β H] : MulAction β (α ⧸ H) where
+instance quotient [QuotientAction X H] : MulAction X (G ⧸ H) where
smul b :=
Quotient.map' (b • ·) fun _ _ h =>
leftRel_apply.mpr <| QuotientAction.inv_mul_mem b <| leftRel_apply.mp h
- one_smul q := Quotient.inductionOn' q fun a => congr_arg Quotient.mk'' (one_smul β a)
+ one_smul q := Quotient.inductionOn' q fun a => congr_arg Quotient.mk'' (one_smul X a)
mul_smul b b' q := Quotient.inductionOn' q fun a => congr_arg Quotient.mk'' (mul_smul b b' a)
-variable {β}
+variable {X}
@[to_additive (attr := simp)]
-theorem Quotient.smul_mk [QuotientAction β H] (b : β) (a : α) :
- (b • QuotientGroup.mk a : α ⧸ H) = QuotientGroup.mk (b • a) :=
+theorem Quotient.smul_mk [QuotientAction X H] (b : X) (g : G) :
+ (b • QuotientGroup.mk g : G ⧸ H) = QuotientGroup.mk (b • g) :=
rfl
@[to_additive (attr := simp)]
-theorem Quotient.smul_coe [QuotientAction β H] (b : β) (a : α) :
- b • (a : α ⧸ H) = (↑(b • a) : α ⧸ H) :=
+theorem Quotient.smul_coe [QuotientAction X H] (b : X) (g : G) :
+ b • (g : G ⧸ H) = (↑(b • g) : G ⧸ H) :=
rfl
@[to_additive (attr := simp)]
-theorem Quotient.mk_smul_out [QuotientAction β H] (b : β) (q : α ⧸ H) :
+theorem Quotient.mk_smul_out [QuotientAction X H] (b : X) (q : G ⧸ H) :
QuotientGroup.mk (b • q.out) = b • q := by rw [← Quotient.smul_mk, QuotientGroup.out_eq']
@[to_additive]
-theorem Quotient.coe_smul_out [QuotientAction β H] (b : β) (q : α ⧸ H) : ↑(b • q.out) = b • q := by
+theorem Quotient.coe_smul_out [QuotientAction X H] (b : X) (q : G ⧸ H) : ↑(b • q.out) = b • q := by
simp
-theorem _root_.QuotientGroup.out_conj_pow_minimalPeriod_mem (a : α) (q : α ⧸ H) :
- q.out⁻¹ * a ^ Function.minimalPeriod (a • ·) q * q.out ∈ H := by
+theorem _root_.QuotientGroup.out_conj_pow_minimalPeriod_mem (g : G) (q : G ⧸ H) :
+ q.out⁻¹ * g ^ Function.minimalPeriod (g • ·) q * q.out ∈ H := by
rw [mul_assoc, ← QuotientGroup.eq, QuotientGroup.out_eq', ← smul_eq_mul, Quotient.mk_smul_out,
eq_comm, pow_smul_eq_iff_minimalPeriod_dvd]
@@ -117,51 +117,51 @@ end QuotientAction
open QuotientGroup
/-- The canonical map to the left cosets. -/
-def _root_.MulActionHom.toQuotient (H : Subgroup α) : α →[α] α ⧸ H where
+def _root_.MulActionHom.toQuotient (H : Subgroup G) : G →[G] G ⧸ H where
toFun := (↑); map_smul' := Quotient.smul_coe H
@[simp]
-theorem _root_.MulActionHom.toQuotient_apply (H : Subgroup α) (g : α) :
+theorem _root_.MulActionHom.toQuotient_apply (H : Subgroup G) (g : G) :
MulActionHom.toQuotient H g = g :=
rfl
@[to_additive (attr := simp)]
-theorem coe_quotient_smul {H : Subgroup α} [H.Normal] [SMul α β]
- [MulAction (α ⧸ H) β] [IsScalarTower α (α ⧸ H) β] (g : α) (x : β) :
- (g : α ⧸ H) • x = g • x := by
- rw [← smul_one_smul (α ⧸ H) g x, ← QuotientGroup.mk_one, Quotient.smul_coe,
+theorem coe_quotient_smul {H : Subgroup G} [H.Normal] [SMul G X]
+ [MulAction (G ⧸ H) X] [IsScalarTower G (G ⧸ H) X] (g : G) (x : X) :
+ (g : G ⧸ H) • x = g • x := by
+ rw [← smul_one_smul (G ⧸ H) g x, ← QuotientGroup.mk_one, Quotient.smul_coe,
smul_eq_mul, mul_one]
@[to_additive]
-instance mulLeftCosetsCompSubtypeVal (H I : Subgroup α) : MulAction I (α ⧸ H) :=
- MulAction.compHom (α ⧸ H) (Subgroup.subtype I)
+instance mulLeftCosetsCompSubtypeVal (H I : Subgroup G) : MulAction I (G ⧸ H) :=
+ MulAction.compHom (G ⧸ H) (Subgroup.subtype I)
-variable (α)
-variable [MulAction α β] (x : β)
+variable (G)
+variable [MulAction G X] (x : X)
/-- The canonical map from the quotient of the stabilizer to the set. -/
@[to_additive /-- The canonical map from the quotient of the stabilizer to the set. -/]
-def ofQuotientStabilizer (g : α ⧸ MulAction.stabilizer α x) : β :=
+def ofQuotientStabilizer (g : G ⧸ MulAction.stabilizer G x) : X :=
Quotient.liftOn' g (· • x) fun g1 g2 H =>
calc
g1 • x = g1 • (g1⁻¹ * g2) • x := congr_arg _ (leftRel_apply.mp H).symm
_ = g2 • x := by rw [smul_smul, mul_inv_cancel_left]
@[to_additive (attr := simp)]
-theorem ofQuotientStabilizer_mk (g : α) : ofQuotientStabilizer α x (QuotientGroup.mk g) = g • x :=
+theorem ofQuotientStabilizer_mk (g : G) : ofQuotientStabilizer G x (QuotientGroup.mk g) = g • x :=
rfl
@[to_additive]
-theorem ofQuotientStabilizer_mem_orbit (g) : ofQuotientStabilizer α x g ∈ orbit α x :=
+theorem ofQuotientStabilizer_mem_orbit (g) : ofQuotientStabilizer G x g ∈ orbit G x :=
Quotient.inductionOn' g fun g => ⟨g, rfl⟩
@[to_additive]
-theorem ofQuotientStabilizer_smul (g : α) (g' : α ⧸ MulAction.stabilizer α x) :
- ofQuotientStabilizer α x (g • g') = g • ofQuotientStabilizer α x g' :=
+theorem ofQuotientStabilizer_smul (g : G) (g' : G ⧸ MulAction.stabilizer G x) :
+ ofQuotientStabilizer G x (g • g') = g • ofQuotientStabilizer G x g' :=
Quotient.inductionOn' g' fun _ => mul_smul _ _ _
@[to_additive]
-theorem injective_ofQuotientStabilizer : Function.Injective (ofQuotientStabilizer α x) :=
+theorem injective_ofQuotientStabilizer : Function.Injective (ofQuotientStabilizer G x) :=
fun y₁ y₂ =>
Quotient.inductionOn₂' y₁ y₂ fun g₁ g₂ (H : g₁ • x = g₂ • x) =>
Quotient.sound' <| by
@@ -171,29 +171,29 @@ theorem injective_ofQuotientStabilizer : Function.Injective (ofQuotientStabilize
/-- **Orbit-stabilizer theorem**. -/
@[to_additive /-- Orbit-stabilizer theorem. -/]
-noncomputable def orbitEquivQuotientStabilizer (b : β) : orbit α b ≃ α ⧸ stabilizer α b :=
+noncomputable def orbitEquivQuotientStabilizer (b : X) : orbit G b ≃ G ⧸ stabilizer G b :=
Equiv.symm <|
- Equiv.ofBijective (fun g => ⟨ofQuotientStabilizer α b g, ofQuotientStabilizer_mem_orbit α b g⟩)
- ⟨fun x y hxy => injective_ofQuotientStabilizer α b (by convert! congr_arg Subtype.val hxy),
+ Equiv.ofBijective (fun g => ⟨ofQuotientStabilizer G b g, ofQuotientStabilizer_mem_orbit G b g⟩)
+ ⟨fun x y hxy => injective_ofQuotientStabilizer G b (by convert! congr_arg Subtype.val hxy),
fun ⟨_, ⟨g, hgb⟩⟩ => ⟨g, Subtype.ext hgb⟩⟩
/-- Orbit-stabilizer theorem. -/
@[to_additive AddAction.orbitProdStabilizerEquivAddGroup /-- Orbit-stabilizer theorem. -/]
-noncomputable def orbitProdStabilizerEquivGroup (b : β) : orbit α b × stabilizer α b ≃ α :=
- (Equiv.prodCongr (orbitEquivQuotientStabilizer α _) (Equiv.refl _)).trans
+noncomputable def orbitProdStabilizerEquivGroup (b : X) : orbit G b × stabilizer G b ≃ G :=
+ (Equiv.prodCongr (orbitEquivQuotientStabilizer G _) (Equiv.refl _)).trans
Subgroup.groupEquivQuotientProdSubgroup.symm
/-- Orbit-stabilizer theorem. -/
@[to_additive AddAction.card_orbit_mul_card_stabilizer_eq_card_addGroup
/-- Orbit-stabilizer theorem. -/]
-theorem card_orbit_mul_card_stabilizer_eq_card_group (b : β) [Fintype α] [Fintype <| orbit α b]
- [Fintype <| stabilizer α b] :
- Fintype.card (orbit α b) * Fintype.card (stabilizer α b) = Fintype.card α := by
- rw [← Fintype.card_prod, Fintype.card_congr (orbitProdStabilizerEquivGroup α b)]
+theorem card_orbit_mul_card_stabilizer_eq_card_group (b : X) [Fintype G] [Fintype <| orbit G b]
+ [Fintype <| stabilizer G b] :
+ Fintype.card (orbit G b) * Fintype.card (stabilizer G b) = Fintype.card G := by
+ rw [← Fintype.card_prod, Fintype.card_congr (orbitProdStabilizerEquivGroup G b)]
@[to_additive (attr := simp)]
-theorem orbitEquivQuotientStabilizer_symm_apply (b : β) (a : α) :
- ((orbitEquivQuotientStabilizer α b).symm a : β) = a • b :=
+theorem orbitEquivQuotientStabilizer_symm_apply (b : X) (g : G) :
+ ((orbitEquivQuotientStabilizer G b).symm g : X) = g • b :=
rfl
@[to_additive (attr := simp)]
@@ -202,75 +202,77 @@ theorem stabilizer_quotient {G} [Group G] (H : Subgroup G) :
ext
simp [QuotientGroup.eq]
-variable (β)
+variable (X)
-local notation "Ω" => Quotient <| orbitRel α β
+local notation "Ω" => Quotient <| orbitRel G X
-/-- **Class formula** : given `G` a group acting on `X` and `φ` a function mapping each orbit of `X`
-under this action (that is, each element of the quotient of `X` by the relation `orbitRel G X`) to
-an element in this orbit, this gives a (noncomputable) bijection between `X` and the disjoint union
-of `G/Stab(φ(ω))` over all orbits `ω`. In most cases you'll want `φ` to be `Quotient.out`, so we
-provide `MulAction.selfEquivSigmaOrbitsQuotientStabilizer'` as a special case. -/
+/-- **Class formula** : let `G` be a group acting on `X` and let `φ` be a function mapping each
+orbit of `X` under this action (that is, each element of the quotient of `G` by the relation
+`orbitRel G X`) to an element in this orbit. We provide a (noncomputable) bijection between `X`
+and the disjoint union of `G/Stab(φ(ω))` over all orbits `ω : Ω`. In most cases you'll want `φ`
+to be `Quotient.out`, so we provide `MulAction.selfEquivSigmaOrbitsQuotientStabilizer'` as a
+special case. -/
@[to_additive
- /-- **Class formula** : given `G` an additive group acting on `X` and `φ` a function
- mapping each orbit of `X` under this action (that is, each element of the quotient of `X` by
- the relation `orbit_rel G X`) to an element in this orbit, this gives a (noncomputable)
- bijection between `X` and the disjoint union of `G/Stab(φ(ω))` over all orbits `ω`. In most
- cases you'll want `φ` to be `Quotient.out`, so we provide
+ /-- **Class formula** : let `G` be an additive group acting on `X` and let `φ` be a function
+ mapping each orbit of `X` under this action (that is, each element of the quotient of `X` by
+ the relation `orbitRel G X`) to an element in this orbit. This definition is a (noncomputable)
+ bijection between `X` and the disjoint union of `G/Stab(φ(ω))` over all orbits `ω : Ω`. In
+ most cases you'll want `φ` to be `Quotient.out`, so we provide
`AddAction.selfEquivSigmaOrbitsQuotientStabilizer'` as a special case. -/]
-noncomputable def selfEquivSigmaOrbitsQuotientStabilizer' {φ : Ω → β}
- (hφ : LeftInverse Quotient.mk'' φ) : β ≃ Σ ω : Ω, α ⧸ stabilizer α (φ ω) :=
+noncomputable def selfEquivSigmaOrbitsQuotientStabilizer' {φ : Ω → X}
+ (hφ : LeftInverse Quotient.mk'' φ) : X ≃ Σ ω : Ω, G ⧸ stabilizer G (φ ω) :=
calc
- β ≃ Σ ω : Ω, orbitRel.Quotient.orbit ω := selfEquivSigmaOrbits' α β
- _ ≃ Σ ω : Ω, α ⧸ stabilizer α (φ ω) :=
+ X ≃ Σ ω : Ω, orbitRel.Quotient.orbit ω := selfEquivSigmaOrbits' G X
+ _ ≃ Σ ω : Ω, G ⧸ stabilizer G (φ ω) :=
Equiv.sigmaCongrRight fun ω =>
(Equiv.setCongr <| orbitRel.Quotient.orbit_eq_orbit_out _ hφ).trans <|
- orbitEquivQuotientStabilizer α (φ ω)
+ orbitEquivQuotientStabilizer G (φ ω)
/-- **Class formula**. This is a special case of
`MulAction.self_equiv_sigma_orbits_quotient_stabilizer'` with `φ = Quotient.out`. -/
@[to_additive
/-- **Class formula**. This is a special case of
`AddAction.self_equiv_sigma_orbits_quotient_stabilizer'` with `φ = Quotient.out`. -/]
-noncomputable def selfEquivSigmaOrbitsQuotientStabilizer : β ≃ Σ ω : Ω, α ⧸ stabilizer α ω.out :=
- selfEquivSigmaOrbitsQuotientStabilizer' α β Quotient.out_eq'
+noncomputable def selfEquivSigmaOrbitsQuotientStabilizer : X ≃ Σ ω : Ω, G ⧸ stabilizer G ω.out :=
+ selfEquivSigmaOrbitsQuotientStabilizer' G X Quotient.out_eq'
/-- **Burnside's lemma** : a (noncomputable) bijection between the disjoint union of all
-`{x ∈ X | g • x = x}` for `g ∈ G` and the product `G × X/G`, where `G` is a group acting on `X` and
-`X/G` denotes the quotient of `X` by the relation `orbitRel G X`. -/
+`{x ∈ X | g • x = x}` for `g ∈ G` and the product `G × Ω`, where `G` is a group acting on `X`
+and `Ω = X/G` denotes the quotient of `X` by the relation `orbitRel G X`. -/
@[to_additive AddAction.sigmaFixedByEquivOrbitsProdAddGroup
/-- **Burnside's lemma** : a (noncomputable) bijection between the disjoint union of all
- `{x ∈ X | g • x = x}` for `g ∈ G` and the product `G × X/G`, where `G` is an additive group
- acting on `X` and `X/G` denotes the quotient of `X` by the relation `orbitRel G X`. -/]
-noncomputable def sigmaFixedByEquivOrbitsProdGroup : (Σ a : α, fixedBy β a) ≃ Ω × α :=
+ `{x ∈ X | g • x = x}` for `g ∈ G` and the product `G × Ω`, where `G` is an additive group
+ acting on `X` and `Ω = X/G` denotes the quotient of `X` by the relation `orbitRel G X`. -/]
+noncomputable def sigmaFixedByEquivOrbitsProdGroup : (Σ g : G, fixedBy X g) ≃ Ω × G :=
calc
- (Σ a : α, fixedBy β a) ≃ { ab : α × β // ab.1 • ab.2 = ab.2 } :=
+ (Σ g : G, fixedBy X g) ≃ { ab : G × X // ab.1 • ab.2 = ab.2 } :=
(Equiv.subtypeProdEquivSigmaSubtype _).symm
- _ ≃ { ba : β × α // ba.2 • ba.1 = ba.1 } := (Equiv.prodComm α β).subtypeEquiv fun _ => Iff.rfl
- _ ≃ Σ b : β, stabilizer α b :=
- Equiv.subtypeProdEquivSigmaSubtype fun (b : β) a => a ∈ stabilizer α b
- _ ≃ Σ ωb : Σ ω : Ω, orbit α ω.out, stabilizer α (ωb.2 : β) :=
- (selfEquivSigmaOrbits α β).sigmaCongrLeft'
- _ ≃ Σ ω : Ω, Σ b : orbit α ω.out, stabilizer α (b : β) :=
- Equiv.sigmaAssoc fun (ω : Ω) (b : orbit α ω.out) => stabilizer α (b : β)
- _ ≃ Σ ω : Ω, Σ _ : orbit α ω.out, stabilizer α ω.out :=
+ _ ≃ { ba : X × G // ba.2 • ba.1 = ba.1 } := (Equiv.prodComm G X).subtypeEquiv fun _ => Iff.rfl
+ _ ≃ Σ b : X, stabilizer G b :=
+ Equiv.subtypeProdEquivSigmaSubtype fun (b : X) a => a ∈ stabilizer G b
+ _ ≃ Σ ωb : Σ ω : Ω, orbit G ω.out, stabilizer G (ωb.2 : X) :=
+ (selfEquivSigmaOrbits G X).sigmaCongrLeft'
+ _ ≃ Σ ω : Ω, Σ b : orbit G ω.out, stabilizer G (b : X) :=
+ Equiv.sigmaAssoc fun (ω : Ω) (b : orbit G ω.out) => stabilizer G (b : X)
+ _ ≃ Σ ω : Ω, Σ _ : orbit G ω.out, stabilizer G ω.out :=
Equiv.sigmaCongrRight fun _ =>
Equiv.sigmaCongrRight fun ⟨_, hb⟩ => (stabilizerEquivStabilizerOfOrbitRel hb).toEquiv
- _ ≃ Σ ω : Ω, orbit α ω.out × stabilizer α ω.out :=
+ _ ≃ Σ ω : Ω, orbit G ω.out × stabilizer G ω.out :=
Equiv.sigmaCongrRight fun _ => Equiv.sigmaEquivProd _ _
- _ ≃ Σ _ : Ω, α := Equiv.sigmaCongrRight fun ω => orbitProdStabilizerEquivGroup α ω.out
- _ ≃ Ω × α := Equiv.sigmaEquivProd Ω α
+ _ ≃ Σ _ : Ω, G := Equiv.sigmaCongrRight fun ω => orbitProdStabilizerEquivGroup G ω.out
+ _ ≃ Ω × G := Equiv.sigmaEquivProd Ω G
-/-- **Burnside's lemma** : given a finite group `G` acting on a set `X`, the average number of
-elements fixed by each `g ∈ G` is the number of orbits. -/
+/-- **Burnside's lemma** : given a finite group `G` acting on a type `X`, the sum the orders of the
+stabilisers coincides with the number of orbits multiplied by the order of `G`. -/
@[to_additive (attr := wikidata Q1330377)
AddAction.sum_card_fixedBy_eq_card_orbits_mul_card_addGroup
- /-- **Burnside's lemma** : given a finite additive group `G` acting on a set `X`,
- the average number of elements fixed by each `g ∈ G` is the number of orbits. -/]
-theorem sum_card_fixedBy_eq_card_orbits_mul_card_group [Fintype α] [∀ a : α, Fintype <| fixedBy β a]
- [Fintype Ω] : (∑ a : α, Fintype.card (fixedBy β a)) = Fintype.card Ω * Fintype.card α := by
+ /-- **Burnside's lemma** : given a finite additive group `G` acting on a type `X`,
+ the sum the orders of the stabilisers coincides with the number of orbits multiplied by the
+ order of `G`. -/]
+theorem sum_card_fixedBy_eq_card_orbits_mul_card_group [Fintype G] [∀ g : G, Fintype <| fixedBy X g]
+ [Fintype Ω] : (∑ g : G, Fintype.card (fixedBy X g)) = Fintype.card Ω * Fintype.card G := by
rw [← Fintype.card_prod, ← Fintype.card_sigma,
- Fintype.card_congr (sigmaFixedByEquivOrbitsProdGroup α β)]
+ Fintype.card_congr (sigmaFixedByEquivOrbitsProdGroup G X)]
@[to_additive]
instance isPretransitive_quotient (G) [Group G] (H : Subgroup G) : IsPretransitive G (G ⧸ H) where
@@ -279,16 +281,16 @@ instance isPretransitive_quotient (G) [Group G] (H : Subgroup G) : IsPretransiti
refine ⟨y * x⁻¹, QuotientGroup.eq.mpr ?_⟩
simp only [smul_eq_mul, H.one_mem, inv_mul_cancel, inv_mul_cancel_right]}
-variable {α}
+variable {G}
@[to_additive]
-instance finite_quotient_of_pretransitive_of_finite_quotient [IsPretransitive α β] {H : Subgroup α}
- [Finite (α ⧸ H)] : Finite <| orbitRel.Quotient H β := by
- rcases isEmpty_or_nonempty β with he | ⟨⟨b⟩⟩
+instance finite_quotient_of_pretransitive_of_finite_quotient [IsPretransitive G X] {H : Subgroup G}
+ [Finite (G ⧸ H)] : Finite <| orbitRel.Quotient H X := by
+ rcases isEmpty_or_nonempty X with he | ⟨⟨b⟩⟩
· exact Quotient.finite _
· have h' : Finite (Quotient (rightRel H)) :=
Finite.of_equiv _ (quotientRightRelEquivQuotientLeftRel _).symm
- let f : Quotient (rightRel H) → orbitRel.Quotient H β :=
+ let f : Quotient (rightRel H) → orbitRel.Quotient H X :=
fun a ↦ Quotient.liftOn' a (fun g ↦ ⟦g • b⟧) fun g₁ g₂ r ↦ by
replace r := Setoid.symm' _ r
rw [rightRel_eq] at r
@@ -297,14 +299,14 @@ instance finite_quotient_of_pretransitive_of_finite_quotient [IsPretransitive α
exact Finite.of_surjective f ((Quotient.surjective_liftOn' _).2
(Quotient.mk''_surjective.comp (MulAction.surjective_smul _ _)))
-variable {β} in
+variable {X} in
/-- A bijection between the quotient of the action of a subgroup `H` on an orbit, and a
corresponding quotient expressed in terms of `Setoid.comap Subtype.val`. -/
@[to_additive /-- A bijection between the quotient of the action of an additive subgroup `H` on an
orbit, and a corresponding quotient expressed in terms of `Setoid.comap Subtype.val`. -/]
-noncomputable def equivSubgroupOrbitsSetoidComap (H : Subgroup α) (ω : Ω) :
+noncomputable def equivSubgroupOrbitsSetoidComap (H : Subgroup G) (ω : Ω) :
orbitRel.Quotient H (orbitRel.Quotient.orbit ω) ≃
- Quotient ((orbitRel H β).comap (Subtype.val : Quotient.mk (orbitRel α β) ⁻¹' {ω} → β)) where
+ Quotient ((orbitRel H X).comap (Subtype.val : Quotient.mk (orbitRel G X) ⁻¹' {ω} → X)) where
toFun := fun q ↦ q.liftOn' (fun x ↦ ⟦⟨↑x, by
simp only [Set.mem_preimage, Set.mem_singleton_iff]
have hx := x.property
@@ -330,43 +332,43 @@ noncomputable def equivSubgroupOrbitsSetoidComap (H : Subgroup α) (ω : Ω) :
induction q using Quotient.inductionOn'
rfl
-/-- A bijection between the orbits under the action of a subgroup `H` on `β`, and the orbits
+/-- A bijection between the orbits under the action of a subgroup `H` on `X`, and the orbits
under the action of `H` on each orbit under the action of `G`. -/
@[to_additive /-- A bijection between the orbits under the action of an additive subgroup `H` on
-`β`, and the orbits under the action of `H` on each orbit under the action of `G`. -/]
-noncomputable def equivSubgroupOrbits (H : Subgroup α) :
- orbitRel.Quotient H β ≃ Σ ω : Ω, orbitRel.Quotient H (orbitRel.Quotient.orbit ω) :=
+`X`, and the orbits under the action of `H` on each orbit under the action of `G`. -/]
+noncomputable def equivSubgroupOrbits (H : Subgroup G) :
+ orbitRel.Quotient H X ≃ Σ ω : Ω, orbitRel.Quotient H (orbitRel.Quotient.orbit ω) :=
(Setoid.sigmaQuotientEquivOfLe (orbitRel_subgroup_le H)).symm.trans
(Equiv.sigmaCongrRight fun ω ↦ (equivSubgroupOrbitsSetoidComap H ω).symm)
-variable {β}
+variable {X}
@[to_additive]
-instance finite_quotient_of_finite_quotient_of_finite_quotient {H : Subgroup α}
- [Finite (orbitRel.Quotient α β)] [Finite (α ⧸ H)] :
- Finite <| orbitRel.Quotient H β := by
- rw [(equivSubgroupOrbits β H).finite_iff]
+instance finite_quotient_of_finite_quotient_of_finite_quotient {H : Subgroup G}
+ [Finite (orbitRel.Quotient G X)] [Finite (G ⧸ H)] :
+ Finite <| orbitRel.Quotient H X := by
+ rw [(equivSubgroupOrbits X H).finite_iff]
infer_instance
/-- Given a group acting freely and transitively, an equivalence between the orbits under the
-action of a subgroup and the quotient group. -/
+action of a subgroup and the quotient of the group by the subgroup. -/
@[to_additive /-- Given an additive group acting freely and transitively, an equivalence between the
-orbits under the action of an additive subgroup and the quotient group. -/]
-noncomputable def equivSubgroupOrbitsQuotientGroup [IsPretransitive α β]
- [IsCancelSMul α β] (H : Subgroup α) :
- orbitRel.Quotient H β ≃ α ⧸ H where
- toFun := fun q ↦ q.liftOn' (fun y ↦ (exists_smul_eq α y x).choose) (by
+orbits under the action of an additive subgroup and the quotient of the group by the subgroup. -/]
+noncomputable def equivSubgroupOrbitsQuotientGroup [IsPretransitive G X]
+ [IsCancelSMul G X] (H : Subgroup G) :
+ orbitRel.Quotient H X ≃ G ⧸ H where
+ toFun := fun q ↦ q.liftOn' (fun y ↦ (exists_smul_eq G y x).choose) (by
intro y₁ y₂ h
rw [orbitRel_apply] at h
rw [Quotient.eq'', leftRel_eq]
dsimp only
rcases h with ⟨g, rfl⟩
dsimp only
- suffices (exists_smul_eq α (g • y₂) x).choose = (exists_smul_eq α y₂ x).choose * g⁻¹ by
+ suffices (exists_smul_eq G (g • y₂) x).choose = (exists_smul_eq G y₂ x).choose * g⁻¹ by
simp [this]
refine IsCancelSMul.right_cancel _ _ (g • y₂) ?_
- rw [(exists_smul_eq α (g • y₂) x).choose_spec, Subgroup.smul_def, Subgroup.coe_inv,
- smul_smul, inv_mul_cancel_right, (exists_smul_eq α y₂ x).choose_spec])
+ rw [(exists_smul_eq G (g • y₂) x).choose_spec, Subgroup.smul_def, Subgroup.coe_inv,
+ smul_smul, inv_mul_cancel_right, (exists_smul_eq G y₂ x).choose_spec])
invFun := fun q ↦ q.liftOn' (fun g ↦ ⟦g⁻¹ • x⟧) (by
intro g₁ g₂ h
rw [leftRel_eq] at h
@@ -377,7 +379,7 @@ noncomputable def equivSubgroupOrbitsQuotientGroup [IsPretransitive α β]
simp only [Quotient.liftOn'_mk'']
rw [← @Quotient.mk''_eq_mk, Quotient.eq'', orbitRel_apply]
convert! mem_orbit_self _
- rw [inv_smul_eq_iff, (exists_smul_eq α _ x).choose_spec]
+ rw [inv_smul_eq_iff, (exists_smul_eq G _ x).choose_spec]
right_inv := fun g ↦ by
cases g using Quotient.inductionOn' with | _ g
simp only [Quotient.liftOn'_mk'', QuotientGroup.mk]
@@ -386,30 +388,30 @@ noncomputable def equivSubgroupOrbitsQuotientGroup [IsPretransitive α β]
convert! one_mem H
rw [inv_mul_eq_one, eq_comm, ← inv_mul_eq_one, ← Subgroup.mem_bot,
← IsCancelSMul.stabilizer_eq_bot (g⁻¹ • x), mem_stabilizer_iff, mul_smul,
- (exists_smul_eq α (g⁻¹ • x) x).choose_spec]
+ (exists_smul_eq G (g⁻¹ • x) x).choose_spec]
-/-- If `α` acts on `β` with trivial stabilizers, `β` is equivalent
-to the product of the quotient of `β` by `α` and `α`.
+/-- If `G` acts on `X` with trivial stabilizers, `X` is equivalent
+to the product of the quotient of `X` by `G` and `G`.
See `MulAction.selfEquivOrbitsQuotientProd` with `φ = Quotient.out`. -/
-@[to_additive selfEquivOrbitsQuotientProd' /-- If `α` acts freely on `β`, `β` is equivalent
-to the product of the quotient of `β` by `α` and `α`.
+@[to_additive selfEquivOrbitsQuotientProd' /-- If `G` acts freely on `X`, `X` is equivalent
+to the product of the quotient of `X` by `G` and `G`.
See `AddAction.selfEquivOrbitsQuotientProd` with `φ = Quotient.out`. -/]
noncomputable def selfEquivOrbitsQuotientProd'
- {φ : Quotient (MulAction.orbitRel α β) → β} (hφ : Function.LeftInverse Quotient.mk'' φ)
- (h : ∀ b : β, MulAction.stabilizer α b = ⊥) :
- β ≃ Quotient (MulAction.orbitRel α β) × α :=
- (MulAction.selfEquivSigmaOrbitsQuotientStabilizer' α β hφ).trans <|
+ {φ : Quotient (MulAction.orbitRel G X) → X} (hφ : Function.LeftInverse Quotient.mk'' φ)
+ (h : ∀ b : X, MulAction.stabilizer G b = ⊥) :
+ X ≃ Quotient (MulAction.orbitRel G X) × G :=
+ (MulAction.selfEquivSigmaOrbitsQuotientStabilizer' G X hφ).trans <|
(Equiv.sigmaCongrRight <| fun _ ↦
- (Subgroup.quotientEquivOfEq (h _)).trans (QuotientGroup.quotientEquivSelf α)).trans <|
+ (Subgroup.quotientEquivOfEq (h _)).trans (QuotientGroup.quotientEquivSelf G)).trans <|
Equiv.sigmaEquivProd _ _
-/-- If `α` acts freely on `β`, `β` is equivalent to the product of the quotient of `β` by `α` and
-`α`. -/
+/-- If `G` acts freely on `X`, `X` is equivalent to the product of the quotient of `X` by `G` and
+`G`. -/
@[to_additive selfEquivOrbitsQuotientProd
- /-- If `α` acts freely on `β`, `β` is equivalent to the product of the quotient of `β` by
-`α` and `α`. -/]
-noncomputable def selfEquivOrbitsQuotientProd (h : ∀ b : β, MulAction.stabilizer α b = ⊥) :
- β ≃ Quotient (MulAction.orbitRel α β) × α :=
+ /-- If `G` acts freely on `X`, `X` is equivalent to the product of the quotient of `X` by
+`G` and `G`. -/]
+noncomputable def selfEquivOrbitsQuotientProd (h : ∀ b : X, MulAction.stabilizer G b = ⊥) :
+ X ≃ Quotient (MulAction.orbitRel G X) × G :=
MulAction.selfEquivOrbitsQuotientProd' Quotient.out_eq' h
end MulAction
diff --git a/Mathlib/GroupTheory/Schreier.lean b/Mathlib/GroupTheory/Schreier.lean
index f368c740032423..1133fbe3ba7e88 100644
--- a/Mathlib/GroupTheory/Schreier.lean
+++ b/Mathlib/GroupTheory/Schreier.lean
@@ -87,8 +87,8 @@ theorem closure_mul_image_mul_eq_top
rw [mul_assoc, ← inv_inv s, ← mul_inv_rev, inv_inv]
exact hR.toRightFun_mul_inv_mem (r * s⁻¹)
-/-- **Schreier's Lemma**: If `R : Set G` is a `rightTransversal` of `H : Subgroup G`
- with `1 ∈ R`, and if `G` is generated by `S : Set G`, then `H` is generated by the `Set`
+/-- **Schreier's Lemma**: If `R : Set G` and `H : Subgroup G` are complements with `1 ∈ R`, and if
+`G` is generated by `S : Set G`, then `H` is generated by the `Set`
`(R * S).image (fun g ↦ g * (hR.toRightFun g)⁻¹)`. -/
@[wikidata Q3229345]
theorem closure_mul_image_eq (hR : IsComplement H R) (hR1 : (1 : G) ∈ R)
@@ -108,8 +108,8 @@ theorem closure_mul_image_eq (hR : IsComplement H R) (hR1 : (1 : G) ∈ R)
· rw [Subtype.coe_mk, inv_one, mul_one]
exact (H.mul_mem_cancel_left (hU hg)).mp hh
-/-- **Schreier's Lemma**: If `R : Set G` is a `rightTransversal` of `H : Subgroup G`
- with `1 ∈ R`, and if `G` is generated by `S : Set G`, then `H` is generated by the `Set`
+/-- **Schreier's Lemma**: If `R : Set G` and `H : Subgroup G` are complements with `1 ∈ R`, and if
+`G` is generated by `S : Set G`, then `H` is generated by the `Set`
`(R * S).image (fun g ↦ g * (hR.toRightFun g)⁻¹)`. -/
theorem closure_mul_image_eq_top (hR : IsComplement H R) (hR1 : (1 : G) ∈ R)
(hS : closure S = ⊤) : closure ((R * S).image fun g =>
@@ -117,8 +117,8 @@ theorem closure_mul_image_eq_top (hR : IsComplement H R) (hR1 : (1 : G) ∈ R)
rw [eq_top_iff, ← map_subtype_le_map_subtype, MonoidHom.map_closure, Set.image_image]
exact (map_subtype_le ⊤).trans (ge_of_eq (closure_mul_image_eq hR hR1 hS))
-/-- **Schreier's Lemma**: If `R : Finset G` is a `rightTransversal` of `H : Subgroup G`
- with `1 ∈ R`, and if `G` is generated by `S : Finset G`, then `H` is generated by the `Finset`
+/-- **Schreier's Lemma**: If `R : Finset G` and `H : Subgroup G` are complements with `1 ∈ R`, and
+if `G` is generated by `S : Finset G`, then `H` is generated by the `Finset`
`(R * S).image (fun g ↦ g * (hR.toRightFun g)⁻¹)`. -/
theorem closure_mul_image_eq_top' [DecidableEq G] {R S : Finset G}
(hR : IsComplement (H : Set G) R) (hR1 : (1 : G) ∈ R)
From 3de9307b457ac5ce7fac8a3da986f3e75b4cf09c Mon Sep 17 00:00:00 2001
From: Michael Rothgang <10105016+grunweg@users.noreply.github.com>
Date: Tue, 23 Jun 2026 15:06:30 +0000
Subject: [PATCH 0292/1300] perf: lower priority for IsSimpleGroup.toNontrivial
instances (#40327)
These instances have very weak keys ([Nontrivial, *]), hence are always applied. Let's try lowering their priority.
---
Mathlib/GroupTheory/Subgroup/Simple.lean | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Mathlib/GroupTheory/Subgroup/Simple.lean b/Mathlib/GroupTheory/Subgroup/Simple.lean
index 71df18745f6fcd..9222b5b591229a 100644
--- a/Mathlib/GroupTheory/Subgroup/Simple.lean
+++ b/Mathlib/GroupTheory/Subgroup/Simple.lean
@@ -39,12 +39,16 @@ class IsSimpleGroup : Prop extends Nontrivial G where
/-- Any normal subgroup is either `⊥` or `⊤` -/
eq_bot_or_eq_top_of_normal : ∀ H : Subgroup G, H.Normal → H = ⊥ ∨ H = ⊤
+attribute [instance 100] IsSimpleGroup.toNontrivial
+
/-- An `AddGroup` is simple when it has exactly two normal `AddSubgroup`s. -/
@[mk_iff]
class IsSimpleAddGroup : Prop extends Nontrivial A where
/-- Any normal additive subgroup is either `⊥` or `⊤` -/
eq_bot_or_eq_top_of_normal : ∀ H : AddSubgroup A, H.Normal → H = ⊥ ∨ H = ⊤
+attribute [instance 100] IsSimpleAddGroup.toNontrivial
+
attribute [to_additive existing] IsSimpleGroup isSimpleGroup_iff
variable {G} {A}
From f5681a0ca2ba7ff28bcfaeb4d7c2ebd9ac036459 Mon Sep 17 00:00:00 2001
From: Jireh Loreaux
Date: Tue, 23 Jun 2026 15:06:32 +0000
Subject: [PATCH 0293/1300] feat: `Monoid` and `Group` instances for `OrderHom`
and `OrderIso` (#40515)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
These will be used in an upcoming PR to turn `fun x ↦ (x * · * star x)` into a term of type `R →* (R →o R)` when `R` is a `StarOrderedRing`, and likewise also `Rˣ →* (R ≃o R)`.
Co-authored-by: Eric Wieser
---
Mathlib/Algebra/Order/Group/End.lean | 30 ++++++++++++++++++++++++++++
Mathlib/Data/FunLike/IsApply.lean | 15 ++++++++++++++
2 files changed, 45 insertions(+)
diff --git a/Mathlib/Algebra/Order/Group/End.lean b/Mathlib/Algebra/Order/Group/End.lean
index 92ab1280f939a9..fb0326451bc75f 100644
--- a/Mathlib/Algebra/Order/Group/End.lean
+++ b/Mathlib/Algebra/Order/Group/End.lean
@@ -6,10 +6,21 @@ Authors: Mario Carneiro
module
public import Mathlib.Algebra.Group.Defs
+public import Mathlib.Order.Hom.Basic
public import Mathlib.Order.RelIso.Basic
+public import Mathlib.Data.FunLike.IsApply
/-!
# Relation isomorphisms form a group
+
+This file contains `Monoid` instances for `RelHom` and `OrderHom`, where multiplication is
+given by composition. Likewise there is a `Group` instance for `RelIso`. Because `OrderIso`
+is an abbreviation for `RelIso`, there is no need for an additional instance.
+
+## TODO
+
++ Rename the `mul_def`/`one_def` lemmas to `mul_eq_comp`/`one_eq_id`.
++ Use the `IsMulApplyEqComp` and `IsOneApplyEqSelf` classes for `RelHom` and `RelIso`.
-/
@[expose] public section
@@ -87,3 +98,22 @@ theorem apply_inv_self (e : r ≃r r) (x) : e (e⁻¹ x) = x :=
e.apply_symm_apply x
end RelIso
+
+namespace OrderHom
+
+variable [Preorder α]
+
+instance : Mul (α →o α) where mul f g := f.comp g
+instance : One (α →o α) where one := .id
+instance : IsMulApplyEqComp (α →o α) α where mul_apply_eq_comp _ _ _ := rfl
+instance : IsOneApplyEqSelf (α →o α) α where one_apply_eq_self _ := rfl
+
+lemma mul_eq_comp (f g : α →o α) : (f * g : α →o α) = f.comp g := rfl
+lemma one_eq_id : (1 : α →o α) = .id := rfl
+
+instance : Monoid (α →o α) where
+ mul_assoc f g h := by simp [DFunLike.ext_iff]
+ one_mul f := by simp [DFunLike.ext_iff]
+ mul_one f := by simp [DFunLike.ext_iff]
+
+end OrderHom
diff --git a/Mathlib/Data/FunLike/IsApply.lean b/Mathlib/Data/FunLike/IsApply.lean
index 170abd0432cac1..e8848acbcec5a7 100644
--- a/Mathlib/Data/FunLike/IsApply.lean
+++ b/Mathlib/Data/FunLike/IsApply.lean
@@ -6,7 +6,9 @@ Authors: Moritz Doll
module
public import Mathlib.Algebra.Notation.Pi.Defs
+public import Mathlib.Algebra.Group.Defs
public import Mathlib.Data.FunLike.Basic
+public import Mathlib.Logic.Function.Iterate
/-! # Typeclasses for `FunLike` and algebraic operations
In this file we provide typeclasses for the compatibility of algebraic structures and `FunLike`
@@ -110,6 +112,14 @@ class IsMulApplyEqComp (F : Type*) (α : outParam Type*) [FunLike F α α] [Mul
@[simp, grind =]
alias mul_apply_eq_comp := IsMulApplyEqComp.mul_apply_eq_comp
+@[simp, grind =]
+lemma pow_apply_eq_iterate {F α : Type*} [FunLike F α α] [Monoid F] [IsOneApplyEqSelf F α]
+ [IsMulApplyEqComp F α] (f : F) (n : ℕ) (x : α) :
+ (f ^ n) x = f^[n] x := by
+ induction n with
+ | zero => simp
+ | succ n ih => simp [pow_succ', ih, ← Function.iterate_succ_apply']
+
end Add
section Sub
@@ -245,6 +255,11 @@ theorem coe_one_eq_id_iff [One F'] [IsOneApplyEqSelf F' α] (f : F') : (f : α
theorem coe_mul_eq_comp [Mul F'] [IsMulApplyEqComp F' α] (f g : F') : ↑(f * g) = f ∘ g := by
ext; simp
+@[norm_cast]
+lemma coe_pow_eq_iterate [Monoid F'] [IsMulApplyEqComp F' α] [IsOneApplyEqSelf F' α]
+ (f : F') (n : ℕ) : ⇑(f ^ n) = f^[n] :=
+ funext <| pow_apply_eq_iterate f n
+
@[norm_cast]
theorem coe_natCast [NatCast F'] [One F'] [SMul Nat α] [SMul Nat F'] [IsSMulApply Nat F' α α]
[IsNatCastApply F' α] [IsOneApplyEqSelf F' α] (n : Nat) :
From a163fd2f6fb7408d4102dbb5faaca2b635ba1c0d Mon Sep 17 00:00:00 2001
From: Jireh Loreaux
Date: Tue, 23 Jun 2026 15:06:35 +0000
Subject: [PATCH 0294/1300] feat: introduce `SelfAdjointDecompose` class
(#40530)
---
Mathlib/Algebra/Order/Star/Basic.lean | 36 +++++++++++++++++++
Mathlib/Algebra/Order/Star/Real.lean | 7 ++++
.../CStarAlgebra/PositiveLinearMap.lean | 27 --------------
.../PosPart/Basic.lean | 3 ++
Mathlib/LinearAlgebra/Complex/Module.lean | 23 ++++++++++++
5 files changed, 69 insertions(+), 27 deletions(-)
diff --git a/Mathlib/Algebra/Order/Star/Basic.lean b/Mathlib/Algebra/Order/Star/Basic.lean
index 9aba3e1e74e5d3..5ba1bd85cc6b43 100644
--- a/Mathlib/Algebra/Order/Star/Basic.lean
+++ b/Mathlib/Algebra/Order/Star/Basic.lean
@@ -81,6 +81,24 @@ class StarOrderedRing (R : Type*) [NonUnitalSemiring R] [PartialOrder R] [StarRi
le_iff :
∀ x y : R, x ≤ y ↔ ∃ p, p ∈ AddSubmonoid.closure (Set.range fun s => star s * s) ∧ y = x + p
+/-- A class to encode that self-adjoint elements may be expressed as the
+difference of nonnegative elements. This is satisfied by any type with a
+`NonUnitalContinuousFunctionalCalculus ℝ A IsSelfAdjoint` instance.
+However, it can also be satisfied by continuous linear functionals equipped
+with the intrinsic star operation.
+
+This type class can be used to guarantee `PositiveLinearMap` is a `StarHomClass`. -/
+class SelfAdjointDecompose (R : Type*) [AddGroup R] [Star R]
+ [PartialOrder R] where
+ /-- Every self-adjoint element is the difference of nonnegative elements. -/
+ exists_nonneg_sub_nonneg {a : R} (ha : IsSelfAdjoint a) :
+ ∃ (b c : R), 0 ≤ b ∧ 0 ≤ c ∧ a = b - c
+
+lemma IsSelfAdjoint.exists_nonneg_sub_nonneg {R : Type*} [AddGroup R] [Star R]
+ [PartialOrder R] [SelfAdjointDecompose R] {a : R} (ha : IsSelfAdjoint a) :
+ ∃ (b c : R), 0 ≤ b ∧ 0 ≤ c ∧ a = b - c :=
+ SelfAdjointDecompose.exists_nonneg_sub_nonneg ha
+
namespace StarOrderedRing
section NonUnitalSemiring
variable [NonUnitalSemiring R] [PartialOrder R] [StarRing R]
@@ -452,6 +470,24 @@ instance (priority := 100) StarRingEquivClass.instOrderIsoClass [EquivLike F R S
rw [← f_inv_f x, ← f_inv_f y]
exact NonUnitalStarRingHom.map_le_map_of_map_star f_inv h
+/-- While `IsSelfAdjoint.map` assumes the map is star-preserving, this lemma instead assumes the
+map is an order-preserving additive map from a space where self-adjoint elements can be expressed as
+differences of nonnegative elemens, and whose codomain is a star-ordered ring. When such maps are
+linear over `ℂ`, they are also star-preserving, and this lemma is used to establish that one by
+splitting into real and imaginary parts. -/
+@[aesop safe apply (rule_sets := [CStarAlgebra])]
+lemma IsSelfAdjoint.map' {F E R : Type*} [AddCommGroup E] [PartialOrder E] [StarAddMonoid E]
+ [NonUnitalRing R] [PartialOrder R] [StarRing R] [StarOrderedRing R]
+ [SelfAdjointDecompose E] [FunLike F E R] [OrderHomClass F E R] [AddMonoidHomClass F E R]
+ {a : E} (ha : IsSelfAdjoint a) (f : F) :
+ IsSelfAdjoint (f a) := by
+ obtain ⟨b, c, hb, hc, rfl⟩ := ha.exists_nonneg_sub_nonneg
+ have h₁ := OrderHomClass.mono f hb
+ have h₂ := OrderHomClass.mono f hc
+ cfc_tac
+
+@[deprecated (since := "2026-06-12")] alias map_isSelfAdjoint := IsSelfAdjoint.map'
+
end OrderClass
instance Nat.instStarOrderedRing : StarOrderedRing ℕ where
diff --git a/Mathlib/Algebra/Order/Star/Real.lean b/Mathlib/Algebra/Order/Star/Real.lean
index 4462717d45506f..736f21b2290e79 100644
--- a/Mathlib/Algebra/Order/Star/Real.lean
+++ b/Mathlib/Algebra/Order/Star/Real.lean
@@ -34,3 +34,10 @@ instance NNReal.instStarOrderedRing : StarOrderedRing ℝ≥0 := by
simp only [star_trivial, mul_self_sqrt]
· rintro ⟨p, -, rfl⟩
exact le_self_add
+
+-- for lack of a better place with the necessary imports, we place this here
+-- this exists only to satisfy the trivial instances of this class
+instance {R : Type*} [AddGroup R] [Lattice R] [AddLeftMono R] [Star R] :
+ SelfAdjointDecompose R where
+ exists_nonneg_sub_nonneg {a} _ :=
+ ⟨a⁺, a⁻, posPart_nonneg a, negPart_nonneg a, by simp⟩
diff --git a/Mathlib/Analysis/CStarAlgebra/PositiveLinearMap.lean b/Mathlib/Analysis/CStarAlgebra/PositiveLinearMap.lean
index a5d0adb3aab838..22ba7dd887b9cf 100644
--- a/Mathlib/Analysis/CStarAlgebra/PositiveLinearMap.lean
+++ b/Mathlib/Analysis/CStarAlgebra/PositiveLinearMap.lean
@@ -31,23 +31,6 @@ open scoped NNReal
variable {A₁ A₂ B₁ B₂ : Type*}
-section CFC
-
-variable [NonUnitalRing A₁] [Module ℂ A₁] [SMulCommClass ℝ A₁ A₁] [IsScalarTower ℝ A₁ A₁]
- [StarRing A₁] [TopologicalSpace A₁] [NonUnitalContinuousFunctionalCalculus ℝ A₁ IsSelfAdjoint]
- [PartialOrder A₁] [StarOrderedRing A₁]
-
-variable [NonUnitalRing A₂] [Module ℂ A₂] [StarRing A₂] [PartialOrder A₂] [StarOrderedRing A₂]
-
-@[aesop safe apply (rule_sets := [CStarAlgebra])]
-lemma map_isSelfAdjoint (f : A₁ →ₚ[ℂ] A₂) (a : A₁) (ha : IsSelfAdjoint a) :
- IsSelfAdjoint (f a) := by
- rw [← CFC.posPart_sub_negPart a ha]
- cfc_tac
-
-end CFC
-
-
section CStarAlgebra
namespace PositiveLinearMap
@@ -134,16 +117,6 @@ instance {F : Type*} [FunLike F A₁ A₂] [LinearMapClass F ℂ A₁ A₂] [Ord
exact ⟨C, h⟩
exact (LinearMap.mkContinuousOfExistsBound (f : A₁ →ₗ[ℂ] A₂) hbound).continuous
-instance {F : Type*} [FunLike F A₁ A₂] [LinearMapClass F ℂ A₁ A₂] [OrderHomClass F A₁ A₂] :
- StarHomClass F A₁ A₂ where
- map_star f a := by
- obtain ⟨y, hy_nonneg, hy_norm, hy⟩ := CStarAlgebra.exists_sum_four_nonneg a
- have hy' : ∀ x : Fin 4, star (y x) = y x := fun x => by
- rw [IsSelfAdjoint.star_eq (hy_nonneg x).isSelfAdjoint]
- have hy'' : ∀ x : Fin 4, star (f (y x)) = f (y x) := fun x => by
- rw [IsSelfAdjoint.star_eq (map_nonneg f (hy_nonneg x)).isSelfAdjoint]
- simp [hy, hy', hy'']
-
end PositiveLinearMap
end CStarAlgebra
diff --git a/Mathlib/Analysis/SpecialFunctions/ContinuousFunctionalCalculus/PosPart/Basic.lean b/Mathlib/Analysis/SpecialFunctions/ContinuousFunctionalCalculus/PosPart/Basic.lean
index f4a81b97557852..2af2e998881d35 100644
--- a/Mathlib/Analysis/SpecialFunctions/ContinuousFunctionalCalculus/PosPart/Basic.lean
+++ b/Mathlib/Analysis/SpecialFunctions/ContinuousFunctionalCalculus/PosPart/Basic.lean
@@ -148,6 +148,9 @@ lemma negPart_nonneg (a : A) :
0 ≤ a⁻ :=
cfcₙ_nonneg (fun x _ ↦ by positivity)
+instance : SelfAdjointDecompose A where
+ exists_nonneg_sub_nonneg {a} ha := ⟨a⁺, a⁻, by cfc_tac, by cfc_tac, (posPart_sub_negPart a).symm⟩
+
lemma posPart_eq_of_eq_sub_negPart {a b : A} (hab : a = b - a⁻) (hb : 0 ≤ b := by cfc_tac) :
a⁺ = b := by
have ha := hab.symm ▸ hb.isSelfAdjoint.sub (negPart_nonneg a).isSelfAdjoint
diff --git a/Mathlib/LinearAlgebra/Complex/Module.lean b/Mathlib/LinearAlgebra/Complex/Module.lean
index 6b9d1cdd32c577..26fb146affe3e9 100644
--- a/Mathlib/LinearAlgebra/Complex/Module.lean
+++ b/Mathlib/LinearAlgebra/Complex/Module.lean
@@ -491,6 +491,19 @@ lemma ComplexStarModule.ext_iff {x y : A} : x = y ↔ ℜ x = ℜ y ∧ ℑ x =
mp := by grind
mpr h := ext h.1 h.2
+section StarHomClass
+
+variable {B F : Type*} [AddCommGroup B] [Module ℂ B] [StarAddMonoid B] [StarModule ℂ B]
+ [FunLike F A B] [StarHomClass F A B] [LinearMapClass F ℂ A B]
+
+lemma map_realPart (f : F) (x : A) : f (ℜ x) = ℜ (f x) := by
+ simp [realPart_apply_coe, ← Complex.coe_smul, map_star]
+
+lemma map_imaginaryPart (f : F) (x : A) : f (ℑ x) = ℑ (f x) := by
+ simp [imaginaryPart_apply_coe, ← Complex.coe_smul, map_star]
+
+end StarHomClass
+
@[simp]
theorem ker_imaginaryPart : imaginaryPart.ker = selfAdjoint.submodule ℝ A := by
ext x
@@ -638,4 +651,14 @@ lemma mem_unitary_iff_isStarNormal_and_realPart_sq_add_imaginaryPart_sq_eq_one [
exact ⟨this, by simp [sq, ← star_mul_self_eq_realPart_sq_add_imaginaryPart_sq x, h]⟩
· simp [← hx.star_comm_self.eq, star_mul_self_eq_realPart_sq_add_imaginaryPart_sq, ← sq, h]
+instance {F E A : Type*} [AddCommGroup E] [PartialOrder E]
+ [StarAddMonoid E] [SelfAdjointDecompose E] [Module ℂ E] [StarModule ℂ E]
+ [NonUnitalRing A] [PartialOrder A] [StarRing A]
+ [StarOrderedRing A] [Module ℂ A] [StarModule ℂ A]
+ [FunLike F E A] [OrderHomClass F E A] [LinearMapClass F ℂ E A] :
+ StarHomClass F E A where
+ map_star φ x := by
+ rw [← realPart_add_I_smul_imaginaryPart x]
+ simp [(ℜ x).2.map' φ, IsSelfAdjoint.star_eq, (ℑ x).2.map' φ]
+
end RealImaginaryPart
From 8b654812405b70d267f42ee5d967ab350721c1f0 Mon Sep 17 00:00:00 2001
From: Jack McCarthy <37917934+Deicyde@users.noreply.github.com>
Date: Tue, 23 Jun 2026 15:46:24 +0000
Subject: [PATCH 0295/1300] doc: add wikidata attributes (#40861)
This PR adds a batch of 25 `@[wikidata]` attributes.
Claude helped generate the list of crossrefs (by scanning Wikidata + Mathlib). Comments are generated by [crossref-report](https://github.com/jcommelin/mathlib-crossref-report) and Wikilean.
See https://wikilean.jackmccarthy.org/review?pr=40861 for reviewer UI.
Co-authored-by: wikilean-bot
---
Mathlib/Algebra/LinearRecurrence.lean | 2 ++
Mathlib/Analysis/InnerProductSpace/PiL2.lean | 2 ++
Mathlib/LinearAlgebra/Basis/Defs.lean | 2 ++
Mathlib/LinearAlgebra/CliffordAlgebra/Basic.lean | 2 ++
Mathlib/LinearAlgebra/Dual/Defs.lean | 2 ++
Mathlib/LinearAlgebra/RootSystem/Defs.lean | 2 ++
.../MeasureTheory/Function/ConditionalExpectation/Basic.lean | 2 ++
.../Integral/IntervalIntegral/IntegrationByParts.lean | 2 ++
.../Integral/IntervalIntegral/TrapezoidalRule.lean | 2 ++
Mathlib/NumberTheory/LSeries/RiemannZeta.lean | 1 +
Mathlib/Order/Filter/Extr.lean | 2 ++
Mathlib/RingTheory/LocalRing/ResidueField/Defs.lean | 2 ++
Mathlib/Topology/Algebra/Module/LocallyConvex.lean | 2 ++
Mathlib/Topology/UniformSpace/Cauchy.lean | 2 ++
14 files changed, 27 insertions(+)
diff --git a/Mathlib/Algebra/LinearRecurrence.lean b/Mathlib/Algebra/LinearRecurrence.lean
index 6634b8b7951ff4..cbbc654009610a 100644
--- a/Mathlib/Algebra/LinearRecurrence.lean
+++ b/Mathlib/Algebra/LinearRecurrence.lean
@@ -8,6 +8,7 @@ module
public import Mathlib.Algebra.Polynomial.Degree.Operations
public import Mathlib.Algebra.Polynomial.Eval.Defs
public import Mathlib.LinearAlgebra.Dimension.Constructions
+public import Mathlib.Tactic.CrossRefAttribute
/-!
# Linear recurrence
@@ -48,6 +49,7 @@ open Polynomial
/-- A "linear recurrence relation" over a commutative semiring is given by its
order `n` and `n` coefficients. -/
+@[wikidata Q364089]
structure LinearRecurrence (R : Type*) [CommSemiring R] where
/-- Order of the linear recurrence -/
order : ℕ
diff --git a/Mathlib/Analysis/InnerProductSpace/PiL2.lean b/Mathlib/Analysis/InnerProductSpace/PiL2.lean
index 9836ea7f5a2ede..f915ca4763ad8d 100644
--- a/Mathlib/Analysis/InnerProductSpace/PiL2.lean
+++ b/Mathlib/Analysis/InnerProductSpace/PiL2.lean
@@ -10,6 +10,7 @@ public import Mathlib.Analysis.Normed.Lp.PiLp
public import Mathlib.Analysis.Normed.Lp.Matrix
public import Mathlib.LinearAlgebra.FiniteDimensional.Lemmas
public import Mathlib.LinearAlgebra.UnitaryGroup
+public import Mathlib.Tactic.CrossRefAttribute
public import Mathlib.Util.Superscript
/-!
@@ -108,6 +109,7 @@ space use `EuclideanSpace 𝕜 (Fin n)`.
For the case when `n = Fin _`, there is `!₂[x, y, ...]` notation for building elements of this type,
analogous to `![x, y, ...]` notation. -/
+@[wikidata Q17295]
abbrev EuclideanSpace (𝕜 : Type*) (n : Type*) : Type _ :=
PiLp 2 fun _ : n => 𝕜
diff --git a/Mathlib/LinearAlgebra/Basis/Defs.lean b/Mathlib/LinearAlgebra/Basis/Defs.lean
index c66920d1a2c6eb..c4ff6d939ed635 100644
--- a/Mathlib/LinearAlgebra/Basis/Defs.lean
+++ b/Mathlib/LinearAlgebra/Basis/Defs.lean
@@ -6,6 +6,7 @@ Authors: Johannes Hölzl, Mario Carneiro, Alexander Bentkamp
module
public import Mathlib.LinearAlgebra.Finsupp.LinearCombination
+public import Mathlib.Tactic.CrossRefAttribute
/-!
# Bases
@@ -85,6 +86,7 @@ To turn a linear independent family of vectors spanning `M` into a basis, use `B
They are internally represented as linear equivs `M ≃ₗ[R] (ι →₀ R)`,
available as `Basis.repr`.
-/
+@[wikidata Q189569]
structure Basis where
/-- `Basis.ofRepr` constructs a basis given an assignment of coordinates to each vector. -/
ofRepr ::
diff --git a/Mathlib/LinearAlgebra/CliffordAlgebra/Basic.lean b/Mathlib/LinearAlgebra/CliffordAlgebra/Basic.lean
index d6eb7a6bedd007..33ca6fe5da724c 100644
--- a/Mathlib/LinearAlgebra/CliffordAlgebra/Basic.lean
+++ b/Mathlib/LinearAlgebra/CliffordAlgebra/Basic.lean
@@ -9,6 +9,7 @@ public import Mathlib.RingTheory.Congruence.Hom
public import Mathlib.LinearAlgebra.TensorAlgebra.Basic
public import Mathlib.LinearAlgebra.QuadraticForm.Isometry
public import Mathlib.LinearAlgebra.QuadraticForm.IsometryEquiv
+public import Mathlib.Tactic.CrossRefAttribute
/-!
# Clifford Algebras
@@ -69,6 +70,7 @@ end CliffordAlgebra
/-- The Clifford algebra of an `R`-module `M` equipped with a `QuadraticForm` `Q`.
-/
+@[wikidata Q674689]
def CliffordAlgebra := CliffordAlgebra.ringCon Q |>.Quotient
deriving Inhabited, Ring, Algebra R
diff --git a/Mathlib/LinearAlgebra/Dual/Defs.lean b/Mathlib/LinearAlgebra/Dual/Defs.lean
index 62a06d2c073886..0323991b305053 100644
--- a/Mathlib/LinearAlgebra/Dual/Defs.lean
+++ b/Mathlib/LinearAlgebra/Dual/Defs.lean
@@ -7,6 +7,7 @@ module
public import Mathlib.LinearAlgebra.BilinearMap
public import Mathlib.LinearAlgebra.Span.Defs
+public import Mathlib.Tactic.CrossRefAttribute
/-!
# Dual vector spaces
@@ -57,6 +58,7 @@ variable (R A M : Type*)
variable [CommSemiring R] [AddCommMonoid M] [Module R M]
/-- The left dual space of an R-module M is the R-module of linear maps `M → R`. -/
+@[wikidata Q752487]
abbrev Dual (R M : Type*) [Semiring R] [AddCommMonoid M] [Module R M] :=
M →ₗ[R] R
diff --git a/Mathlib/LinearAlgebra/RootSystem/Defs.lean b/Mathlib/LinearAlgebra/RootSystem/Defs.lean
index 1b5b341eac3111..3dd44ce2f78b71 100644
--- a/Mathlib/LinearAlgebra/RootSystem/Defs.lean
+++ b/Mathlib/LinearAlgebra/RootSystem/Defs.lean
@@ -7,6 +7,7 @@ module
public import Mathlib.LinearAlgebra.PerfectPairing.Basic
public import Mathlib.LinearAlgebra.Reflection
+public import Mathlib.Tactic.CrossRefAttribute
/-!
# Root data and root systems
@@ -113,6 +114,7 @@ variable {ι R M N}
variable (P : RootPairing ι R M N) (i j : ι)
/-- A root system is a root pairing for which the roots and coroots span their ambient modules. -/
+@[wikidata Q534131]
class IsRootSystem : Prop where
span_root_eq_top : span R (range P.root) = ⊤
span_coroot_eq_top : span R (range P.coroot) = ⊤
diff --git a/Mathlib/MeasureTheory/Function/ConditionalExpectation/Basic.lean b/Mathlib/MeasureTheory/Function/ConditionalExpectation/Basic.lean
index fda5b38bd6fb45..6cfccad643f8bb 100644
--- a/Mathlib/MeasureTheory/Function/ConditionalExpectation/Basic.lean
+++ b/Mathlib/MeasureTheory/Function/ConditionalExpectation/Basic.lean
@@ -6,6 +6,7 @@ Authors: Rémy Degenne
module
public import Mathlib.MeasureTheory.Function.ConditionalExpectation.CondexpL1
+public import Mathlib.Tactic.CrossRefAttribute
import Mathlib.MeasureTheory.Function.LpSpace.InfiniteSum
@@ -97,6 +98,7 @@ It is defined as 0 if any one of the following conditions is true:
- `m` is not a sub-σ-algebra of `m₀`,
- `μ` is not σ-finite with respect to `m`,
- `f` is not integrable. -/
+@[wikidata Q772232]
noncomputable irreducible_def condExp (μ : Measure[m₀] α) (f : α → E) : α → E :=
if hm : m ≤ m₀ then
if h : SigmaFinite (μ.trim hm) ∧ Integrable f μ then
diff --git a/Mathlib/MeasureTheory/Integral/IntervalIntegral/IntegrationByParts.lean b/Mathlib/MeasureTheory/Integral/IntervalIntegral/IntegrationByParts.lean
index 7e7ca233026342..358f01e9d06fb1 100644
--- a/Mathlib/MeasureTheory/Integral/IntervalIntegral/IntegrationByParts.lean
+++ b/Mathlib/MeasureTheory/Integral/IntervalIntegral/IntegrationByParts.lean
@@ -7,6 +7,7 @@ module
public import Mathlib.MeasureTheory.Function.JacobianOneDim
public import Mathlib.MeasureTheory.Integral.IntervalIntegral.FundThmCalculus
+public import Mathlib.Tactic.CrossRefAttribute
/-!
# Integration by parts and by substitution
@@ -526,6 +527,7 @@ theorem integral_comp_mul_deriv' {f f' g : ℝ → ℝ} (h : ∀ x ∈ uIcc a b,
and `g` is continuous, then we can substitute `u = f x` to get
`∫ x in a..b, (g ∘ f) x * f' x = ∫ u in f a..f b, g u`.
-/
+@[wikidata Q1071270]
theorem integral_comp_mul_deriv {f f' g : ℝ → ℝ} (h : ∀ x ∈ uIcc a b, HasDerivAt f (f' x) x)
(h' : ContinuousOn f' (uIcc a b)) (hg : Continuous g) :
(∫ x in a..b, (g ∘ f) x * f' x) = ∫ x in f a..f b, g x :=
diff --git a/Mathlib/MeasureTheory/Integral/IntervalIntegral/TrapezoidalRule.lean b/Mathlib/MeasureTheory/Integral/IntervalIntegral/TrapezoidalRule.lean
index 243d55e8c4436f..0fe73938b60340 100644
--- a/Mathlib/MeasureTheory/Integral/IntervalIntegral/TrapezoidalRule.lean
+++ b/Mathlib/MeasureTheory/Integral/IntervalIntegral/TrapezoidalRule.lean
@@ -6,6 +6,7 @@ Authors: P. Michael Kielstra
module
public import Mathlib.Analysis.SpecialFunctions.Integrals.Basic
+public import Mathlib.Tactic.CrossRefAttribute
public import Mathlib.Tactic.Field
/-!
@@ -29,6 +30,7 @@ open MeasureTheory intervalIntegral Interval Finset HasDerivWithinAt Set
/-- Integration of `f` from `a` to `b` using the trapezoidal rule with `N+1` total evaluations of
`f`. (Note the off-by-one problem here: `N` counts the number of trapezoids, not the number of
evaluations.) -/
+@[wikidata Q833293]
noncomputable def trapezoidal_integral (f : ℝ → ℝ) (N : ℕ) (a b : ℝ) : ℝ :=
((b - a) / N) * ((f a + f b) / 2 + ∑ k ∈ range (N - 1), f (a + (k + 1) * (b - a) / N))
diff --git a/Mathlib/NumberTheory/LSeries/RiemannZeta.lean b/Mathlib/NumberTheory/LSeries/RiemannZeta.lean
index fdff2c94adcfd8..3c92195fce20df 100644
--- a/Mathlib/NumberTheory/LSeries/RiemannZeta.lean
+++ b/Mathlib/NumberTheory/LSeries/RiemannZeta.lean
@@ -117,6 +117,7 @@ lemma completedRiemannZeta_residue_one :
-/
/-- The Riemann zeta function `ζ(s)`. -/
+@[wikidata Q187235]
def riemannZeta := hurwitzZetaEven 0
lemma HurwitzZeta.hurwitzZetaEven_zero : hurwitzZetaEven 0 = riemannZeta := rfl
diff --git a/Mathlib/Order/Filter/Extr.lean b/Mathlib/Order/Filter/Extr.lean
index 0c646f48feabca..f8337827d8f967 100644
--- a/Mathlib/Order/Filter/Extr.lean
+++ b/Mathlib/Order/Filter/Extr.lean
@@ -9,6 +9,7 @@ public import Mathlib.Order.Filter.Tendsto
public import Mathlib.Order.ConditionallyCompleteLattice.Indexed
public import Mathlib.Algebra.Order.Group.Defs
public import Mathlib.Data.Finset.Lattice.Fold
+public import Mathlib.Tactic.CrossRefAttribute
/-!
# Minimum and maximum w.r.t. a filter and on a set
@@ -114,6 +115,7 @@ def IsMaxOn :=
IsMaxFilter f (𝓟 s) a
/-- `IsExtrOn f s a` means `IsMinOn f s a` or `IsMaxOn f s a` -/
+@[wikidata Q845060]
def IsExtrOn : Prop :=
IsExtrFilter f (𝓟 s) a
diff --git a/Mathlib/RingTheory/LocalRing/ResidueField/Defs.lean b/Mathlib/RingTheory/LocalRing/ResidueField/Defs.lean
index 5841aa7aa67bef..dd141c6c82a710 100644
--- a/Mathlib/RingTheory/LocalRing/ResidueField/Defs.lean
+++ b/Mathlib/RingTheory/LocalRing/ResidueField/Defs.lean
@@ -7,6 +7,7 @@ module
public import Mathlib.RingTheory.Ideal.Quotient.Basic
public import Mathlib.RingTheory.LocalRing.MaximalIdeal.Basic
+public import Mathlib.Tactic.CrossRefAttribute
/-!
@@ -25,6 +26,7 @@ namespace IsLocalRing
variable (R : Type*) [CommRing R] [IsLocalRing R]
/-- The residue field of a local ring is the quotient of the ring by its maximal ideal. -/
+@[wikidata Q7315530]
def ResidueField :=
R ⧸ maximalIdeal R
deriving CommRing, Inhabited
diff --git a/Mathlib/Topology/Algebra/Module/LocallyConvex.lean b/Mathlib/Topology/Algebra/Module/LocallyConvex.lean
index c252b2838872f4..c73f0cfd0adb75 100644
--- a/Mathlib/Topology/Algebra/Module/LocallyConvex.lean
+++ b/Mathlib/Topology/Algebra/Module/LocallyConvex.lean
@@ -6,6 +6,7 @@ Authors: Anatole Dedecker
module
public import Mathlib.Analysis.Convex.Topology
+public import Mathlib.Tactic.CrossRefAttribute
public import Mathlib.Topology.Connected.LocallyPathConnected
public import Mathlib.Analysis.Convex.PathConnected
@@ -46,6 +47,7 @@ section Semimodule
/-- A `LocallyConvexSpace` is a topological semimodule over an ordered semiring in which convex
neighborhoods of a point form a neighborhood basis at that point. -/
+@[wikidata Q1572357]
class LocallyConvexSpace (𝕜 E : Type*) [Semiring 𝕜] [PartialOrder 𝕜]
[AddCommMonoid E] [Module 𝕜 E] [TopologicalSpace E] : Prop where
convex_basis : ∀ x : E, (𝓝 x).HasBasis (fun s : Set E => s ∈ 𝓝 x ∧ Convex 𝕜 s) id
diff --git a/Mathlib/Topology/UniformSpace/Cauchy.lean b/Mathlib/Topology/UniformSpace/Cauchy.lean
index 343f98439c2ac3..74fd21c99e4b5b 100644
--- a/Mathlib/Topology/UniformSpace/Cauchy.lean
+++ b/Mathlib/Topology/UniformSpace/Cauchy.lean
@@ -5,6 +5,7 @@ Authors: Johannes Hölzl, Mario Carneiro
-/
module
+public import Mathlib.Tactic.CrossRefAttribute
public import Mathlib.Topology.Algebra.Constructions
public import Mathlib.Topology.Bases
public import Mathlib.Algebra.Order.Group.Nat
@@ -365,6 +366,7 @@ theorem isComplete_iUnion_separated {ι : Sort*} {s : ι → Set α} (hs : ∀ i
/-- A complete space is defined here using uniformities. A uniform space
is complete if every Cauchy filter converges. -/
+@[wikidata Q848569]
class CompleteSpace (α : Type u) [UniformSpace α] : Prop where
/-- In a complete uniform space, every Cauchy filter converges. -/
complete : ∀ {f : Filter α}, Cauchy f → ∃ x, f ≤ 𝓝 x
From 778bd8fa0d3511efda61642ac6e6907c3bf481cc Mon Sep 17 00:00:00 2001
From: Oliver Nash <7734364+ocfnash@users.noreply.github.com>
Date: Tue, 23 Jun 2026 16:50:10 +0000
Subject: [PATCH 0296/1300] =?UTF-8?q?feat:=20`=CF=80=E2=82=81(E=E2=A7=B8G)?=
=?UTF-8?q?=20=E2=89=83*=20Multiplicative=20G`=20for=20`E`=20simply=20conn?=
=?UTF-8?q?ected=20(#40947)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This is the additive version of #33108
---
Mathlib/GroupTheory/GroupAction/Basic.lean | 8 +++
Mathlib/Topology/Covering/Quotient.lean | 20 ++++++
Mathlib/Topology/Homotopy/Lifting.lean | 80 ++++++++++++++++++++++
3 files changed, 108 insertions(+)
diff --git a/Mathlib/GroupTheory/GroupAction/Basic.lean b/Mathlib/GroupTheory/GroupAction/Basic.lean
index 4c8a3f07e6cf7b..73dfdc011f2e33 100644
--- a/Mathlib/GroupTheory/GroupAction/Basic.lean
+++ b/Mathlib/GroupTheory/GroupAction/Basic.lean
@@ -359,3 +359,11 @@ lemma Module.stabilizer_units_eq_bot_of_ne_zero {x : M} (hx : x ≠ 0) :
rw [← sub_eq_zero, ← smul_eq_zero_iff_left hx, Units.val_one, sub_smul, hg, one_smul, sub_self]
end
+
+@[simp] lemma Multiplicative.mulAction_orbit {α β : Type*} [VAdd α β] (b : β) :
+ MulAction.orbit (Multiplicative α) b = AddAction.orbit α b :=
+ rfl
+
+@[simp] lemma Additive.mulAction_orbit {α β : Type*} [SMul α β] (b : β) :
+ AddAction.orbit (Additive α) b = MulAction.orbit α b :=
+ rfl
diff --git a/Mathlib/Topology/Covering/Quotient.lean b/Mathlib/Topology/Covering/Quotient.lean
index 931aaa815baca6..476030c955bc6e 100644
--- a/Mathlib/Topology/Covering/Quotient.lean
+++ b/Mathlib/Topology/Covering/Quotient.lean
@@ -41,6 +41,26 @@ structure IsQuotientCoveringMap : Prop extends IsQuotientMap f, ContinuousConstS
attribute [to_additive] isQuotientCoveringMap_iff
+lemma IsAddQuotientCoveringMap.toMultiplicative (G) [AddGroup G] [AddAction G E]
+ (hf : IsAddQuotientCoveringMap f G) :
+ IsQuotientCoveringMap f (Multiplicative G) where
+ __ := hf.toIsQuotientMap
+ continuous_const_smul g := by simpa using hf.continuous_const_vadd (Multiplicative.ofAdd.symm g)
+ apply_eq_iff_mem_orbit {e₁ e₂} := by simp [hf.apply_eq_iff_mem_orbit]
+ disjoint e := by
+ obtain ⟨U, hU, hU'⟩ := hf.disjoint e
+ exact ⟨U, hU, fun g ↦ by simpa using hU' (Multiplicative.ofAdd.symm g)⟩
+
+lemma IsQuotientCoveringMap.toAdditive (G) [Group G] [MulAction G E]
+ (hf : IsQuotientCoveringMap f G) :
+ IsAddQuotientCoveringMap f (Additive G) where
+ __ := hf.toIsQuotientMap
+ continuous_const_vadd g := by simpa using hf.continuous_const_smul (Additive.ofMul.symm g)
+ apply_eq_iff_mem_orbit {e₁ e₂} := by simp [hf.apply_eq_iff_mem_orbit]
+ disjoint e := by
+ obtain ⟨U, hU, hU'⟩ := hf.disjoint e
+ exact ⟨U, hU, fun g ↦ by simpa using hU' (Additive.ofMul.symm g)⟩
+
namespace IsQuotientCoveringMap
@[to_additive] theorem subgroup_congr (S S' : Subgroup G) (eq : S = S') :
diff --git a/Mathlib/Topology/Homotopy/Lifting.lean b/Mathlib/Topology/Homotopy/Lifting.lean
index 7c5fb225dd713b..d6bb44b3bfee66 100644
--- a/Mathlib/Topology/Homotopy/Lifting.lean
+++ b/Mathlib/Topology/Homotopy/Lifting.lean
@@ -681,3 +681,83 @@ def fundamentalGroupEquiv [SimplyConnectedSpace E] :
hp.fundamentalGroupToMulOpposite_surjective e⟩
end IsQuotientCoveringMap
+
+namespace IsAddQuotientCoveringMap
+
+variable {G : Type*} [AddGroup G] [AddAction G E] (hp : IsAddQuotientCoveringMap p G) {g : G}
+
+theorem monodromy_toPermFiber {x y : X} {γ : Path.Homotopic.Quotient x y} {e : p ⁻¹' {x}} :
+ letI monodromy := hp.isCoveringMap.monodromy
+ monodromy γ (hp.toMultiplicative.toPermFiber x g e) =
+ hp.toMultiplicative.toPermFiber y g (monodromy γ e) :=
+ hp.toMultiplicative.monodromy_toPermFiber
+
+theorem commute_monodromyPerm_toPermFiber {x : X} {γ : FundamentalGroup X x} :
+ Commute
+ (hp.isCoveringMap.monodromyPerm x γ)
+ (hp.toMultiplicative.toPermFiber x g) :=
+ hp.toMultiplicative.commute_monodromyPerm_toPermFiber
+
+theorem monodromy_ext_iff {x y : X} {γ γ' : Path.Homotopic.Quotient x y} (e : p ⁻¹' {x}) :
+ letI monodromy := hp.isCoveringMap.monodromy
+ monodromy γ e = monodromy γ' e ↔ monodromy γ = monodromy γ' :=
+ hp.toMultiplicative.monodromy_ext_iff e
+
+alias ⟨monodromy_ext, _⟩ := monodromy_ext_iff
+
+variable {x : X} (e : p ⁻¹' {x}) {γ : FundamentalGroup X x}
+
+theorem monodromy_eq_id_iff :
+ hp.isCoveringMap.monodromy γ = id ↔ hp.isCoveringMap.monodromy γ e = e :=
+ hp.toMultiplicative.monodromy_eq_id_iff e
+
+theorem ker_monodromyPerm :
+ (hp.isCoveringMap.monodromyPerm x).ker =
+ (FundamentalGroup.mapOfEq ⟨p, hp.continuous⟩ e.2).range :=
+ hp.toMultiplicative.ker_monodromyPerm e
+
+theorem monodromyPerm_injective [SimplyConnectedSpace E] :
+ Injective (hp.isCoveringMap.monodromyPerm x) :=
+ hp.toMultiplicative.monodromyPerm_injective
+
+/-- Choosing an arbitrary basepoint `e ∈ f ⁻¹' {x}` induces a bijection `f ⁻¹' {x} ≃ G`, and the
+`G`-action on `f ⁻¹' {x}` corresponds to left multiplication. The monodromy action commutes
+with the `G`-action, so each monodromy must corresponds must correspond to a right multiplication.
+-/
+def fundamentalGroupToMulOpposite : FundamentalGroup X x →* (Multiplicative G)ᵐᵒᵖ :=
+ hp.toMultiplicative.fundamentalGroupToMulOpposite e
+
+variable {e} in
+theorem fundamentalGroupToMulOpposite_apply_eq_Iff {g : (Multiplicative G)ᵐᵒᵖ} :
+ hp.fundamentalGroupToMulOpposite e γ = g ↔ g.unop • e.1 = hp.isCoveringMap.monodromy γ e :=
+ hp.toMultiplicative.fundamentalGroupToMulOpposite_apply_eq_Iff
+
+variable {e} in
+theorem unop_fundamentalGroupToMulOpposite_smul :
+ (hp.fundamentalGroupToMulOpposite e γ).unop • e.1 = hp.isCoveringMap.monodromy γ e :=
+ hp.toMultiplicative.unop_fundamentalGroupToMulOpposite_smul
+
+variable {e} in
+theorem fundamentalGroupToMulOpposite_eq_one_iff :
+ hp.fundamentalGroupToMulOpposite e γ = 1 ↔ hp.isCoveringMap.monodromy γ e = e :=
+ hp.toMultiplicative.fundamentalGroupToMulOpposite_eq_one_iff
+
+theorem ker_fundamentalGroupToMulOpposite :
+ (hp.fundamentalGroupToMulOpposite e).ker = (hp.isCoveringMap.monodromyPerm x).ker :=
+ hp.toMultiplicative.ker_fundamentalGroupToMulOpposite e
+
+theorem fundamentalGroupToMulOpposite_surjective [PathConnectedSpace E] :
+ Surjective (hp.fundamentalGroupToMulOpposite e) :=
+ hp.toMultiplicative.fundamentalGroupToMulOpposite_surjective e
+
+lemma fundamentalGroupToMulOpposite_injective [SimplyConnectedSpace E] :
+ Injective (hp.fundamentalGroupToMulOpposite e) :=
+ hp.toMultiplicative.fundamentalGroupToMulOpposite_injective e
+
+/-- The fundamental group of the base of simply-connected covering map is contravariantly
+equivalent to the group of the covering map. -/
+def fundamentalGroupEquiv [SimplyConnectedSpace E] :
+ FundamentalGroup X x ≃* (Multiplicative G)ᵐᵒᵖ :=
+ hp.toMultiplicative.fundamentalGroupEquiv e
+
+end IsAddQuotientCoveringMap
From a0c885bf3a591b29b29120f070fa7c1e4121c951 Mon Sep 17 00:00:00 2001
From: Jovan Gerbscheid <56355248+JovanGerb@users.noreply.github.com>
Date: Tue, 23 Jun 2026 17:27:13 +0000
Subject: [PATCH 0297/1300] chore(Data/List/InsertNth): delete stub file
(#40962)
As the file itself explains, it can be removed now.
The module was emptied in #17985.
---
Mathlib.lean | 1 -
Mathlib/Data/List/InsertNth.lean | 18 ------------------
2 files changed, 19 deletions(-)
delete mode 100644 Mathlib/Data/List/InsertNth.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 001a649d202c66..4fc076a92d63d5 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -4053,7 +4053,6 @@ public import Mathlib.Data.List.Indexes
public import Mathlib.Data.List.Induction
public import Mathlib.Data.List.Infix
public import Mathlib.Data.List.InsertIdx
-public import Mathlib.Data.List.InsertNth
public import Mathlib.Data.List.Intervals
public import Mathlib.Data.List.Iterate
public import Mathlib.Data.List.Lattice
diff --git a/Mathlib/Data/List/InsertNth.lean b/Mathlib/Data/List/InsertNth.lean
deleted file mode 100644
index 56dc8fbabb529d..00000000000000
--- a/Mathlib/Data/List/InsertNth.lean
+++ /dev/null
@@ -1,18 +0,0 @@
-/-
-Copyright (c) 2024 Lean FRO. All rights reserved.
-Released under Apache 2.0 license as described in the file LICENSE.
-Authors: Kim Morrison
--/
-module
-
-public import Mathlib.Tactic.Common
-public import Mathlib.Util.CompileInductive
-
-/-!
-This is a stub file for importing `Mathlib/Data/List/InsertNth.lean`,
-which has been renamed to `Mathlib/Data/List/InsertIdx.lean`.
-
-This file can be removed once the deprecation for `List.insertNth` is removed.
--/
-
-public section
From 3a6310fe915f51a4701645394d8714192e984ca2 Mon Sep 17 00:00:00 2001
From: Marcelo Lynch
Date: Tue, 23 Jun 2026 18:34:10 +0000
Subject: [PATCH 0298/1300] fix(cache): skip fork-PR diagnostics on
nightly-testing (#40958)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
On `nightly-testing` / `nightly-testing-green`, `cache get` prints a spurious "no cache found for HEAD … on fork …" note and `cache query` reports "no cached CI build found for fork …", even though the cache reads fine from the `nightly-testing` container.
These are fork-PR diagnostics about the per-commit `forks` namespace. They gated on "is `forks` in the read chain", which became true for the nightly-testing repo once it gained a `forks` fallback (#40770) so they now misfire on the canonical repos. Gate on a new `isCanonicalRepo` predicate instead; `cache query` on a canonical repo now says it only applies to fork PRs.
Follow-up to #40770 / #40035.
---
Cache/Infra.lean | 5 +++++
Cache/Query.lean | 12 +++++++++++-
Cache/Warning.lean | 6 +++---
3 files changed, 19 insertions(+), 4 deletions(-)
diff --git a/Cache/Infra.lean b/Cache/Infra.lean
index 99f6fc271f52e0..6379923853ba72 100644
--- a/Cache/Infra.lean
+++ b/Cache/Infra.lean
@@ -24,6 +24,11 @@ def MATHLIBREPO := "leanprover-community/mathlib4"
/-- The full name of the Mathlib nightly-testing GitHub repository. -/
def NIGHTLY_TESTING_REPO := "leanprover-community/mathlib4-nightly-testing"
+/-- Whether `repo` is a first-party Mathlib repo rather than a fork. Forks cache
+into the per-commit `forks` namespace; the canonical repos do not. -/
+def isCanonicalRepo (repo : String) : Bool :=
+ repo == MATHLIBREPO || repo == NIGHTLY_TESTING_REPO
+
/--
Canonical form of a GitHub `owner/repo` name for use as a cache blob path
segment.
diff --git a/Cache/Query.lean b/Cache/Query.lean
index 8dd967501722be..2721183acdf9ea 100644
--- a/Cache/Query.lean
+++ b/Cache/Query.lean
@@ -161,9 +161,15 @@ def resolveQueryRepo (repoExplicit? : Option String) : IO String := do
Boolean probe for a single commit: prints `cached` or `not cached` and exits
with status 0 / 1 respectively. Intended for scripting.
-Probes the `forks` container's per-SHA marker, the only SHA-scoped container.
+Probes the `forks` per-SHA marker, the only SHA-scoped container. Canonical repos
+have no per-commit namespace, so it exits non-zero with a note instead of a
+misleading `not cached`.
-/
def cacheQuerySingle (repo sha : String) : IO Unit := do
+ if isCanonicalRepo repo then
+ IO.eprintln s!"{repo} caches by file hash, not per commit, so there is no per-commit build to query."
+ (← IO.getStderr).flush
+ IO.Process.exit 1
let cached ← probeContainerForSHA Container.forks repo sha
if cached then
IO.println s!"cached: {sha}"
@@ -183,6 +189,10 @@ This is a diagnostic-only command: it prints the SHA to stdout but does not
auto-apply it. The user manually passes the result to `cache get` if desired.
-/
def cacheQuery (repo : String) (cap : Nat := 50) (cwd : FilePath := ".") : IO Unit := do
+ if isCanonicalRepo repo then
+ IO.println s!"`cache query` locates a fork PR's per-commit cache. {repo} reads \
+ its own cache container directly, so there is nothing to query for it."
+ return
-- Determine merge base with master. If not reachable, use cap-only walk.
let mergeBase? ← gitMergeBase "master" cwd
let stopRef := mergeBase?.getD ""
diff --git a/Cache/Warning.lean b/Cache/Warning.lean
index 2aba0a12428b87..c878c90028c800 100644
--- a/Cache/Warning.lean
+++ b/Cache/Warning.lean
@@ -179,8 +179,8 @@ Fires only on naive `cache get` invocations:
picked a scope and the non-default-scope warning is doing the talking)
- no `--cache-from` override (else they've already taken explicit
responsibility for the lookup chain)
-- the resolved repo's default lookup chain reads from `forks` (otherwise SHA
- scoping is not relevant)
+- the repo is a fork, not a first-party repo: the canonical repos don't build
+ into the per-commit `forks` namespace this note checks
- HEAD is not already an ancestor of `master`. From a personal-fork checkout
sitting on `master` (or an undiverged branch), the fork's SHA-scoped marker is
structurally absent, but `master` is first in the fork lookup chain and serves
@@ -195,7 +195,7 @@ mix with `cache get`'s stdout output.
def informIfHeadNotBuilt (repo : String) : IO Unit := do
if (← getRepoScope).isSome then return
if (← cacheFromOverride.get).isSome then return
- unless (defaultContainersForRepo repo).contains Container.forks do return
+ if isCanonicalRepo repo then return
-- HEAD already on (an ancestor of) master: master CI builds these commits and
-- the master container (first in the fork lookup chain) serves their artifacts
-- by hash, so there is nothing fork-specific to build. The forks marker is
From b4c2709405383ebfbd798defaafcfcd33dd8359e Mon Sep 17 00:00:00 2001
From: FordUniver <61389961+FordUniver@users.noreply.github.com>
Date: Tue, 23 Jun 2026 18:56:55 +0000
Subject: [PATCH 0299/1300] feat(Analysis/Calculus/Gradient): add
`toDual_gradient` and companions (#39202)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add `toDual_gradient`, `toDual_gradientWithin`, and the composed variants `toDual_comp_gradient`, `toDual_comp_gradientWithin` — the natural inverse direction of the gradient's defining equation `∇ f x := (toDual 𝕜 F).symm (fderiv 𝕜 f x)`. These identify `(toDual 𝕜 F) (∇ f x)` with `fderiv 𝕜 f x` (and the `gradientWithin` and composed forms with the corresponding fderiv versions), making the Riesz isomorphism between the two derivative views explicit. The proofs of `DifferentiableAt.hasGradientAt` and `DifferentiableWithinAt.hasGradientWithinAt` in the same file are simplified to use them.
Co-authored-by: Sebastian Pokutta <23001135+pokutta@users.noreply.github.com>
Co-authored-by: Christoph Spiegel
---
Mathlib/Analysis/Calculus/Gradient/Basic.lean | 25 +++++++++++++++----
1 file changed, 20 insertions(+), 5 deletions(-)
diff --git a/Mathlib/Analysis/Calculus/Gradient/Basic.lean b/Mathlib/Analysis/Calculus/Gradient/Basic.lean
index 3722367d7b138f..e954b2d8320958 100644
--- a/Mathlib/Analysis/Calculus/Gradient/Basic.lean
+++ b/Mathlib/Analysis/Calculus/Gradient/Basic.lean
@@ -118,6 +118,24 @@ alias ⟨HasFDerivAt.hasGradientAt, _⟩ := hasFDerivAt_iff_hasGradientAt
theorem gradient_eq_zero_of_not_differentiableAt (h : ¬DifferentiableAt 𝕜 f x) : ∇ f x = 0 := by
rw [gradient, fderiv_zero_of_not_differentiableAt h, map_zero]
+@[simp]
+lemma toDual_gradientWithin :
+ (toDual 𝕜 F) (gradientWithin f s x) = fderivWithin 𝕜 f s x := by
+ rw [gradientWithin, (toDual 𝕜 F).apply_symm_apply]
+
+@[simp]
+lemma toDual_gradient : (toDual 𝕜 F) (∇ f x) = fderiv 𝕜 f x := by
+ rw [gradient, (toDual 𝕜 F).apply_symm_apply]
+
+@[simp]
+lemma toDual_comp_gradientWithin :
+ (toDual 𝕜 F) ∘ gradientWithin f s = fderivWithin 𝕜 f s :=
+ funext fun _ => toDual_gradientWithin
+
+@[simp]
+lemma toDual_comp_gradient : (toDual 𝕜 F) ∘ ∇ f = fderiv 𝕜 f :=
+ funext fun _ => toDual_gradient
+
theorem HasGradientAt.unique {gradf gradg : F}
(hf : HasGradientAt f gradf x) (hg : HasGradientAt f gradg x) :
gradf = gradg :=
@@ -125,8 +143,7 @@ theorem HasGradientAt.unique {gradf gradg : F}
theorem DifferentiableAt.hasGradientAt (h : DifferentiableAt 𝕜 f x) :
HasGradientAt f (∇ f x) x := by
- rw [hasGradientAt_iff_hasFDerivAt, gradient, (toDual 𝕜 F).apply_symm_apply (fderiv 𝕜 f x)]
- exact h.hasFDerivAt
+ simpa [hasGradientAt_iff_hasFDerivAt] using h.hasFDerivAt
theorem HasGradientAt.differentiableAt (h : HasGradientAt f f' x) :
DifferentiableAt 𝕜 f x :=
@@ -134,9 +151,7 @@ theorem HasGradientAt.differentiableAt (h : HasGradientAt f f' x) :
theorem DifferentiableWithinAt.hasGradientWithinAt (h : DifferentiableWithinAt 𝕜 f s x) :
HasGradientWithinAt f (gradientWithin f s x) s x := by
- rw [hasGradientWithinAt_iff_hasFDerivWithinAt, gradientWithin,
- (toDual 𝕜 F).apply_symm_apply (fderivWithin 𝕜 f s x)]
- exact h.hasFDerivWithinAt
+ simpa [hasGradientWithinAt_iff_hasFDerivWithinAt] using h.hasFDerivWithinAt
theorem HasGradientWithinAt.differentiableWithinAt (h : HasGradientWithinAt f f' s x) :
DifferentiableWithinAt 𝕜 f s x :=
From 397c8ebf720aaa3418a0f4d96591ba74e137963e Mon Sep 17 00:00:00 2001
From: Jovan Gerbscheid <56355248+JovanGerb@users.noreply.github.com>
Date: Tue, 23 Jun 2026 20:09:38 +0000
Subject: [PATCH 0300/1300] fix(ClickSuggestions): set up the config correctly
(#40956)
This PR fixes the bug in `#click_suggestions` reported at https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/.23click_suggestions.20doesn.27t.20find.20lemma/with/605957884
The problem was that `#click_suggestion` was using the default meta configuration, rather than the elaboration meta configuration.
Unfortunately, it's not possible to add a test for this; due to how the tests work, the config there is already set to the elaboration meta config. I have verified locally that this PR fixes the original problem.
---
Mathlib/Tactic/ClickSuggestions.lean | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Mathlib/Tactic/ClickSuggestions.lean b/Mathlib/Tactic/ClickSuggestions.lean
index 7c6ea97903033a..0164cde3a2f665 100644
--- a/Mathlib/Tactic/ClickSuggestions.lean
+++ b/Mathlib/Tactic/ClickSuggestions.lean
@@ -129,7 +129,7 @@ public def rpc (props : PanelWidgetProps) : RequestM (RequestTask Html) :=
let goals := if useAfter then tacticInfo.goalsAfter else tacticInfo.goalsBefore
goals.contains loc.mvarId
| return .text "#click_suggestions: Please reload the tactic state"
- goal.ctx.val.runMetaM {} do loc.mvarId.withContext do
+ goal.ctx.val.runMetaM {} do withConfig Elab.Term.setElabConfig do loc.mvarId.withContext do
let (statusHtml, statusToken) ← mkRefreshComponent
let (solvedHtml, solvedToken) ← mkRefreshComponent
let targetHtml ←
From d9a189b1cceddaa57dd921ba74c1d5aaf51d0a13 Mon Sep 17 00:00:00 2001
From: Christian Merten <136261474+chrisflav@users.noreply.github.com>
Date: Tue, 23 Jun 2026 21:10:28 +0000
Subject: [PATCH 0301/1300] =?UTF-8?q?feat(RingTheory):=20category=20of=20f?=
=?UTF-8?q?inite=20=C3=A9tale=20algebras=20over=20a=20separably=20closed?=
=?UTF-8?q?=20field=20(#38054)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
We define the category of finite étale `R`-algebras for a ring `R` and show it is equivalent to `FintypeCat` if `R` is a separably closed field.
From Pi1.
---
Mathlib.lean | 1 +
.../IntermediateField/Adjoin/Basic.lean | 5 +
Mathlib/RingTheory/Etale/Finite.lean | 193 ++++++++++++++++++
Mathlib/RingTheory/TensorProduct/Maps.lean | 13 ++
Mathlib/RingTheory/TotallySplit.lean | 6 +
5 files changed, 218 insertions(+)
create mode 100644 Mathlib/RingTheory/Etale/Finite.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 4fc076a92d63d5..d3f6317619d23a 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -6506,6 +6506,7 @@ public import Mathlib.RingTheory.EssentialFiniteness
public import Mathlib.RingTheory.Etale.Basic
public import Mathlib.RingTheory.Etale.Descent
public import Mathlib.RingTheory.Etale.Field
+public import Mathlib.RingTheory.Etale.Finite
public import Mathlib.RingTheory.Etale.Kaehler
public import Mathlib.RingTheory.Etale.Locus
public import Mathlib.RingTheory.Etale.Pi
diff --git a/Mathlib/FieldTheory/IntermediateField/Adjoin/Basic.lean b/Mathlib/FieldTheory/IntermediateField/Adjoin/Basic.lean
index a1f6d27897f034..b6280f1e11761d 100644
--- a/Mathlib/FieldTheory/IntermediateField/Adjoin/Basic.lean
+++ b/Mathlib/FieldTheory/IntermediateField/Adjoin/Basic.lean
@@ -784,3 +784,8 @@ theorem AdjoinPair.algebraMap_gen₂ : (algebraMap (↥K⟮x, y⟯) L) (gen₂ K
end AdjoinPair
end IntermediateField
+
+instance (R : Type*) [CommSemiring R] (K : Type*) [Field K] [Algebra R K]
+ (S : Type*) [Semiring S] [Algebra R S] [Module.Finite R S] :
+ Finite (S →ₐ[R] K) :=
+ .of_equiv _ (Algebra.TensorProduct.liftEquivRight _ K _ _).symm
diff --git a/Mathlib/RingTheory/Etale/Finite.lean b/Mathlib/RingTheory/Etale/Finite.lean
new file mode 100644
index 00000000000000..18b0bc0c2ac738
--- /dev/null
+++ b/Mathlib/RingTheory/Etale/Finite.lean
@@ -0,0 +1,193 @@
+/-
+Copyright (c) 2026 Christian Merten. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Christian Merten
+-/
+module
+
+public import Mathlib.Algebra.Category.CommAlgCat.Basic
+public import Mathlib.CategoryTheory.FintypeCat
+public import Mathlib.RingTheory.TotallySplit
+
+/-!
+# Category of finite étale `R`-algebras
+
+In this file we define the category of finite étale `R`-algebras over a ring `R`. For any
+geometric point `Ω` of `R`, we define a fiber functor sending a finite étale `R`-algebra
+`S` to the finite set of `R`-algebra homomorphisms `S →ₐ[R] Ω`.
+
+## Main definitions
+
+- `CommAlgCat.FiniteEtale`: The category of finite étale `R`-algebras.
+- `CommAlgCat.FiniteEtale.fiber`: For a geometric point `Ω` of `R`, the fiber functor
+ `S ↦ (S →ₐ[R] Ω)`.
+
+## Main results
+
+- `CommAlgCat.FiniteEtale.equivOfIsSepClosed`: If `R = Ω` is separably closed,
+ the category of finite étale `Ω`-algebras is anti-equivalent to `FintypeCat`.
+ In particular, the functor `CommAlgCat.FiniteEtale.fiber` is an equivalence
+ of categories in this case.
+-/
+
+public section
+
+open CategoryTheory TensorProduct
+
+universe v w u
+
+namespace CommAlgCat
+
+variable (R : Type u) [CommRing R] (k : Type u) [Field k]
+
+section
+
+/-- The object property of finite `R`-algebras. -/
+abbrev finite : ObjectProperty (CommAlgCat.{v} R) :=
+ fun S ↦ Module.Finite R S
+
+/-- The object property of étale `R`-algebras. -/
+abbrev etale : ObjectProperty (CommAlgCat.{v} R) :=
+ fun S ↦ Algebra.Etale R S
+
+/-- The object property of finite étale `R`-algebras. -/
+abbrev finiteEtale : ObjectProperty (CommAlgCat.{v} R) :=
+ finite R ⊓ etale R
+
+/-- The category of finite étale `R`-algebras. -/
+abbrev FiniteEtale (R : Type u) [CommRing R] : Type _ :=
+ (finiteEtale.{v} R).FullSubcategory
+
+instance : CoeSort (FiniteEtale.{v} R) (Type v) := ⟨fun R ↦ R.obj⟩
+
+instance (S : FiniteEtale.{v} R) : Algebra.Etale R S :=
+ S.property.right
+
+instance (S : FiniteEtale.{v} R) : Module.Finite R S :=
+ S.property.left
+
+/-- Construct a term of `FiniteEtale R` from a finite étale `R`-algebra. -/
+@[simps obj]
+abbrev FiniteEtale.of (S : Type v) [CommRing S] [Algebra R S]
+ [Module.Finite R S] [Algebra.Etale R S] :
+ FiniteEtale.{v} R where
+ obj := .of R S
+ property := ⟨‹_›, ‹_›⟩
+
+variable {R}
+
+/-- Construct a morphism in `FiniteEtale R` from an algebra map. -/
+@[simps]
+abbrev FiniteEtale.ofHom {S T : Type v} [CommRing S] [CommRing T]
+ [Algebra R S] [Algebra R T] [Module.Finite R S] [Algebra.Etale R S] [Module.Finite R T]
+ [Algebra.Etale R T] (f : S →ₐ[R] T) :
+ FiniteEtale.of R S ⟶ FiniteEtale.of R T where
+ hom := CommAlgCat.ofHom f
+
+/-- Construct an isomorphism in `FiniteEtale R` from an algebra equivalence. -/
+abbrev FiniteEtale.isoMk {S T : FiniteEtale R} (e : S.obj ≃ₐ[R] T.obj) :
+ S ≅ T :=
+ ObjectProperty.isoMk _ (CommAlgCat.isoMk e)
+
+end
+
+instance (R : FiniteEtale k) : IsArtinianRing R :=
+ have := Algebra.FormallyUnramified.finite_of_free k R
+ isArtinian_of_tower k inferInstance
+
+variable (Ω : Type w) [Field Ω] [Algebra R Ω]
+ (S : Type w) [CommRing S] [Algebra R S] [Algebra S Ω] [IsScalarTower R S Ω]
+
+/-- If `S` is an `R`-algebra, this is the base change functor `A ↦ S ⊗[R] A`. -/
+@[expose, simps]
+def FiniteEtale.baseChange : FiniteEtale.{v} R ⥤ FiniteEtale.{max w v} S where
+ obj A := .of S (S ⊗[R] A)
+ map {A B} f := FiniteEtale.ofHom (Algebra.TensorProduct.map (.id _ _) f.hom.hom)
+
+/-- Base change from `R` to `R` is isomorphic to the identity. -/
+@[expose]
+def FiniteEtale.baseChangeSelfIso : baseChange R R ≅ 𝟭 (FiniteEtale R) :=
+ NatIso.ofComponents (fun A ↦ isoMk (Algebra.TensorProduct.lid _ _)) <| fun {A B} f ↦ by
+ dsimp [baseChange]
+ ext
+ simp
+
+/-- The fiber functor for finite étale `R`-algebras at the geometric point `Ω`: This is the
+functor sending `S` to `R`-algebra homomorphisms `S →ₐ[R] Ω`. -/
+@[expose, simps]
+def FiniteEtale.fiber (R : Type u) [CommRing R] (Ω : Type w) [Field Ω] [Algebra R Ω] :
+ (FiniteEtale.{v} R)ᵒᵖ ⥤ FintypeCat.{max v w} where
+ obj S := .of (S.unop →ₐ[R] Ω)
+ map {S T} f := FintypeCat.homMk (·.comp f.unop.hom.hom)
+
+/-- If `k` is a field, this is the `Spec` functor sending a finite étale `k`-algebra `R`
+to its finite prime spectrum. -/
+@[expose, simps]
+def FiniteEtale.finiteSpec (k : Type u) [Field k] : (FiniteEtale.{v} k)ᵒᵖ ⥤ FintypeCat.{v} where
+ obj R := .of (PrimeSpectrum R.unop.obj)
+ map f := FintypeCat.homMk (PrimeSpectrum.comap f.unop.hom.hom)
+
+set_option backward.defeqAttrib.useBackward true in
+/-- If the geometric point `Ω` factors through `S`, the fiber can be computed after base change
+to `S`. -/
+@[expose]
+def FiniteEtale.fiberIsoBaseChangeFiber :
+ FiniteEtale.fiber.{v} R Ω ≅
+ (FiniteEtale.baseChange.{v} R S).op ⋙ FiniteEtale.fiber S Ω :=
+ NatIso.ofComponents
+ (fun A ↦ FintypeCat.equivEquivIso (Algebra.TensorProduct.liftEquivRight _ _ _ _))
+
+/-- If `Ω` is separably closed, the fiber functor for finite étale `Ω`-algebras
+is naturally isomorphic to the (finite) `Spec` functor. -/
+@[expose]
+noncomputable def FiniteEtale.fiberIsoFiniteSpec [IsSepClosed Ω] :
+ FiniteEtale.fiber Ω Ω ≅ FiniteEtale.finiteSpec Ω :=
+ NatIso.ofComponents
+ fun R ↦ FintypeCat.equivEquivIso (Algebra.IsFiniteSplit.algHomEquivPrimeSpectrum _ _)
+
+/-- If `Ω` is separably closed, the fiber `S →ₐ[R] Ω`
+is isomorphic to the prime spectrum of the base change `Ω ⊗[R] S`. -/
+@[expose]
+noncomputable def FiniteEtale.fiberIsoComp [IsSepClosed Ω] :
+ FiniteEtale.fiber.{v} R Ω ≅
+ (FiniteEtale.baseChange.{v} R Ω).op ⋙ FiniteEtale.finiteSpec.{max w v} Ω :=
+ fiberIsoBaseChangeFiber _ _ Ω ≪≫ Functor.isoWhiskerLeft _ (fiberIsoFiniteSpec _)
+
+set_option backward.defeqAttrib.useBackward true in
+set_option backward.isDefEq.respectTransparency false in
+/-- If `Ω` is a separably closed field, the category of finite étale `Ω`-algebras is
+anti-equivalent to `FintypeCat`. -/
+@[expose, simps! functor inverse_obj inverse_map]
+noncomputable def FiniteEtale.equivOfIsSepClosed (Ω : Type u) [Field Ω] [IsSepClosed Ω] :
+ (FiniteEtale.{u} Ω)ᵒᵖ ≌ FintypeCat.{u} := .symm
+ { functor.obj X := .op (.of _ (X → Ω))
+ functor.map {X Y} f := .op (FiniteEtale.ofHom <| AlgHom.pi fun i ↦ Pi.evalAlgHom _ _ (f i))
+ inverse := FiniteEtale.finiteSpec Ω
+ counitIso :=
+ NatIso.ofComponents
+ (fun R ↦ (FiniteEtale.isoMk (Algebra.FormallyEtale.equivPiOfIsSepClosed Ω R.unop)).op)
+ fun {R S} f ↦ by
+ apply Quiver.Hom.unop_inj
+ ext x
+ exact funext fun p ↦ Algebra.FormallyEtale.equivPiOfIsSepClosed_comap _ _ _
+ unitIso := NatIso.ofComponents
+ fun X ↦ FintypeCat.equivEquivIso <|
+ (Equiv.sigmaUnique _ _).symm.trans (PrimeSpectrum.sigmaToPiHomeo _).toEquiv
+ functor_unitIso_comp X := by
+ dsimp [FiniteEtale.finiteSpec]
+ apply Quiver.Hom.unop_inj
+ ext x i
+ dsimp
+ rw [FintypeCat.equivEquivIso_apply_hom, FintypeCat.homMk_apply]
+ dsimp
+ rw [← Pi.coe_evalAlgHom Ω]
+ simp [Algebra.FormallyEtale.equivPiOfIsSepClosed_comap,
+ Algebra.FormallyEtale.equivPiOfIsSepClosed_self_apply] }
+
+instance (Ω : Type u) [Field Ω] [IsSepClosed Ω] : (FiniteEtale.finiteSpec.{u} Ω).IsEquivalence :=
+ (FiniteEtale.equivOfIsSepClosed.{u} Ω).isEquivalence_functor
+
+instance (Ω : Type u) [Field Ω] [IsSepClosed Ω] : (FiniteEtale.fiber.{u} Ω Ω).IsEquivalence :=
+ Functor.isEquivalence_of_iso (FiniteEtale.fiberIsoFiniteSpec _).symm
+
+end CommAlgCat
diff --git a/Mathlib/RingTheory/TensorProduct/Maps.lean b/Mathlib/RingTheory/TensorProduct/Maps.lean
index d1b56d3faa6a5e..36f0c51b51f6f2 100644
--- a/Mathlib/RingTheory/TensorProduct/Maps.lean
+++ b/Mathlib/RingTheory/TensorProduct/Maps.lean
@@ -209,6 +209,19 @@ def liftEquiv : {fg : (A →ₐ[S] C) × (B →ₐ[R] C) // ∀ x y, Commute (fg
left_inv fg := by ext <;> simp
right_inv f' := by ext <;> simp
+variable (R S B) in
+/--
+Algebra maps `S ⊗[R] B →ₐ[S] C` are the same as algebra maps `B →ₐ[R] C`.
+Variant of `Algebra.TensorProduct.liftEquiv` where the left map is fixed.
+-/
+@[simps]
+def liftEquivRight (C : Type*) [CommRing C] [Algebra R C] [Algebra S C] [IsScalarTower R S C] :
+ (B →ₐ[R] C) ≃ (S ⊗[R] B →ₐ[S] C) where
+ toFun f := Algebra.TensorProduct.lift (Algebra.ofId _ _) f fun _ _ ↦ .all _ _
+ invFun f := AlgHom.comp (f.restrictScalars R) Algebra.TensorProduct.includeRight
+ left_inv _ := by ext; simp
+ right_inv _ := by ext; simp
+
theorem restrictScalars_lift [CommSemiring R'] [Algebra R R'] [Algebra R' S]
[Algebra R' A] [IsScalarTower R R' A] [IsScalarTower R' S A]
[Algebra R' C] [IsScalarTower R R' C] [IsScalarTower R' S C]
diff --git a/Mathlib/RingTheory/TotallySplit.lean b/Mathlib/RingTheory/TotallySplit.lean
index 5e6bb5483999f0..bc0467f1d9a2cc 100644
--- a/Mathlib/RingTheory/TotallySplit.lean
+++ b/Mathlib/RingTheory/TotallySplit.lean
@@ -99,6 +99,7 @@ lemma bijective_algebraMap_quotient [IsFiniteSplit k R] (p : Ideal R) [p.IsPrime
variable (k R) in
/-- If `R` is finite split over a field `k`, the `k`-rational points of `R`
are in one-to-one correspondence with its prime spectrum. -/
+@[expose]
noncomputable
def algHomEquivPrimeSpectrum [IsFiniteSplit k R] : (R →ₐ[k] k) ≃ PrimeSpectrum R where
toFun f := ⟨RingHom.ker f, RingHom.ker_isPrime f⟩
@@ -121,6 +122,11 @@ def algHomEquivPrimeSpectrum [IsFiniteSplit k R] : (R →ₐ[k] k) ≃ PrimeSpec
AlgHom.ker_coe_equiv, ← RingHom.ker_eq_comap_bot, ← RingHom.ker_coe_toRingHom,
Ideal.Quotient.mkₐ_ker]
+@[simp]
+lemma coe_algHomEquivPrimeSpectrum [IsFiniteSplit k R] (f : R →ₐ[k] k) :
+ algHomEquivPrimeSpectrum k R f = RingHom.ker f :=
+ rfl
+
instance [IsSepClosed k] [EssFiniteType k R] [FormallyEtale k R] : IsFiniteSplit k R := by
have := FormallyUnramified.finite_of_free k R
have : IsArtinianRing R := isArtinian_of_tower k inferInstance
From d255f67ec87ccff56cf443418adec90ab0788342 Mon Sep 17 00:00:00 2001
From: Felix Pernegger <188575194+felixpernegger@users.noreply.github.com>
Date: Tue, 23 Jun 2026 22:14:25 +0000
Subject: [PATCH 0302/1300] style: `simp [- foo]` => `simp [-foo]` (#40971)
To adhere to mathlib style. I was told about this [here](https://github.com/leanprover-community/mathlib4/pull/38943#discussion_r3455400592); the `whitespace` linter would enforce this (if it were active on proof bodies).
Co-authored-by: Batixx
---
Mathlib/Algebra/GroupWithZero/Submonoid/Instances.lean | 2 +-
Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean | 2 +-
Mathlib/AlgebraicGeometry/Limits.lean | 2 +-
Mathlib/AlgebraicGeometry/Morphisms/FormallyUnramified.lean | 4 ++--
Mathlib/CategoryTheory/Limits/Shapes/SplitEqualizer.lean | 2 +-
Mathlib/CategoryTheory/Sites/ConcreteSheafification.lean | 2 +-
Mathlib/GroupTheory/SpecificGroups/Quaternion.lean | 2 +-
.../Homological/GroupHomology/Functoriality.lean | 2 +-
Mathlib/RingTheory/ZariskisMainTheorem.lean | 2 +-
Mathlib/Topology/Algebra/Valued/WithVal.lean | 2 +-
10 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/Mathlib/Algebra/GroupWithZero/Submonoid/Instances.lean b/Mathlib/Algebra/GroupWithZero/Submonoid/Instances.lean
index 618119245f44d2..41a52266808e81 100644
--- a/Mathlib/Algebra/GroupWithZero/Submonoid/Instances.lean
+++ b/Mathlib/Algebra/GroupWithZero/Submonoid/Instances.lean
@@ -43,7 +43,7 @@ instance [GroupWithZero G] [GroupWithZero H] (f : G →*₀ H) :
obtain ⟨y, hy⟩ := x.prop
use y⁻¹
simp [← hy]⟩
- exists_pair_ne := ⟨⟨f 0, 0, rfl⟩, ⟨f 1, by simp [- map_one]⟩, by simp⟩
+ exists_pair_ne := ⟨⟨f 0, 0, rfl⟩, ⟨f 1, by simp [-map_one]⟩, by simp⟩
inv_zero := Subtype.ext inv_zero
mul_inv_cancel := by
rintro ⟨a, ha⟩ h
diff --git a/Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean b/Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean
index 69443cf0fe5b99..0bfee543aed3b0 100644
--- a/Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean
+++ b/Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean
@@ -1174,7 +1174,7 @@ private nonrec lemma Scheme.exists_π_app_comp_eq_of_locallyOfFinitePresentation
obtain ⟨R, rfl⟩ := hS
wlog hX : ∃ S, X = Spec S generalizing X
· obtain ⟨i, f, hf⟩ := this (a ≫ X.isoSpec.hom) (X.isoSpec.inv ≫ f)
- (by simp [ha, - Functor.map_comp]) ⟨_, rfl⟩
+ (by simp [ha, -Functor.map_comp]) ⟨_, rfl⟩
exact ⟨i, f ≫ X.isoSpec.inv, by simpa [← Iso.comp_inv_eq] using! hf⟩
obtain ⟨S, rfl⟩ := hX
obtain ⟨φ, rfl⟩ := Spec.map_surjective f
diff --git a/Mathlib/AlgebraicGeometry/Limits.lean b/Mathlib/AlgebraicGeometry/Limits.lean
index a832ac5e0c047b..d2058253707e20 100644
--- a/Mathlib/AlgebraicGeometry/Limits.lean
+++ b/Mathlib/AlgebraicGeometry/Limits.lean
@@ -442,7 +442,7 @@ lemma isPullback_inl_inl_coprodMap {X Y X' Y' : Scheme.{u}}
· simp only [coprodMk_inr, ← Scheme.Hom.comp_apply, coprod.inr_map] at hxy
cases Set.disjoint_iff_forall_ne.mp (isCompl_range_inl_inr _ _).1 ⟨y, rfl⟩ ⟨_, rfl⟩ hxy
· rintro _ ⟨x, rfl⟩
- exact ⟨f x, by simp [← Scheme.Hom.comp_apply, - Scheme.Hom.comp_base]⟩
+ exact ⟨f x, by simp [← Scheme.Hom.comp_apply, -Scheme.Hom.comp_base]⟩
set_option backward.isDefEq.respectTransparency false in
lemma isPullback_inr_inr_coprodMap {X Y X' Y' : Scheme.{u}}
diff --git a/Mathlib/AlgebraicGeometry/Morphisms/FormallyUnramified.lean b/Mathlib/AlgebraicGeometry/Morphisms/FormallyUnramified.lean
index 897283b0a8f0f7..49fe5adb97ebef 100644
--- a/Mathlib/AlgebraicGeometry/Morphisms/FormallyUnramified.lean
+++ b/Mathlib/AlgebraicGeometry/Morphisms/FormallyUnramified.lean
@@ -212,7 +212,7 @@ protected lemma hom_ext {Z' Z : Scheme} (i : Z' ⟶ Z) (hi : IsNilpotent i.ker)
Scheme.Hom.appLE_comp_appLE]⟩
let ψ₂ : Γ(X, V) →ₐ[Γ(Y, U)] Γ(Z, W) := ⟨(g₂.appLE _ _ (hWV.trans inf_le_right)).hom, fun r ↦ by
simp [RingHom.algebraMap_toAlgebra, ← CategoryTheory.comp_apply, -CommRingCat.hom_comp,
- Scheme.Hom.appLE_comp_appLE, hgf, - Scheme.Hom.comp_appLE]⟩
+ Scheme.Hom.appLE_comp_appLE, hgf, -Scheme.Hom.comp_appLE]⟩
suffices ψ₁ = ψ₂ by
simpa [ψ₁, ψ₂, -Iso.cancel_iso_hom_left, IsAffineOpen.isoSpec_hom] using
congr(hW.isoSpec.hom ≫ Spec.map (CommRingCat.ofHom ($this).toRingHom) ≫ hV.fromSpec)
@@ -220,7 +220,7 @@ protected lemma hom_ext {Z' Z : Scheme} (i : Z' ⟶ Z) (hi : IsNilpotent i.ker)
· obtain ⟨n, hn⟩ := hi
exact ⟨n, by simpa using congr(($hn).ideal ⟨W, hW⟩)⟩
· simp [ψ₁, ψ₂, ← CategoryTheory.comp_apply, -CommRingCat.hom_comp, hig,
- Scheme.Hom.app_eq_appLE, Scheme.Hom.appLE_comp_appLE, - Scheme.Hom.comp_appLE]
+ Scheme.Hom.app_eq_appLE, Scheme.Hom.appLE_comp_appLE, -Scheme.Hom.comp_appLE]
/--
To show that `f : X ⟶ Y` is formally unramified,
diff --git a/Mathlib/CategoryTheory/Limits/Shapes/SplitEqualizer.lean b/Mathlib/CategoryTheory/Limits/Shapes/SplitEqualizer.lean
index 9c6a0fcccc6428..e9605bfa71774f 100644
--- a/Mathlib/CategoryTheory/Limits/Shapes/SplitEqualizer.lean
+++ b/Mathlib/CategoryTheory/Limits/Shapes/SplitEqualizer.lean
@@ -120,7 +120,7 @@ def IsSplitEqualizer.isEqualizer {W : C} {h : W ⟶ X} (t : IsSplitEqualizer f g
IsLimit t.asFork :=
Fork.IsLimit.mk' _ fun s =>
⟨ s.ι ≫ t.leftRetraction,
- by simp [- top_rightRetraction, ← t.top_rightRetraction, s.condition_assoc],
+ by simp [-top_rightRetraction, ← t.top_rightRetraction, s.condition_assoc],
fun hm => by simp [← hm] ⟩
end
diff --git a/Mathlib/CategoryTheory/Sites/ConcreteSheafification.lean b/Mathlib/CategoryTheory/Sites/ConcreteSheafification.lean
index 05152c715b8756..e4fea004e81e05 100644
--- a/Mathlib/CategoryTheory/Sites/ConcreteSheafification.lean
+++ b/Mathlib/CategoryTheory/Sites/ConcreteSheafification.lean
@@ -122,7 +122,7 @@ theorem equiv_symm_eq_apply {X : C} {P : Cᵒᵖ ⥤ D} {S : J.Cover X} [HasMult
-- We can hint `ConcreteCategory.hom (Y := P.obj (op I.Y))` below to put it into `simp`-normal
-- form, but that doesn't seem to fix the `erw`s below...
(Multiequalizer.ι (S.index P) I) ((Meq.equiv P S).symm x) = x I := by
- simp [- GrothendieckTopology.Cover.index_left, ← equiv_apply]
+ simp [-GrothendieckTopology.Cover.index_left, ← equiv_apply]
end Meq
diff --git a/Mathlib/GroupTheory/SpecificGroups/Quaternion.lean b/Mathlib/GroupTheory/SpecificGroups/Quaternion.lean
index cf44529992f105..8fb79b38b4cf4a 100644
--- a/Mathlib/GroupTheory/SpecificGroups/Quaternion.lean
+++ b/Mathlib/GroupTheory/SpecificGroups/Quaternion.lean
@@ -168,7 +168,7 @@ instance [NeZero n] : Fintype (QuaternionGroup n) :=
Fintype.ofEquiv _ fintypeHelper
instance : Nontrivial (QuaternionGroup n) :=
- ⟨⟨a 0, xa 0, by simp [- a_zero]⟩⟩
+ ⟨⟨a 0, xa 0, by simp [-a_zero]⟩⟩
/-- If `0 < n`, then `QuaternionGroup n` has `4n` elements.
-/
diff --git a/Mathlib/RepresentationTheory/Homological/GroupHomology/Functoriality.lean b/Mathlib/RepresentationTheory/Homological/GroupHomology/Functoriality.lean
index 90993978ab1507..12df1c5618ad2b 100644
--- a/Mathlib/RepresentationTheory/Homological/GroupHomology/Functoriality.lean
+++ b/Mathlib/RepresentationTheory/Homological/GroupHomology/Functoriality.lean
@@ -574,7 +574,7 @@ and `Y - ∑ aᵢ·sᵢ` is a cycle. -/
rcases chains₁ToCoinvariantsKer_surjective
(res S.subtype A) ⟨d₁₀ A Y, this⟩ with ⟨(Z : S →₀ A), hZ⟩
have H : d₁₀ A (Y - mapDomain S.subtype Z) = 0 := by
- simpa [map_sub, sub_eq_zero, chains₁ToCoinvariantsKer, - LinearMap.sub_apply, d₁₀,
+ simpa [map_sub, sub_eq_zero, chains₁ToCoinvariantsKer, -LinearMap.sub_apply, d₁₀,
sum_mapDomain_index_inj] using! Subtype.ext_iff.1 hZ.symm
use H1π A ⟨Y - mapDomain S.subtype Z, H⟩
simp only [H1CoresCoinf_X₃, H1CoresCoinf_X₂, H1CoresCoinf_g,
diff --git a/Mathlib/RingTheory/ZariskisMainTheorem.lean b/Mathlib/RingTheory/ZariskisMainTheorem.lean
index 56b7325c374066..920e9ed36be5b7 100644
--- a/Mathlib/RingTheory/ZariskisMainTheorem.lean
+++ b/Mathlib/RingTheory/ZariskisMainTheorem.lean
@@ -241,7 +241,7 @@ lemma exists_leadingCoeff_pow_smul_mem_conductor
exists_isIntegral_leadingCoeff_pow_smul_sub_of_isIntegralElem_of_mul_mem_range φ _ p
(hφ.to_isIntegral (t * x)) (by convert! this using 1; ring)
obtain ⟨r, hr : algebraMap _ _ r = _⟩ := hRS.le hn
- exact ⟨n, (C r + q), by simp [← Polynomial.algebraMap_eq, - Polynomial.algebraMap_apply, hr]⟩
+ exact ⟨n, (C r + q), by simp [← Polynomial.algebraMap_eq, -Polynomial.algebraMap_apply, hr]⟩
choose n hn using this
obtain ⟨s, hs⟩ := Module.Finite.fg_top (R := R[X]) (M := S)
refine ⟨s.sup n, fun x ↦ ?_⟩
diff --git a/Mathlib/Topology/Algebra/Valued/WithVal.lean b/Mathlib/Topology/Algebra/Valued/WithVal.lean
index 6cd812ef591d80..52af5959ca41f2 100644
--- a/Mathlib/Topology/Algebra/Valued/WithVal.lean
+++ b/Mathlib/Topology/Algebra/Valued/WithVal.lean
@@ -264,7 +264,7 @@ instance {P : Type*} [Ring S] [SMul P S] [SMul R S] [SMul P R]
instance {P : Type*} [Ring S] [SMul P R] [SMul S R] [SMul P S]
[IsScalarTower P S R] (v : Valuation S Γ₀) : IsScalarTower P (WithVal v) R where
- smul_assoc := by simp [smul_right_def, smul_left_def, - toVal_smul]
+ smul_assoc := by simp [smul_right_def, smul_left_def, -toVal_smul]
instance [AddCommMonoid S] [Module R S] : Module (WithVal v) S :=
.compHom S (equiv v).toRingHom
From 5cbb508e3c6d4f61810d681accf75353c5e655fc Mon Sep 17 00:00:00 2001
From: Christian Merten <136261474+chrisflav@users.noreply.github.com>
Date: Tue, 23 Jun 2026 22:34:19 +0000
Subject: [PATCH 0303/1300] chore(CategoryTheory/Sites): fix order of universe
variables in `Presieve.ofArrows_pUnit` (#40974)
The free universe variable should be the first one in the declaration, not the third.
---
Mathlib/AlgebraicGeometry/Sites/Fpqc.lean | 2 +-
Mathlib/AlgebraicGeometry/Sites/MorphismProperty.lean | 2 +-
Mathlib/CategoryTheory/Sites/MorphismProperty.lean | 2 +-
Mathlib/CategoryTheory/Sites/PrecoverageToGrothendieck.lean | 2 +-
Mathlib/CategoryTheory/Sites/Sieves.lean | 6 +++---
5 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/Mathlib/AlgebraicGeometry/Sites/Fpqc.lean b/Mathlib/AlgebraicGeometry/Sites/Fpqc.lean
index 3e343f60a1f49d..77045d93db95c8 100644
--- a/Mathlib/AlgebraicGeometry/Sites/Fpqc.lean
+++ b/Mathlib/AlgebraicGeometry/Sites/Fpqc.lean
@@ -97,7 +97,7 @@ instance : fppfTopology.Subcanonical :=
lemma Hom.singleton_mem_fppfPrecoverage {X Y : Scheme.{u}} (f : X ⟶ Y) [Flat f] [Surjective f]
[LocallyOfFinitePresentation f] :
Presieve.singleton f ∈ fppfPrecoverage Y := by
- rw [← Presieve.ofArrows_pUnit.{_, _, 0}]
+ rw [← Presieve.ofArrows_pUnit.{0}]
exact (f.cover (P := @Flat ⊓ @LocallyOfFinitePresentation) ⟨‹_›, ‹_›⟩).mem₀
@[simp]
diff --git a/Mathlib/AlgebraicGeometry/Sites/MorphismProperty.lean b/Mathlib/AlgebraicGeometry/Sites/MorphismProperty.lean
index 7fb519d3c2dbe7..bc84f286e65bbc 100644
--- a/Mathlib/AlgebraicGeometry/Sites/MorphismProperty.lean
+++ b/Mathlib/AlgebraicGeometry/Sites/MorphismProperty.lean
@@ -88,7 +88,7 @@ lemma ofArrows_mem_precoverage_iff {S : Scheme.{u}} {ι : Type*} {X : ι → Sch
@[simp]
lemma singleton_mem_precoverage_iff {X S : Scheme.{u}} (f : X ⟶ S) :
Presieve.singleton f ∈ precoverage P S ↔ Function.Surjective f.base ∧ P f := by
- rw [← Presieve.ofArrows_pUnit.{_, _, 0}, ofArrows_mem_precoverage_iff]
+ rw [← Presieve.ofArrows_pUnit.{0}, ofArrows_mem_precoverage_iff]
aesop
lemma bot_mem_precoverage (X : Scheme.{u}) [IsEmpty X] : ⊥ ∈ Scheme.precoverage P X :=
diff --git a/Mathlib/CategoryTheory/Sites/MorphismProperty.lean b/Mathlib/CategoryTheory/Sites/MorphismProperty.lean
index 7d6f1c72ace666..81bd3600419586 100644
--- a/Mathlib/CategoryTheory/Sites/MorphismProperty.lean
+++ b/Mathlib/CategoryTheory/Sites/MorphismProperty.lean
@@ -49,7 +49,7 @@ lemma ofArrows_mem_precoverage {X : C} {ι : Type*} {Y : ι → C} {f : ∀ i, Y
@[simp, grind =]
lemma singleton_mem_precoverage {X Y : C} (f : X ⟶ Y) :
.singleton f ∈ precoverage P Y ↔ P f := by
- simp [← Presieve.ofArrows_pUnit.{_, _, 0}]
+ simp [← Presieve.ofArrows_pUnit.{0}]
instance [P.ContainsIdentities] [P.RespectsIso] : P.precoverage.HasIsos where
mem_coverings_of_isIso f _ _ _ := fun ⟨⟩ ↦ P.of_isIso f
diff --git a/Mathlib/CategoryTheory/Sites/PrecoverageToGrothendieck.lean b/Mathlib/CategoryTheory/Sites/PrecoverageToGrothendieck.lean
index 9ae4abb0b79848..fc344b9950c132 100644
--- a/Mathlib/CategoryTheory/Sites/PrecoverageToGrothendieck.lean
+++ b/Mathlib/CategoryTheory/Sites/PrecoverageToGrothendieck.lean
@@ -232,7 +232,7 @@ lemma Presieve.isSheafFor_singleton_iff_of_iso {F : Cᵒᵖ ⥤ Type*} {S X Y :
(g : Y ⟶ S) (e : X ≅ Y) (he : e.hom ≫ g = f) :
(singleton f).IsSheafFor F ↔ (singleton g).IsSheafFor F := by
subst he
- rw [← Presieve.ofArrows_pUnit.{_, _, 0}, ← Presieve.ofArrows_pUnit,
+ rw [← Presieve.ofArrows_pUnit.{0}, ← Presieve.ofArrows_pUnit,
Presieve.isSheafFor_ofArrows_comp_iff]
open Limits
diff --git a/Mathlib/CategoryTheory/Sites/Sieves.lean b/Mathlib/CategoryTheory/Sites/Sieves.lean
index 8a23a45ba149dd..56a4042b21ee1e 100644
--- a/Mathlib/CategoryTheory/Sites/Sieves.lean
+++ b/Mathlib/CategoryTheory/Sites/Sieves.lean
@@ -271,7 +271,7 @@ lemma ofArrows_of_unique {X : C} {ι : Type*} [Unique ι] {Y : ι → C} (f :
obtain rfl : i = default := Subsingleton.elim _ _
simp
-theorem ofArrows_pUnit : (ofArrows _ fun _ : PUnit => f) = singleton f := by
+theorem ofArrows_pUnit : (ofArrows _ fun _ : PUnit.{w + 1} => f) = singleton f := by
rw [ofArrows_of_unique]
@[grind =]
@@ -316,7 +316,7 @@ lemma pushforward_ofArrows {ι : Type*} {U : ι → C} {X Y : C} (g : ∀ i, U i
lemma pushforward_singleton {X Y Z : C} (f : X ⟶ Y) (g : Y ⟶ Z) :
(singleton f).pushforward g = .singleton (f ≫ g) := by
- rw [← ofArrows_pUnit.{_, _, 0}, pushforward_ofArrows, ofArrows_pUnit.{_, _, 0}]
+ rw [← ofArrows_pUnit.{0}, pushforward_ofArrows, ofArrows_pUnit.{0}]
/-- The pullback of a presieve `R` on `Y` along a morphism `f : X ⟶ Y` is the presieve on `X`
given by all morphisms `g : Z ⟶ X` such that `f ≫ g` is in `R`. -/
@@ -475,7 +475,7 @@ lemma map_ofArrows {X : C} {ι : Type*} {Y : ι → C} (f : ∀ i, Y i ⟶ X) :
@[simp]
lemma map_singleton {X Y : C} (f : X ⟶ Y) : (singleton f).map F = singleton (F.map f) := by
- rw [← ofArrows_pUnit.{_, _, 0}, map_ofArrows, ofArrows_pUnit]
+ rw [← ofArrows_pUnit.{0}, map_ofArrows, ofArrows_pUnit]
lemma map_le_iff_le_functorPullback {R : Presieve X} {S : Presieve (F.obj X)} :
R.map F ≤ S ↔ R ≤ S.functorPullback F :=
From 1aeada15afe7990503183188dd86b65f8a936ef7 Mon Sep 17 00:00:00 2001
From: Vasilii Nesterov <118051017+vasnesterov@users.noreply.github.com>
Date: Tue, 23 Jun 2026 22:55:02 +0000
Subject: [PATCH 0304/1300] feat(Analysis/Analytic): alternating geometric
series (#40029)
* Define `geometricAlternatingSeries`: `1 - x + x ^ 2 - ...` as a `FormalMultilinearSeries`.
* Prove the same lemmas as for existing `formalMultilinearSeries_geometric`.
* Move the "Composition with a linear map" section above as it's needed to directly translate the results about `formalMultilinearSeries_geometric` to results about `geometricAlternatingSeries`.
The series for `1/(1+x)` is more common in "practical" calculus than the series for `1/(1-x)`. I use it in the `compute_asymptotics` tactic to derive the series expansion of `1 / f(x)` as `x -> 0` when the series expansion of `f(x)` is known. I decompose `f(x) = C + g(x)` with `g(x) = o(1)` and derive it as `1 / (C + g(x)) = 1/C * 1/(1 + g(x)/C) = 1/C * (1 - g(x) / C + g(x)^2 / C^2 - ...)`.
---
Mathlib/Analysis/Analytic/Constructions.lean | 188 ++++++++++++-------
1 file changed, 123 insertions(+), 65 deletions(-)
diff --git a/Mathlib/Analysis/Analytic/Constructions.lean b/Mathlib/Analysis/Analytic/Constructions.lean
index 8d7fa921173079..8cf6ff6eaaf355 100644
--- a/Mathlib/Analysis/Analytic/Constructions.lean
+++ b/Mathlib/Analysis/Analytic/Constructions.lean
@@ -716,6 +716,71 @@ lemma AnalyticOnNhd.zpow_nonneg {f : E → 𝕝} {s : Set E} {n : ℤ} (hf : Ana
simp_rw [(Eq.symm (Int.toNat_of_nonneg hn) : n = OfNat.ofNat n.toNat), zpow_ofNat]
apply pow hf
+/-!
+### Composition with a linear map
+-/
+
+section compContinuousLinearMap
+
+variable {u : E →L[𝕜] F} {f : F → G} {pf : FormalMultilinearSeries 𝕜 F G} {s : Set F} {x : E}
+ {r : ℝ≥0∞}
+
+theorem HasFPowerSeriesWithinOnBall.compContinuousLinearMap
+ (hf : HasFPowerSeriesWithinOnBall f pf s (u x) r) :
+ HasFPowerSeriesWithinOnBall (f ∘ u) (pf.compContinuousLinearMap u) (u ⁻¹' s) x (r / ‖u‖ₑ) where
+ r_le := by
+ calc
+ _ ≤ pf.radius / ‖u‖ₑ := by
+ gcongr
+ exact hf.r_le
+ _ ≤ _ := pf.div_le_radius_compContinuousLinearMap _
+ r_pos := by
+ simp only [ENNReal.div_pos_iff, ne_eq, enorm_ne_top, not_false_eq_true, and_true]
+ exact pos_iff_ne_zero.mp hf.r_pos
+ hasSum hy1 hy2 := by
+ convert! hf.hasSum _ _
+ · simp
+ · simp only [Set.mem_insert_iff, add_eq_left, Set.mem_preimage, map_add] at hy1 ⊢
+ rcases hy1 with (hy1 | hy1) <;> simp [hy1]
+ · simp only [Metric.eball, edist_zero_right, Set.mem_setOf_eq] at hy2 ⊢
+ exact lt_of_le_of_lt (ContinuousLinearMap.le_opNorm_enorm _ _) (mul_lt_of_lt_div' hy2)
+
+theorem HasFPowerSeriesOnBall.compContinuousLinearMap (hf : HasFPowerSeriesOnBall f pf (u x) r) :
+ HasFPowerSeriesOnBall (f ∘ u) (pf.compContinuousLinearMap u) x (r / ‖u‖ₑ) := by
+ rw [← hasFPowerSeriesWithinOnBall_univ] at hf ⊢
+ exact hf.compContinuousLinearMap
+
+theorem HasFPowerSeriesAt.compContinuousLinearMap (hf : HasFPowerSeriesAt f pf (u x)) :
+ HasFPowerSeriesAt (f ∘ u) (pf.compContinuousLinearMap u) x :=
+ let ⟨r, hr⟩ := hf
+ ⟨r / ‖u‖ₑ, hr.compContinuousLinearMap⟩
+
+theorem HasFPowerSeriesWithinAt.compContinuousLinearMap
+ (hf : HasFPowerSeriesWithinAt f pf s (u x)) :
+ HasFPowerSeriesWithinAt (f ∘ u) (pf.compContinuousLinearMap u) (u ⁻¹' s) x :=
+ let ⟨r, hr⟩ := hf
+ ⟨r / ‖u‖ₑ, hr.compContinuousLinearMap⟩
+
+theorem AnalyticAt.compContinuousLinearMap (hf : AnalyticAt 𝕜 f (u x)) :
+ AnalyticAt 𝕜 (f ∘ u) x :=
+ let ⟨p, hp⟩ := hf
+ ⟨p.compContinuousLinearMap u, hp.compContinuousLinearMap⟩
+
+theorem AnalyticAtWithin.compContinuousLinearMap (hf : AnalyticWithinAt 𝕜 f s (u x)) :
+ AnalyticWithinAt 𝕜 (f ∘ u) (u ⁻¹' s) x :=
+ let ⟨p, hp⟩ := hf
+ ⟨p.compContinuousLinearMap u, hp.compContinuousLinearMap⟩
+
+theorem AnalyticOn.compContinuousLinearMap (hf : AnalyticOn 𝕜 f s) :
+ AnalyticOn 𝕜 (f ∘ u) (u ⁻¹' s) := fun x hx =>
+ AnalyticAtWithin.compContinuousLinearMap (hf (u x) hx)
+
+theorem AnalyticOnNhd.compContinuousLinearMap (hf : AnalyticOnNhd 𝕜 f s) :
+ AnalyticOnNhd 𝕜 (f ∘ u) (u ⁻¹' s) := fun x hx =>
+ AnalyticAt.compContinuousLinearMap (hf (u x) hx)
+
+end compContinuousLinearMap
+
/-!
### Restriction of scalars
-/
@@ -822,6 +887,53 @@ lemma analyticAt_inverse_one_sub [HasSummableGeomSeries A] :
AnalyticAt 𝕜 (fun x : A ↦ (1 - x)⁻¹ʳ) 0 :=
⟨_, ⟨_, hasFPowerSeriesOnBall_inverse_one_sub 𝕜 A⟩⟩
+/-- The alternating geometric series `1 - x + x ^ 2 - ...` as a `FormalMultilinearSeries`. -/
+def alternatingGeometricSeries : FormalMultilinearSeries 𝕜 A A :=
+ .ofScalars A fun n ↦ (-1 : 𝕜) ^ n
+
+lemma alternatingGeometricSeries_eq_formalMultilinearSeries_geometric_comp_neg :
+ alternatingGeometricSeries 𝕜 A =
+ (formalMultilinearSeries_geometric 𝕜 A).compContinuousLinearMap
+ (-ContinuousLinearMap.id 𝕜 A) := by
+ simp [formalMultilinearSeries_geometric_eq_ofScalars, alternatingGeometricSeries,
+ FormalMultilinearSeries.ofScalars_comp_neg_id]
+
+lemma alternatingGeometricSeries_apply_norm_le (n : ℕ) :
+ ‖alternatingGeometricSeries 𝕜 A n‖ ≤ max 1 ‖(1 : A)‖ := by
+ simpa [alternatingGeometricSeries] using
+ ContinuousMultilinearMap.norm_mkPiAlgebraFin_le
+
+lemma alternatingGeometricSeries_apply_norm [NormOneClass A] (n : ℕ) :
+ ‖alternatingGeometricSeries 𝕜 A n‖ = 1 := by
+ simp [alternatingGeometricSeries]
+
+lemma one_le_alternatingGeometricSeries_radius [Nontrivial A] :
+ 1 ≤ (alternatingGeometricSeries 𝕜 A).radius := by
+ simpa only [FormalMultilinearSeries.radius_compNeg,
+ alternatingGeometricSeries_eq_formalMultilinearSeries_geometric_comp_neg]
+ using one_le_formalMultilinearSeries_geometric_radius 𝕜 A
+
+lemma alternatingGeometricSeries_radius [NormOneClass A] :
+ (alternatingGeometricSeries 𝕜 A).radius = 1 :=
+ FormalMultilinearSeries.ofScalars_radius_eq_of_tendsto A _ one_ne_zero (by simp)
+
+lemma hasFPowerSeriesOnBall_inverse_one_add [HasSummableGeomSeries A] [Nontrivial A] :
+ HasFPowerSeriesOnBall (fun x : A ↦ Ring.inverse (1 + x))
+ (alternatingGeometricSeries 𝕜 A) 0 1 := by
+ rw [alternatingGeometricSeries_eq_formalMultilinearSeries_geometric_comp_neg]
+ convert_to HasFPowerSeriesOnBall ((fun x ↦ Ring.inverse (1 - x)) ∘ (-ContinuousLinearMap.id 𝕜 A))
+ ((formalMultilinearSeries_geometric 𝕜 A).compContinuousLinearMap (-ContinuousLinearMap.id 𝕜 A))
+ 0 1
+ · ext; simp
+ convert HasFPowerSeriesOnBall.compContinuousLinearMap _ (r := 1)
+ · simp [← ofReal_norm]
+ · simpa using (hasFPowerSeriesOnBall_inverse_one_sub 𝕜 A)
+
+@[fun_prop]
+lemma analyticAt_inverse_one_add [HasSummableGeomSeries A] [Nontrivial A] :
+ AnalyticAt 𝕜 (fun x : A ↦ Ring.inverse (1 + x)) 0 :=
+ ⟨_, ⟨_, hasFPowerSeriesOnBall_inverse_one_add 𝕜 A⟩⟩
+
end Geometric
/-- If `A` is a normed algebra over `𝕜` with summable geometric series, then inversion on `A` is
@@ -871,6 +983,17 @@ variable (𝕝) in
lemma analyticAt_inv_one_sub : AnalyticAt 𝕜 (fun x : 𝕝 ↦ (1 - x)⁻¹) 0 :=
⟨_, ⟨_, hasFPowerSeriesOnBall_inv_one_sub 𝕜 𝕝⟩⟩
+variable (𝕜 𝕝) in
+lemma hasFPowerSeriesOnBall_inv_one_add :
+ HasFPowerSeriesOnBall (fun x : 𝕝 ↦ (1 + x)⁻¹) (alternatingGeometricSeries 𝕜 𝕝) 0 1 := by
+ convert! hasFPowerSeriesOnBall_inverse_one_add 𝕜 𝕝
+ exact Ring.inverse_eq_inv'.symm
+
+variable (𝕝) in
+@[fun_prop]
+lemma analyticAt_inv_one_add : AnalyticAt 𝕜 (fun x : 𝕝 ↦ (1 + x)⁻¹) 0 :=
+ ⟨_, ⟨_, hasFPowerSeriesOnBall_inv_one_add 𝕜 𝕝⟩⟩
+
/-- If `𝕝` is a normed field extension of `𝕜`, then the inverse map `𝕝 → 𝕝` is `𝕜`-analytic
away from 0. -/
@[fun_prop]
@@ -1159,68 +1282,3 @@ theorem HasFPowerSeriesWithinAt.unshift (hf : HasFPowerSeriesWithinAt f pf s x)
hrf.unshift.hasFPowerSeriesWithinAt
end
-
-/-!
-### Composition with a linear map
--/
-
-section compContinuousLinearMap
-
-variable {u : E →L[𝕜] F} {f : F → G} {pf : FormalMultilinearSeries 𝕜 F G} {s : Set F} {x : E}
- {r : ℝ≥0∞}
-
-theorem HasFPowerSeriesWithinOnBall.compContinuousLinearMap
- (hf : HasFPowerSeriesWithinOnBall f pf s (u x) r) :
- HasFPowerSeriesWithinOnBall (f ∘ u) (pf.compContinuousLinearMap u) (u ⁻¹' s) x (r / ‖u‖ₑ) where
- r_le := by
- calc
- _ ≤ pf.radius / ‖u‖ₑ := by
- gcongr
- exact hf.r_le
- _ ≤ _ := pf.div_le_radius_compContinuousLinearMap _
- r_pos := by
- simp only [ENNReal.div_pos_iff, ne_eq, enorm_ne_top, not_false_eq_true, and_true]
- exact pos_iff_ne_zero.mp hf.r_pos
- hasSum hy1 hy2 := by
- convert! hf.hasSum _ _
- · simp
- · simp only [Set.mem_insert_iff, add_eq_left, Set.mem_preimage, map_add] at hy1 ⊢
- rcases hy1 with (hy1 | hy1) <;> simp [hy1]
- · simp only [Metric.eball, edist_zero_right, Set.mem_setOf_eq] at hy2 ⊢
- exact lt_of_le_of_lt (ContinuousLinearMap.le_opNorm_enorm _ _) (mul_lt_of_lt_div' hy2)
-
-theorem HasFPowerSeriesOnBall.compContinuousLinearMap (hf : HasFPowerSeriesOnBall f pf (u x) r) :
- HasFPowerSeriesOnBall (f ∘ u) (pf.compContinuousLinearMap u) x (r / ‖u‖ₑ) := by
- rw [← hasFPowerSeriesWithinOnBall_univ] at hf ⊢
- exact hf.compContinuousLinearMap
-
-theorem HasFPowerSeriesAt.compContinuousLinearMap (hf : HasFPowerSeriesAt f pf (u x)) :
- HasFPowerSeriesAt (f ∘ u) (pf.compContinuousLinearMap u) x :=
- let ⟨r, hr⟩ := hf
- ⟨r / ‖u‖ₑ, hr.compContinuousLinearMap⟩
-
-theorem HasFPowerSeriesWithinAt.compContinuousLinearMap
- (hf : HasFPowerSeriesWithinAt f pf s (u x)) :
- HasFPowerSeriesWithinAt (f ∘ u) (pf.compContinuousLinearMap u) (u ⁻¹' s) x :=
- let ⟨r, hr⟩ := hf
- ⟨r / ‖u‖ₑ, hr.compContinuousLinearMap⟩
-
-theorem AnalyticAt.compContinuousLinearMap (hf : AnalyticAt 𝕜 f (u x)) :
- AnalyticAt 𝕜 (f ∘ u) x :=
- let ⟨p, hp⟩ := hf
- ⟨p.compContinuousLinearMap u, hp.compContinuousLinearMap⟩
-
-theorem AnalyticAtWithin.compContinuousLinearMap (hf : AnalyticWithinAt 𝕜 f s (u x)) :
- AnalyticWithinAt 𝕜 (f ∘ u) (u ⁻¹' s) x :=
- let ⟨p, hp⟩ := hf
- ⟨p.compContinuousLinearMap u, hp.compContinuousLinearMap⟩
-
-theorem AnalyticOn.compContinuousLinearMap (hf : AnalyticOn 𝕜 f s) :
- AnalyticOn 𝕜 (f ∘ u) (u ⁻¹' s) := fun x hx =>
- AnalyticAtWithin.compContinuousLinearMap (hf (u x) hx)
-
-theorem AnalyticOnNhd.compContinuousLinearMap (hf : AnalyticOnNhd 𝕜 f s) :
- AnalyticOnNhd 𝕜 (f ∘ u) (u ⁻¹' s) := fun x hx =>
- AnalyticAt.compContinuousLinearMap (hf (u x) hx)
-
-end compContinuousLinearMap
From 62dc8f267ea2dc711dd18b746f454fcf013be912 Mon Sep 17 00:00:00 2001
From: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com>
Date: Wed, 24 Jun 2026 00:45:07 +0000
Subject: [PATCH 0305/1300] =?UTF-8?q?feat(GroupTheory/IsPerfect):=20Gr?=
=?UTF-8?q?=C3=BCn's=20lemma=20(#39956)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Grün's lemma: In a perfect group (`commutator G = ⊤`), `center (G ⧸ center G) = ⊥`.
Also the `derivedSeries` and the `lowerCentralSeries` are a constant `⊤`, and the `upperCentralSeries` starts with `⊥` and afterwards is a constant `center G`.
---
Mathlib/Algebra/Group/Subgroup/Ker.lean | 22 +++++++++++
Mathlib/GroupTheory/Commutator/Basic.lean | 22 +++++++++++
Mathlib/GroupTheory/IsPerfect.lean | 37 +++++++++++++++++++
Mathlib/GroupTheory/Nilpotent.lean | 8 ++++
Mathlib/GroupTheory/Subgroup/Centralizer.lean | 4 ++
5 files changed, 93 insertions(+)
diff --git a/Mathlib/Algebra/Group/Subgroup/Ker.lean b/Mathlib/Algebra/Group/Subgroup/Ker.lean
index 5f0826515c0693..17796f56170410 100644
--- a/Mathlib/Algebra/Group/Subgroup/Ker.lean
+++ b/Mathlib/Algebra/Group/Subgroup/Ker.lean
@@ -82,6 +82,11 @@ theorem mem_range {f : G →* N} {y : N} : y ∈ f.range ↔ ∃ x, f x = y :=
@[to_additive]
theorem range_eq_map (f : G →* N) : f.range = (⊤ : Subgroup G).map f := by ext; simp
+@[to_additive (attr := simp)]
+theorem comap_range_self (f : G →* N) : f.range.comap f = ⊤ := by
+ ext
+ simp
+
@[to_additive]
instance _root_.Subgroup.range_isMulCommutative {G : Type*} [Group G] [IsMulCommutative G]
{N : Type*} [Group N] (f : G →* N) :
@@ -270,6 +275,10 @@ theorem comap_ker {P : Type*} [MulOneClass P] (g : N →* P) (f : G →* N) :
theorem comap_bot (f : G →* N) : (⊥ : Subgroup N).comap f = f.ker :=
rfl
+@[to_additive]
+theorem ker_le_comap (f : G →* N) (H : Subgroup N) : f.ker ≤ H.comap f :=
+ comap_mono bot_le
+
@[to_additive (attr := simp)]
theorem ker_restrict (f : G →* M) : (f.restrict K).ker = f.ker.subgroupOf K :=
rfl
@@ -401,6 +410,10 @@ theorem map_eq_bot_iff {f : G →* N} : H.map f = ⊥ ↔ H ≤ f.ker :=
theorem map_eq_bot_iff_of_injective {f : G →* N} (hf : Function.Injective f) :
H.map f = ⊥ ↔ H = ⊥ := by rw [map_eq_bot_iff, f.ker_eq_bot hf, le_bot_iff]
+@[to_additive (attr := simp)]
+theorem map_ker_self (f : G →* N) : f.ker.map f = ⊥ := by
+ rw [map_eq_bot_iff]
+
open MonoidHom
variable (f : G →* N)
@@ -458,6 +471,15 @@ theorem comap_lt_comap_of_surjective {f : G →* N} {K L : Subgroup N} (hf : Fun
theorem comap_injective {f : G →* N} (h : Function.Surjective f) : Function.Injective (comap f) :=
fun K L => by simp only [le_antisymm_iff, comap_le_comap_of_surjective h, imp_self]
+@[to_additive (attr := simp)]
+theorem comap_eq_ker {f : G →* N} {H : Subgroup N} : H.comap f = f.ker ↔ Disjoint H f.range := by
+ rw [← H.ker_le_comap f |>.ge_iff_eq', ← map_eq_bot_iff, map_comap_eq, disjoint_iff, inf_comm]
+
+@[to_additive]
+theorem comap_eq_ker_of_surjective {f : G →* N} (hf : Surjective f) {H : Subgroup N} :
+ H.comap f = f.ker ↔ H = ⊥ := by
+ rw [comap_eq_ker, f.range_eq_top_of_surjective hf, disjoint_top]
+
@[to_additive]
theorem comap_map_eq_self {f : G →* N} {H : Subgroup G} (h : f.ker ≤ H) :
comap f (map f H) = H := by
diff --git a/Mathlib/GroupTheory/Commutator/Basic.lean b/Mathlib/GroupTheory/Commutator/Basic.lean
index c2602aad4ef00e..bae1b8b7e53cd9 100644
--- a/Mathlib/GroupTheory/Commutator/Basic.lean
+++ b/Mathlib/GroupTheory/Commutator/Basic.lean
@@ -183,6 +183,28 @@ theorem commutator_comm_le : ⁅H₁, H₂⁆ ≤ ⁅H₂, H₁⁆ :=
theorem commutator_comm : ⁅H₁, H₂⁆ = ⁅H₂, H₁⁆ :=
le_antisymm (commutator_comm_le H₁ H₂) (commutator_comm_le H₂ H₁)
+@[to_additive]
+theorem commutator_self_eq_bot_iff : ⁅H, H⁆ = ⊥ ↔ IsMulCommutative H := by
+ rw [commutator_eq_bot_iff_le_centralizer, le_centralizer_iff_isMulCommutative]
+
+@[to_additive (attr := simp)]
+theorem commutator_top_right_eq_bot_iff_le_center : ⁅H, (⊤ : Subgroup G)⁆ = ⊥ ↔ H ≤ center G := by
+ rw [commutator_eq_bot_iff_le_centralizer, coe_top, centralizer_univ]
+
+@[to_additive (attr := simp)]
+theorem commutator_top_left_eq_bot_iff_le_center : ⁅(⊤ : Subgroup G), H⁆ = ⊥ ↔ H ≤ center G := by
+ rw [commutator_comm, commutator_top_right_eq_bot_iff_le_center]
+
+variable (H) in
+@[to_additive (attr := simp)]
+theorem commutator_center_right : ⁅H, center G⁆ = ⊥ := by
+ simp [commutator_eq_bot_iff_le_centralizer]
+
+variable (H) in
+@[to_additive (attr := simp)]
+theorem commutator_center_left : ⁅center G, H⁆ = ⊥ :=
+ commutator_eq_bot_iff_le_centralizer.mpr <| center_le_centralizer _
+
section Normal
@[to_additive]
diff --git a/Mathlib/GroupTheory/IsPerfect.lean b/Mathlib/GroupTheory/IsPerfect.lean
index 878b92ee0cd9bc..ca51df8499158c 100644
--- a/Mathlib/GroupTheory/IsPerfect.lean
+++ b/Mathlib/GroupTheory/IsPerfect.lean
@@ -104,4 +104,41 @@ lemma ofSurjective [IsPerfect G] (hf : Function.Surjective f) : IsPerfect G' :=
instance instQuotientSubgroup [H.Normal] [IsPerfect G] : IsPerfect (G ⧸ H) :=
ofSurjective (QuotientGroup.mk'_surjective H)
+variable (G) in
+@[simp]
+theorem derivedSeries_eq_top [IsPerfect G] (n : ℕ) : derivedSeries G n = ⊤ := by
+ match n with
+ | 0 => simp
+ | n + 1 =>
+ rw [derivedSeries_succ, derivedSeries_eq_top, commutator_eq_self]
+
+@[simp]
+theorem lowerCentralSeries_eq_top (H : Subgroup G) [IsPerfect H] (n : ℕ) :
+ H.lowerCentralSeries n = H := by
+ match n with
+ | 0 => simp
+ | n + 1 =>
+ rw [Subgroup.lowerCentralSeries_succ, lowerCentralSeries_eq_top, commutator_eq_self]
+
+variable (G) in
+@[simp]
+theorem upperCentralSeries_eq_center [IsPerfect G] {n : ℕ} (hn : n ≠ 0) :
+ Subgroup.upperCentralSeries G n = center G := by
+ rw [← Subgroup.upperCentralSeries_one, eq_comm]
+ apply Subgroup.upperCentralSeries.eq_ge_of_eq_succ <| by lia
+ apply le_antisymm <| Subgroup.upperCentralSeries_mono G one_le_two
+ rw [Subgroup.upperCentralSeries_one, ← commutator_top_right_eq_bot_iff_le_center,
+ ← commutator_eq_top, commutator_comm, commutator_def]
+ suffices ⁅⁅Subgroup.upperCentralSeries G 2, ⊤⁆, ⊤⁆ = ⊥ from
+ commutator_commutator_eq_bot_of_rotate (by simpa [commutator_comm]) this
+ rw [commutator_top_right_eq_bot_iff_le_center, ← Subgroup.upperCentralSeries_one]
+ apply commutator_upperCentralSeries_top_le
+
+variable (G) in
+/-- **Grün's lemma** -/
+theorem center_quotient_center_eq_bot [IsPerfect G] : center (G ⧸ center G) = ⊥ := by
+ rw [← Subgroup.upperCentralSeries_one (G ⧸ center G),
+ ← comap_eq_ker_of_surjective <| QuotientGroup.mk'_surjective _, QuotientGroup.ker_mk',
+ Subgroup.comap_upperCentralSeries_quotient_center, upperCentralSeries_eq_center G <| by lia]
+
end Group.IsPerfect
diff --git a/Mathlib/GroupTheory/Nilpotent.lean b/Mathlib/GroupTheory/Nilpotent.lean
index 65630cb084419b..f8c36afe217b70 100644
--- a/Mathlib/GroupTheory/Nilpotent.lean
+++ b/Mathlib/GroupTheory/Nilpotent.lean
@@ -218,6 +218,14 @@ theorem mem_upperCentralSeries_succ_iff {n : ℕ} {x : G} :
x ∈ upperCentralSeries G (n + 1) ↔ ∀ y : G, ⁅x, y⁆ ∈ upperCentralSeries G n :=
Iff.rfl
+variable (G) in
+@[to_additive]
+theorem commutator_upperCentralSeries_top_le (n : ℕ) :
+ ⁅upperCentralSeries G (n + 1), ⊤⁆ ≤ upperCentralSeries G n := by
+ apply closure_le _ |>.mpr
+ rintro _ ⟨h, hh, g, _, rfl⟩
+ exact mem_upperCentralSeries_succ_iff.mp hh g
+
@[to_additive (attr := simp)]
lemma comap_upperCentralSeries {H : Type*} [Group H] (e : H ≃* G) :
∀ n, (upperCentralSeries G n).comap e = upperCentralSeries H n
diff --git a/Mathlib/GroupTheory/Subgroup/Centralizer.lean b/Mathlib/GroupTheory/Subgroup/Centralizer.lean
index f0954a435f7858..110e7845251dea 100644
--- a/Mathlib/GroupTheory/Subgroup/Centralizer.lean
+++ b/Mathlib/GroupTheory/Subgroup/Centralizer.lean
@@ -74,6 +74,10 @@ theorem centralizer_le {s t : Set G} (h : s ⊆ t) : centralizer t ≤ centraliz
theorem centralizer_eq_top_iff_subset {s : Set G} : centralizer s = ⊤ ↔ s ⊆ center G :=
SetLike.ext'_iff.trans Set.centralizer_eq_top_iff_subset
+@[to_additive (attr := simp)]
+theorem centralizer_center : centralizer (center G : Set G) = ⊤ :=
+ centralizer_eq_top_iff_subset.mpr le_rfl
+
@[to_additive]
theorem map_centralizer_le_centralizer_image (s : Set G) (f : G →* G') :
(Subgroup.centralizer s).map f ≤ Subgroup.centralizer (f '' s) := by
From 47e5a92e3e50719cfd16002ac2dd144af7cf947e Mon Sep 17 00:00:00 2001
From: Joris Roos <170825715+roos-j@users.noreply.github.com>
Date: Wed, 24 Jun 2026 02:55:26 +0000
Subject: [PATCH 0306/1300] feat(Analysis/Calculus): add `uniqueDiffOn_uIcc`
(#40702)
---
Mathlib/Analysis/Calculus/TangentCone/Real.lean | 3 +++
1 file changed, 3 insertions(+)
diff --git a/Mathlib/Analysis/Calculus/TangentCone/Real.lean b/Mathlib/Analysis/Calculus/TangentCone/Real.lean
index 1f389e2985e4c9..cae58175fd1a14 100644
--- a/Mathlib/Analysis/Calculus/TangentCone/Real.lean
+++ b/Mathlib/Analysis/Calculus/TangentCone/Real.lean
@@ -99,6 +99,9 @@ theorem uniqueDiffOn_Iio (a : ℝ) : UniqueDiffOn ℝ (Iio a) :=
theorem uniqueDiffOn_Icc {a b : ℝ} (hab : a < b) : UniqueDiffOn ℝ (Icc a b) :=
uniqueDiffOn_convex (convex_Icc a b) <| by simp only [interior_Icc, nonempty_Ioo, hab]
+theorem uniqueDiffOn_uIcc {a b : ℝ} (hab : a ≠ b) : UniqueDiffOn ℝ (uIcc a b) :=
+ uniqueDiffOn_Icc <| min_lt_max.mpr hab
+
theorem uniqueDiffOn_Ico (a b : ℝ) : UniqueDiffOn ℝ (Ico a b) :=
if hab : a < b then
uniqueDiffOn_convex (convex_Ico a b) <| by simp only [interior_Ico, nonempty_Ioo, hab]
From db8879cb2c88093b5b369570a01b503c871c8151 Mon Sep 17 00:00:00 2001
From: Bryan Gin-ge Chen <5209952+bryangingechen@users.noreply.github.com>
Date: Wed, 24 Jun 2026 03:53:14 +0000
Subject: [PATCH 0307/1300] ci: split build job so post-build chain overlaps
test+lint (#40233)
Peel `test` and `lint` out of the self-hosted `build` job into a new self-hosted `test_lint` job, so the GitHub-hosted post-build chain (`upload_cache` -> `post_steps`) no longer waits behind the ~300s test+lint tail of `build`.
- New action `setup-build-env`: contains shared setup steps between `build` and `test_lint` (added in #40733 by @marcelolynch)
- New `test_lint` job (self-hosted, `needs: [build, upload_cache]`): fetches this run's oleans from an artifact uploaded at the end of `build` + cache from `master` using the action added in #40678 + verifies the Mathlib cache is complete (mirrors `post_steps`), then runs test, lint, and the nightly-testing comment.
- `build` job: `test`/`lint` removed; `noisy` moved up to right after the Counterexamples build (it only needs the just-built oleans), so it stays in `build` and `test_lint` need not fetch Archive/Counterexamples. `test`/`lint` outputs move to `test_lint`.
- `final`: now also `needs: test_lint` and requires its success.
Prepared with Claude code
Co-authored-by: Marcelo Lynch
---
.github/actions/setup-build-env/action.yml | 157 ++++++++++
.github/workflows/build_template.yml | 324 +++++++++------------
bors.toml | 2 +-
3 files changed, 302 insertions(+), 181 deletions(-)
create mode 100644 .github/actions/setup-build-env/action.yml
diff --git a/.github/actions/setup-build-env/action.yml b/.github/actions/setup-build-env/action.yml
new file mode 100644
index 00000000000000..31298cdb1a88d6
--- /dev/null
+++ b/.github/actions/setup-build-env/action.yml
@@ -0,0 +1,157 @@
+# Shared runner setup for the self-hosted `build` and `test_lint` jobs in
+# build_template.yml. Both run on the same Hoskinson pool and need the same
+# preparation before they diverge (build → build + stage, test_lint →
+# fetch + test + lint): toolchain hygiene, jq, the cache-trust and get-tools
+# wiring, the PR-branch checkout, the elan toolchain and LEAN_SRC_PATH
+# environment, and the dependency download.
+#
+# The caller runs `Checkout local actions` (sparse `.github/actions` →
+# `workflow-actions/`) before invoking this action: that checkout puts both this
+# action and the sibling actions it calls (`cache-trust-dispatch`, `get-tools`)
+# on disk under `workflow-actions/`, so the `uses: ./workflow-actions/...` paths
+# below resolve relative to the workspace root, as in the calling workflow.
+#
+# Each `run:` step sets its own `shell:`, since composite steps do not inherit the
+# job's landrun `defaults.run.shell`.
+name: Set up build environment
+description: Shared self-hosted runner setup for the build and test_lint jobs.
+
+inputs:
+ pr_branch_ref:
+ description: Git ref of the PR branch to check out and build.
+ required: true
+ tools_branch_ref:
+ description: Git ref to build the CI tools from when not using the prebuilt artifact.
+ required: false
+ default: ''
+
+runs:
+ using: composite
+ steps:
+ # Prune old toolchains from `~/.elan`. That directory is mounted from the host
+ # and shared across the ephemeral job containers, so toolchains accumulate and
+ # nothing else trims them (host-side housekeeping only trims the mathlib `.ltar`
+ # cache). Keep the 5 most recent plus `nightly`/`stable`.
+ - name: prune old toolchains
+ shell: bash # just deletes old files; safe to run outside landrun
+ run: |
+ # Make sure to delete both the `~/.elan/toolchains/X` directory and the `~/.elan/update-hashes/X` file.
+ # Skip symbolic links (`-type d`), the current directory (`! -name .`), and `nightly` and `stable`.
+ if cd ~/.elan/toolchains && find . -maxdepth 1 -type d ! -name . -print0 | xargs -0 ls -1td | grep -v 'nightly$' | grep -v 'stable$' | tail -n +6 | xargs -I {} sh -c 'echo {} && rm -rf "{}" && rm "../update-hashes/{}"'; then
+ : # Do nothing on success
+ else
+ : # Do nothing on failure, but suppress errors
+ fi
+
+ # The Hoskinson runners may not have jq installed, so do that now.
+ - name: 'Setup jq'
+ uses: dcarbone/install-jq-action@b7ef57d46ece78760b4019dbc4080a1ba2a40b45 # v3.2.0
+
+ # Compute the trust-classified container target and read fallback for this
+ # job. Sets MATHLIB_CACHE_FROM / MATHLIB_CACHE_PRIMARY in env so every
+ # subsequent `cache get` inherits them without per-call flag plumbing. Loaded
+ # from the trust-rooted `workflow-actions/` checkout, not the PR branch.
+ - name: Compute cache trust dispatch
+ uses: ./workflow-actions/.github/actions/cache-trust-dispatch
+ with:
+ repo: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
+ branch: ${{ github.head_ref || github.ref_name }}
+ head-sha: ${{ github.event.pull_request.head.sha || github.sha }}
+
+ # Checkout the PR branch into a subdirectory. HEAD only (fetch-depth: 1) is
+ # enough: the cache is fetched HEAD-scoped and warmed from the master snapshot,
+ # so no parent-commit history is needed. This is untrusted (potentially fork)
+ # code we build, so don't leave the GITHUB_TOKEN in pr-branch/.git/config where
+ # that code could read it.
+ - name: Checkout PR branch
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ with:
+ ref: ${{ inputs.pr_branch_ref }}
+ fetch-depth: 1
+ path: pr-branch
+ persist-credentials: false
+
+ # Create empty directories so landrun doesn't complain.
+ - name: Create empty directories
+ shell: bash # We need to run this outside landrun, as it is a prerequisite for landrun!
+ run: |
+ mkdir -p pr-branch/.lake/
+ mkdir -p .cache/mathlib/
+ mkdir -p _work
+
+ # NOTE: if you copy this, consider using `leanprover/lean-action` instead.
+ # We install manually, to avoid running lean outside landrun.
+ - name: install elan
+ shell: bash
+ run: |
+ set -o pipefail
+ curl -o elan-init.sh -sSfL https://elan.lean-lang.org/elan-init.sh
+ chmod +x elan-init.sh
+ ./elan-init.sh -y --default-toolchain none
+ echo "$HOME/.elan/bin" >> "${GITHUB_PATH}"
+
+ - name: set toolchain directory
+ shell: bash
+ run: |
+ cd pr-branch
+ # Get the lake binary path from elan and extract toolchain directory
+ LAKE_PATH=$(elan which lake)
+ echo "Lake path: $LAKE_PATH"
+
+ # Extract the toolchain directory by removing /bin/lake from the end
+ TOOLCHAIN_DIR=$(dirname "$LAKE_PATH")
+ TOOLCHAIN_DIR=$(dirname "$TOOLCHAIN_DIR")
+ echo "Toolchain directory: $TOOLCHAIN_DIR"
+
+ # Set it as an environment variable for subsequent steps
+ echo "TOOLCHAIN_DIR=$TOOLCHAIN_DIR" >> "$GITHUB_ENV"
+
+ - name: set LEAN_SRC_PATH
+ shell: bash
+ run: |
+ cd pr-branch
+
+ # Start with the base paths
+ LEAN_SRC_PATH=".:$TOOLCHAIN_DIR/src/lean/lake"
+
+ # Extract package names from lake-manifest.json and validate them
+ # Only allow A-Z, a-z, 0-9, _, and - characters
+ # Build the LEAN_SRC_PATH by appending each validated package
+ PACKAGE_NAMES=$(jq -r '.packages[].name' lake-manifest.json)
+ for pkg in $PACKAGE_NAMES; do
+ if [[ "$pkg" =~ ^[A-Za-z0-9_-]+$ ]]; then
+ LEAN_SRC_PATH="$LEAN_SRC_PATH:.lake/packages/$pkg"
+ else
+ echo "Warning: Skipping invalid package name: $pkg"
+ fi
+ done
+
+ echo "LEAN_SRC_PATH=$LEAN_SRC_PATH"
+
+ # Set it as an environment variable for subsequent steps
+ echo "LEAN_SRC_PATH=$LEAN_SRC_PATH" >> "$GITHUB_ENV"
+
+ # Populate `tools-branch/` with the trusted CI tooling (the `cache` binary and
+ # the `lake-build-*` helper scripts invoked by path). Fast path: download the
+ # prebuilt `tools-bin` artifact published from master (canonical mathlib4 only,
+ # and only when the branch under test doesn't change the cache tool — get-tools
+ # makes that comparison against `source_dir`). Source build otherwise. See the
+ # get-tools action for the full trust rationale.
+ - name: Get CI tools
+ uses: ./workflow-actions/.github/actions/get-tools
+ with:
+ use_artifact: ${{ inputs.tools_branch_ref == '' && github.repository == 'leanprover-community/mathlib4' }}
+ tools_source_ref: ${{ inputs.tools_branch_ref != '' && inputs.tools_branch_ref || (github.event.pull_request.head.repo.fork && 'master' || inputs.pr_branch_ref) }}
+ source_dir: ${{ github.event.pull_request.head.repo.fork != true && 'pr-branch' || '' }}
+ github_token: ${{ github.token }}
+
+ - name: download dependencies
+ # We need network access to download dependencies. We run this inside
+ # landrun, but restrict disk access:
+ # - --rox access to `~/.elan` and `~/actions-runner/_work` (GitHub CI needs this)
+ # - --unrestricted-network as we need this to download dependencies
+ # - git needs read only access to `/etc`.
+ shell: landrun --unrestricted-network --rox /etc --rox /usr --rw /dev --rox /home/lean/.elan --rox /home/lean/actions-runner/_work --rw pr-branch/.lake/ --env PATH --env HOME --env GITHUB_OUTPUT --env CI -- bash -euxo pipefail {0}
+ run: |
+ cd pr-branch
+ lake env
diff --git a/.github/workflows/build_template.yml b/.github/workflows/build_template.yml
index 518274f97c7627..9e742026b1aec5 100644
--- a/.github/workflows/build_template.yml
+++ b/.github/workflows/build_template.yml
@@ -57,11 +57,11 @@ jobs:
archive-outcome: ${{ steps.archive.outcome }}
counterexamples-outcome: ${{ steps.counterexamples.outcome }}
cache-staging-has-files: ${{ steps.cache_staging_check.outputs.has_files }}
- lint-outcome: ${{ steps.lint.outcome }}
mk_all-outcome: ${{ steps.mk_all.outcome }}
noisy-outcome: ${{ steps.noisy.outcome }}
# shake-outcome: ${{ steps.shake.outcome }}
- test-outcome: ${{ steps.test.outcome }}
+ # `test`/`lint` outcomes are exposed by the `test_lint` job (and consumed by
+ # its nightly-testing comment step).
defaults: # On Hoskinson runners, landrun is already installed.
run: # note that .pr-branch/.lake must be created in a step below before we use this
shell: landrun --rox /usr --ro /etc/timezone --rw /dev --rox /home/lean/.elan --rox /home/lean/actions-runner/_work --rox /home/lean/.cache/mathlib/ --rw pr-branch/.lake/ --env PATH --env HOME --env GITHUB_OUTPUT --env CI -- bash -euxo pipefail {0}
@@ -77,26 +77,6 @@ jobs:
shell: bash # there is no script body, so this is safe to "run" outside landrun.
run: |
# We just populate the env vars for this step to make them viewable in the logs
- - name: cleanup
- shell: bash # This *just* deletes old files, so is safe to run outside landrun.
- run: |
- if ! find . -mindepth 1 -exec rm -rf -- {} +; then
- echo "ERROR: Initial cleanup failed, waiting 5 seconds and retrying..."
- sleep 5
- find . -mindepth 1 -exec rm -rf -- {} +
- fi
- # Delete all but the 5 most recent toolchains.
- # Make sure to delete both the `~/.elan/toolchains/X` directory and the `~/.elan/update-hashes/X` file.
- # Skip symbolic links (`-type d`), the current directory (`! -name .`), and `nightly` and `stable`.
- if cd ~/.elan/toolchains && find . -maxdepth 1 -type d ! -name . -print0 | xargs -0 ls -1td | grep -v 'nightly$' | grep -v 'stable$' | tail -n +6 | xargs -I {} sh -c 'echo {} && rm -rf "{}" && rm "../update-hashes/{}"'; then
- : # Do nothing on success
- else
- : # Do nothing on failure, but suppress errors
- fi
-
- # The Hoskinson runners may not have jq installed, so do that now.
- - name: 'Setup jq'
- uses: dcarbone/install-jq-action@b7ef57d46ece78760b4019dbc4080a1ba2a40b45 # v3.2.0
- name: Checkout local actions
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
@@ -106,43 +86,15 @@ jobs:
sparse-checkout: .github/actions
path: workflow-actions
- # We need to 'duplicate' this step below because GitHub Actions does not let a `uses:` step
- # conditionally omit a single `with:` key, and passing `ref: ''` would override
- # the composite action's default to the empty string instead of falling back to it
- # Note only one of the two below will actually run in any given run
- - name: Get mathlib-ci
- if: ${{ inputs.mathlib_ci_ref == '' }}
- uses: ./workflow-actions/.github/actions/get-mathlib-ci
-
- - name: Get mathlib-ci
- if: ${{ inputs.mathlib_ci_ref != '' }}
- uses: ./workflow-actions/.github/actions/get-mathlib-ci
- with:
- ref: ${{ inputs.mathlib_ci_ref }}
-
- # Compute the trust-classified container target and read fallback
- # for this job. Sets MATHLIB_CACHE_FROM / MATHLIB_CACHE_PRIMARY in
- # env so every subsequent `cache get` in this job inherits them
- # without per-call flag plumbing. Loaded from master via the sparse
- # `workflow-actions/` checkout above, not from the PR branch — this
- # keeps the trust policy out of fork-controllable file paths.
- - name: Compute cache trust dispatch
- uses: ./workflow-actions/.github/actions/cache-trust-dispatch
+ # Shared self-hosted setup (toolchain prune, jq, cache-trust dispatch,
+ # PR-branch checkout, elan + toolchain/LEAN_SRC_PATH env, CI tools, dependency
+ # download); see the action for per-step rationale. It is loaded from the
+ # `Checkout local actions` checkout above, which therefore runs first.
+ - name: Set up build environment
+ uses: ./workflow-actions/.github/actions/setup-build-env
with:
- repo: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
- branch: ${{ github.head_ref || github.ref_name }}
- head-sha: ${{ github.event.pull_request.head.sha || github.sha }}
-
- # Checkout the PR branch into a subdirectory
- - name: Checkout PR branch
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- with:
- ref: ${{ inputs.pr_branch_ref }}
- fetch-depth: 2 # we may fetch cache from the commit before this one (or earlier)
- path: pr-branch
- # This is an untrusted (potentially fork) checkout whose code we build.
- # Don't leave the GITHUB_TOKEN in pr-branch/.git/config, where that code could read it.
- persist-credentials: false
+ pr_branch_ref: ${{ inputs.pr_branch_ref }}
+ tools_branch_ref: ${{ inputs.tools_branch_ref }}
# TEMPORARY (cache storage-layout migration, PR #40035): remove once
# active branches have merged the new cache tool.
@@ -194,103 +146,6 @@ jobs:
cp ../lean-toolchain .
echo "lean-toolchain copied successfully to DownstreamTest."
- # Create empty directories so landrun doesn't complain
- - name: Create empty directories
- shell: bash # We need to run this outside landrun, as it is a prerequisite for landrun!
- run: |
- mkdir -p pr-branch/.lake/
- mkdir -p .cache/mathlib/
- mkdir -p _work
-
- # NOTE: if you copy this, consider using `leanprover/lean-action` instead.
- # We install manually, to avoid running lean outside landrun.
- - name: install elan
- shell: bash
- run: |
- set -o pipefail
- curl -o elan-init.sh -sSfL https://elan.lean-lang.org/elan-init.sh
- chmod +x elan-init.sh
- ./elan-init.sh -y --default-toolchain none
- echo "$HOME/.elan/bin" >> "${GITHUB_PATH}"
-
- - name: set toolchain directory
- shell: bash
- run: |
- cd pr-branch
- # Get the lake binary path from elan and extract toolchain directory
- LAKE_PATH=$(elan which lake)
- echo "Lake path: $LAKE_PATH"
-
- # Extract the toolchain directory by removing /bin/lake from the end
- TOOLCHAIN_DIR=$(dirname "$LAKE_PATH")
- TOOLCHAIN_DIR=$(dirname "$TOOLCHAIN_DIR")
- echo "Toolchain directory: $TOOLCHAIN_DIR"
-
- # Set it as an environment variable for subsequent steps
- echo "TOOLCHAIN_DIR=$TOOLCHAIN_DIR" >> "$GITHUB_ENV"
-
- - name: set LEAN_SRC_PATH
- shell: bash
- run: |
- cd pr-branch
-
- # Start with the base paths
- LEAN_SRC_PATH=".:$TOOLCHAIN_DIR/src/lean/lake"
-
- # Extract package names from lake-manifest.json and validate them
- # Only allow A-Z, a-z, 0-9, _, and - characters
- # Build the LEAN_SRC_PATH by appending each validated package
- PACKAGE_NAMES=$(jq -r '.packages[].name' lake-manifest.json)
- for pkg in $PACKAGE_NAMES; do
- if [[ "$pkg" =~ ^[A-Za-z0-9_-]+$ ]]; then
- LEAN_SRC_PATH="$LEAN_SRC_PATH:.lake/packages/$pkg"
- else
- echo "Warning: Skipping invalid package name: $pkg"
- fi
- done
-
- echo "LEAN_SRC_PATH=$LEAN_SRC_PATH"
-
- # Set it as an environment variable for subsequent steps
- echo "LEAN_SRC_PATH=$LEAN_SRC_PATH" >> "$GITHUB_ENV"
-
- # Populate `tools-branch/` with the trusted CI tooling (the `cache` binary and
- # the `lake-build-*` helper scripts invoked by path below).
- #
- # - Fast path: download the prebuilt `tools-bin` artifact published from
- # `master` by `publish_tools.yml` — only on canonical mathlib4, and only
- # when the branch under test doesn't change the cache tool. `get-tools`
- # makes that comparison itself against `source_dir` (the already-checked-
- # out `pr-branch`), so the tool-source path list lives in the action, not
- # duplicated here.
- # - Source build: from `tools_source_ref`, pointed at the branch *under
- # test* (`pr_branch_ref`) — bors `staging`/`trying`, in-repo dev branches,
- # and the nightly-testing repo (which has no `master` branch) — so the
- # built tool matches the tree. Fork PRs keep `master` and skip the
- # comparison (empty `source_dir`): they run master's `build_template` via
- # `pull_request_target`, never this one, and their untrusted tool must
- # never be built/run with cache credentials (bors exercises tool changes
- # in-repo instead).
- - name: Get CI tools
- uses: ./workflow-actions/.github/actions/get-tools
- with:
- use_artifact: ${{ inputs.tools_branch_ref == '' && github.repository == 'leanprover-community/mathlib4' }}
- tools_source_ref: ${{ inputs.tools_branch_ref != '' && inputs.tools_branch_ref || (github.event.pull_request.head.repo.fork && 'master' || inputs.pr_branch_ref) }}
- source_dir: ${{ github.event.pull_request.head.repo.fork != true && 'pr-branch' || '' }}
- github_token: ${{ github.token }}
-
- - name: download dependencies
- # We need network access to download dependencies
- # We run this inside landrun, but restrict disk access.
- # Landrun argument notes:
- # - we give --rox access to `~/.elan` and `~/actions-runner/_work` (GitHub CI needs this)
- # - we give --unrestricted-network as we need this to download dependencies
- # - git needs read only access to `/etc`.
- shell: landrun --unrestricted-network --rox /etc --rox /usr --rw /dev --rox /home/lean/.elan --rox /home/lean/actions-runner/_work --rw pr-branch/.lake/ --env PATH --env HOME --env GITHUB_OUTPUT --env CI -- bash -euxo pipefail {0}
- run: |
- cd pr-branch
- lake env
-
- name: validate lake-manifest.json inputRevs
# Only enforce this on the main mathlib4 repository, not on nightly-testing
if: github.repository == 'leanprover-community/mathlib4' && github.ref_name != 'nightly-testing'
@@ -397,6 +252,24 @@ jobs:
../tools-branch/scripts/lake-build-with-retry.sh Counterexamples
# results of build at pr-branch/.lake/build_summary_Counterexamples.json
+ # Runs in the build job because it only needs the freshly-built Mathlib/
+ # Archive/Counterexamples oleans, which are present here; keeping it in
+ # `build` also spares `test_lint` from fetching Archive/Counterexamples.
+ - name: check for noisy stdout lines
+ id: noisy
+ run: |
+ cd pr-branch
+ buildMsgs="$(
+ ## we exploit `lake`s replay feature: since the cache is present, running
+ ## `lake build` will reproduce all the outputs without having to recompute
+ lake build -q --iofail Mathlib Archive Counterexamples
+ )"
+ if [ -n "${buildMsgs}" ]
+ then
+ printf $'%s\n' "${buildMsgs}"
+ exit 1
+ fi
+
- name: prepare staging directory
if: ${{ always() && (steps.build.outcome == 'success' || steps.build.outcome == 'failure' || steps.build.outcome == 'cancelled') }}
shell: bash
@@ -443,6 +316,8 @@ jobs:
with:
name: cache-staging
path: cache-staging/
+ # The `.ltar` files are already zstd-compressed; recompressing wastes CPU.
+ compression-level: 0
# Prune to this commit's `.ltar` set so the published snapshot is exactly master's
# current cache (the local dir also holds the previous snapshot it warmed from).
@@ -489,23 +364,127 @@ jobs:
echo "'mk_all --check' passed successfully."
fi
+ test_lint:
+ name: Test and lint
+ needs: [build]
+ # Runs `test` and `lint` on the same self-hosted pool as `build`, but as a
+ # separate job so it runs in parallel with the GitHub-hosted post-build chain
+ # (upload_cache -> post_steps) rather than ahead of it. `build` and `test_lint`
+ # run sequentially on the pool, so peak per-run concurrency is unchanged; the
+ # only cost is a second setup prefix on a fresh runner.
+ #
+ # It does not depend on `upload_cache`: it reconstitutes this run's oleans
+ # without the paid Azure round-trip, by downloading the `cache-staging` artifact
+ # `build` produces (this commit's freshly-built delta) and warming the rest from
+ # the master `cache-snapshot` via the `get-cache` action. The `verify the Mathlib
+ # cache is complete` step below fails loudly if that reconstitution is incomplete,
+ # rather than silently rebuilding under landrun (which has no network).
+ #
+ # Gating: runs when `build` succeeded or failed (not cancelled/skipped). Running
+ # on build FAILURE too means `lint` still reports results on a failed build,
+ # best-effort over whatever oleans were built. The per-step conditions below
+ # match: `test` only on a clean build, `lint` on success-or-failure.
+ if: ${{ always() && (needs.build.result == 'success' || needs.build.result == 'failure') }}
+ runs-on: ${{ inputs.runs_on }}
+ outputs:
+ lint-outcome: ${{ steps.lint.outcome }}
+ test-outcome: ${{ steps.test.outcome }}
+ defaults: # On Hoskinson runners, landrun is already installed.
+ run: # note that pr-branch/.lake must be created in a step below before we use this
+ shell: landrun --rox /usr --ro /etc/timezone --rw /dev --rox /home/lean/.elan --rox /home/lean/actions-runner/_work --rox /home/lean/.cache/mathlib/ --rw pr-branch/.lake/ --env PATH --env HOME --env GITHUB_OUTPUT --env CI -- bash -euxo pipefail {0}
+ steps:
+ - name: Checkout local actions
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ with:
+ ref: ${{ github.workflow_sha }}
+ fetch-depth: 1
+ sparse-checkout: .github/actions
+ path: workflow-actions
+
+ # Shared self-hosted setup (see the action).
+ - name: Set up build environment
+ uses: ./workflow-actions/.github/actions/setup-build-env
+ with:
+ pr_branch_ref: ${{ inputs.pr_branch_ref }}
+ tools_branch_ref: ${{ inputs.tools_branch_ref }}
+
+ # mathlib-ci provides the scripts for the lean-pr-testing comment at the end of
+ # this job, which runs only on the nightly-testing repo — so check it out only
+ # there. Written twice because a `uses:` step cannot conditionally omit the
+ # single `ref` key (`ref: ''` would override the action's pinned default).
+ - name: Get mathlib-ci
+ if: ${{ github.repository == 'leanprover-community/mathlib4-nightly-testing' && inputs.mathlib_ci_ref == '' }}
+ uses: ./workflow-actions/.github/actions/get-mathlib-ci
+
+ - name: Get mathlib-ci
+ if: ${{ github.repository == 'leanprover-community/mathlib4-nightly-testing' && inputs.mathlib_ci_ref != '' }}
+ uses: ./workflow-actions/.github/actions/get-mathlib-ci
+ with:
+ ref: ${{ inputs.mathlib_ci_ref }}
+
+ # Pre-populate the local cache with this commit's freshly-built delta: the
+ # `cache-staging` artifact `build` produces holds its newly-built `.ltar` —
+ # the oleans not already in the master snapshot. `get-cache` below then warms
+ # the unchanged bulk from the master `cache-snapshot` and unpacks everything,
+ # so the bulk of the cache is not re-downloaded from paid Azure storage.
+ # Gated on `cache-staging-has-files`: a full cache hit stages nothing (no
+ # artifact is uploaded), and `get-cache` alone then covers the commit.
+ - name: download cache staging artifact
+ if: ${{ needs.build.outputs.cache-staging-has-files == 'true' }}
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: cache-staging
+ path: /home/lean/.cache/mathlib
+
+ # Warm the unchanged bulk from the master `cache-snapshot` and fetch/unpack
+ # this commit's oleans. The staged `.ltar` placed above are already local, so
+ # `get-cache`'s `cache get` decompresses them rather than re-downloading; only
+ # anything still missing is pulled from Azure.
+ - name: Get cache
+ uses: ./workflow-actions/.github/actions/get-cache
+ with:
+ working_directory: pr-branch
+ cache_bin: ../tools-branch/.lake/build/bin/cache
+
+ - name: verify the Mathlib cache is complete
+ # Enforced only when the build succeeded (then the cache must be complete):
+ # fail fast rather than silently trying to rebuild under landrun (which has
+ # no network). On a failed build the cache is expected to be partial, so this
+ # is skipped and `lint` runs best-effort below.
+ if: ${{ needs.build.outputs.build-outcome == 'success' }}
+ # TODO: remove if there are no issues
+ continue-on-error: true
+ run: |
+ cd pr-branch
+ lake build --no-build --rehash -v Mathlib
+
+ # `test` runs only on a clean build: `build` itself, the `mk_all` check, and
+ # the archive/counterexamples builds all succeeded. The condition reads those
+ # from the build job's outputs, and the problem-matcher wrap is gated to match.
- name: begin gh-problem-match-wrap for test step
+ if: ${{ needs.build.outputs.build-outcome == 'success' && needs.build.outputs.mk_all-outcome == 'success' && needs.build.outputs.archive-outcome == 'success' && needs.build.outputs.counterexamples-outcome == 'success' }}
uses: leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23
with:
action: add # In order to be able to run a multiline script, we need to add/remove the problem matcher before and after.
linters: lean
- name: test mathlib
+ if: ${{ needs.build.outputs.build-outcome == 'success' && needs.build.outputs.mk_all-outcome == 'success' && needs.build.outputs.archive-outcome == 'success' && needs.build.outputs.counterexamples-outcome == 'success' }}
id: test
run: |
cd pr-branch
../tools-branch/scripts/lake-build-wrapper.py .lake/build_summary_MathlibTest.json lake --iofail test
- name: end gh-problem-match-wrap for test step
+ if: ${{ needs.build.outputs.build-outcome == 'success' && needs.build.outputs.mk_all-outcome == 'success' && needs.build.outputs.archive-outcome == 'success' && needs.build.outputs.counterexamples-outcome == 'success' }}
uses: leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23
with:
action: remove
linters: lean
+ # `lint` runs on a build that succeeded or failed (not cancelled). On a failed
+ # build it lints best-effort over whatever oleans the cache has, so partial
+ # lint feedback is still reported. The problem-matcher wrap is gated to match.
- name: begin gh-problem-match-wrap for shake and lint steps
+ if: ${{ always() && (needs.build.outputs.build-outcome == 'success' || needs.build.outputs.build-outcome == 'failure') }}
uses: leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23
with:
action: add # In order to be able to run a multiline script, we need to add/remove the problem matcher before and after.
@@ -519,7 +498,7 @@ jobs:
# cd pr-branch
# env LEAN_ABORT_ON_PANIC=1 lake exe shake --gh-style
- name: lint mathlib
- if: ${{ always() && steps.build.outcome == 'success' || steps.build.outcome == 'failure' }}
+ if: ${{ always() && (needs.build.outputs.build-outcome == 'success' || needs.build.outputs.build-outcome == 'failure') }}
id: lint
timeout-minutes: 40
run: |
@@ -558,29 +537,12 @@ jobs:
done
- name: end gh-problem-match-wrap for shake and lint steps
+ if: ${{ always() && (needs.build.outputs.build-outcome == 'success' || needs.build.outputs.build-outcome == 'failure') }}
uses: leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23
with:
action: remove
linters: gcc
- - name: check for noisy stdout lines
- id: noisy
- run: |
- cd pr-branch
- buildMsgs="$(
- ## we exploit `lake`s replay feature: since the cache is present, running
- ## `lake build` will reproduce all the outputs without having to recompute
- lake build Mathlib Archive Counterexamples |
- ## we filter out the output lines that begin with `✔ [xx/yy]`, where xx, yy
- ## are either numbers or ?, and the "Build completed successfully." message.
- ## We keep the rest, which are actual outputs of the files
- awk '!($0 ~ "^\\s*✔ \\[[?0-9]*/[?0-9]*\\]" || $0 ~ "^Build completed successfully( \\([0-9]+ jobs\\))?\\.?$"){ print $0 }')"
- if [ -n "${buildMsgs}" ]
- then
- printf $'%s\n' "${buildMsgs}"
- exit 1
- fi
-
# Generate a fresh token just before posting comments.
# GitHub App tokens expire after 1 hour, and the build can take longer than that.
- name: Generate lean-pr-testing app token
@@ -602,10 +564,12 @@ jobs:
TOKEN: ${{ steps.lean-pr-testing-token.outputs.token }}
GITHUB_CONTEXT: ${{ toJson(github) }}
WORKFLOW_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
- BUILD_OUTCOME: ${{ steps.build.outcome }}
- NOISY_OUTCOME: ${{ steps.noisy.outcome }}
- ARCHIVE_OUTCOME: ${{ steps.archive.outcome }}
- COUNTEREXAMPLES_OUTCOME: ${{ steps.counterexamples.outcome }}
+ # build/archive/counterexamples/noisy outcomes come from the `build` job's
+ # outputs; test/lint are produced by this job's own steps.
+ BUILD_OUTCOME: ${{ needs.build.outputs.build-outcome }}
+ NOISY_OUTCOME: ${{ needs.build.outputs.noisy-outcome }}
+ ARCHIVE_OUTCOME: ${{ needs.build.outputs.archive-outcome }}
+ COUNTEREXAMPLES_OUTCOME: ${{ needs.build.outputs.counterexamples-outcome }}
LINT_OUTCOME: ${{ steps.lint.outcome }}
TEST_OUTCOME: ${{ steps.test.outcome }}
NIGHTLY_TESTING_REPO: leanprover-community/mathlib4-nightly-testing
@@ -868,8 +832,8 @@ jobs:
final:
name: Post-CI job
# ensure that this runs iff direct dependencies succeeded even if transitive dependencies were skipped
- if: ${{ always() && inputs.run_post_ci && needs.style_lint.result == 'success' && needs.build.result == 'success' && needs.post_steps.result == 'success' }}
- needs: [style_lint, build, post_steps]
+ if: ${{ always() && inputs.run_post_ci && needs.style_lint.result == 'success' && needs.build.result == 'success' && needs.test_lint.result == 'success' && needs.post_steps.result == 'success' }}
+ needs: [style_lint, build, test_lint, post_steps]
runs-on: ubuntu-latest
steps:
- id: PR
diff --git a/bors.toml b/bors.toml
index 0dd32adf0ec53e..f9b7e3550fb4d6 100644
--- a/bors.toml
+++ b/bors.toml
@@ -1,4 +1,4 @@
-status = ["ci (staging) / Build", "ci (staging) / Lint style", "ci (staging) / Post-Build Step", "ci (staging) / Post-CI job"]
+status = ["ci (staging) / Build", "ci (staging) / Test and lint", "ci (staging) / Lint style", "ci (staging) / Post-Build Step", "ci (staging) / Post-CI job"]
use_squash_merge = true
timeout_sec = 7200
block_labels = ["WIP", "blocked-by-other-PR", "merge-conflict", "awaiting-CI"]
From fa97836994f0cf44850c4335da2a1df47b51f38f Mon Sep 17 00:00:00 2001
From: Bryan Gin-ge Chen <5209952+bryangingechen@users.noreply.github.com>
Date: Wed, 24 Jun 2026 04:01:48 +0000
Subject: [PATCH 0308/1300] ci: let bors manage PR lifecycle labels (#40763)
After https://github.com/leanprover-community/bors-ng/pull/53, bors is now capable of managing PR lifecycle labels directly from its own state (queue membership and delegations), opt-in via a `[labels]` table in `bors.toml`. This fixes `delegated` labels stranded by expirations, as well as `ready-to-merge` labels stuck on PRs that fell off the queue, and also allows us to add and manage 2 new labels:
- `bors-staging`: PRs that are in the currently building batch will get this label
- `awaiting-requeue`: PRs that were on the bors queue but now require a requeue due to something going wrong will get this label
Changes in this PR:
- bors.toml: add a `[labels]` table mapping all four lifecycle concerns to mathlib4's label names: `on_queue` -> `ready-to-merge`, `building` -> `bors-staging`, `failed` -> `awaiting-requeue`, `delegated` -> `delegated`. (`bors-staging` and `awaiting-requeue` are new labels for this repo.)
- maintainer_bors.yml / maintainer_bors_wf_run.yml: strip the `ready-to-merge`/`delegated` add/remove logic (and the `Build failed:` -> `delegated` re-apply quirk and the `bors r-`/`d-` removal path) now that bors owns those labels. The workflows keep their other jobs: detecting merge/delegate commands, removing `awaiting-author` / `maintainer-merge`, and updating Zulip emoji reactions. Renamed to "Bors merge/delegate follow-up" to match, keeping the workflow_run linkage consistent.
- docs/workflows.md: update the two workflow descriptions.
Co-Authored-By: Claude Opus 4.8 (1M context)
---
.github/workflows/maintainer_bors.yml | 36 +++++---------
.github/workflows/maintainer_bors_wf_run.yml | 51 +++++---------------
bors.toml | 7 +++
docs/workflows.md | 4 +-
4 files changed, 34 insertions(+), 64 deletions(-)
diff --git a/.github/workflows/maintainer_bors.yml b/.github/workflows/maintainer_bors.yml
index 44ff2d6b88e8a8..19b8e505d9488f 100644
--- a/.github/workflows/maintainer_bors.yml
+++ b/.github/workflows/maintainer_bors.yml
@@ -1,4 +1,4 @@
-name: Add "ready-to-merge" and "delegated" label
+name: Bors merge/delegate follow-up
# triggers the action when
on:
@@ -21,7 +21,7 @@ permissions:
contents: read
jobs:
- add_ready_to_merge_label:
+ bors_command_followup:
# we set some variables. The ones of the form `${{ X }}${{ Y }}` are typically not
# both set simultaneously: depending on the event that triggers the PR, usually only one is set
env:
@@ -29,8 +29,7 @@ jobs:
COMMENT_EVENT: ${{ github.event.comment.body }}
COMMENT_REVIEW: ${{ github.event.review.body }}
PR_NUMBER: ${{ github.event.issue.number }}${{ github.event.pull_request.number }}
- HAS_DELEGATED_LABEL: ${{ contains(github.event.issue.labels.*.name, 'delegated') }}
- name: Add ready-to-merge or delegated label
+ name: Detect bors merge/delegate command
runs-on: ubuntu-latest
if: >- # coarse prefilter to avoid running on irrelevant comments/reviews
github.repository == 'leanprover-community/mathlib4' &&
@@ -38,8 +37,7 @@ jobs:
(
contains(format('{0}{1}', github.event.comment.body, github.event.review.body), 'bors merge') ||
contains(format('{0}{1}', github.event.comment.body, github.event.review.body), 'bors d') ||
- contains(format('{0}{1}', github.event.comment.body, github.event.review.body), 'bors r') ||
- contains(format('{0}{1}', github.event.comment.body, github.event.review.body), 'Build failed:')
+ contains(format('{0}{1}', github.event.comment.body, github.event.review.body), 'bors r')
)
steps:
- name: Find bors merge/delegate
@@ -51,26 +49,20 @@ jobs:
# for debugging, we print some information
printf '%s' "${COMMENT}" | hexdump -cC
printf 'Comment:"%s"\n' "${COMMENT}"
- if [ "${AUTHOR}" == 'mathlib-bors[bot]' ] && [ "${HAS_DELEGATED_LABEL}" == 'true' ]
- then
- m_or_d="$(printf '%s' "${COMMENT}" |
- sed -n 's=^Build failed:=delegated=p' | head -1)"
- else
- m_or_d="$(printf '%s' "${COMMENT}" |
- sed -n 's=^bors *\(merge\|r+\) *$=ready-to-merge=p; s=^bors *\(delegate\|d+\|d\=\).*=delegated=p' | head -1)"
- fi
-
- remove_labels="$(printf '%s' "${COMMENT}" |
- sed -n 's=^bors *\(merge\|r\|d\)- *$=remove-labels=p' | head -1)"
+ # `mOrD` records which command this is ("ready-to-merge" or "delegated").
+ # It no longer drives any label: bors now manages the `ready-to-merge` and
+ # `delegated` lifecycle labels itself (see the `[labels]` table in bors.toml).
+ # It is kept only to select the Zulip emoji and to gate the
+ # awaiting-author/maintainer-merge cleanup in `maintainer_bors_wf_run.yml`.
+ m_or_d="$(printf '%s' "${COMMENT}" |
+ sed -n 's=^bors *\(merge\|r+\) *$=ready-to-merge=p; s=^bors *\(delegate\|d+\|d\=\).*=delegated=p' | head -1)"
printf $'"bors delegate" or "bors merge" found? \'%s\'\n' "${m_or_d}"
- printf $'"bors r-" or "bors d-" found? \'%s\'\n' "${remove_labels}"
printf $'AUTHOR: \'%s\'\n' "${AUTHOR}"
printf $'PR_NUMBER: \'%s\'\n' "${PR_NUMBER}"
printf $'%s' "${PR_NUMBER}" | hexdump -cC
printf $'mOrD=%s\n' "${m_or_d}" >> "${GITHUB_OUTPUT}"
- printf $'removeLabels=%s\n' "${remove_labels}" >> "${GITHUB_OUTPUT}"
if [ "${AUTHOR}" == 'leanprover-community-mathlib4-bot' ] ||
[ "${AUTHOR}" == 'leanprover-community-bot-assistant' ] ||
[ "${AUTHOR}" == 'mathlib-bors[bot]' ] ||
@@ -83,20 +75,18 @@ jobs:
printf $'bot=false\n' >> "${GITHUB_OUTPUT}"
fi
- - if: ${{ ! steps.merge_or_delegate.outputs.mOrD == '' || ! steps.merge_or_delegate.outputs.removeLabels == '' }}
+ - if: ${{ ! steps.merge_or_delegate.outputs.mOrD == '' }}
name: Prepare bridge outputs
run: |
jq -n \
--arg bot "${{ steps.merge_or_delegate.outputs.bot }}" \
- --arg removeLabels "${{ steps.merge_or_delegate.outputs.removeLabels }}" \
--arg mOrD "${{ steps.merge_or_delegate.outputs.mOrD }}" \
'{
bot: $bot,
- removeLabels: $removeLabels,
mOrD: $mOrD,
}' > bridge-outputs.json
- - if: ${{ ! steps.merge_or_delegate.outputs.mOrD == '' || ! steps.merge_or_delegate.outputs.removeLabels == '' }}
+ - if: ${{ ! steps.merge_or_delegate.outputs.mOrD == '' }}
name: Emit bridge artifact
uses: leanprover-community/privilege-escalation-bridge/emit@f5dfe313a79647c07315b451b2dc2a81a161a50d # v1.2.0
with:
diff --git a/.github/workflows/maintainer_bors_wf_run.yml b/.github/workflows/maintainer_bors_wf_run.yml
index d67089472846df..7d4b4f7f62a187 100644
--- a/.github/workflows/maintainer_bors_wf_run.yml
+++ b/.github/workflows/maintainer_bors_wf_run.yml
@@ -1,8 +1,8 @@
-name: Add "ready-to-merge" and "delegated" label (workflow_run)
+name: Bors merge/delegate follow-up (workflow_run)
on:
workflow_run:
- workflows: ['Add "ready-to-merge" and "delegated" label']
+ workflows: ['Bors merge/delegate follow-up']
types:
- completed
@@ -12,8 +12,8 @@ permissions:
jobs:
- add_ready_to_merge_label:
- name: Add ready-to-merge or delegated label
+ bors_command_followup:
+ name: Bors merge/delegate follow-up
runs-on: ubuntu-latest
if: ${{ github.repository == 'leanprover-community/mathlib4' && github.event.workflow_run.conclusion == 'success' }}
permissions:
@@ -26,7 +26,7 @@ jobs:
uses: leanprover-community/privilege-escalation-bridge/consume@f5dfe313a79647c07315b451b2dc2a81a161a50d # v1.2.0
with:
artifact: workflow-data
- source_workflow: Add "ready-to-merge" and "delegated" label
+ source_workflow: Bors merge/delegate follow-up
require_event: issue_comment,pull_request_review,pull_request_review_comment
fail_on_missing: false
token: ${{ github.token }}
@@ -34,7 +34,6 @@ jobs:
author=event.comment.user.login|event.review.user.login
pr_number=meta.pr_number
bot=outputs.bot
- removeLabels=outputs.removeLabels
mOrD=outputs.mOrD
- name: Download legacy artifact (fallback)
@@ -62,7 +61,6 @@ jobs:
echo "author=$(jq -r '.author // empty' "${data_file}")"
echo "pr_number=$(jq -r '.pr_number // empty' "${data_file}")"
echo "bot=$(jq -r '.bot // empty' "${data_file}")"
- echo "removeLabels=$(jq -r '.removeLabels // .remove_labels // empty' "${data_file}")"
echo "mOrD=$(jq -r '.mOrD // .m_or_d // empty' "${data_file}")"
} | tee -a "$GITHUB_OUTPUT"
@@ -73,7 +71,6 @@ jobs:
INPUT_AUTHOR: ${{ steps.bridge.outputs.author }}${{ steps.legacy.outputs.author }}
INPUT_PR_NUMBER: ${{ steps.bridge.outputs.pr_number }}${{ steps.legacy.outputs.pr_number }}
INPUT_BOT: ${{ steps.bridge.outputs.bot }}${{ steps.legacy.outputs.bot }}
- INPUT_REMOVE_LABELS: ${{ steps.bridge.outputs.removeLabels }}${{ steps.legacy.outputs.removeLabels }}
INPUT_MORD: ${{ steps.bridge.outputs.mOrD }}${{ steps.legacy.outputs.mOrD }}
INPUT_SOURCE: ${{ steps.bridge.outputs.pr_number != '' && 'bridge' || (steps.legacy.outputs.pr_number != '' && 'legacy' || 'none') }}
run: |
@@ -81,7 +78,6 @@ jobs:
echo "author=${INPUT_AUTHOR}"
echo "pr_number=${INPUT_PR_NUMBER}"
echo "bot=${INPUT_BOT}"
- echo "removeLabels=${INPUT_REMOVE_LABELS}"
echo "mOrD=${INPUT_MORD}"
echo "input_source=${INPUT_SOURCE}"
} | tee -a "$GITHUB_OUTPUT"
@@ -94,7 +90,7 @@ jobs:
- name: Check whether user is a mathlib admin
id: user_permission
- if: ${{ ! steps.inputs.outputs.mOrD == '' || ! steps.inputs.outputs.removeLabels == '' }}
+ if: ${{ ! steps.inputs.outputs.mOrD == '' }}
uses: actions-cool/check-user-permission@c21884f3dda18dafc2f8b402fe807ccc9ec1aa5e # v2.4.0
with:
username: ${{ steps.inputs.outputs.author }}
@@ -103,7 +99,7 @@ jobs:
- name: Generate app token
id: app-token
uses: leanprover-community/mathlib-ci/.github/actions/azure-create-github-app-token@3bb576208589a435eeaeac9b144a1b7c3e948760
- if: ${{ ! steps.inputs.outputs.mOrD == '' || ! steps.inputs.outputs.removeLabels == '' }}
+ if: ${{ ! steps.inputs.outputs.mOrD == '' }}
with:
app-id: ${{ secrets.MATHLIB_TRIAGE_APP_ID }}
key-vault-name: ${{ vars.MATHLIB_AZ_KEY_VAULT_NAME }}
@@ -111,23 +107,11 @@ jobs:
azure-client-id: ${{ vars.GH_APP_AZURE_CLIENT_ID_TRIAGE }}
azure-tenant-id: ${{ secrets.LPC_AZ_TENANT_ID }}
- - name: Add ready-to-merge or delegated label
- id: add_label
- if: ${{ ! steps.inputs.outputs.mOrD == '' &&
- ( steps.user_permission.outputs.require-result == 'true' ||
- steps.inputs.outputs.bot == 'true' ) }}
- uses: octokit/request-action@b91aabaa861c777dcdb14e2387e30eddf04619ae # v3.0.0
- with:
- route: POST /repos/:repository/issues/:issue_number/labels
- # Unexpected input warning from the following is expected:
- # https://github.com/octokit/request-action?tab=readme-ov-file#warnings
- repository: ${{ github.repository }}
- issue_number: ${{ steps.inputs.outputs.pr_number }}
- labels: '["${{ steps.inputs.outputs.mOrD }}"]'
- env:
- # This token is masked by the token minting action and will not be logged accidentally
- GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
-
+ # The `ready-to-merge` / `delegated` lifecycle labels are managed by bors
+ # itself (see the `[labels]` table in bors.toml); this workflow no longer
+ # adds or removes them. It still removes the `awaiting-author` and
+ # `maintainer-merge` labels and updates the Zulip emoji on a merge/delegate
+ # command.
- if: ${{ ! steps.inputs.outputs.mOrD == '' &&
( steps.user_permission.outputs.require-result == 'true' ||
steps.inputs.outputs.bot == 'true' ) }}
@@ -142,17 +126,6 @@ jobs:
--header 'authorization: Bearer ${{ steps.app-token.outputs.token }}'
done
- - name: On bors r/d-, remove ready-to-merge or delegated label
- if: ${{ ! steps.inputs.outputs.removeLabels == '' && steps.user_permission.outputs.require-result == 'true' }}
- # we use curl rather than octokit/request-action so that the job won't fail
- # (and send an annoying email) if the labels don't exist
- run: |
- for label in ready-to-merge delegated; do
- curl --request DELETE \
- --url "https://api.github.com/repos/${{ github.repository }}/issues/${{ steps.inputs.outputs.pr_number }}/labels/${label}" \
- --header 'authorization: Bearer ${{ steps.app-token.outputs.token }}'
- done
-
- name: Checkout local actions
if: ${{ ! steps.inputs.outputs.mOrD == '' &&
( steps.user_permission.outputs.require-result == 'true' ||
diff --git a/bors.toml b/bors.toml
index f9b7e3550fb4d6..acdb67c060ed84 100644
--- a/bors.toml
+++ b/bors.toml
@@ -6,6 +6,13 @@ delete_merged_branches = true
update_base_for_deletes = true
cut_body_after = "\n---"
max_batch_size = 16
+
+[labels]
+on_queue = "ready-to-merge" # PR is on the merge queue (a batch waiting or running)
+building = "bors-staging" # PR is currently building on staging (such PRs will also have the on_queue label)
+failed = "awaiting-requeue" # PR's merge build failed terminally and was dropped; needs a re-queue
+delegated = "delegated" # PR has at least one active (non-expired) delegation
+
[delegation]
default_expiry_sec = 1209600 # 2 weeks
# Entries below are Erlang `:glob` patterns (NOT gitignore): `*` matches across `/`,
diff --git a/docs/workflows.md b/docs/workflows.md
index f64770727cb929..60eef0312e97ee 100644
--- a/docs/workflows.md
+++ b/docs/workflows.md
@@ -59,7 +59,7 @@ Primary trigger for this section: PR/merge-queue events (`pull_request`, `pull_r
| File | Name | Importance | Triggers | Description |
|---|---|---|---|---|
-| [`maintainer_bors.yml`](../.github/workflows/maintainer_bors.yml) | Add "ready-to-merge" and "delegated" label]
[](https://github.com/leanprover-community/mathlib4/actions/workflows/maintainer_bors.yml) | High | `issue_comment, pull_request_review, pull_request_review_comment` | Processes bors merge/delegate commands, updates labels, and emits artifact/context for follow-up workflows. |
+| [`maintainer_bors.yml`](../.github/workflows/maintainer_bors.yml) | Bors merge/delegate follow-up
[](https://github.com/leanprover-community/mathlib4/actions/workflows/maintainer_bors.yml) | High | `issue_comment, pull_request_review, pull_request_review_comment` | Detects bors merge/delegate commands and emits a bridge artifact for the privileged follow-up workflow. The `ready-to-merge`/`delegated` lifecycle labels are managed by bors itself (see `bors.toml`). |
| [`maintainer_merge.yml`](../.github/workflows/maintainer_merge.yml) | Maintainer merge
[](https://github.com/leanprover-community/mathlib4/actions/workflows/maintainer_merge.yml) | High | `issue_comment, pull_request_review, pull_request_review_comment` | Handles maintainer merge/delegate commands, performs permission checks, and posts Zulip/PR notifications. |
| [`labels_from_comment.yml`](../.github/workflows/labels_from_comment.yml) | Label PR based on Comment
[](https://github.com/leanprover-community/mathlib4/actions/workflows/labels_from_comment.yml) | Medium | `issue_comment, pull_request_review, pull_request_review_comment` | Adds/removes an allowlisted set of labels based on comment/review text commands. |
| [`bot_fix_style.yaml`](../.github/workflows/bot_fix_style.yaml) | bot fix style
[](https://github.com/leanprover-community/mathlib4/actions/workflows/bot_fix_style.yaml) | Low | `issue_comment, pull_request_review, pull_request_review_comment` | Responds to review/comment events and runs `lint-style-action` in `fix` mode. |
@@ -100,7 +100,7 @@ Primary trigger for this section: completion of other workflows (`workflow_run`)
|---|---|---|---|---|
| [`nightly_detect_failure.yml`](../.github/workflows/nightly_detect_failure.yml) | Post to zulip if the nightly-testing branch is failing.
[](https://github.com/leanprover-community/mathlib4/actions/workflows/nightly_detect_failure.yml) | High | `workflow_run` | Reacts to nightly-testing CI outcomes; posts status updates and performs branch/tag maintenance on success. |
| [`update_dependencies_zulip.yml`](../.github/workflows/update_dependencies_zulip.yml) | Monitor Dependency Update Failures
[](https://github.com/leanprover-community/mathlib4/actions/workflows/update_dependencies_zulip.yml) | High | `workflow_run` | Watches dependency-update CI runs and sends Zulip success/failure messages with PR/label handling. |
-| [`maintainer_bors_wf_run.yml`](../.github/workflows/maintainer_bors_wf_run.yml) | Add "ready-to-merge" and "delegated" label (workflow_run)
[](https://github.com/leanprover-community/mathlib4/actions/workflows/maintainer_bors_wf_run.yml) | Medium | `workflow_run` | Manages labels plus Zulip emoji updates for Bors commands. |
+| [`maintainer_bors_wf_run.yml`](../.github/workflows/maintainer_bors_wf_run.yml) | Bors merge/delegate follow-up (workflow_run)
[](https://github.com/leanprover-community/mathlib4/actions/workflows/maintainer_bors_wf_run.yml) | Medium | `workflow_run` | Privileged companion: removes `awaiting-author`/`maintainer-merge` and updates Zulip emoji reactions on bors merge/delegate commands. The `ready-to-merge`/`delegated` lifecycle labels are managed by bors itself. |
| [`maintainer_merge_wf_run.yml`](../.github/workflows/maintainer_merge_wf_run.yml) | Maintainer merge (workflow_run)
[](https://github.com/leanprover-community/mathlib4/actions/workflows/maintainer_merge_wf_run.yml) | Medium | `workflow_run` | Manages labels and posts on Zulip for maintainer merge/delegate commands. |
| [`olean_report_wf_run.yaml`](../.github/workflows/olean_report_wf_run.yaml) | olean report (workflow_run)
[](https://github.com/leanprover-community/mathlib4/actions/workflows/olean_report_wf_run.yaml) | Low | `workflow_run` | Privileged companion to `olean_report.yaml`. Downloads the bridge artifact and posts or updates the olean diff as a comment on the PR. |
| [`decls-diff.yml`](../.github/workflows/decls-diff.yml) | Declarations diff (post-build)
[](https://github.com/leanprover-community/mathlib4/actions/workflows/decls-diff.yml) | Low | `workflow_run` | Post-build companion to `ci` that diffs the `import-graph` artifact of a PR build against its master merge-base and patches the `### PR summary` comment's declarations-diff section with the Lean-aware result (or a cache-miss notice). |
From 2af912edc30e3960c7cf95558a834746d840dcb8 Mon Sep 17 00:00:00 2001
From: Christian Merten <136261474+chrisflav@users.noreply.github.com>
Date: Wed, 24 Jun 2026 07:32:42 +0000
Subject: [PATCH 0309/1300] feat(CategoryTheory/Sites): source local closure of
a morphism property (#40528)
Co-authored by: Edward van de Meent
---
Mathlib.lean | 1 +
.../Shapes/Pullback/IsPullback/Basic.lean | 50 +++++++
.../MorphismProperty/LocalClosure.lean | 141 ++++++++++++++++++
.../CategoryTheory/Sites/Hypercover/Zero.lean | 3 +
4 files changed, 195 insertions(+)
create mode 100644 Mathlib/CategoryTheory/MorphismProperty/LocalClosure.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index d3f6317619d23a..881db5f16663e8 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -3177,6 +3177,7 @@ public import Mathlib.CategoryTheory.MorphismProperty.IsSmall
public import Mathlib.CategoryTheory.MorphismProperty.LiftingProperty
public import Mathlib.CategoryTheory.MorphismProperty.Limits
public import Mathlib.CategoryTheory.MorphismProperty.Local
+public import Mathlib.CategoryTheory.MorphismProperty.LocalClosure
public import Mathlib.CategoryTheory.MorphismProperty.LocalEpi
public import Mathlib.CategoryTheory.MorphismProperty.OfObjectProperty
public import Mathlib.CategoryTheory.MorphismProperty.OverAdjunction
diff --git a/Mathlib/CategoryTheory/Limits/Shapes/Pullback/IsPullback/Basic.lean b/Mathlib/CategoryTheory/Limits/Shapes/Pullback/IsPullback/Basic.lean
index fc8dafa3356779..e598b03ba20ed7 100644
--- a/Mathlib/CategoryTheory/Limits/Shapes/Pullback/IsPullback/Basic.lean
+++ b/Mathlib/CategoryTheory/Limits/Shapes/Pullback/IsPullback/Basic.lean
@@ -994,4 +994,54 @@ end IsPullback
end IsPullbackOverPullback
+namespace Limits
+
+instance {X Y Z : C} (f : X ⟶ Z) (g : Y ⟶ Z) {X' : C} (i : X' ⟶ X) [IsIso i] [HasPullback f g] :
+ HasPullback (i ≫ f) g :=
+ IsPullback.paste_vert
+ (IsPullback.of_vert_isIso_mono (fst := pullback.fst _ _ ≫ inv i) (snd := 𝟙 (pullback f g)) <|
+ ⟨by simp⟩) (.of_hasPullback f g) |>.hasPullback
+
+@[simp]
+lemma HasPullback.comp_left_left_iff_of_isIso
+ {X Y Z : C} {f : X ⟶ Z} {g : Y ⟶ Z} {X' : C} (i : X' ⟶ X) [IsIso i] :
+ HasPullback (i ≫ f) g ↔ HasPullback f g := by
+ refine ⟨fun h ↦ ?_, fun _ ↦ inferInstance⟩
+ rw [← IsIso.inv_hom_id_assoc i f]
+ infer_instance
+
+instance {X Y Z Z' : C} {f : X ⟶ Z} {g : Y ⟶ Z'} (i : Z ⟶ Z') [IsIso i] [HasPullback (f ≫ i) g] :
+ HasPullback f (g ≫ inv i) := by
+ simpa using hasPullback_of_comp_mono (f ≫ i) g (inv i)
+
+lemma HasPullback.comp_left_right_iff_of_isIso
+ {X Y Z Z' : C} {f : X ⟶ Z} {g : Y ⟶ Z'} (i : Z ⟶ Z') [IsIso i] :
+ HasPullback (f ≫ i) g ↔ HasPullback f (g ≫ inv i) :=
+ ⟨fun h ↦ inferInstance, fun h ↦ by simpa using hasPullback_of_comp_mono f (g ≫ inv i) i⟩
+
+instance {X Y Z : C} (f : Z ⟶ X) (g : Z ⟶ Y) {X' : C} (i : X ⟶ X') [IsIso i] [HasPushout f g] :
+ HasPushout (f ≫ i) g :=
+ IsPushout.paste_horiz (.of_hasPushout f g)
+ (IsPushout.of_horiz_isIso_epi (inl := inv i ≫ pushout.inl _ _) (inr := 𝟙 (pushout f g)) <|
+ ⟨by simp⟩) |>.hasPushout
+
+@[simp]
+lemma HasPushout.comp_left_left_iff_of_isIso
+ {X Y Z : C} {f : Z ⟶ X} {g : Z ⟶ Y} {X' : C} (i : X ⟶ X') [IsIso i] :
+ HasPushout (f ≫ i) g ↔ HasPushout f g := by
+ refine ⟨fun h ↦ ?_, fun _ ↦ inferInstance⟩
+ rw [← Category.comp_id f, ← IsIso.hom_inv_id i, ← Category.assoc]
+ infer_instance
+
+instance {X Y Z Z' : C} {f : Z ⟶ X} {g : Z' ⟶ Y} (i : Z' ⟶ Z) [IsIso i] [HasPushout (i ≫ f) g] :
+ HasPushout f (inv i ≫ g) := by
+ simpa using hasPushout_of_epi_comp (i ≫ f) g (inv i)
+
+lemma HasPushout.comp_left_right_iff_of_isIso
+ {X Y Z Z' : C} {f : Z ⟶ X} {g : Z' ⟶ Y} (i : Z' ⟶ Z) [IsIso i] :
+ HasPushout (i ≫ f) g ↔ HasPushout f (inv i ≫ g) :=
+ ⟨fun h ↦ inferInstance, fun h ↦ by simpa using hasPushout_of_epi_comp f (inv i ≫ g) i⟩
+
+end Limits
+
end CategoryTheory
diff --git a/Mathlib/CategoryTheory/MorphismProperty/LocalClosure.lean b/Mathlib/CategoryTheory/MorphismProperty/LocalClosure.lean
new file mode 100644
index 00000000000000..153ee9d616bd1c
--- /dev/null
+++ b/Mathlib/CategoryTheory/MorphismProperty/LocalClosure.lean
@@ -0,0 +1,141 @@
+/-
+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.MorphismProperty.Local
+public import Mathlib.CategoryTheory.Sites.MorphismProperty
+
+/-!
+# Local closure of morphism properties
+
+We define the source local closure of a morphism property `P` w.r.t. a precoverage `K` as the
+weakest property containing `P` that is `K`-local on the source.
+-/
+
+@[expose] public section
+
+universe w v u
+
+open CategoryTheory Limits MorphismProperty
+
+variable {C : Type u} [Category.{v} C]
+
+namespace CategoryTheory.MorphismProperty
+
+variable {K : Precoverage C}
+
+/-- The source-local closure of `P` along a precoverage `K` is the weakest property
+containing `P` that is local on the source. -/
+inductive sourceLocalClosure (K : Precoverage C) (P : MorphismProperty C) : MorphismProperty C
+ /-- Force `P ≤ sourceLocalClosure K P`. -/
+ | of {X Y : C} (f : X ⟶ Y) : P f → sourceLocalClosure K P f
+ /-- Force `RespectsIso`. -/
+ | of_iso {X Y X' Y' : C} (f : X ⟶ Y) (g : X' ⟶ Y') (e : Arrow.mk f ≅ Arrow.mk g) :
+ sourceLocalClosure K P f → sourceLocalClosure K P g
+ | comp {X Y : C} (f : X ⟶ Y) (hf : sourceLocalClosure K P f) (R : Presieve X) (hR : R ∈ K X)
+ {U : C} (g : U ⟶ X) : R g → sourceLocalClosure K P (g ≫ f)
+ | of_presieve {X Y : C} (f : X ⟶ Y) (R : Presieve X) (hR : R ∈ K X)
+ (h : ∀ (U : C) (g : U ⟶ X), R g → sourceLocalClosure K P (g ≫ f)) :
+ sourceLocalClosure K P f
+
+namespace sourceLocalClosure
+
+attribute [grind .] of
+
+variable {P Q : MorphismProperty C} {X Y : C}
+
+instance : (sourceLocalClosure K P).IsLocalAtSource K where
+ precomp i hi f hf := .of_iso _ _ (Arrow.isoMk' _ _ (asIso i).symm (.refl _)) hf
+ postcomp i hi f hf := .of_iso _ _ (Arrow.isoMk' _ _ (.refl _) (asIso i)) hf
+ comp hR _ g hg hf := .comp _ hf _ hR _ hg
+ of_forall_comp hR h := .of_presieve _ _ hR h
+
+lemma le : P ≤ sourceLocalClosure K P :=
+ fun _ _ _ ↦ .of _
+
+lemma le_of_isLocalAtSource (h : P ≤ Q) [Q.IsLocalAtSource K] : sourceLocalClosure K P ≤ Q := by
+ intro X Y f hf
+ induction hf with
+ | of f hf => exact h _ hf
+ | of_iso f g e _ hf => rwa [Q.arrow_mk_iso_iff e.symm]
+ | comp f hf R hR g hg ih => apply IsLocalAtSource.comp hR _ hg ih
+ | of_presieve f R hR h ih => apply IsLocalAtSource.of_forall_comp hR fun U g hg ↦ ih _ _ hg
+
+instance [P.ContainsIdentities] : ContainsIdentities (sourceLocalClosure K P) where
+ id_mem _ := le _ (P.id_mem _)
+
+set_option backward.isDefEq.respectTransparency false in
+instance [P.IsStableUnderBaseChange] [K.IsStableUnderBaseChange] [HasPullbacks C] :
+ IsStableUnderBaseChange (sourceLocalClosure K P) where
+ of_isPullback {Y} X W Z g f fst snd h hf := by
+ induction hf generalizing W snd with
+ | of f' hf' => exact .of _ (P.of_isPullback h hf')
+ | of_iso f' g' e hf' ih =>
+ exact ih _ (g ≫ e.inv.right) (fst ≫ e.inv.left) _ (h.paste_horiz (.of_horiz_isIso ⟨e.inv.w⟩))
+ | comp f' hf' R hR g' hg' ih =>
+ let u : W ⟶ pullback g f' := pullback.lift snd (fst ≫ g') (by simp [h.w.symm])
+ have : snd = u ≫ pullback.fst g f' := by simp [u]
+ rw [this] at h ⊢
+ let e : W ≅ pullback g' (pullback.snd g f') :=
+ IsPullback.isoPullback (.of_bot h (by simp [u]) (.flip <| .of_hasPullback _ _))
+ rw [← (sourceLocalClosure K P).cancel_left_of_respectsIso e.inv, ← Category.assoc]
+ refine .comp _ (ih _ _ _ _ (.flip (.of_hasPullback _ _))) _
+ (K.pullbackArrows_mem (pullback.snd _ _) hR) _ ?_
+ simpa [e, u] using .mk _ _ hg'
+ | of_presieve f R hR h ih =>
+ refine .of_presieve _ _ (K.pullbackArrows_mem fst hR) ?_
+ intro U v ⟨Z, u, hu⟩
+ exact ih _ _ hu _ g (pullback.fst _ _) _ (.paste_vert (.of_hasPullback _ _) h)
+
+set_option backward.defeqAttrib.useBackward true in
+set_option backward.isDefEq.respectTransparency false in
+lemma sourceLocalClosure_iff_of_respectsLeft [P.RespectsIso] [P.RespectsLeft K.morphismProperty]
+ [K.HasIsos] [K.IsStableUnderBaseChange] [K.IsStableUnderComposition] [K.HasPullbacks] {X Y : C}
+ {f : X ⟶ Y} :
+ sourceLocalClosure K P f ↔ ∃ R ∈ K X, ∀ (U : C) (g : U ⟶ X), R g → P (g ≫ f) := by
+ refine ⟨?_, ?_⟩
+ · intro h
+ induction h with
+ | of f hf => exact ⟨.singleton (𝟙 _), K.mem_coverings_of_isIso _, fun U g ⟨⟩ ↦ by simpa⟩
+ | of_iso f g e hf h =>
+ obtain ⟨R, hR, h⟩ := h
+ rw [K.mem_iff_exists_zeroHypercover] at hR
+ obtain ⟨E, rfl⟩ := hR
+ refine ⟨_, (E.pushforward e.hom.left (K.mem_coverings_of_isIso _)).mem₀, ?_⟩
+ intro U v ⟨i⟩
+ dsimp
+ simp only [Category.assoc, Arrow.w_mk_right, Arrow.mk_left, Arrow.mk_right, Arrow.mk_hom]
+ rw [← Category.assoc, P.cancel_right_of_respectsIso]
+ exact h _ _ ⟨i⟩
+ | comp f hf R hR g hg ih =>
+ obtain ⟨S, hS, h⟩ := ih
+ rw [K.mem_iff_exists_zeroHypercover] at hS hR
+ obtain ⟨E, rfl⟩ := hS
+ obtain ⟨F, rfl⟩ := hR
+ refine ⟨(E.pullback₁ g).presieve₀, (E.pullback₁ g).mem₀, ?_⟩
+ intro U v ⟨i⟩
+ dsimp
+ rw [pullback.condition_assoc]
+ refine RespectsLeft.precomp (Q := K.morphismProperty) _ ?_ _ ?_
+ · obtain ⟨j⟩ := hg
+ exact (F.pullback₂ (E.f i)).morphismProperty j
+ · exact h _ _ ⟨i⟩
+ | of_presieve f R hR h ih =>
+ rw [K.mem_iff_exists_zeroHypercover] at hR
+ obtain ⟨E, rfl⟩ := hR
+ choose S hS h' using fun i : E.I₀ ↦ ih _ _ ⟨i⟩
+ simp_rw [K.mem_iff_exists_zeroHypercover] at hS
+ choose F hF using hS
+ refine ⟨_, (E.bind F).mem₀, fun U g ⟨j⟩ ↦ ?_⟩
+ dsimp
+ rw [Category.assoc]
+ exact h' _ _ _ (by simp [hF])
+ · intro ⟨R, hR, h⟩
+ exact .of_presieve _ _ hR (by grind)
+
+end sourceLocalClosure
+
+end CategoryTheory.MorphismProperty
diff --git a/Mathlib/CategoryTheory/Sites/Hypercover/Zero.lean b/Mathlib/CategoryTheory/Sites/Hypercover/Zero.lean
index b245a70e8c9c81..78d156f96d8089 100644
--- a/Mathlib/CategoryTheory/Sites/Hypercover/Zero.lean
+++ b/Mathlib/CategoryTheory/Sites/Hypercover/Zero.lean
@@ -915,6 +915,9 @@ lemma le_of_zeroHypercover {J K : Precoverage C}
class Small (J : Precoverage C) : Prop where
zeroHypercoverSmall : ∀ {S : C} (E : ZeroHypercover.{max u v} J S), ZeroHypercover.Small.{w'} E
+instance (K : Precoverage C) : Small.{max u v} K where
+ zeroHypercoverSmall := inferInstance
+
instance (J : Precoverage C) [Small.{w} J] {S : C} (E : ZeroHypercover.{w'} J S) :
ZeroHypercover.Small.{w} E := by
have : ZeroHypercover.Small.{w} (ZeroHypercover.restrictIndexOfSmall.{max u v} E) :=
From b9251c8115165ef805d290dcce9d040077cd5d37 Mon Sep 17 00:00:00 2001
From: Ben Eltschig <43812953+peabrainiac@users.noreply.github.com>
Date: Wed, 24 Jun 2026 08:46:23 +0000
Subject: [PATCH 0310/1300] chore(Topology): tag
`continuous_of_indiscreteTopology` with `fun_prop` (#40987)
Tag `continuous_of_indiscreteTopology` with `fun_prop`. This in particular allows `fun_prop` to prove that functions to subsingletons are continuous, which it previously couldn't do.
---
Mathlib/Topology/Order.lean | 1 +
1 file changed, 1 insertion(+)
diff --git a/Mathlib/Topology/Order.lean b/Mathlib/Topology/Order.lean
index f87c4a05190af6..56f78491229fab 100644
--- a/Mathlib/Topology/Order.lean
+++ b/Mathlib/Topology/Order.lean
@@ -292,6 +292,7 @@ theorem closure_indiscrete [IndiscreteTopology α] {s : Set α} (h : s.Nonempty)
closure s = Set.univ := Dense.closure_eq (dense_indiscrete h)
/-- Every function to the indiscrete topology is continuous -/
+@[fun_prop]
theorem continuous_of_indiscreteTopology {β} [TopologicalSpace β] [IndiscreteTopology β]
{f : α → β} : Continuous f where
isOpen_preimage := by simp [IndiscreteTopology.isOpen_iff]
From 35fc73662776f4704736b39635edaccd99726fc6 Mon Sep 17 00:00:00 2001
From: "mathlib-update-dependencies[bot]"
<258990618+mathlib-update-dependencies[bot]@users.noreply.github.com>
Date: Wed, 24 Jun 2026 09:59:17 +0000
Subject: [PATCH 0311/1300] chore: update Mathlib dependencies 2026-06-24
(#40989)
This PR updates the Mathlib dependencies.
---
lake-manifest.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lake-manifest.json b/lake-manifest.json
index 07ac27b85df079..5ebacc94fd75e7 100644
--- a/lake-manifest.json
+++ b/lake-manifest.json
@@ -5,7 +5,7 @@
"type": "git",
"subDir": null,
"scope": "leanprover-community",
- "rev": "f3f26cc72646205ca167117487c008ee1dafe816",
+ "rev": "f3c7bd5061bd81b4480295c524d4f245c8b7e4e2",
"name": "plausible",
"manifestFile": "lake-manifest.json",
"inputRev": "main",
From a4bf45d42ebfbd6ff32df72735270d200257f8d0 Mon Sep 17 00:00:00 2001
From: Chris Lloyd <868215+cjrl@users.noreply.github.com>
Date: Wed, 24 Jun 2026 10:56:52 +0000
Subject: [PATCH 0312/1300] feat(Data/Fintype/Card):
existsUnique_notMem_image_of_injective_of_card_succ (#37720)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This pull requests adds a small theorem `existsUnique_notMem_image_of_injective_of_card_succ` to `Mathlib/Data/Fintype/Card` that says given an injective map f : α → β such that β has cardinality one more than α, there exists a unique element of β not in the image of f.
This can be viewed as going in the opposite direction of `card_lt_of_injective_of_notMem`.
This little fact is needed for our Latin Square PR #36698.
Co-authored-by: Christopher J. R. Lloyd
Co-authored-by: George H. Seelinger
---
Mathlib/Data/Finset/Card.lean | 13 +++++++++++++
Mathlib/Data/Fintype/Card.lean | 7 +++++++
2 files changed, 20 insertions(+)
diff --git a/Mathlib/Data/Finset/Card.lean b/Mathlib/Data/Finset/Card.lean
index 2fd5eac026ce27..cc5329230c10c2 100644
--- a/Mathlib/Data/Finset/Card.lean
+++ b/Mathlib/Data/Finset/Card.lean
@@ -680,6 +680,9 @@ theorem card_eq_one : #s = 1 ↔ ∃ a, s = {a} := by
cases s
simp only [Multiset.card_eq_one, Finset.card, ← val_inj, singleton_val]
+theorem card_eq_one_iff_existsUnique : #s = 1 ↔ ∃! a, a ∈ s := by
+ simp [card_eq_one, Finset.singleton_iff_unique_mem]
+
theorem exists_eq_insert_iff [DecidableEq α] :
(∃ a ∉ s, insert a s = t) ↔ s ⊆ t ∧ #s + 1 = #t := by
constructor
@@ -738,6 +741,16 @@ theorem one_lt_card_iff_nontrivial : 1 < #s ↔ s.Nontrivial := by
rw [← not_iff_not, not_lt, Finset.Nontrivial, ← Set.nontrivial_coe_sort,
not_nontrivial_iff_subsingleton, card_le_one_iff_subsingleton_coe, coe_sort_coe]
+/-- Given an injective map `f : α → β` for finite sets `s ⊂ α` and `t ⊂ β` such that `t` has
+ cardinality one more than `s`, there exists a unique element of `t` not in `f(s)`. -/
+theorem existsUnique_notMem_image_of_injOn_of_card_eq_add_one
+ {t : Finset β} [DecidableEq β]
+ (hf : Set.InjOn f s) (hf' : Set.MapsTo f s t) (h : #t = #s + 1) :
+ ∃! x, x ∈ t ∧ x ∉ s.image f := by
+ have : #(t \ s.image f) = 1 := by
+ grind [card_sdiff_of_subset hf'.finsetImage_subset, card_image_of_injOn hf]
+ simpa [card_eq_one_iff_existsUnique] using this
+
/-- If a Finset in a Pi type is nontrivial (has at least two elements), then
its projection to some factor is nontrivial, and the fibers of the projection
are proper subsets. -/
diff --git a/Mathlib/Data/Fintype/Card.lean b/Mathlib/Data/Fintype/Card.lean
index fa521717a60e9c..3d37ede9458d79 100644
--- a/Mathlib/Data/Fintype/Card.lean
+++ b/Mathlib/Data/Fintype/Card.lean
@@ -255,6 +255,13 @@ theorem card_lt_of_injective_of_notMem (f : α → β) (h : Function.Injective f
Finset.card_lt_univ_of_notMem (x := b) <| by
rwa [← mem_coe, coe_map, coe_univ, Set.image_univ]
+/-- Given an injective map `f : α → β` such that `β` has cardinality one more
+than `α`, there exists a unique element of `β` not in the image of `f`. -/
+theorem existsUnique_notMem_image_of_injective_of_card_eq_add_one [DecidableEq β]
+ (f : α → β) (hf : f.Injective) (h : card β = card α + 1) : ∃! x, x ∉ univ.image f := by
+ simpa using existsUnique_notMem_image_of_injOn_of_card_eq_add_one
+ (s := .univ) (t := .univ) (Set.injOn_of_injective hf) (by simp) (by simpa)
+
theorem card_lt_of_injective_not_surjective (f : α → β) (h : Function.Injective f)
(h' : ¬Function.Surjective f) : card α < card β :=
let ⟨_y, hy⟩ := not_forall.1 h'
From cd4c002ea0b293f24faf1d93a3e7af63389a9de6 Mon Sep 17 00:00:00 2001
From: zw810-ctrl <248219010+zw810-ctrl@users.noreply.github.com>
Date: Wed, 24 Jun 2026 10:56:54 +0000
Subject: [PATCH 0313/1300] feat: API and proof of `isStrictMap_prodMap`
(#38937)
Co-authored-by: Oliver Nash
Co-authored-by: Monica Omar <23701951+themathqueen@users.noreply.github.com>
Co-authored-by: ADedecker
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Anatole Dedecker
---
Mathlib/Topology/Algebra/Group/Basic.lean | 7 ++++
Mathlib/Topology/Maps/Strict/Basic.lean | 49 +++++++++++++++++++++++
2 files changed, 56 insertions(+)
diff --git a/Mathlib/Topology/Algebra/Group/Basic.lean b/Mathlib/Topology/Algebra/Group/Basic.lean
index 6f485a3f813c0c..7d82c9db6f5b06 100644
--- a/Mathlib/Topology/Algebra/Group/Basic.lean
+++ b/Mathlib/Topology/Algebra/Group/Basic.lean
@@ -914,6 +914,13 @@ lemma MonoidHom.isOpenQuotientMap_of_isQuotientMap {A : Type*} [Group A]
use x * k, hx
rw [map_mul, hk, mul_one]
+@[to_additive]
+lemma MonoidHom.isOpenQuotientMap_iff_isQuotientMap {A : Type*} [Group A]
+ [TopologicalSpace A] [ContinuousMul A] {B : Type*} [Group B] [TopologicalSpace B]
+ {F : Type*} [FunLike F A B] [MonoidHomClass F A B] {φ : F} :
+ IsOpenQuotientMap φ ↔ IsQuotientMap φ :=
+ ⟨fun hf => hf.isQuotientMap, MonoidHom.isOpenQuotientMap_of_isQuotientMap⟩
+
@[to_additive]
theorem IsTopologicalGroup.ext {G : Type*} [Group G] {t t' : TopologicalSpace G}
(tg : @IsTopologicalGroup G t _) (tg' : @IsTopologicalGroup G t' _)
diff --git a/Mathlib/Topology/Maps/Strict/Basic.lean b/Mathlib/Topology/Maps/Strict/Basic.lean
index dedc38c5e810f8..ec2a2ed345a7a0 100644
--- a/Mathlib/Topology/Maps/Strict/Basic.lean
+++ b/Mathlib/Topology/Maps/Strict/Basic.lean
@@ -9,6 +9,8 @@ public import Mathlib.Topology.Maps.Basic
public import Mathlib.Topology.Homeomorph.Quotient
public import Mathlib.Topology.Constructions
public import Mathlib.Data.Setoid.Basic
+public import Mathlib.Topology.Algebra.Group.Quotient
+
/-!
# Bourbaki Strict Maps
@@ -33,6 +35,18 @@ We provide several equivalent ways to characterize a strict map `f`:
the canonical bijection `Quotient (Setoid.ker f) ≃ Set.range f` is a homeomorphism.
* `Topology.isStrictMap_iff_isEmbedding_kerLift`: `f` is strict if and only if
the canonical injection `Quotient (Setoid.ker f) → Y` (`Setoid.kerLift f`) is an embedding.
+
+### Group homomorphisms
+
+In general, the product (in the sense of `Prod.map`) of two strict maps need not be strict.
+But thanks to `MonoidHom.isOpenQuotientMap_of_isQuotientMap`, we can replace `IsQuotientMap`
+by `IsOpenQuotientMap` in the setting of group homomorphisms. Therefore we provide several
+important properties of strict group homomorphisms :
+
+* `isStrictMap_iff_isOpenQuotientMap_rangeRestrict`: `f` is a strict group homomorphism if
+ and only if the `rangeRestrict` of `f` is an open quotient map.
+* `isStrictMap_prodMap`: The product (in the sense of Prod.map) of strict group homomorphisms
+ is strict.
-/
@[expose] public section
@@ -153,4 +167,39 @@ lemma isEmbedding_iff_isStrictMap_injective :
(Homeomorph.Quotient.congrRight <| by simp [f_inj.eq_iff]).trans Homeomorph.quotientBot
exact f_strict.comp Φ.symm.isEmbedding
+/-- Strict maps are preserved when precomposing with a homeomorphism. -/
+lemma Homeomorph.isStrictMap_comp_iff (e : X ≃ₜ Y) {f : Y → Z} :
+ IsStrictMap (f ∘ e) ↔ IsStrictMap f :=
+ e.isQuotientMap.isStrictMap_iff.symm
+
+/-- Strict maps are preserved when postcomposing with a homeomorphism. -/
+lemma Homeomorph.comp_isStrictMap_iff (e : Y ≃ₜ Z) {f : X → Y} :
+ IsStrictMap (e ∘ f) ↔ IsStrictMap f :=
+ e.isEmbedding.isStrictMap_iff.symm
+
end Topology
+
+namespace MonoidHom
+
+variable {G H G' H' : Type*} [Group G'] [Group H'] [Group G] [Group H] (f : G →* H) (g : G' →* H')
+ [TopologicalSpace G] [IsTopologicalGroup G] [TopologicalSpace H]
+
+/-- A group homomorphism is strict if and only if its `rangeRestrict` is an open quotient map. -/
+@[to_additive] lemma isStrictMap_iff_isOpenQuotientMap_rangeRestrict :
+ IsStrictMap f ↔ IsOpenQuotientMap f.rangeRestrict := by
+ rw [isOpenQuotientMap_iff_isQuotientMap]
+ rfl
+
+variable {f g} [TopologicalSpace G'] [IsTopologicalGroup G'] [TopologicalSpace H']
+
+/-- The product (in the sense of `Prod.map`) of strict group homomorphisms is strict -/
+@[to_additive isStrictMap_prodMap] lemma isStrictMap_prodMap (hf : IsStrictMap f)
+ (hg : IsStrictMap g) : IsStrictMap (f.prodMap g) := by
+ rw [isStrictMap_iff_isOpenQuotientMap_rangeRestrict] at hf hg ⊢
+ let aux : (f.prodMap g).range ≃ₜ f.range × g.range :=
+ (Homeomorph.setCongr (by simp [Subgroup.coe_prod])).trans (Homeomorph.Set.prod _ _)
+ exact aux.symm.isOpenQuotientMap.comp (hf.prodMap hg)
+
+-- TODO Add the lemma `isStrictMap_piMap` once `MonoidHom.piMap` has been defined.
+
+end MonoidHom
From c54f3c8d2c79fb760dbffc7a25dbe18f1b8fbf5f Mon Sep 17 00:00:00 2001
From: Felix Pernegger <188575194+felixpernegger@users.noreply.github.com>
Date: Wed, 24 Jun 2026 10:56:57 +0000
Subject: [PATCH 0314/1300] feat(Topology/CWComplex/Classical): discrete spaces
are CW complexes (#38943)
as well as some useful lemmas
This is partly based on code by @scholzhannah
I think this is notable to include, since it is pretty much the only (easy) "topological" sufficient condition for something being a CW complex there is.
Co-authored-by: Batixx
---
Mathlib/Algebra/Group/Fin/Tuple.lean | 3 +
.../Topology/CWComplex/Classical/Basic.lean | 55 +++++++++++++++++++
Mathlib/Topology/Covering/Basic.lean | 2 +-
Mathlib/Topology/DiscreteSubset.lean | 33 ++++++++++-
Mathlib/Topology/Irreducible.lean | 4 +-
5 files changed, 92 insertions(+), 5 deletions(-)
diff --git a/Mathlib/Algebra/Group/Fin/Tuple.lean b/Mathlib/Algebra/Group/Fin/Tuple.lean
index 7d566c015fa76d..5c332bc478acc6 100644
--- a/Mathlib/Algebra/Group/Fin/Tuple.lean
+++ b/Mathlib/Algebra/Group/Fin/Tuple.lean
@@ -103,6 +103,9 @@ variable [Zero α]
@[simp] lemma zero_empty : (0 : Fin 0 → α) = ![] := empty_eq _
+@[simp] lemma finZeroElim_eq_zero : (@finZeroElim fun _ ↦ α) = 0 := by
+ rw [Matrix.empty_eq finZeroElim, Matrix.zero_empty]
+
@[simp] lemma cons_zero_zero : vecCons (0 : α) (0 : Fin n → α) = 0 := by
ext i; exact i.cases rfl (by simp)
diff --git a/Mathlib/Topology/CWComplex/Classical/Basic.lean b/Mathlib/Topology/CWComplex/Classical/Basic.lean
index 97b4720eaba62a..78eab46d614820 100644
--- a/Mathlib/Topology/CWComplex/Classical/Basic.lean
+++ b/Mathlib/Topology/CWComplex/Classical/Basic.lean
@@ -289,6 +289,20 @@ lemma RelCWComplex.map_zero_mem_closedCell [RelCWComplex C D] (n : ℕ) (i : cel
map n i 0 ∈ closedCell n i :=
openCell_subset_closedCell _ _ (map_zero_mem_openCell _ _)
+lemma RelCWComplex.openCell_nonempty [RelCWComplex C D] (n : ℕ) (j : cell C n) :
+ (openCell n j).Nonempty :=
+ ⟨(map n j) 0, map_zero_mem_openCell n j⟩
+
+lemma RelCWComplex.closedCell_nonempty [RelCWComplex C D] (n : ℕ) (j : cell C n) :
+ (closedCell n j).Nonempty :=
+ ⟨(map n j) 0, map_zero_mem_closedCell n j⟩
+
+/-- If two open cells are equal, so are the underlying cells. -/
+lemma RelCWComplex.openCell_congr [RelCWComplex C D] (n : ℕ) {s t : cell C n}
+ (st : openCell n s = openCell n t) : s = t := by
+ contrapose! st
+ exact (disjoint_openCell_of_ne (by simpa)).ne (openCell_nonempty n s).ne_empty
+
/-- This is an auxiliary lemma used to prove `RelCWComplex.eq_of_eq_union_iUnion`. -/
private lemma RelCWComplex.subset_of_eq_union_iUnion [RelCWComplex C D] (I J : Π n, Set (cell C n))
(hIJ : D ∪ ⋃ (n : ℕ) (j : I n), openCell (C := C) n j =
@@ -1061,4 +1075,45 @@ lemma RelCWComplex.disjoint_interior_base_iUnion_closedCell [T2Space X] [RelCWCo
simp_rw [disjoint_iff_inter_eq_empty, inter_iUnion, disjoint_interior_base_closedCell.inter_eq,
iUnion_empty]
+/-- A closed discrete subset of a space is a CW complex. -/
+@[reducible, simps -isSimp]
+def CWComplex.OfDiscreteClosed (hD : IsDiscrete D) (Dc : IsClosed D) : CWComplex D where
+ cell n := match n with
+ | 0 => D
+ | (_ + 1) => PEmpty
+ map n i := match n with
+ | 0 => PartialEquiv.single ![] i
+ | (_ + 1) => i.elim
+ source_eq n i := match n with
+ | 0 => by simp [ball, Matrix.empty_eq, eq_univ_iff_forall]
+ | (_ + 1) => i.elim
+ continuousOn n i := match n with
+ | 0 => continuousOn_const
+ | (_ + 1) => i.elim
+ continuousOn_symm n i := match n with
+ | 0 => continuousOn_const
+ | (_ + 1) => i.elim
+ pairwiseDisjoint' := by
+ simp_rw [PairwiseDisjoint, Set.Pairwise, Function.onFun]
+ rintro ⟨_|n, j⟩ _ ⟨_|m, i⟩ _ ne
+ · simp_all [Subtype.coe_injective.ne]
+ · exact i.elim
+ · tauto
+ · exact i.elim
+ mapsTo' n i := match n with
+ | 0 => by simp [Matrix.zero_empty, sphere_eq_empty_of_subsingleton]
+ | (_ + 1) => i.elim
+ closed' A AD _ := isClosed_of_subset_discrete_closed AD hD Dc
+ union' := by
+ apply subset_antisymm (iUnion₂_subset_iff.mpr fun n ↦ by cases n <;> simp)
+ intro x xD
+ simp only [mem_iUnion, mem_image, mem_closedBall, dist_zero_right]
+ refine ⟨0, ?_⟩
+ simpa [-Matrix.zero_empty]
+
+/-- A discrete space is a CW complex. -/
+instance CWComplex.ofDiscreteTopology {X : Type*} [TopologicalSpace X] [DiscreteTopology X] :
+ CWComplex (univ : Set X) :=
+ CWComplex.OfDiscreteClosed IsDiscrete.univ isClosed_univ
+
end Topology
diff --git a/Mathlib/Topology/Covering/Basic.lean b/Mathlib/Topology/Covering/Basic.lean
index 45d1952f2dde26..8400516ab95c2c 100644
--- a/Mathlib/Topology/Covering/Basic.lean
+++ b/Mathlib/Topology/Covering/Basic.lean
@@ -497,7 +497,7 @@ variable (f) in
theorem IsDiscrete.of_openPartialHomeomorph {t : Set E} {x : X}
(htx : t ⊆ f ⁻¹' {x}) (hf : ∀ e ∈ t, ∃ φ : OpenPartialHomeomorph E X, e ∈ φ.source ∧ φ = f) :
IsDiscrete t :=
- isDiscrete_iff_forall_exists_isOpen.mpr fun e he ↦ by
+ isDiscrete_iff_forall_mem_exists_isOpen.mpr fun e he ↦ by
obtain ⟨φ, hφ, rfl⟩ := hf e he
exact ⟨_, φ.open_source, subset_antisymm (fun e' he' ↦ φ.injOn he'.1 hφ <|
(htx he'.2).trans (htx he).symm) <| Set.singleton_subset_iff.mpr ⟨hφ, he⟩⟩
diff --git a/Mathlib/Topology/DiscreteSubset.lean b/Mathlib/Topology/DiscreteSubset.lean
index 6cfe19a31b5217..b949ac0ba8702a 100644
--- a/Mathlib/Topology/DiscreteSubset.lean
+++ b/Mathlib/Topology/DiscreteSubset.lean
@@ -65,10 +65,39 @@ lemma discreteTopology_subtype_iff' {S : Set Y} :
simp [discreteTopology_iff_isOpen_singleton, isOpen_induced_iff, Set.ext_iff]
grind
-theorem isDiscrete_iff_forall_exists_isOpen {S : Set Y} :
- IsDiscrete S ↔ ∀ y ∈ S, ∃ U, IsOpen U ∧ U ∩ S = {y} := by
+/-- A set `s` is discrete iff for every `y ∈ s` there is an open `u` with `u ∩ s = {y}`.
+See `isDiscrete_iff_forall_subset_exists_isOpen'` for a related version of this with subsets. -/
+theorem isDiscrete_iff_forall_mem_exists_isOpen {s : Set Y} :
+ IsDiscrete s ↔ ∀ y ∈ s, ∃ u, IsOpen u ∧ u ∩ s = {y} := by
rw [isDiscrete_iff_discreteTopology, discreteTopology_subtype_iff']
+@[deprecated (since := "2026-06-24")]
+alias isDiscrete_iff_forall_exists_isOpen := isDiscrete_iff_forall_mem_exists_isOpen
+
+/-- A set `s` is discrete iff for every `t ⊆ s` there is an open `u` with `u ∩ s = t`.
+See `isDiscrete_iff_forall_mem_exists_isOpen` for a similar version of this with singletons. -/
+theorem isDiscrete_iff_forall_subset_exists_isOpen {s : Set X} :
+ IsDiscrete s ↔ ∀ t ⊆ s, ∃ u, IsOpen u ∧ u ∩ s = t := by
+ simp_rw [isDiscrete_iff_discreteTopology, discreteTopology_iff_forall_isOpen,
+ isOpen_induced_iff, ← image_eq_image (Subtype.val_injective), Subtype.image_preimage_coe,
+ Subtype.forall_set_subtype (p := fun t ↦ ∃ u, IsOpen u ∧ s ∩ u = t), inter_comm]
+
+/-- A set `s` is discrete iff for every `t ⊆ s` there is a closed `u` with `u ∩ s = t`. -/
+theorem isDiscrete_iff_forall_mem_exists_isClosed {S : Set X} :
+ IsDiscrete S ↔ ∀ s ⊆ S, ∃ U, IsClosed U ∧ U ∩ S = s := by
+ rw [isDiscrete_iff_forall_subset_exists_isOpen]
+ constructor <;> intro h s sS
+ · obtain ⟨U, Uo, Us⟩ := h (sᶜ ∩ S) inter_subset_right
+ exact ⟨Uᶜ, isClosed_compl_iff.mpr Uo, by rw [left_eq_inter.mpr sS]; simp_all [Set.ext_iff]⟩
+ · obtain ⟨U, Uo, Us⟩ := h (sᶜ ∩ S) inter_subset_right
+ exact ⟨Uᶜ, isOpen_compl_iff.mpr Uo, by rw [left_eq_inter.mpr sS]; simp_all [Set.ext_iff]⟩
+
+theorem isClosed_of_subset_discrete_closed {s t : Set X} (sd : s ⊆ t)
+ (ht : IsDiscrete t) (tc : IsClosed t) : IsClosed s := by
+ obtain ⟨_, rp, rt⟩ := isDiscrete_iff_forall_mem_exists_isClosed.mp ht s sd
+ rw [← rt]
+ exact rp.inter tc
+
lemma Set.Subsingleton.isDiscrete (hs : s.Subsingleton) : IsDiscrete s :=
have : Subsingleton s := (Set.subsingleton_coe s).mpr hs
⟨inferInstance⟩
diff --git a/Mathlib/Topology/Irreducible.lean b/Mathlib/Topology/Irreducible.lean
index 8f5fb12cf5bd77..e7e91d08c1cfee 100644
--- a/Mathlib/Topology/Irreducible.lean
+++ b/Mathlib/Topology/Irreducible.lean
@@ -524,8 +524,8 @@ end
lemma IsDiscrete.subsingleton_of_isPreirreducible (hs : IsDiscrete s) (hs' : IsPreirreducible s) :
s.Subsingleton := by
intro x hxs y hys
- obtain ⟨U, hU, hUx⟩ := isDiscrete_iff_forall_exists_isOpen.mp hs x hxs
- obtain ⟨V, hV, hVy⟩ := isDiscrete_iff_forall_exists_isOpen.mp hs y hys
+ obtain ⟨U, hU, hUx⟩ := isDiscrete_iff_forall_mem_exists_isOpen.mp hs x hxs
+ obtain ⟨V, hV, hVy⟩ := isDiscrete_iff_forall_mem_exists_isOpen.mp hs y hys
obtain ⟨z, hz⟩ := hs' _ _ hU hV ⟨x, by grind⟩ ⟨y, by grind⟩
exact (hUx.le (by grind)).symm.trans (b := z) (hVy.le (by grind))
From 8b553276df7d1c279d0d229506833c79c81322d6 Mon Sep 17 00:00:00 2001
From: Justus Springer <50165510+justus-springer@users.noreply.github.com>
Date: Wed, 24 Jun 2026 10:56:59 +0000
Subject: [PATCH 0315/1300] feat(Algebra/MvPolynomial/Basic):
`coeff_C_of_ne_zero` and `coeff_add_single_C` (#39623)
These lemmas are multivariate analogs to `Polynomial.coeff_C_of_ne_zero`, `Polynomial.coeff_C_succ` and `PowerSeries.coeff_C_of_ne_zero` and `PowerSeries.coeff_succ_C`. They are useful for defining partial derivatives for multivariate power series, see PR #39626.
- [x] depends on: #39632
---
Mathlib/Algebra/MvPolynomial/Basic.lean | 9 +++++++++
Mathlib/RingTheory/MvPowerSeries/Basic.lean | 9 +++++++++
2 files changed, 18 insertions(+)
diff --git a/Mathlib/Algebra/MvPolynomial/Basic.lean b/Mathlib/Algebra/MvPolynomial/Basic.lean
index 5bc4d8845577ea..74f35d21f7e3d5 100644
--- a/Mathlib/Algebra/MvPolynomial/Basic.lean
+++ b/Mathlib/Algebra/MvPolynomial/Basic.lean
@@ -593,6 +593,15 @@ theorem coeff_C [DecidableEq σ] (m) (a) :
coeff m (C a : MvPolynomial σ R) = if 0 = m then a else 0 :=
Finsupp.single_apply
+theorem coeff_C_of_ne_zero {m : σ →₀ ℕ} (h : m ≠ 0) (a : R) : coeff m (C a) = 0 := by
+ classical rw [coeff_C, if_neg h.symm]
+
+-- The intended use case of this theorem is for `n = 1` (often useful for `pderiv`).
+@[simp]
+theorem coeff_add_single_C {n : ℕ} [NeZero n] {m : σ →₀ ℕ} (a : R) (i : σ) :
+ coeff (m + Finsupp.single i n) (C a) = 0 :=
+ coeff_C_of_ne_zero (fun H ↦ by simpa [NeZero.ne] using congr($(H) i)) a
+
lemma eq_C_of_isEmpty [IsEmpty σ] (p : MvPolynomial σ R) :
p = C (p.coeff 0) := by
obtain ⟨x, rfl⟩ := C_surjective σ p
diff --git a/Mathlib/RingTheory/MvPowerSeries/Basic.lean b/Mathlib/RingTheory/MvPowerSeries/Basic.lean
index 741d71157d56c1..9fc6b1497de12c 100644
--- a/Mathlib/RingTheory/MvPowerSeries/Basic.lean
+++ b/Mathlib/RingTheory/MvPowerSeries/Basic.lean
@@ -351,6 +351,15 @@ theorem coeff_C [DecidableEq σ] (n : σ →₀ ℕ) (a : R) :
theorem coeff_zero_C (a : R) : coeff (0 : σ →₀ ℕ) (C a) = a :=
coeff_monomial_same 0 a
+theorem coeff_C_of_ne_zero {n : σ →₀ ℕ} (h : n ≠ 0) (a : R) : coeff n (C a) = 0 := by
+ classical rw [coeff_C, if_neg h]
+
+-- The intended use case of this theorem is for `m = 1` (often useful for `pderiv`).
+@[simp]
+theorem coeff_add_single_C {m : ℕ} [NeZero m] {n : σ →₀ ℕ} (a : R) (i : σ) :
+ coeff (n + single i m) (C a) = 0 :=
+ coeff_C_of_ne_zero (fun H ↦ by simpa [NeZero.ne] using congr($(H) i)) a
+
@[grind inj]
theorem C_injective : Function.Injective (C : R → MvPowerSeries σ R) := by
intro a b h
From 6e9880829d70296832473f9100904cbf3c5a56e6 Mon Sep 17 00:00:00 2001
From: Jovan Gerbscheid <56355248+JovanGerb@users.noreply.github.com>
Date: Wed, 24 Jun 2026 10:57:01 +0000
Subject: [PATCH 0316/1300] chore(CategoryTheory/LiftingProperties/Basic): use
`to_dual` more (#40943)
This PR finishes using `to_dual` in `LiftingProperties/Basic`, finishing the work in #38174.
Some missing prerequisites have also been tagged, in particular `Retract`.
---
Mathlib/CategoryTheory/Balanced.lean | 10 +--
Mathlib/CategoryTheory/Comma/Arrow.lean | 3 +
.../LiftingProperties/Basic.lean | 40 ++++--------
Mathlib/CategoryTheory/Retract.lean | 64 +++++++++++--------
4 files changed, 57 insertions(+), 60 deletions(-)
diff --git a/Mathlib/CategoryTheory/Balanced.lean b/Mathlib/CategoryTheory/Balanced.lean
index b6499a5f170c0f..d9f2b95a9f04f7 100644
--- a/Mathlib/CategoryTheory/Balanced.lean
+++ b/Mathlib/CategoryTheory/Balanced.lean
@@ -27,19 +27,19 @@ namespace CategoryTheory
variable {C : Type u} [Category.{v} C]
-section
-
-variable (C)
-
+variable (C) in
/-- A category is called balanced if any morphism that is both monic and epic is an isomorphism. -/
class Balanced : Prop where
isIso_of_mono_of_epi : ∀ {X Y : C} (f : X ⟶ Y) [Mono f] [Epi f], IsIso f
-end
+attribute [to_dual self (reorder := X Y, 7 8)] Balanced.isIso_of_mono_of_epi
+attribute [to_dual self (reorder := isIso_of_mono_of_epi (X Y, 4 5))] Balanced.mk
+@[to_dual self (reorder := X Y, 7 8)]
theorem isIso_of_mono_of_epi [Balanced C] {X Y : C} (f : X ⟶ Y) [Mono f] [Epi f] : IsIso f :=
Balanced.isIso_of_mono_of_epi _
+@[to_dual isIso_iff_epi_and_mono]
theorem isIso_iff_mono_and_epi [Balanced C] {X Y : C} (f : X ⟶ Y) : IsIso f ↔ Mono f ∧ Epi f :=
⟨fun _ => ⟨inferInstance, inferInstance⟩, fun ⟨_, _⟩ => isIso_of_mono_of_epi _⟩
diff --git a/Mathlib/CategoryTheory/Comma/Arrow.lean b/Mathlib/CategoryTheory/Comma/Arrow.lean
index 62ee97723e7a57..5ea218a8f31358 100644
--- a/Mathlib/CategoryTheory/Comma/Arrow.lean
+++ b/Mathlib/CategoryTheory/Comma/Arrow.lean
@@ -78,6 +78,9 @@ def mk {X Y : T} (f : X ⟶ Y) : Arrow T where
right := Y
hom := f
+attribute [to_dual existing] mk_left
+attribute [to_dual self] mk_hom
+
@[simp]
theorem mk_eq (f : Arrow T) : Arrow.mk f.hom = f := by
cases f
diff --git a/Mathlib/CategoryTheory/LiftingProperties/Basic.lean b/Mathlib/CategoryTheory/LiftingProperties/Basic.lean
index dbb84330753cbc..185a3dd028503c 100644
--- a/Mathlib/CategoryTheory/LiftingProperties/Basic.lean
+++ b/Mathlib/CategoryTheory/LiftingProperties/Basic.lean
@@ -36,6 +36,8 @@ open Category
variable {C : Type*} [Category* C] {A B B' X Y Y' : C} (i : A ⟶ B) (i' : B ⟶ B') (p : X ⟶ Y)
(p' : Y ⟶ Y')
+to_dual_name_hint Left Right, A Y, B X, I P
+
/-- `HasLiftingProperty i p` means that `i` has the left lifting
property with respect to `p`, or equivalently that `p` has
the right lifting property with respect to `i`. -/
@@ -80,7 +82,7 @@ theorem iff_unop {A B X Y : Cᵒᵖ} (i : A ⟶ B) (p : X ⟶ Y) :
variable (i p)
-@[to_dual of_right_iso]
+@[to_dual]
instance (priority := 100) of_left_iso [IsIso i] : HasLiftingProperty i p :=
⟨fun {f} {g} sq =>
CommSq.HasLift.mk'
@@ -88,7 +90,7 @@ instance (priority := 100) of_left_iso [IsIso i] : HasLiftingProperty i p :=
fac_left := by simp only [IsIso.hom_inv_id_assoc]
fac_right := by simp only [sq.w, assoc, IsIso.inv_hom_id_assoc] }⟩
-@[to_dual of_comp_right]
+@[to_dual]
instance of_comp_left [HasLiftingProperty i p] [HasLiftingProperty i' p] :
HasLiftingProperty (i ≫ i') p :=
⟨fun {f} {g} sq => by
@@ -101,52 +103,30 @@ instance of_comp_left [HasLiftingProperty i p] [HasLiftingProperty i' p] :
fac_right := by simp only [CommSq.fac_right] }⟩
set_option backward.isDefEq.respectTransparency false in
+@[to_dual (reorder := i i' e p)]
theorem of_arrow_iso_left {A B A' B' X Y : C} {i : A ⟶ B} {i' : A' ⟶ B'}
(e : Arrow.mk i ≅ Arrow.mk i') (p : X ⟶ Y) [hip : HasLiftingProperty i p] :
HasLiftingProperty i' p := by
rw [Arrow.iso_w' e]
infer_instance
-set_option backward.isDefEq.respectTransparency false in
-theorem of_arrow_iso_right {A B X Y X' Y' : C} (i : A ⟶ B) {p : X ⟶ Y} {p' : X' ⟶ Y'}
- (e : Arrow.mk p ≅ Arrow.mk p') [hip : HasLiftingProperty i p] : HasLiftingProperty i p' := by
- rw [Arrow.iso_w' e]
- infer_instance
-
+@[to_dual (reorder := i i' e p)]
theorem iff_of_arrow_iso_left {A B A' B' X Y : C} {i : A ⟶ B} {i' : A' ⟶ B'}
(e : Arrow.mk i ≅ Arrow.mk i') (p : X ⟶ Y) :
HasLiftingProperty i p ↔ HasLiftingProperty i' p := by
constructor <;> intro
exacts [of_arrow_iso_left e p, of_arrow_iso_left e.symm p]
-theorem iff_of_arrow_iso_right {A B X Y X' Y' : C} (i : A ⟶ B) {p : X ⟶ Y} {p' : X' ⟶ Y'}
- (e : Arrow.mk p ≅ Arrow.mk p') : HasLiftingProperty i p ↔ HasLiftingProperty i p' := by
- constructor <;> intro
- exacts [of_arrow_iso_right i e, of_arrow_iso_right i e.symm]
-
end HasLiftingProperty
-set_option backward.defeqAttrib.useBackward true in
-set_option backward.isDefEq.respectTransparency false in
-lemma RetractArrow.leftLiftingProperty
- {X Y Z W Z' W' : C} {g : Z ⟶ W} {g' : Z' ⟶ W'}
- (h : RetractArrow g' g) (f : X ⟶ Y) [HasLiftingProperty g f] : HasLiftingProperty g' f where
- sq_hasLift := fun {u v} sq ↦ by
- have sq' : CommSq (h.r.left ≫ u) g f (h.r.right ≫ v) := by simp only [Arrow.mk_left,
- Arrow.mk_right, Category.assoc, sq.w, Arrow.w_mk_right_assoc, Arrow.mk_hom, CommSq.mk]
- exact
- ⟨⟨{ l := h.i.right ≫ sq'.lift
- fac_left := by
- simp only [← h.i_w_assoc, sq'.fac_left, h.retract_left_assoc,
- Arrow.mk_left, Category.id_comp]}⟩⟩
-
set_option backward.isDefEq.respectTransparency false in
+@[to_dual]
lemma RetractArrow.rightLiftingProperty
{X Y Z W X' Y' : C} {f : X ⟶ Y} {f' : X' ⟶ Y'}
(h : RetractArrow f' f) (g : Z ⟶ W) [HasLiftingProperty g f] : HasLiftingProperty g f' where
sq_hasLift := fun {u v} sq ↦
have sq' : CommSq (u ≫ h.i.left) g f (v ≫ h.i.right) :=
- ⟨by rw [← Category.assoc, ← sq.w, Category.assoc, RetractArrow.i_w, Category.assoc]⟩
+ ⟨by rw [← sq.w_assoc, Category.assoc, RetractArrow.i_w]⟩
⟨⟨{ l := sq'.lift ≫ h.r.left}⟩⟩
namespace Arrow
@@ -154,9 +134,11 @@ namespace Arrow
/-- Given a morphism `φ : f ⟶ g` in the category `Arrow C`, this is an
abbreviation for the `CommSq.LiftStruct` structure for
the square corresponding to `φ`. -/
+@[to_dual self]
abbrev LiftStruct {f g : Arrow C} (φ : f ⟶ g) := (CommSq.mk φ.w).LiftStruct
set_option backward.isDefEq.respectTransparency false in
+@[to_dual self]
lemma hasLiftingProperty_iff {A B X Y : C} (i : A ⟶ B) (p : X ⟶ Y) :
HasLiftingProperty i p ↔
∀ (φ : Arrow.mk i ⟶ Arrow.mk p), Nonempty (LiftStruct φ) := by
@@ -172,7 +154,7 @@ end Arrow
/-- Given morphisms `i : A ⟶ B`, `p : X ⟶ Y`, `t : A ⟶ X`,
this is the property that a lifting exists for all squares
with `i` on left, `p` on the right and `t` on the top. -/
-@[to_dual (rename := i ↔ p, t → b, A ↔ Y, B ↔ X) (reorder := i p)
+@[to_dual (rename := t → b) (reorder := i p)
/-- Given morphisms `i : A ⟶ B`, `p : X ⟶ Y`, `b : B ⟶ Y`,
this is the property that a lifting exists for all squares
with `i` on left, `p` on the right and `b` on the bottom. -/]
diff --git a/Mathlib/CategoryTheory/Retract.lean b/Mathlib/CategoryTheory/Retract.lean
index 455b39b836faaa..c0986d2ae21ec4 100644
--- a/Mathlib/CategoryTheory/Retract.lean
+++ b/Mathlib/CategoryTheory/Retract.lean
@@ -32,6 +32,11 @@ structure Retract (X Y : C) where
r : Y ⟶ X
retract : i ≫ r = 𝟙 X := by cat_disch
+to_dual_name_hint I R, IArrow RArrow, Left Right
+
+attribute [to_dual existing] Retract.i
+attribute [to_dual self] Retract.mk
+
namespace Retract
attribute [reassoc (attr := simp)] retract
@@ -47,6 +52,8 @@ def op : Retract (op X) (op Y) where
r := h.i.op
retract := by simp [← op_comp, h.retract]
+attribute [to_dual existing] op_i
+
/-- If `X` is a retract of `Y`, then `F.obj X` is a retract of `F.obj Y`. -/
@[simps]
def map (F : C ⥤ D) : Retract (F.obj X) (F.obj Y) where
@@ -54,18 +61,16 @@ def map (F : C ⥤ D) : Retract (F.obj X) (F.obj Y) where
r := F.map h.r
retract := by rw [← F.map_comp h.i h.r, h.retract, F.map_id]
+attribute [to_dual existing] map_i
+
/-- a retract determines a split epimorphism. -/
-@[simps] def splitEpi : SplitEpi h.r where
+@[to_dual (attr := simps)/-- a retract determines a split monomorphism. -/]
+def splitEpi : SplitEpi h.r where
section_ := h.i
-/-- a retract determines a split monomorphism. -/
-@[simps] def splitMono : SplitMono h.i where
- retraction := h.r
-
+@[to_dual]
instance : IsSplitEpi h.r := ⟨⟨h.splitEpi⟩⟩
-instance : IsSplitMono h.i := ⟨⟨h.splitMono⟩⟩
-
variable (X) in
/-- Any object is a retract of itself. -/
@[simps]
@@ -73,12 +78,16 @@ def refl : Retract X X where
i := 𝟙 X
r := 𝟙 X
+attribute [to_dual existing] refl_i
+
/-- A retract of a retract is a retract. -/
@[simps]
def trans {Z : C} (h' : Retract Y Z) : Retract X Z where
i := h.i ≫ h'.i
r := h'.r ≫ h.r
+attribute [to_dual existing] trans_i
+
/-- If `e : X ≅ Y`, then `X` is a retract of `Y`. -/
def ofIso (e : X ≅ Y) : Retract X Y where
i := e.hom
@@ -98,6 +107,7 @@ end Retract
```
A morphism `f : X ⟶ Y` is a retract of `g : Z ⟶ W` if there are morphisms `i : f ⟶ g`
and `r : g ⟶ f` in the arrow category such that `i ≫ r = 𝟙 f`. -/
+@[to_dual self]
abbrev RetractArrow {X Y Z W : C} (f : X ⟶ Y) (g : Z ⟶ W) := Retract (Arrow.mk f) (Arrow.mk g)
namespace RetractArrow
@@ -105,60 +115,58 @@ namespace RetractArrow
variable {X Y Z W : C} {f : X ⟶ Y} {g : Z ⟶ W} (h : RetractArrow f g)
set_option backward.isDefEq.respectTransparency false in -- This is needed for `MorphismProperty/Retract.lean`
-@[reassoc]
+@[to_dual none, reassoc]
lemma i_w : h.i.left ≫ g = f ≫ h.i.right := h.i.w
-@[reassoc]
+@[to_dual none, reassoc]
lemma r_w : h.r.left ≫ f = g ≫ h.r.right := h.r.w
/-- The top of a retract diagram of morphisms determines a retract of objects. -/
-@[simps!]
+@[to_dual (attr := simps!)
+/-- The bottom of a retract diagram of morphisms determines a retract of objects. -/]
def left : Retract X Z := h.map Arrow.leftFunc
-/-- The bottom of a retract diagram of morphisms determines a retract of objects. -/
-@[simps!]
-def right : Retract Y W := h.map Arrow.rightFunc
-
-@[reassoc (attr := simp)]
+@[to_dual (attr := reassoc (attr := simp))]
lemma retract_left : h.i.left ≫ h.r.left = 𝟙 X := h.left.retract
-@[reassoc (attr := simp)]
-lemma retract_right : h.i.right ≫ h.r.right = 𝟙 Y := h.right.retract
-
+@[to_dual]
instance : IsSplitEpi h.r.left := ⟨⟨h.left.splitEpi⟩⟩
+@[to_dual]
instance : IsSplitEpi h.r.right := ⟨⟨h.right.splitEpi⟩⟩
-instance : IsSplitMono h.i.left := ⟨⟨h.left.splitMono⟩⟩
-
-instance : IsSplitMono h.i.right := ⟨⟨h.right.splitMono⟩⟩
-
/-- If a morphism `f` is a retract of `g`,
then `F.map f` is a retract of `F.map g` for any functor `F`. -/
-@[simps!]
+@[to_dual self, simps!]
def map (F : C ⥤ D) : RetractArrow (F.map f) (F.map g) :=
Retract.map h F.mapArrow
+attribute [to_dual existing] map_i_left map_i_right
+
set_option backward.defeqAttrib.useBackward true in
set_option backward.isDefEq.respectTransparency false in
/-- If a morphism `f` is a retract of `g`, then `f.op` is a retract of `g.op`. -/
-@[simps]
+@[to_dual self, simps]
def op : RetractArrow f.op g.op where
i := Arrow.homMk (h.r.right.op) (h.r.left.op) (by simp [← op_comp])
r := Arrow.homMk (h.i.right.op) (h.i.left.op) (by simp [← op_comp])
retract := by ext <;> simp [← op_comp]
+attribute [to_dual existing (reorder := X Y, Z W)] op_i
+
set_option backward.defeqAttrib.useBackward true in
set_option backward.isDefEq.respectTransparency false in
/-- If a morphism `f` in the opposite category is a retract of `g`,
then `f.unop` is a retract of `g.unop`. -/
-@[simps]
+@[to_dual self, simps]
def unop {X Y Z W : Cᵒᵖ} {f : X ⟶ Y} {g : Z ⟶ W} (h : RetractArrow f g) :
RetractArrow f.unop g.unop where
i := Arrow.homMk (h.r.right.unop) (h.r.left.unop) (by simp [← unop_comp])
r := Arrow.homMk (h.i.right.unop) (h.i.left.unop) (by simp [← unop_comp])
retract := by ext <;> simp [← unop_comp]
+attribute [to_dual existing (reorder := X Y, Z W)] unop_i
+
end RetractArrow
namespace Iso
@@ -169,16 +177,20 @@ def retract {X Y : C} (e : X ≅ Y) : Retract X Y where
i := e.hom
r := e.inv
+attribute [to_dual existing] retract_i
+
end Iso
set_option backward.defeqAttrib.useBackward true in
/-- If `X` is a retract of `Y`, then for any natural transformation `τ`,
the natural transformation `τ.app X` is a retract of `τ.app Y`. -/
-@[simps]
+@[to_dual self, simps]
def NatTrans.retractArrowApp {F G : C ⥤ D}
(τ : F ⟶ G) {X Y : C} (h : Retract X Y) : RetractArrow (τ.app X) (τ.app Y) where
i := Arrow.homMk (F.map h.i) (G.map h.i) (by simp)
r := Arrow.homMk (F.map h.r) (G.map h.r) (by simp)
retract := by ext <;> simp [← Functor.map_comp]
+attribute [to_dual existing (reorder := F G)] NatTrans.retractArrowApp_i
+
end CategoryTheory
From 3b0b30fd15768f38512a31e77829059749e29b0e Mon Sep 17 00:00:00 2001
From: Jovan Gerbscheid <56355248+JovanGerb@users.noreply.github.com>
Date: Wed, 24 Jun 2026 10:57:03 +0000
Subject: [PATCH 0317/1300] chore(CategoryTheory/EqToHom): use `to_dual none`
(#40965)
This PR tags `eqToHom` with `to_dual`. Unfortunately ,`eqToHom` is not conveniently self dual, because for this the direction of the equality needs to be swapped. So, we mostly use `to_dual none` to generate non-user-facing duals. This is needed to dualize some proofs later on.
---
Mathlib/CategoryTheory/EqToHom.lean | 65 ++++++++++++++++-------
Mathlib/CategoryTheory/Functor/Basic.lean | 1 +
Mathlib/CategoryTheory/Functor/Const.lean | 2 +
3 files changed, 50 insertions(+), 18 deletions(-)
diff --git a/Mathlib/CategoryTheory/EqToHom.lean b/Mathlib/CategoryTheory/EqToHom.lean
index 803eb1b07b990e..84483ea2b59654 100644
--- a/Mathlib/CategoryTheory/EqToHom.lean
+++ b/Mathlib/CategoryTheory/EqToHom.lean
@@ -45,6 +45,12 @@ def eqToHom {C : Type u₁} [CategoryStruct.{v₁} C] {X Y : C} (p : X = Y) :
rw [p]
exact 𝟙 _
+/-- `eqToHom'` is the dual of `eqToHom`, which we need for `to_dual`.
+Please avoid using this directly. -/
+@[to_dual existing eqToHom]
+abbrev eqToHom' {C : Type u₁} [CategoryStruct.{v₁} C] {X Y : C} (p : X = Y) : Y ⟶ X :=
+ eqToHom p.symm
+
@[simp]
theorem eqToHom_refl {C : Type u₁} [CategoryStruct.{v₁} C] (X : C) (p : X = X) :
eqToHom p = 𝟙 X :=
@@ -52,7 +58,7 @@ theorem eqToHom_refl {C : Type u₁} [CategoryStruct.{v₁} C] (X : C) (p : X =
variable {C : Type u₁} [Category.{v₁} C]
-@[reassoc (attr := simp)]
+@[to_dual none, reassoc (attr := simp)]
theorem eqToHom_trans {X Y Z : C} (p : X = Y) (q : Y = Z) :
eqToHom p ≫ eqToHom q = eqToHom (p.trans q) := by
cases p
@@ -60,95 +66,108 @@ theorem eqToHom_trans {X Y Z : C} (p : X = Y) (q : Y = Z) :
simp
/-- `eqToHom h` is heterogeneously equal to the identity of its domain. -/
+@[to_dual none]
lemma eqToHom_heq_id_dom (X Y : C) (h : X = Y) : eqToHom h ≍ 𝟙 X := by
subst h; rfl
/-- `eqToHom h` is heterogeneously equal to the identity of its codomain. -/
+@[to_dual none]
lemma eqToHom_heq_id_cod (X Y : C) (h : X = Y) : eqToHom h ≍ 𝟙 Y := by
subst h; rfl
/-- Two morphisms are conjugate via eqToHom if and only if they are heterogeneously equal.
Note this used to be in the Functor namespace, where it doesn't belong. -/
+@[to_dual none]
theorem conj_eqToHom_iff_heq {W X Y Z : C} (f : W ⟶ X) (g : Y ⟶ Z) (h : W = Y) (h' : X = Z) :
f = eqToHom h ≫ g ≫ eqToHom h'.symm ↔ f ≍ g := by
cases h
cases h'
simp
+@[to_dual none]
theorem conj_eqToHom_iff_heq' {C} [Category* C] {W X Y Z : C}
(f : W ⟶ X) (g : Y ⟶ Z) (h : W = Y) (h' : Z = X) :
f = eqToHom h ≫ g ≫ eqToHom h' ↔ f ≍ g := conj_eqToHom_iff_heq _ _ _ h'.symm
+@[to_dual none]
theorem comp_eqToHom_iff {X Y Y' : C} (p : Y = Y') (f : X ⟶ Y) (g : X ⟶ Y') :
f ≫ eqToHom p = g ↔ f = g ≫ eqToHom p.symm :=
{ mp h := by simp [← h]
mpr h := by simp [eq_whisker h (eqToHom p)] }
+@[to_dual none]
theorem eqToHom_comp_iff {X X' Y : C} (p : X = X') (f : X ⟶ Y) (g : X' ⟶ Y) :
eqToHom p ≫ g = f ↔ g = eqToHom p.symm ≫ f :=
{ mp h := by simp [← h]
mpr h := by simp [h] }
+@[to_dual none]
theorem eqToHom_comp_heq {C} [Category* C] {W X Y : C}
(f : Y ⟶ X) (h : W = Y) : eqToHom h ≫ f ≍ f := by
rw [← conj_eqToHom_iff_heq _ _ h rfl, eqToHom_refl, Category.comp_id]
-@[simp] theorem eqToHom_comp_heq_iff {C} [Category* C] {W X Y Z Z' : C}
+@[simp, to_dual none]
+theorem eqToHom_comp_heq_iff {C} [Category* C] {W X Y Z Z' : C}
(f : Y ⟶ X) (g : Z ⟶ Z') (h : W = Y) :
eqToHom h ≫ f ≍ g ↔ f ≍ g :=
⟨(eqToHom_comp_heq ..).symm.trans, (eqToHom_comp_heq ..).trans⟩
-@[simp] theorem heq_eqToHom_comp_iff {C} [Category* C] {W X Y Z Z' : C}
+@[simp, to_dual none]
+theorem heq_eqToHom_comp_iff {C} [Category* C] {W X Y Z Z' : C}
(f : Y ⟶ X) (g : Z ⟶ Z') (h : W = Y) :
g ≍ eqToHom h ≫ f ↔ g ≍ f :=
⟨(·.trans (eqToHom_comp_heq ..)), (·.trans (eqToHom_comp_heq ..).symm)⟩
+@[to_dual none]
theorem comp_eqToHom_heq {C} [Category* C] {X Y Z : C}
(f : X ⟶ Y) (h : Y = Z) : f ≫ eqToHom h ≍ f := by
rw [← conj_eqToHom_iff_heq' _ _ rfl h, eqToHom_refl, Category.id_comp]
-@[simp] theorem comp_eqToHom_heq_iff {C} [Category* C] {W X Y Z Z' : C}
+@[simp, to_dual none]
+theorem comp_eqToHom_heq_iff {C} [Category* C] {W X Y Z Z' : C}
(f : X ⟶ Y) (g : Z ⟶ Z') (h : Y = W) :
f ≫ eqToHom h ≍ g ↔ f ≍ g :=
⟨(comp_eqToHom_heq ..).symm.trans, (comp_eqToHom_heq ..).trans⟩
-@[simp] theorem heq_comp_eqToHom_iff {C} [Category* C] {W X Y Z Z' : C}
+@[simp, to_dual none]
+theorem heq_comp_eqToHom_iff {C} [Category* C] {W X Y Z Z' : C}
(f : X ⟶ Y) (g : Z ⟶ Z') (h : Y = W) :
g ≍ f ≫ eqToHom h ↔ g ≍ f :=
⟨(·.trans (comp_eqToHom_heq ..)), (·.trans (comp_eqToHom_heq ..).symm)⟩
+@[to_dual self (reorder := X Z, X' Z', f g, f' g', eq1 eq3, H1 H2)]
theorem heq_comp {C} [Category* C] {X Y Z X' Y' Z' : C}
{f : X ⟶ Y} {g : Y ⟶ Z} {f' : X' ⟶ Y'} {g' : Y' ⟶ Z'}
(eq1 : X = X') (eq2 : Y = Y') (eq3 : Z = Z')
(H1 : f ≍ f') (H2 : g ≍ g') :
f ≫ g ≍ f' ≫ g' := by
- grind
+ congr!
variable {β : Sort*}
/-- We can push `eqToHom` to the left through families of morphisms. -/
-@[reassoc (attr := simp)]
+@[to_dual none, reassoc (attr := simp)]
theorem eqToHom_naturality {f g : β → C} (z : ∀ b, f b ⟶ g b) {j j' : β} (w : j = j') :
z j ≫ eqToHom (by simp [w]) = eqToHom (by simp [w]) ≫ z j' := by
cases w
simp
/-- A variant on `eqToHom_naturality` that helps Lean identify the families `f` and `g`. -/
-@[reassoc (attr := simp)]
+@[to_dual none, reassoc (attr := simp)]
theorem eqToHom_iso_hom_naturality {f g : β → C} (z : ∀ b, f b ≅ g b) {j j' : β} (w : j = j') :
(z j).hom ≫ eqToHom (by simp [w]) = eqToHom (by simp [w]) ≫ (z j').hom := by
cases w
simp
/-- A variant on `eqToHom_naturality` that helps Lean identify the families `f` and `g`. -/
-@[reassoc (attr := simp)]
+@[to_dual none, reassoc (attr := simp)]
theorem eqToHom_iso_inv_naturality {f g : β → C} (z : ∀ b, f b ≅ g b) {j j' : β} (w : j = j') :
(z j).inv ≫ eqToHom (by simp [w]) = eqToHom (by simp [w]) ≫ (z j').inv := by
cases w
simp
/-- Reducible form of `congrArg_mpr_hom_left` -/
-@[simp]
+@[simp, to_dual none]
theorem congrArg_cast_hom_left {X Y Z : C} (p : X = Y) (q : Y ⟶ Z) :
cast (congrArg (fun W : C => W ⟶ Z) p.symm) q = eqToHom p ≫ q := by
cases p
@@ -161,13 +180,14 @@ we can replace the resulting `_.mpr f` term by a composition with an `eqToHom`.
It may be advisable to introduce any necessary `eqToHom` morphisms manually,
rather than relying on this lemma firing.
-/
+@[to_dual none]
theorem congrArg_mpr_hom_left {X Y Z : C} (p : X = Y) (q : Y ⟶ Z) :
(congrArg (fun W : C => W ⟶ Z) p).mpr q = eqToHom p ≫ q := by
cases p
simp
/-- Reducible form of `congrArg_mpr_hom_right` -/
-@[simp]
+@[simp, to_dual none]
theorem congrArg_cast_hom_right {X Y Z : C} (p : X ⟶ Y) (q : Z = Y) :
cast (congrArg (fun W : C => X ⟶ W) q.symm) p = p ≫ eqToHom q.symm := by
cases q
@@ -180,6 +200,7 @@ we can replace the resulting `_.mpr f` term by a composition with an `eqToHom`.
It may be advisable to introduce any necessary `eqToHom` morphisms manually,
rather than relying on this lemma firing.
-/
+@[to_dual none]
theorem congrArg_mpr_hom_right {X Y Z : C} (p : X ⟶ Y) (q : Z = Y) :
(congrArg (fun W : C => X ⟶ W) q).mpr p = p ≫ eqToHom q.symm := by
cases q
@@ -197,7 +218,7 @@ def eqToIso {X Y : C} (p : X = Y) : X ≅ Y :=
theorem eqToIso.hom {X Y : C} (p : X = Y) : (eqToIso p).hom = eqToHom p :=
rfl
-@[simp]
+@[simp, to_dual existing hom]
theorem eqToIso.inv {X Y : C} (p : X = Y) : (eqToIso p).inv = eqToHom p.symm :=
rfl
@@ -209,21 +230,22 @@ theorem eqToIso_refl {X : C} (p : X = X) : eqToIso p = Iso.refl X :=
theorem eqToIso_trans {X Y Z : C} (p : X = Y) (q : Y = Z) :
eqToIso p ≪≫ eqToIso q = eqToIso (p.trans q) := by ext; simp
-@[simp]
+@[simp, to_dual none]
theorem eqToHom_op {X Y : C} (h : X = Y) : (eqToHom h).op = eqToHom (congr_arg op h.symm) := by
cases h
rfl
-@[simp]
+@[simp, to_dual none]
theorem eqToHom_unop {X Y : Cᵒᵖ} (h : X = Y) :
(eqToHom h).unop = eqToHom (congr_arg unop h.symm) := by
cases h
rfl
+@[to_dual none]
instance {X Y : C} (h : X = Y) : IsIso (eqToHom h) :=
(eqToIso h).isIso_hom
-@[simp]
+@[simp, to_dual none]
theorem inv_eqToHom {X Y : C} (h : X = Y) : inv (eqToHom h) = eqToHom h.symm := by
cat_disch
@@ -233,6 +255,7 @@ namespace Functor
/-- Proving equality between functors. This isn't an extensionality lemma,
because usually you don't really want to do this. -/
+@[to_dual none]
theorem ext {F G : C ⥤ D} (h_obj : ∀ X, F.obj X = G.obj X)
(h_map : ∀ X Y f,
F.map f = eqToHom (h_obj X) ≫ G.map f ≫ eqToHom (h_obj Y).symm := by cat_disch) :
@@ -246,6 +269,7 @@ theorem ext {F G : C ⥤ D} (h_obj : ∀ X, F.obj X = G.obj X)
funext X Y f
simpa using h_map X Y f
+@[to_dual none]
lemma ext_of_iso {F G : C ⥤ D} (e : F ≅ G) (hobj : ∀ X, F.obj X = G.obj X)
(happ : ∀ X, e.hom.app X = eqToHom (hobj X) := by cat_disch) : F = G :=
Functor.ext hobj (fun X Y f => by
@@ -253,6 +277,7 @@ lemma ext_of_iso {F G : C ⥤ D} (e : F ≅ G) (hobj : ∀ X, F.obj X = G.obj X)
Category.assoc, eqToHom_trans, eqToHom_refl, Category.comp_id])
/-- Proving equality between functors using heterogeneous equality. -/
+@[to_dual none]
theorem hext {F G : C ⥤ D} (h_obj : ∀ X, F.obj X = G.obj X)
(h_map : ∀ (X Y) (f : X ⟶ Y), F.map f ≍ G.map f) : F = G :=
Functor.ext h_obj fun _ _ f => (conj_eqToHom_iff_heq _ _ (h_obj _) (h_obj _)).2 <| h_map _ _ f
@@ -260,7 +285,7 @@ theorem hext {F G : C ⥤ D} (h_obj : ∀ X, F.obj X = G.obj X)
-- Using equalities between functors.
theorem congr_obj {F G : C ⥤ D} (h : F = G) (X) : F.obj X = G.obj X := by rw [h]
-@[reassoc]
+@[to_dual none, reassoc]
theorem congr_hom {F G : C ⥤ D} (h : F = G) {X Y} (f : X ⟶ Y) :
F.map f = eqToHom (congr_obj h X) ≫ G.map f ≫ eqToHom (congr_obj h Y).symm := by
subst h; simp
@@ -314,10 +339,11 @@ e.g. the naturality of a natural transformation.
In some files it may be appropriate to use `attribute [local simp] eqToHom_map`, however.
-/
+@[to_dual none]
theorem eqToHom_map (F : C ⥤ D) {X Y : C} (p : X = Y) :
F.map (eqToHom p) = eqToHom (congr_arg F.obj p) := by cases p; simp
-@[reassoc (attr := simp)]
+@[to_dual none, reassoc (attr := simp)]
theorem eqToHom_map_comp (F : C ⥤ D) {X Y Z : C} (p : X = Y) (q : Y = Z) :
F.map (eqToHom p) ≫ F.map (eqToHom q) = F.map (eqToHom <| p.trans q) := by cat_disch
@@ -330,18 +356,21 @@ theorem eqToIso_map (F : C ⥤ D) {X Y : C} (p : X = Y) :
theorem eqToIso_map_trans (F : C ⥤ D) {X Y Z : C} (p : X = Y) (q : Y = Z) :
F.mapIso (eqToIso p) ≪≫ F.mapIso (eqToIso q) = F.mapIso (eqToIso <| p.trans q) := by cat_disch
-@[simp]
+@[simp, to_dual none]
theorem eqToHom_app {F G : C ⥤ D} (h : F = G) (X : C) :
(eqToHom h : F ⟶ G).app X = eqToHom (Functor.congr_obj h X) := by subst h; rfl
+@[to_dual none]
theorem NatTrans.congr {F G : C ⥤ D} (α : F ⟶ G) {X Y : C} (h : X = Y) :
α.app X = F.map (eqToHom h) ≫ α.app Y ≫ G.map (eqToHom h.symm) := by
rw [α.naturality_assoc]
simp [eqToHom_map]
+@[to_dual none]
theorem eq_conj_eqToHom {X Y : C} (f : X ⟶ Y) : f = eqToHom rfl ≫ f ≫ eqToHom rfl := by
simp only [Category.id_comp, eqToHom_refl, Category.comp_id]
+@[to_dual none]
theorem dcongr_arg {ι : Type*} {F G : ι → C} (α : ∀ i, F i ⟶ G i) {i j : ι} (h : i = j) :
α i = eqToHom (congr_arg F h) ≫ α j ≫ eqToHom (congr_arg G h.symm) := by
subst h
diff --git a/Mathlib/CategoryTheory/Functor/Basic.lean b/Mathlib/CategoryTheory/Functor/Basic.lean
index a17461442eee7e..9031718383e63f 100644
--- a/Mathlib/CategoryTheory/Functor/Basic.lean
+++ b/Mathlib/CategoryTheory/Functor/Basic.lean
@@ -58,6 +58,7 @@ attribute [grind =] Functor.map_id
attribute [grind _=_] Functor.map_comp
attribute [to_dual self] Functor.map Functor.map_comp
attribute [to_dual self (reorder := map (X Y), map_comp (X Z, f g))] Functor.mk
+attribute [to_dual self (reorder := mk (map (X Y), map_comp (X Z, f g)))] Functor.casesOn
-- Note: We manually add this lemma which could be generated by `reassoc`,
-- since we will import this file into `Mathlib/Tactic/CategoryTheory/Reassoc.lean`.
diff --git a/Mathlib/CategoryTheory/Functor/Const.lean b/Mathlib/CategoryTheory/Functor/Const.lean
index 970b034f3908e7..bda33f17f1d728 100644
--- a/Mathlib/CategoryTheory/Functor/Const.lean
+++ b/Mathlib/CategoryTheory/Functor/Const.lean
@@ -39,6 +39,8 @@ def const : C ⥤ J ⥤ C where
map := fun _ => 𝟙 X }
map f := { app := fun _ => f }
+attribute [to_dual self] const_obj_map
+
namespace const
open Opposite
From e14cf926ad86a02390bdae31e696eee6b4c1d238 Mon Sep 17 00:00:00 2001
From: Hannah Scholz <70071345+scholzhannah@users.noreply.github.com>
Date: Wed, 24 Jun 2026 11:46:53 +0000
Subject: [PATCH 0318/1300] chore: add deprecation for
`OpenPartialHomeomorph.prod_toPartialEquiv` (#40992)
As requested (privately) by @grunweg .
---
Mathlib/Topology/OpenPartialHomeomorph/Constructions.lean | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/Mathlib/Topology/OpenPartialHomeomorph/Constructions.lean b/Mathlib/Topology/OpenPartialHomeomorph/Constructions.lean
index 7da001d1a1cebe..39cc3938b52177 100644
--- a/Mathlib/Topology/OpenPartialHomeomorph/Constructions.lean
+++ b/Mathlib/Topology/OpenPartialHomeomorph/Constructions.lean
@@ -86,6 +86,11 @@ def prod (eX : OpenPartialHomeomorph X X') (eY : OpenPartialHomeomorph Y Y') :
continuousOn_invFun := eX.continuousOn_symm.prodMap eY.continuousOn_symm
toPartialEquiv := eX.toPartialEquiv.prod eY.toPartialEquiv
+@[deprecated "deprecated in favour of `OpenPartialHomeomorph.prod_toPartialHomeomorph`"
+ (since := "2026-06-24")]
+lemma prod_toPartialEquiv (eX : OpenPartialHomeomorph X X') (eY : OpenPartialHomeomorph Y Y') :
+ (eX.prod eY).toPartialHomeomorph.toPartialEquiv = eX.toPartialEquiv.prod eY.toPartialEquiv :=
+ rfl
@[simp, mfld_simps]
theorem prod_symm (eX : OpenPartialHomeomorph X X') (eY : OpenPartialHomeomorph Y Y') :
(eX.prod eY).symm = eX.symm.prod eY.symm :=
From 9e46613652ab97dc557552292f12f08b95c932ee Mon Sep 17 00:00:00 2001
From: "Filippo A. E. Nuccio" <65080144+faenuccio@users.noreply.github.com>
Date: Wed, 24 Jun 2026 12:05:15 +0000
Subject: [PATCH 0319/1300] perf(RingTheory/Kaehler/JacobiZariski): remove
useless instances (#40858)
This PR removes some instances that were previously needed to avoid some timeouts.
Co-authored-by: faenuccio
---
Mathlib/RingTheory/Kaehler/JacobiZariski.lean | 8 --------
1 file changed, 8 deletions(-)
diff --git a/Mathlib/RingTheory/Kaehler/JacobiZariski.lean b/Mathlib/RingTheory/Kaehler/JacobiZariski.lean
index 1220b5ccd0bf32..0efb5430ddfabc 100644
--- a/Mathlib/RingTheory/Kaehler/JacobiZariski.lean
+++ b/Mathlib/RingTheory/Kaehler/JacobiZariski.lean
@@ -130,12 +130,6 @@ def CotangentSpace.compEquiv :
(Q.comp P).cotangentSpaceBasis.repr.trans
(Q.cotangentSpaceBasis.prod (P.cotangentSpaceBasis.baseChange T)).repr.symm
-section instanceProblem
-
--- Note: these instances are needed to prevent instance search timeouts.
-attribute [local instance 999999] Zero.toOfNat0 SemilinearMapClass.distribMulActionSemiHomClass
- SemilinearEquivClass.instSemilinearMapClass instAddZeroClassTensorProduct AddZero.toZero
-
lemma CotangentSpace.compEquiv_symm_inr :
(compEquiv Q P).symm.toLinearMap ∘ₗ
LinearMap.inr T Q.toExtension.CotangentSpace (T ⊗[S] P.toExtension.CotangentSpace) =
@@ -448,8 +442,6 @@ lemma exact_map_δ' (f : Hom W Q) :
end H1Cotangent
-end instanceProblem
-
end Generators
variable {T : Type u₃} [CommRing T] [Algebra R T] [Algebra S T] [IsScalarTower R S T]
From 2ef78a0069c7db7c2e8d4dadd6aa8fe38f5525a9 Mon Sep 17 00:00:00 2001
From: Bingyu Xia <71547343+BryceT233@users.noreply.github.com>
Date: Wed, 24 Jun 2026 12:22:10 +0000
Subject: [PATCH 0320/1300] feat(RingTheory/Kaehler/JacobiZariski): exactness
at the left of Jacobi-Zariski sequence under flatness assumption (#39958)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Given algebras `R → S → T` and `T` flat over `S`, this PR adds the exactness of `T ⊗[S] H₁(L_{S/R}) → H₁(L_{T/R}) → H₁(L_{T/S})` at the left of Jacobi-Zariski sequence. Note that the flatness assumption here is stronger than the Tor-vanishing conditions required in the full statement of [Stacks Project, 00S2](https://stacks.math.columbia.edu/tag/00S2), this should be refactored and generalized once more results on Tor modules are available.
---
Mathlib/RingTheory/Kaehler/JacobiZariski.lean | 114 ++++++++++++++++--
1 file changed, 106 insertions(+), 8 deletions(-)
diff --git a/Mathlib/RingTheory/Kaehler/JacobiZariski.lean b/Mathlib/RingTheory/Kaehler/JacobiZariski.lean
index 0efb5430ddfabc..3b95f71029bacd 100644
--- a/Mathlib/RingTheory/Kaehler/JacobiZariski.lean
+++ b/Mathlib/RingTheory/Kaehler/JacobiZariski.lean
@@ -8,26 +8,47 @@ module
public import Mathlib.RingTheory.Extension.Cotangent.Basic
public import Mathlib.RingTheory.Extension.Generators
public import Mathlib.Algebra.Module.SnakeLemma
+public import Mathlib.RingTheory.Flat.Basic
/-!
# The Jacobi-Zariski exact sequence
-Given `R → S → T`, the Jacobi-Zariski exact sequence is
-```
-H¹(L_{T/R}) → H¹(L_{T/S}) → T ⊗[S] Ω[S/R] → Ω[T/R] → Ω[T/S] → 0
-```
-The maps are
+Given algebras $R \to S \to T$, the Jacobi-Zariski exact sequence is a long exact sequence
+relating the first homology of the naive cotangent complexes and the Kähler differentials of
+the respective algebras. It takes the form:
+$$
+H_1(L_{T/R}) \to H_1(L_{T/S}) \to T \otimes_S \Omega_{S/R} \to \Omega_{T/R} \to \Omega_{T/S} \to 0
+$$
+The maps in the sequence are
- `Algebra.H1Cotangent.map`
- `Algebra.H1Cotangent.δ`
- `KaehlerDifferential.mapBaseChange`
- `KaehlerDifferential.map`
-and the exactness lemmas are
+The exactness lemmas are
- `Algebra.H1Cotangent.exact_map_δ`
- `Algebra.H1Cotangent.exact_δ_mapBaseChange`
- `KaehlerDifferential.exact_mapBaseChange_map`
- `KaehlerDifferential.map_surjective`
+
+When $T$ is flat over $S$, the left bottom part of the snake lemma diagram used in
+the construction of the connecting homomorphism `Algebra.Generators.H1Cotangent.δ`
+naturally extends via a base change map. The exactness lemma is
+`Algebra.Generators.H1Cotangent.exact_liftBaseChange_map_of_flat`. Globally, this extends
+the Jacobi-Zariski exact sequence to the left via a natural base change map, taking the form
+$$
+T \otimes_S H_1(L_{S/R}) \to H_1(L_{T/R}) \to H_1(L_{T/S})
+$$
+The exactness lemma is `Algebra.H1Cotangent.exact_liftBaseChange_map_of_flat`.
+
+# TODO
+
+The flatness assumption in `Algebra.H1Cotangent.exact_liftBaseChange_map_of_flat`
+is stronger than the `Tor`-vanishing conditions required in the full statement of
+[Stacks Project, 00S2], this should be refactored and generalized once more API
+for `Tor` modules is available.
+
-/
@[expose] public section
@@ -440,6 +461,67 @@ lemma exact_map_δ' (f : Hom W Q) :
rw [← Extension.H1Cotangent.map_comp, Extension.H1Cotangent.map_eq _ (Q.ofComp P).toExtensionHom]
exact exact_map_δ Q P
+open LinearMap in
+lemma liftBaseChange_range_le :
+ (liftBaseChange T (Extension.H1Cotangent.map (Q.toComp P).toExtensionHom)).range ≤
+ (Extension.H1Cotangent.map (Q.ofComp P).toExtensionHom).ker := by
+ rw [range_liftBaseChange, coe_range, Submodule.span_le, Set.range_subset_iff]
+ rintro ⟨x, _⟩
+ obtain ⟨⟨(x : P.Ring), x_in⟩, rfl⟩ := Extension.Cotangent.mk_surjective x
+ ext; suffices (Q.ofComp P).toAlgHom ((Q.toComp P).toAlgHom x) ∈ Q.toExtension.ker ^ 2 by
+ simpa [Ideal.toCotangent_eq_zero]
+ rw [← Generators.ker, Generators.ker_eq_ker_aeval_val] at x_in
+ rw [toComp_toAlgHom, toAlgHom_ofComp_rename, Generators.algebraMap_eq, RingHom.coe_coe,
+ x_in, RingHom.map_zero]
+ exact Ideal.zero_mem _
+
+private lemma auxMemKer (z : T ⊗[S] P.toExtension.H1Cotangent) :
+ LinearMap.liftBaseChange T (Extension.Cotangent.map (Q.toComp P).toExtensionHom)
+ ((LinearMap.lTensor T Extension.h1Cotangentι) z) ∈
+ (Q.comp P).toExtension.cotangentComplex.ker := by
+ induction z with
+ | zero => simp
+ | tmul x y => simp [← Extension.CotangentSpace.map_cotangentComplex]
+ | add x y hx hy => simpa using Submodule.add_mem _ hx hy
+
+open LinearMap in
+/-- When $T$ is flat over $S$, the left bottom part of the snake lemma diagram used in
+the construction of the connecting homomorphism `Algebra.Generators.H1Cotangent.δ`
+naturally extends via a base change map. -/
+theorem exact_liftBaseChange_map_of_flat [Module.Flat S T] :
+ Function.Exact ((Extension.H1Cotangent.map (toComp Q P).toExtensionHom).liftBaseChange T)
+ (Extension.H1Cotangent.map (ofComp Q P).toExtensionHom) := by
+ rw [exact_iff]
+ refine le_antisymm ?_ (liftBaseChange_range_le Q P)
+ rintro ⟨x, x_in⟩ hx
+ replace hx : Extension.Cotangent.map (Q.ofComp P).toExtensionHom x = 0 := by
+ simpa [← Extension.h1Cotangentι_injective.eq_iff] using hx
+ rw [← mem_ker, (Cotangent.exact Q P).linearMap_ker_eq] at hx
+ rcases hx with ⟨x, rfl⟩
+ rw [mem_ker, ← comp_apply, ← map_comp_cotangentComplex_baseChange, comp_apply,
+ ← mem_ker, ker_eq_bot.mpr (CotangentSpace.map_toComp_injective Q P), Submodule.mem_bot,
+ baseChange_eq_ltensor, ← mem_ker, (Module.Flat.lTensor_exact T
+ P.toExtension.exact_hCotangentι_cotangentComplex).linearMap_ker_eq] at x_in
+ rcases x_in with ⟨x, rfl⟩
+ use x; induction x with
+ | zero => ext; simp
+ | tmul x y => ext; simp
+ | add x y hx hy => ext; simp [hx (auxMemKer Q P x), hy (auxMemKer Q P y)]
+
+/-- A variant of `exact_liftBaseChange_map_of_flat` that takes in
+arbitrary maps between generators. -/
+theorem exact_liftBaseChange_map_of_flat' [Module.Flat S T] (f : Hom W Q) (g : Hom P W) :
+ Function.Exact ((Extension.H1Cotangent.map g.toExtensionHom).liftBaseChange T)
+ (Extension.H1Cotangent.map f.toExtensionHom) := by
+ rw [← LinearEquiv.conj_exact_iff_exact _ _ (H1Cotangent.equiv W (Q.comp P))]
+ convert! exact_liftBaseChange_map_of_flat Q P
+ · change Extension.H1Cotangent.map (W.defaultHom (Q.comp P)).toExtensionHom ∘ₗ _ = _
+ rw [LinearMap.liftBaseChange_comp, ← Extension.H1Cotangent.map_comp,
+ Extension.H1Cotangent.map_eq]
+ · change (Extension.H1Cotangent.map f.toExtensionHom).restrictScalars T ∘ₗ
+ (Extension.H1Cotangent.map _) = _
+ rw [← Extension.H1Cotangent.map_comp, Extension.H1Cotangent.map_eq]
+
end H1Cotangent
end Generators
@@ -453,13 +535,29 @@ noncomputable
def H1Cotangent.δ : H1Cotangent S T →ₗ[T] T ⊗[S] Ω[S⁄R] :=
Generators.H1Cotangent.δ (Generators.self S T) (Generators.self R S)
-/-- Given algebras `R → S → T`, `H¹(L_{T/R}) → H¹(L_{T/S}) → T ⊗[S] Ω[S/R]` is exact. -/
+/-- Given algebras $R \to S \to T$, the sequence
+$H_1(L_{T/R}) \to H_1(L_{T/S}) \to T \otimes_S \Omega_{S/R}$
+is exact. -/
+@[stacks 00S2]
lemma H1Cotangent.exact_map_δ : Function.Exact (map R S T T) (δ R S T) :=
Generators.H1Cotangent.exact_map_δ' (Generators.self S T)
(Generators.self R S) (Generators.self R T) (Generators.defaultHom _ _)
-/-- Given algebras `R → S → T`, `H¹(L_{T/S}) → T ⊗[S] Ω[S/R] → Ω[T/R]` is exact. -/
+/-- Given algebras $R \to S \to T$, the sequence
+$H_1(L_{T/S}) \to T \otimes_S \Omega_{S/R} \to \Omega_{T/R}$
+is exact. -/
+@[stacks 00S2]
lemma H1Cotangent.exact_δ_mapBaseChange : Function.Exact (δ R S T) (mapBaseChange R S T) :=
Generators.H1Cotangent.exact_δ_map (Generators.self S T) (Generators.self R S)
+/-- Given algebras $R \to S \to T$ and $T$ flat over $S$, the sequence
+$T \otimes_S H_1(L_{S/R}) \to H_1(L_{T/R}) \to H_1(L_{T/S})$
+is exact. -/
+@[stacks 00S2]
+lemma H1Cotangent.exact_liftBaseChange_map_of_flat [Module.Flat S T] :
+ Function.Exact ((map R R S T).liftBaseChange T) (map R S T T) :=
+ Generators.H1Cotangent.exact_liftBaseChange_map_of_flat'
+ (Generators.self S T) (Generators.self R S) (Generators.self R T)
+ (Generators.defaultHom _ _) (Generators.defaultHom _ _)
+
end Algebra
From 1af9e904063980cfb3b4f235d40a579e5c7e4285 Mon Sep 17 00:00:00 2001
From: Christian Merten <136261474+chrisflav@users.noreply.github.com>
Date: Wed, 24 Jun 2026 12:38:53 +0000
Subject: [PATCH 0321/1300] feat(AlgebraicGeometry/Modules): a quasi-coherent
module has a presentation on an affine cover (#40988)
This is merely a rewording of the definition in terms of the scheme API.
---
Mathlib/AlgebraicGeometry/AffineScheme.lean | 16 +++++++
Mathlib/AlgebraicGeometry/Cover/Open.lean | 4 ++
Mathlib/AlgebraicGeometry/Modules/Tilde.lean | 44 +++++++++++++++++++-
Mathlib/AlgebraicGeometry/OpenImmersion.lean | 7 ++++
Mathlib/Topology/Sets/OpenCover.lean | 8 ++++
Mathlib/Topology/Sets/Opens.lean | 13 ++++++
6 files changed, 91 insertions(+), 1 deletion(-)
diff --git a/Mathlib/AlgebraicGeometry/AffineScheme.lean b/Mathlib/AlgebraicGeometry/AffineScheme.lean
index 06bc6b0c1d65fa..9a1138dac3fd1c 100644
--- a/Mathlib/AlgebraicGeometry/AffineScheme.lean
+++ b/Mathlib/AlgebraicGeometry/AffineScheme.lean
@@ -432,6 +432,10 @@ instance isOpenImmersion_fromSpec :
@[reassoc (attr := simp)]
lemma isoSpec_inv_ι : hU.isoSpec.inv ≫ U.ι = hU.fromSpec := rfl
+@[reassoc (attr := simp)]
+lemma isoSpec_hom_fromSpec : hU.isoSpec.hom ≫ hU.fromSpec = U.ι := by
+ simp [← cancel_epi hU.isoSpec.inv]
+
@[reassoc (attr := simp)]
lemma toSpecΓ_fromSpec : U.toSpecΓ ≫ hU.fromSpec = U.ι := toSpecΓ_isoSpec_inv_assoc _ _
@@ -943,6 +947,18 @@ theorem self_le_iSup_basicOpen_iff {s : Set Γ(X, U)} :
end IsAffineOpen
+/-- The affine open cover given by a covering family of affine opens. -/
+@[simps I₀ X f]
+def Scheme.AffineOpenCover.ofIsOpenCover {X : Scheme.{u}} {ι : Type*} (U : ι → X.Opens)
+ (hU : IsOpenCover U) (hU' : ∀ i, IsAffineOpen (U i)) :
+ AffineOpenCover X where
+ I₀ := ι
+ X i := Γ(X, U i)
+ f i := (hU' i).fromSpec
+ idx x := (hU.exists_mem x).choose
+ covers x :=
+ ⟨(hU' _).isoSpec.hom ⟨_, (hU.exists_mem x).choose_spec⟩, by simp [← Scheme.Hom.comp_apply]⟩
+
set_option backward.defeqAttrib.useBackward true in
set_option backward.isDefEq.respectTransparency false in
open _root_.PrimeSpectrum in
diff --git a/Mathlib/AlgebraicGeometry/Cover/Open.lean b/Mathlib/AlgebraicGeometry/Cover/Open.lean
index 95e6a4c5d07fb0..7d4cf90cf18dc8 100644
--- a/Mathlib/AlgebraicGeometry/Cover/Open.lean
+++ b/Mathlib/AlgebraicGeometry/Cover/Open.lean
@@ -44,6 +44,10 @@ variable [∀ x, HasPullback (𝒰.f x ≫ f) g]
instance (i : 𝒰.I₀) : IsOpenImmersion (𝒰.f i) := 𝒰.map_prop i
+instance {𝒱 : OpenCover X} (f : 𝒰 ⟶ 𝒱) (i : 𝒰.I₀) : IsOpenImmersion (f.h₀ i) :=
+ have : IsOpenImmersion (f.h₀ i ≫ 𝒱.f (f.s₀ i)) := by rw [f.w₀]; infer_instance
+ .of_comp _ (𝒱.f _)
+
set_option backward.isDefEq.respectTransparency false in
/-- The affine cover of a scheme. -/
def affineCover (X : Scheme.{u}) : OpenCover X := by
diff --git a/Mathlib/AlgebraicGeometry/Modules/Tilde.lean b/Mathlib/AlgebraicGeometry/Modules/Tilde.lean
index 92eacec3e0c9e2..cdd5f8c98f175d 100644
--- a/Mathlib/AlgebraicGeometry/Modules/Tilde.lean
+++ b/Mathlib/AlgebraicGeometry/Modules/Tilde.lean
@@ -8,7 +8,7 @@ module
public import Mathlib.Algebra.Category.ModuleCat.Localization
public import Mathlib.Algebra.Category.ModuleCat.Sheaf.Quasicoherent
-public import Mathlib.AlgebraicGeometry.AffineScheme
+public import Mathlib.Algebra.Module.LocalizedModule.Away
public import Mathlib.AlgebraicGeometry.Modules.Sheaf
/-!
@@ -553,6 +553,48 @@ theorem isIso_fromTildeΓ_pushforward (M : (Spec S).Modules) [h : IsIso M.fromTi
end IsLocalizing
+set_option backward.isDefEq.respectTransparency false in
+/-- The presentation of `M.restrict f` by restricting a presentation of `M`. -/
+def Scheme.Modules.presentationRestrict {X Y : Scheme.{u}} (f : Y ⟶ X)
+ [IsOpenImmersion f] {M : X.Modules} (pres : M.Presentation) :
+ (M.restrict f).Presentation :=
+ have : PreservesColimitsOfSize.{u, u} (Scheme.Modules.restrictFunctor f) :=
+ inferInstance
+ pres.map (Scheme.Modules.restrictFunctor.{u} f) (Scheme.Modules.restrictUnitIso _).symm
+
+set_option backward.isDefEq.respectTransparency false in
+lemma Scheme.Modules.exists_isOpenCover_presentation {X : Scheme.{u}} (M : X.Modules)
+ [M.IsQuasicoherent] :
+ ∃ (ι : Type u) (U : ι → X.Opens) (_ : ∀ i, (M.restrict (U i).ι).Presentation),
+ IsOpenCover U ∧ (∀ i, IsAffineOpen (U i)) := by
+ obtain ⟨⟨I, W, cov, pres⟩⟩ := SheafOfModules.IsQuasicoherent.nonempty_quasicoherentData (M := M)
+ choose κ hsub heq using fun i ↦ Opens.isBasis_iff_cover.mp X.isBasis_affineOpens (W i)
+ refine ⟨Σ (i : I), κ i, fun j ↦ j.2, fun i ↦ ?_, ?_, ?_⟩
+ · let u := X.homOfLE (U := i.2) (V := W i.1) (by simp [heq, le_sSup])
+ have : PreservesColimitsOfSize.{u, u} (restrictFunctor u) := inferInstance
+ let F := (overEquiv (W i.1)).functor ⋙ restrictFunctor u
+ let iso : SheafOfModules.overFunctor X.ringCatSheaf _ ⋙ F ≅ restrictFunctor
+ (Scheme.Opens.ι i.2.1) := (Functor.associator _ _ _).symm ≪≫
+ Functor.isoWhiskerRight (Scheme.Modules.overFunctorEquiv _) _ ≪≫
+ (restrictFunctorComp _ _).symm ≪≫ (restrictFunctorCongr (by simp [u]))
+ exact SheafOfModules.Presentation.ofIsIso.{u, u, u} (iso.app M).hom <|
+ (pres i.1).map F (Scheme.Modules.restrictUnitIso _).symm
+ · rw [Opens.coversTop_iff, IsOpenCover] at cov
+ rw [IsOpenCover, iSup_sigma, ← cov]
+ refine iSup_congr fun i ↦ ?_
+ rw [heq i, sSup_eq_iSup']
+ · intro j
+ exact hsub _ j.2.2
+
+lemma Scheme.Modules.exists_affineOpenCover_presentation {X : Scheme.{u}} (M : X.Modules)
+ [M.IsQuasicoherent] :
+ ∃ (𝒰 : Scheme.AffineOpenCover.{u} X),
+ ∀ i, Nonempty (M.restrict (𝒰.f i)).Presentation := by
+ obtain ⟨ι, U, pres, hU, hU'⟩ := M.exists_isOpenCover_presentation
+ refine ⟨Scheme.AffineOpenCover.ofIsOpenCover _ hU hU', fun i ↦ ⟨?_⟩⟩
+ exact SheafOfModules.Presentation.ofIsIso.{u, u, u} ((restrictFunctorComp _ _).app M).inv <|
+ (presentationRestrict (hU' i).isoSpec.inv (pres i))
+
end IsQuasicoherent
end AlgebraicGeometry
diff --git a/Mathlib/AlgebraicGeometry/OpenImmersion.lean b/Mathlib/AlgebraicGeometry/OpenImmersion.lean
index aede7ae8f77c45..9a70890f5abce4 100644
--- a/Mathlib/AlgebraicGeometry/OpenImmersion.lean
+++ b/Mathlib/AlgebraicGeometry/OpenImmersion.lean
@@ -288,6 +288,13 @@ instance {R} [CommRing R] (f : R) :
IsOpenImmersion (Spec.map (CommRingCat.ofHom (algebraMap R (Localization.Away f)))) :=
isOpenImmersion_SpecMap_localizationAway (R := .of R) f
+@[simp]
+lemma Hom.opensRange_localizationAway {R : CommRingCat.{u}} (g : R) :
+ (Spec.map <| CommRingCat.ofHom <| algebraMap R (Localization.Away g)).opensRange =
+ PrimeSpectrum.basicOpen g := by
+ rw [SetLike.ext'_iff]
+ exact PrimeSpectrum.localization_away_comap_range _ g
+
lemma _root_.AlgebraicGeometry.IsOpenImmersion.of_isLocalization {R S} [CommRing R] [CommRing S]
[Algebra R S] (f : R) [IsLocalization.Away f S] :
IsOpenImmersion (Spec.map (CommRingCat.ofHom (algebraMap R S))) := by
diff --git a/Mathlib/Topology/Sets/OpenCover.lean b/Mathlib/Topology/Sets/OpenCover.lean
index e821d5edeb2b03..b6b104943bea7c 100644
--- a/Mathlib/Topology/Sets/OpenCover.lean
+++ b/Mathlib/Topology/Sets/OpenCover.lean
@@ -61,6 +61,14 @@ lemma isTopologicalBasis (hu : IsOpenCover u)
IsTopologicalBasis (⋃ i, (Subtype.val '' ·) '' B i) :=
isTopologicalBasis_of_cover (fun i ↦ (u i).2) hu.iSup_set_eq_univ hB
+lemma exists_finite_of_compactSpace (hu : IsOpenCover u) [CompactSpace X] :
+ ∃ (s : Finset ι), IsOpenCover (fun i : s ↦ u i.1) := by
+ rw [IsOpenCover, eq_top_iff, ← SetLike.coe_subset_coe] at hu
+ obtain ⟨s, hs⟩ := IsCompact.elim_finite_subcover isCompact_univ _ (fun i ↦ (u i).2)
+ (by simpa using hu)
+ use s
+ simpa [IsOpenCover, eq_top_iff, ← SetLike.coe_subset_coe, Set.iUnion_subtype] using hs
+
end IsOpenCover
lemma Opens.IsBasis.isOpenCover {S : Set (Opens X)} (hS : Opens.IsBasis S) :
diff --git a/Mathlib/Topology/Sets/Opens.lean b/Mathlib/Topology/Sets/Opens.lean
index 7c3bb782828e04..8ceb5f00263fea 100644
--- a/Mathlib/Topology/Sets/Opens.lean
+++ b/Mathlib/Topology/Sets/Opens.lean
@@ -354,6 +354,19 @@ lemma IsBasis.exists_iSup_eq {X : Type u} [TopologicalSpace X] {ι : Type*}
use Us, fun i ↦ a i.2
simp [hUs, ha, sSup_eq_iSup' Us]
+lemma IsBasis.exists_iSup_eq_of_isCompact {X : Type u} [TopologicalSpace X] {ι : Type*}
+ {U : ι → TopologicalSpace.Opens X} (hU : TopologicalSpace.Opens.IsBasis (Set.range U))
+ (W : TopologicalSpace.Opens X) (hW : IsCompact W.1) :
+ ∃ (κ : Type u) (_ : Finite κ) (a : κ → ι), W = ⨆ (k : κ), U (a k) := by
+ obtain ⟨κ, a, heq⟩ := hU.exists_iSup_eq W
+ obtain ⟨s, hs⟩ := hW.elim_finite_subcover _ (fun k : κ ↦ (U (a k)).2) (by simp [heq])
+ use s, s.finite_toSet, a ∘ Subtype.val
+ refine le_antisymm ?_ ?_
+ · simpa [← SetLike.coe_subset_coe, Set.iUnion_subtype]
+ · rw [heq, iSup_le_iff]
+ intro i
+ exact le_iSup_of_le _ le_rfl
+
/-- If `α` has a basis consisting of compact opens, then an open set in `α` is compact open iff
it is a finite union of some elements in the basis -/
theorem IsBasis.isCompact_open_iff_eq_finite_iUnion {ι : Type*} (b : ι → Opens α)
From 359d4f8b5ce637ae1d614810c3bb9e333504fda4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Attila=20G=C3=A1sp=C3=A1r?=
<58485900+gasparattila@users.noreply.github.com>
Date: Wed, 24 Jun 2026 13:02:29 +0000
Subject: [PATCH 0322/1300] feat(Topology/Sets): compositional continuity lemma
for `(Nonempty)Compacts.map` (#40046)
This can be used to prove the continuity of a function containing `Compacts.map f`, where `f` itself depends on the variable.
---
Mathlib/Topology/Sets/VietorisTopology.lean | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/Mathlib/Topology/Sets/VietorisTopology.lean b/Mathlib/Topology/Sets/VietorisTopology.lean
index 808104e7ce3d9b..262594a067bf4c 100644
--- a/Mathlib/Topology/Sets/VietorisTopology.lean
+++ b/Mathlib/Topology/Sets/VietorisTopology.lean
@@ -37,7 +37,7 @@ incompatible with the Vietoris topology.
open Set Topology
-variable {α β : Type*} [TopologicalSpace α] [TopologicalSpace β] {f : α → β}
+variable {α β γ : Type*} [TopologicalSpace α] [TopologicalSpace β] [TopologicalSpace γ] {f : α → β}
namespace TopologicalSpace
@@ -465,10 +465,17 @@ theorem continuous_prod : Continuous fun p : Compacts α × Compacts β => p.1
(isOpen_inter_nonempty_of_isOpen hV).prod (isOpen_inter_nonempty_of_isOpen hW),
⟨x, hx, hxV⟩, ⟨y, hy, hyW⟩⟩
-@[fun_prop]
theorem _root_.Continuous.compacts_map (hf : Continuous f) : Continuous (Compacts.map f hf) :=
isEmbedding_coe.continuous_iff.mpr <| hf.image_vietoris.comp continuous_coe
+@[fun_prop]
+theorem _root_.Continuous.compacts_map' {f : α → Compacts β} {g : α → β → γ}
+ (hf : Continuous f) (hg : Continuous g.uncurry) :
+ Continuous (fun x => (f x).map (g x) (by fun_prop)) := by
+ conv in Compacts.map _ _ _ => equals ({x} ×ˢ f x).map g.uncurry hg => ext; simp
+ have := hg.compacts_map
+ fun_prop
+
@[fun_prop]
theorem _root_.Topology.IsInducing.compacts_map (hf : IsInducing f) :
IsInducing (Compacts.map f hf.continuous) :=
@@ -751,11 +758,17 @@ theorem continuous_prod :
simp_rw [isEmbedding_toCompacts.continuous_iff, Function.comp_def, toCompacts_prod]
fun_prop
-@[fun_prop]
theorem _root_.Continuous.nonemptyCompacts_map (hf : Continuous f) :
Continuous (NonemptyCompacts.map f hf) :=
isEmbedding_toCompacts.continuous_iff.mpr <| hf.compacts_map.comp continuous_toCompacts
+@[fun_prop]
+theorem _root_.Continuous.nonemptyCompacts_map' {f : α → NonemptyCompacts β} {g : α → β → γ}
+ (hf : Continuous f) (hg : Continuous g.uncurry) :
+ Continuous (fun x => (f x).map (g x) (by fun_prop)) := by
+ simp_rw [isEmbedding_toCompacts.continuous_iff, Function.comp_def, toCompacts_map]
+ fun_prop
+
@[fun_prop]
theorem _root_.Topology.IsInducing.nonemptyCompacts_map (hf : IsInducing f) :
IsInducing (NonemptyCompacts.map f hf.continuous) :=
From 8231e3b02c420c5b378165c8c0685a8e29b75fff Mon Sep 17 00:00:00 2001
From: Felix Pernegger <188575194+felixpernegger@users.noreply.github.com>
Date: Wed, 24 Jun 2026 13:02:32 +0000
Subject: [PATCH 0323/1300] chore: remove some exceptions for
`linter.unusedSimpArgs` (#40999)
Removes all exceptions for the linter when it was indeed right. (tech debt)
Co-authored-by: Batixx
---
Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Formula.lean | 4 +---
Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Point.lean | 4 +---
Mathlib/Analysis/Calculus/Monotone.lean | 4 +---
Mathlib/Analysis/SpecialFunctions/Log/Deriv.lean | 6 ++----
4 files changed, 5 insertions(+), 13 deletions(-)
diff --git a/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Formula.lean b/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Formula.lean
index 942de0e5a8d33f..6adf1fe57679c8 100644
--- a/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Formula.lean
+++ b/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Formula.lean
@@ -365,15 +365,13 @@ lemma addX_eq_addX_negY_sub {x₁ x₂ : F} (y₁ y₂ : F) (hx : x₁ ≠ x₂)
-- Non-terminal simp, used to be field_simp
set_option linter.flexible false in
--- see https://github.com/leanprover-community/mathlib4/issues/29041
-set_option linter.unusedSimpArgs false in
/-- The formula `y(P₁)(x(P₂) - x(P₃)) + y(P₂)(x(P₃) - x(P₁)) + y(P₃)(x(P₁) - x(P₂)) = 0`,
assuming that `P₁ + P₂ + P₃ = O`. -/
lemma cyclic_sum_Y_mul_X_sub_X {x₁ x₂ : F} (y₁ y₂ : F) (hx : x₁ ≠ x₂) :
let x₃ := W.addX x₁ x₂ (W.slope x₁ x₂ y₁ y₂)
y₁ * (x₂ - x₃) + y₂ * (x₃ - x₁) + W.negAddY x₁ x₂ y₁ (W.slope x₁ x₂ y₁ y₂) * (x₁ - x₂) = 0 := by
simp_rw [slope_of_X_ne hx, negAddY, addX]
- simp [field, sub_ne_zero.mpr hx]
+ simp [field]
ring1
/-- The formula `ψ(P₁ + P₂) = (ψ(P₂)(x(P₁) - x(P₃)) - ψ(P₁)(x(P₂) - x(P₃))) / (x(P₂) - x(P₁))`,
diff --git a/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Point.lean b/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Point.lean
index 671eb7272d0753..5e2d6d1ff2f2c2 100644
--- a/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Point.lean
+++ b/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Point.lean
@@ -271,8 +271,6 @@ lemma XYIdeal_eq₁ (x y ℓ : R) : XYIdeal W' x (C y) = XYIdeal W' x (linePolyn
-- Non-terminal simp, used to be field_simp
set_option linter.flexible false in
--- see https://github.com/leanprover-community/mathlib4/issues/29041
-set_option linter.unusedSimpArgs false in
lemma XYIdeal_eq₂ [DecidableEq F] {x₁ x₂ y₁ y₂ : F} (h₁ : W.Equation x₁ y₁) (h₂ : W.Equation x₂ y₂)
(hxy : ¬(x₁ = x₂ ∧ y₁ = W.negY x₂ y₂)) :
XYIdeal W x₂ (C y₂) = XYIdeal W x₂ (linePolynomial x₁ y₁ <| W.slope x₁ x₂ y₁ y₂) := by
@@ -281,7 +279,7 @@ lemma XYIdeal_eq₂ [DecidableEq F] {x₁ x₂ y₁ y₂ : F} (h₁ : W.Equation
· have hy : y₁ ≠ W.negY x₂ y₂ := fun h => hxy ⟨hx, h⟩
rcases hx, Y_eq_of_Y_ne h₁ h₂ hx hy with ⟨rfl, rfl⟩
simp [linePolynomial]
- · simp [field, linePolynomial, slope_of_X_ne hx, sub_ne_zero_of_ne hx]
+ · simp [field, linePolynomial, slope_of_X_ne hx]
ring1
nth_rw 1 [hy₂]
simp only [XYIdeal, XClass, YClass, linePolynomial]
diff --git a/Mathlib/Analysis/Calculus/Monotone.lean b/Mathlib/Analysis/Calculus/Monotone.lean
index 30c72dc98563c3..76a4396b7ee376 100644
--- a/Mathlib/Analysis/Calculus/Monotone.lean
+++ b/Mathlib/Analysis/Calculus/Monotone.lean
@@ -38,8 +38,6 @@ open Set Filter Function Metric MeasureTheory MeasureTheory.Measure IsUnifLocDou
open scoped Topology
--- see https://github.com/leanprover-community/mathlib4/issues/29041
-set_option linter.unusedSimpArgs false in
/-- If `(f y - f x) / (y - x)` converges to a limit as `y` tends to `x`, then the same goes if
`y` is shifted a little bit, i.e., `f (y + (y-x)^2) - f x) / (y - x)` converges to the same limit.
This lemma contains a slightly more general version of this statement (where one considers
@@ -62,7 +60,7 @@ theorem tendsto_apply_add_mul_sq_div_sub {f : ℝ → ℝ} {x a c d : ℝ} {l :
apply Tendsto.congr' _ Z
have : ∀ᶠ y in l, y + c * (y - x) ^ 2 ≠ x := by apply Tendsto.mono_right h' hl self_mem_nhdsWithin
filter_upwards [this] with y hy
- simp [field, sub_ne_zero.2 hy]
+ simp [field]
/-- A Stieltjes function is almost everywhere differentiable, with derivative equal to the
Radon-Nikodym derivative of the associated Stieltjes measure with respect to Lebesgue. -/
diff --git a/Mathlib/Analysis/SpecialFunctions/Log/Deriv.lean b/Mathlib/Analysis/SpecialFunctions/Log/Deriv.lean
index 99ebf667e962ce..d41254b9cd1ef3 100644
--- a/Mathlib/Analysis/SpecialFunctions/Log/Deriv.lean
+++ b/Mathlib/Analysis/SpecialFunctions/Log/Deriv.lean
@@ -253,8 +253,6 @@ theorem abs_log_sub_add_sum_range_le {x : ℝ} (h : |x| < 1) (n : ℕ) :
-- fourth step: conclude by massaging the inequality of the third step
simpa [F, div_mul_eq_mul_div, pow_succ] using C
--- see https://github.com/leanprover-community/mathlib4/issues/29041
-set_option linter.unusedSimpArgs false in
/--
Compute the derivative of the difference between $\frac{1}{2} * \log(\frac{1+x}{1-x})$ and its
Taylor series at `0` up to order `n`. This is an auxiliary lemma for
@@ -270,14 +268,14 @@ lemma hasDerivAt_half_log_one_add_div_one_sub_sub_sum_range
refine ((((((hasDerivAt_id _).const_add _).div ((hasDerivAt_id _).const_sub _) (by grind)).log
?_).const_mul _).sub (HasDerivAt.fun_sum fun i hi ↦ (hasDerivAt_pow _ _).div_const _))
|>.congr_deriv ?_
- · simp only [id_eq, div_ne_zero_iff, Pi.div_apply]; grind
+ · simp only [div_ne_zero_iff, Pi.div_apply]; grind
have : (∑ i ∈ range n, (2 * i + 1) * y ^ (2 * i) / (2 * i + 1)) =
(∑ i ∈ range n, (y ^ 2) ^ i) := by
congr with i
simp [field, mul_comm, ← pow_mul]
have hy₃ : y ^ 2 ≠ 1 := by simp [hy₁.ne', hy₂.ne]
have hy₄ : (1 - y) * (1 + y) = 1 - y ^ 2 := by ring
- simp [this, field, geom_sum_eq hy₃, hy₄, sub_ne_zero_of_ne, hy₃.symm]
+ simp [this, field, geom_sum_eq hy₃, hy₄]
ring
/-- A lemma estimating the difference between $\frac{1}{2} * \log(\frac{1+x}{1-x})$ and its
From abeb53aa8898625b4c1b6305227f074350a6a958 Mon Sep 17 00:00:00 2001
From: Anatole Dedecker
Date: Wed, 24 Jun 2026 13:14:59 +0000
Subject: [PATCH 0324/1300] feat(Topology):
`IsMonoidHom.isStrictMap_prodMap_iff` (#40673)
Co-authored-by: zw810-ctrl
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Oliver Nash
Co-authored-by: Monica Omar <23701951+themathqueen@users.noreply.github.com>
---
Mathlib/Topology/Maps/Strict/Basic.lean | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/Mathlib/Topology/Maps/Strict/Basic.lean b/Mathlib/Topology/Maps/Strict/Basic.lean
index ec2a2ed345a7a0..91558107ed066b 100644
--- a/Mathlib/Topology/Maps/Strict/Basic.lean
+++ b/Mathlib/Topology/Maps/Strict/Basic.lean
@@ -192,13 +192,20 @@ variable {G H G' H' : Type*} [Group G'] [Group H'] [Group G] [Group H] (f : G
variable {f g} [TopologicalSpace G'] [IsTopologicalGroup G'] [TopologicalSpace H']
+/-- The product (in the sense of `Prod.map`) of group homomorphisms is strict if and only if each
+of the morphisms is strict. -/
+@[to_additive isStrictMap_prodMap_iff] lemma isStrictMap_prodMap_iff :
+ IsStrictMap (f.prodMap g) ↔ IsStrictMap f ∧ IsStrictMap g := by
+ simp_rw [isStrictMap_iff_isOpenQuotientMap_rangeRestrict]
+ let Φ : (f.prodMap g).range ≃ₜ f.range × g.range :=
+ (Homeomorph.setCongr (by simp [Subgroup.coe_prod])).trans (Homeomorph.Set.prod _ _)
+ have eq : Φ ∘ (f.prodMap g).rangeRestrict = f.rangeRestrict.prodMap g.rangeRestrict := rfl
+ rw [← Φ.comp_isOpenQuotientMap_iff, eq, MonoidHom.coe_prodMap, isOpenQuotientMap_prodMap_iff]
+
/-- The product (in the sense of `Prod.map`) of strict group homomorphisms is strict -/
@[to_additive isStrictMap_prodMap] lemma isStrictMap_prodMap (hf : IsStrictMap f)
- (hg : IsStrictMap g) : IsStrictMap (f.prodMap g) := by
- rw [isStrictMap_iff_isOpenQuotientMap_rangeRestrict] at hf hg ⊢
- let aux : (f.prodMap g).range ≃ₜ f.range × g.range :=
- (Homeomorph.setCongr (by simp [Subgroup.coe_prod])).trans (Homeomorph.Set.prod _ _)
- exact aux.symm.isOpenQuotientMap.comp (hf.prodMap hg)
+ (hg : IsStrictMap g) : IsStrictMap (f.prodMap g) :=
+ isStrictMap_prodMap_iff.mpr ⟨hf, hg⟩
-- TODO Add the lemma `isStrictMap_piMap` once `MonoidHom.piMap` has been defined.
From 8b62164d808cd0f23b7d8cc28190a6c1a1bb483a Mon Sep 17 00:00:00 2001
From: Jovan Gerbscheid <56355248+JovanGerb@users.noreply.github.com>
Date: Wed, 24 Jun 2026 13:15:02 +0000
Subject: [PATCH 0325/1300] chore(CategoryTheory/Limits/Shapes/StrongEpi): use
`to_dual` (#40959)
This PR generates declarations about `StrongMono` from those about `StrongEpi`.
---
.../Limits/Shapes/StrongEpi.lean | 114 +++++-------------
1 file changed, 31 insertions(+), 83 deletions(-)
diff --git a/Mathlib/CategoryTheory/Limits/Shapes/StrongEpi.lean b/Mathlib/CategoryTheory/Limits/Shapes/StrongEpi.lean
index ccb8b9800f1a6c..e10bb4da61c91f 100644
--- a/Mathlib/CategoryTheory/Limits/Shapes/StrongEpi.lean
+++ b/Mathlib/CategoryTheory/Limits/Shapes/StrongEpi.lean
@@ -45,6 +45,8 @@ namespace CategoryTheory
variable {C : Type u} [Category.{v} C]
variable {P Q : C}
+to_dual_name_hint Epi Mono
+
/-- A strong epimorphism `f` is an epimorphism which has the left lifting property
with respect to monomorphisms. -/
class StrongEpi (f : P ⟶ Q) : Prop where
@@ -53,57 +55,41 @@ class StrongEpi (f : P ⟶ Q) : Prop where
/-- The left lifting property with respect to all monomorphisms -/
llp : ∀ ⦃X Y : C⦄ (z : X ⟶ Y) [Mono z], HasLiftingProperty f z
-
-theorem StrongEpi.mk' {f : P ⟶ Q} [Epi f]
- (hf : ∀ (X Y : C) (z : X ⟶ Y)
- (_ : Mono z) (u : P ⟶ X) (v : Q ⟶ Y) (sq : CommSq u f z v), sq.HasLift) :
- StrongEpi f :=
- { epi := inferInstance
- llp := fun {X Y} z hz => ⟨fun {u v} sq => hf X Y z hz u v sq⟩ }
-
/-- A strong monomorphism `f` is a monomorphism which has the right lifting property
with respect to epimorphisms. -/
+@[to_dual]
class StrongMono (f : P ⟶ Q) : Prop where
/-- The monomorphism condition on `f` -/
mono : Mono f
/-- The right lifting property with respect to all epimorphisms -/
rlp : ∀ ⦃X Y : C⦄ (z : X ⟶ Y) [Epi z], HasLiftingProperty z f
-theorem StrongMono.mk' {f : P ⟶ Q} [Mono f]
- (hf : ∀ (X Y : C) (z : X ⟶ Y) (_ : Epi z) (u : X ⟶ P)
- (v : Y ⟶ Q) (sq : CommSq u z f v), sq.HasLift) : StrongMono f where
- mono := inferInstance
- rlp := fun {X Y} z hz => ⟨fun {u v} sq => hf X Y z hz u v sq⟩
+attribute [to_dual existing] StrongEpi.llp StrongEpi.mk
-attribute [instance 100] StrongEpi.llp
-
-attribute [instance 100] StrongMono.rlp
-
-instance (priority := 100) epi_of_strongEpi (f : P ⟶ Q) [StrongEpi f] : Epi f :=
- StrongEpi.epi
+@[to_dual (reorder := hf (X Y, u v))]
+theorem StrongEpi.mk' {f : P ⟶ Q} [Epi f]
+ (hf : ∀ (X Y : C) (z : X ⟶ Y) (_ : Mono z) (u : P ⟶ X)
+ (v : Q ⟶ Y) (sq : CommSq u f z v), sq.HasLift) : StrongEpi f where
+ epi := inferInstance
+ llp {X Y} z hz := ⟨fun {u v} sq => hf X Y z hz u v sq⟩
-instance (priority := 100) mono_of_strongMono (f : P ⟶ Q) [StrongMono f] : Mono f :=
- StrongMono.mono
+attribute [instance 100] StrongEpi.epi StrongEpi.llp StrongMono.mono StrongMono.rlp
section
variable {R : C} (f : P ⟶ Q) (g : Q ⟶ R)
/-- The composition of two strong epimorphisms is a strong epimorphism. -/
+@[to_dual /-- The composition of two strong monomorphisms is a strong monomorphism. -/]
instance strongEpi_comp [StrongEpi f] [StrongEpi g] : StrongEpi (f ≫ g) :=
{ epi := epi_comp _ _
llp := by
intros
infer_instance }
-/-- The composition of two strong monomorphisms is a strong monomorphism. -/
-instance strongMono_comp [StrongMono f] [StrongMono g] : StrongMono (f ≫ g) :=
- { mono := mono_comp _ _
- rlp := by
- intros
- infer_instance }
-
/-- If `f ≫ g` is a strong epimorphism, then so is `g`. -/
+@[to_dual (reorder := f g) (rename := f ↔ g, P ↔ R)
+/-- If `f ≫ g` is a strong monomorphism, then so is `f`. -/]
theorem strongEpi_of_strongEpi [StrongEpi (f ≫ g)] : StrongEpi g :=
{ epi := epi_of_epi f g
llp := fun {X Y} z _ => by
@@ -115,67 +101,36 @@ theorem strongEpi_of_strongEpi [StrongEpi (f ≫ g)] : StrongEpi g :=
⟨(CommSq.mk h₀).lift, by
simp only [← cancel_mono z, Category.assoc, CommSq.fac_right, sq.w], by simp⟩ }
-/-- If `f ≫ g` is a strong monomorphism, then so is `f`. -/
-theorem strongMono_of_strongMono [StrongMono (f ≫ g)] : StrongMono f :=
- { mono := mono_of_mono f g
- rlp := fun {X Y} z => by
- intros
- constructor
- intro u v sq
- have h₀ : u ≫ f ≫ g = z ≫ v ≫ g := by
- rw [← Category.assoc, eq_whisker sq.w, Category.assoc]
- exact CommSq.HasLift.mk' ⟨(CommSq.mk h₀).lift, by simp, by simp [← cancel_epi z, sq.w]⟩ }
-
/-- An isomorphism is in particular a strong epimorphism. -/
+@[to_dual /-- An isomorphism is in particular a strong monomorphism. -/]
instance (priority := 100) strongEpi_of_isIso [IsIso f] : StrongEpi f where
epi := by infer_instance
llp {_ _} _ := HasLiftingProperty.of_left_iso _ _
-/-- An isomorphism is in particular a strong monomorphism. -/
-instance (priority := 100) strongMono_of_isIso [IsIso f] : StrongMono f where
- mono := by infer_instance
- rlp {_ _} _ := HasLiftingProperty.of_right_iso _ _
-
set_option backward.isDefEq.respectTransparency false in
+@[to_dual]
theorem StrongEpi.of_arrow_iso {A B A' B' : C} {f : A ⟶ B} {g : A' ⟶ B'}
- (e : Arrow.mk f ≅ Arrow.mk g) [h : StrongEpi f] : StrongEpi g :=
- { epi := by
- rw [Arrow.iso_w' e]
- infer_instance
- llp := fun {X Y} z => by
- intro
- apply HasLiftingProperty.of_arrow_iso_left e z }
-
-set_option backward.isDefEq.respectTransparency false in
-theorem StrongMono.of_arrow_iso {A B A' B' : C} {f : A ⟶ B} {g : A' ⟶ B'}
- (e : Arrow.mk f ≅ Arrow.mk g) [h : StrongMono f] : StrongMono g :=
- { mono := by
- rw [Arrow.iso_w' e]
- infer_instance
- rlp := fun {X Y} z => by
- intro
- apply HasLiftingProperty.of_arrow_iso_right z e }
-
+ (e : Arrow.mk f ≅ Arrow.mk g) [h : StrongEpi f] : StrongEpi g where
+ epi := by
+ rw [Arrow.iso_w' e]
+ infer_instance
+ llp := fun {X Y} z => by
+ intro
+ apply HasLiftingProperty.of_arrow_iso_left e z
+
+@[to_dual]
theorem StrongEpi.iff_of_arrow_iso {A B A' B' : C} {f : A ⟶ B} {g : A' ⟶ B'}
(e : Arrow.mk f ≅ Arrow.mk g) : StrongEpi f ↔ StrongEpi g := by
constructor <;> intro
exacts [StrongEpi.of_arrow_iso e, StrongEpi.of_arrow_iso e.symm]
-theorem StrongMono.iff_of_arrow_iso {A B A' B' : C} {f : A ⟶ B} {g : A' ⟶ B'}
- (e : Arrow.mk f ≅ Arrow.mk g) : StrongMono f ↔ StrongMono g := by
- constructor <;> intro
- exacts [StrongMono.of_arrow_iso e, StrongMono.of_arrow_iso e.symm]
-
end
/-- A strong epimorphism that is a monomorphism is an isomorphism. -/
+@[to_dual /-- A strong monomorphism that is an epimorphism is an isomorphism. -/]
theorem isIso_of_mono_of_strongEpi (f : P ⟶ Q) [Mono f] [StrongEpi f] : IsIso f :=
⟨⟨(CommSq.mk (show 𝟙 P ≫ f = f ≫ 𝟙 Q by simp)).lift, by simp⟩⟩
-/-- A strong monomorphism that is an epimorphism is an isomorphism. -/
-theorem isIso_of_epi_of_strongMono (f : P ⟶ Q) [Epi f] [StrongMono f] : IsIso f :=
- ⟨⟨(CommSq.mk (show 𝟙 P ≫ f = f ≫ 𝟙 Q by simp)).lift, by simp⟩⟩
-
section
variable (C)
@@ -186,34 +141,27 @@ class StrongEpiCategory : Prop where
strongEpi_of_epi : ∀ {X Y : C} (f : X ⟶ Y) [Epi f], StrongEpi f
/-- A strong mono category is a category in which every monomorphism is strong. -/
+@[to_dual]
class StrongMonoCategory : Prop where
/-- A strong mono category is a category in which every monomorphism is strong. -/
strongMono_of_mono : ∀ {X Y : C} (f : X ⟶ Y) [Mono f], StrongMono f
+attribute [to_dual existing] StrongEpiCategory.strongEpi_of_epi StrongEpiCategory.mk
+
end
+@[to_dual]
theorem strongEpi_of_epi [StrongEpiCategory C] (f : P ⟶ Q) [Epi f] : StrongEpi f :=
StrongEpiCategory.strongEpi_of_epi _
-theorem strongMono_of_mono [StrongMonoCategory C] (f : P ⟶ Q) [Mono f] : StrongMono f :=
- StrongMonoCategory.strongMono_of_mono _
-
section
attribute [local instance] strongEpi_of_epi
+@[to_dual]
instance (priority := 100) balanced_of_strongEpiCategory [StrongEpiCategory C] : Balanced C where
isIso_of_mono_of_epi _ _ _ := isIso_of_mono_of_strongEpi _
end
-section
-
-attribute [local instance] strongMono_of_mono
-
-instance (priority := 100) balanced_of_strongMonoCategory [StrongMonoCategory C] : Balanced C where
- isIso_of_mono_of_epi _ _ _ := isIso_of_epi_of_strongMono _
-
-end
-
end CategoryTheory
From 9ca31d8b72cf8c317e49c301bfdbfbe91fc49136 Mon Sep 17 00:00:00 2001
From: Christian Merten <136261474+chrisflav@users.noreply.github.com>
Date: Wed, 24 Jun 2026 14:59:29 +0000
Subject: [PATCH 0326/1300] feat(AlgebraicGeometry): `Scheme.Hom.opensFunctor`
preserves `1`-hypercovers (#40990)
We deduce this from the fact that it preserves pullbacks, which we deduce from general facts about thin categories.
---
Mathlib/AlgebraicGeometry/OpenImmersion.lean | 39 +++++++++++++++
Mathlib/CategoryTheory/Discrete/Basic.lean | 8 ++++
Mathlib/CategoryTheory/Limits/Preorder.lean | 15 ++++++
.../Limits/Shapes/Products.lean | 21 ++++++++
.../Shapes/Pullback/IsPullback/Basic.lean | 48 +++++++++++++++++++
Mathlib/Topology/Category/TopCat/Opens.lean | 22 +++++++++
Mathlib/Topology/Sets/Opens.lean | 8 ++++
7 files changed, 161 insertions(+)
diff --git a/Mathlib/AlgebraicGeometry/OpenImmersion.lean b/Mathlib/AlgebraicGeometry/OpenImmersion.lean
index 9a70890f5abce4..a8b61e1491ec98 100644
--- a/Mathlib/AlgebraicGeometry/OpenImmersion.lean
+++ b/Mathlib/AlgebraicGeometry/OpenImmersion.lean
@@ -8,6 +8,7 @@ module
public import Mathlib.Geometry.RingedSpace.OpenImmersion
public import Mathlib.AlgebraicGeometry.Scheme
public import Mathlib.CategoryTheory.MorphismProperty.Limits
+public import Mathlib.CategoryTheory.Limits.Preorder
/-!
# Open immersions of schemes
@@ -87,6 +88,37 @@ theorem mem_opensRange {f : X ⟶ Y} [IsOpenImmersion f] {y : Y} :
def opensFunctor : X.Opens ⥤ Y.Opens :=
LocallyRingedSpace.IsOpenImmersion.opensFunctor f.toLRSHom
+/-- The adjunction image-preimage adjunction for an open immersion of schemes. -/
+def opensFunctorAdjunction : f.opensFunctor ⊣ TopologicalSpace.Opens.map f.base :=
+ IsOpenMap.adjunction ‹IsOpenImmersion f›.base_open.isOpenMap
+
+instance : f.opensFunctor.IsLeftAdjoint :=
+ f.opensFunctorAdjunction.isLeftAdjoint
+
+instance : f.opensFunctor.IsCocontinuous (Opens.grothendieckTopology _)
+ (Opens.grothendieckTopology _) := by
+ rw [f.opensFunctorAdjunction.isCocontinuous_iff_coverPreserving]
+ exact coverPreserving_opens_map f.base
+
+instance : f.opensFunctor.Full :=
+ have : Mono f.base := (TopCat.mono_iff_injective f.base).mpr f.isOpenEmbedding.injective
+ inferInstanceAs f.isOpenEmbedding.functor.Full
+
+lemma coverPreserving_opensFunctor :
+ CoverPreserving (Opens.grothendieckTopology _) (Opens.grothendieckTopology _) f.opensFunctor :=
+ f.isOpenEmbedding.isOpenMap.coverPreserving
+
+instance {X Y : Scheme.{u}} (f : X ⟶ Y) [IsOpenImmersion f] :
+ PreservesLimitsOfShape WalkingCospan (Scheme.Hom.opensFunctor f) := by
+ dsimp [Scheme.Hom.opensFunctor]
+ infer_instance
+
+instance {X Y : Scheme.{u}} (f : X ⟶ Y) [IsOpenImmersion f] :
+ f.opensFunctor.PreservesOneHypercovers (Opens.grothendieckTopology _)
+ (Opens.grothendieckTopology _) := by
+ refine Functor.PreservesOneHypercovers.of_coverPreserving ?_
+ exact Scheme.Hom.coverPreserving_opensFunctor f
+
/-- `f ''ᵁ U` is notation for the image (as an open set) of `U` under an open immersion `f`.
The preferred name in lemmas is `image` and it should be treated as an infix. -/
scoped[AlgebraicGeometry] notation3:90 f:91 " ''ᵁ " U:90 => (Scheme.Hom.opensFunctor f).obj U
@@ -206,6 +238,13 @@ theorem appIso_hom' (U) :
(f.appIso U).hom = f.appLE (f ''ᵁ U) U (preimage_image_eq f U).ge :=
f.appIso_hom U
+set_option backward.defeqAttrib.useBackward true in
+@[reassoc (attr := simp)]
+lemma appIso_hom_naturality {U V : X.Opens} (i : op U ⟶ op V) :
+ dsimp% Y.presheaf.map (f.opensFunctor.op.map i) ≫ (f.appIso V).hom =
+ (f.appIso U).hom ≫ X.presheaf.map i := by
+ simp [← cancel_mono (f.appIso V).inv]
+
@[reassoc (attr := simp)]
theorem app_appIso_inv (U) :
f.app U ≫ (f.appIso (f ⁻¹ᵁ U)).inv =
diff --git a/Mathlib/CategoryTheory/Discrete/Basic.lean b/Mathlib/CategoryTheory/Discrete/Basic.lean
index 7d03d8068c25b7..3f7a993158e56e 100644
--- a/Mathlib/CategoryTheory/Discrete/Basic.lean
+++ b/Mathlib/CategoryTheory/Discrete/Basic.lean
@@ -6,6 +6,7 @@ Authors: Stephen Morgan, Kim Morrison, Floris van Doorn
module
public import Mathlib.CategoryTheory.Pi.Basic
+public import Mathlib.Data.Set.Image
/-!
# Discrete categories
@@ -63,6 +64,9 @@ def discreteEquiv {α : Type u₁} : Discrete α ≃ α where
left_inv := by cat_disch
right_inv := by cat_disch
+lemma Discrete.as_bijective {α : Type*} : (Discrete.as (α := α)).Bijective :=
+ discreteEquiv.bijective
+
instance {α : Type u₁} [DecidableEq α] : DecidableEq (Discrete α) :=
discreteEquiv.decidableEq
@@ -179,6 +183,10 @@ theorem functor_obj_eq_as {I : Type u₁} (F : I → C) (X : Discrete I) :
(Discrete.functor F).obj X = F X.as :=
rfl
+@[simp]
+lemma range_functor {I : Type*} (X : I → C) : Set.range (Discrete.functor X).obj = Set.range X := by
+ simp [Discrete.functor, Set.range_comp, Discrete.as_bijective.surjective.range_eq]
+
@[ext]
lemma functor_ext {I : Type u₁} {G F : Discrete I ⥤ C} (h : (i : I) → G.obj ⟨i⟩ = F.obj ⟨i⟩) :
G = F := by
diff --git a/Mathlib/CategoryTheory/Limits/Preorder.lean b/Mathlib/CategoryTheory/Limits/Preorder.lean
index 2bbcb334ca57be..a4da980b0331aa 100644
--- a/Mathlib/CategoryTheory/Limits/Preorder.lean
+++ b/Mathlib/CategoryTheory/Limits/Preorder.lean
@@ -6,6 +6,7 @@ Authors: Sina Hazratpour, Joël Riou, Fernando Chu
module
public import Mathlib.CategoryTheory.Limits.Shapes.BinaryProducts
+public import Mathlib.CategoryTheory.Limits.Shapes.Products
public import Mathlib.Order.Bounds.Defs
/-!
@@ -211,4 +212,18 @@ instance (priority := low) [SemilatticeSup C] : HasBinaryCoproducts C where
end
+section
+
+/-- The product of elements in a complete lattice is the infimum. -/
+def isLimitIInf [CompleteLattice C] {ι : Type*} (X : ι → C) :
+ IsLimit (Fan.mk (⨅ i, X i) fun i : ι ↦ homOfLE (iInf_le X i)) :=
+ isLimitOfIsGLB _ _ (by simp [isGLB_iInf])
+
+/-- The coproduct of elements in a complete lattice is the supremum. -/
+def isColimitISup [CompleteLattice C] {ι : Type*} (X : ι → C) :
+ IsColimit (Cofan.mk (⨆ i, X i) fun i : ι ↦ homOfLE (le_iSup X i)) :=
+ isColimitOfIsLUB _ _ (by simp [isLUB_iSup])
+
+end
+
end Preorder
diff --git a/Mathlib/CategoryTheory/Limits/Shapes/Products.lean b/Mathlib/CategoryTheory/Limits/Shapes/Products.lean
index 8d0db515c200f9..3bb17a7c11f5f5 100644
--- a/Mathlib/CategoryTheory/Limits/Shapes/Products.lean
+++ b/Mathlib/CategoryTheory/Limits/Shapes/Products.lean
@@ -967,6 +967,27 @@ instance [HasColimit F] [HasCoproduct F.obj] : Epi (Sigma.desc (colimit.ι F)) w
end
+section Thin
+
+variable [Quiver.IsThin C] {J : Type*} [Category* J] {K : J ⥤ C}
+
+/-- If `K : J ⥤ C` is a diagram with `C` thin, a cone for `K` is limiting
+if and only if the cone point is the product of the components. -/
+def isLimitEquivFanOfIsThin (c : Cone K) : IsLimit c ≃ IsLimit (Fan.mk c.pt c.π.app) where
+ toFun hc := Fan.IsLimit.mk _ (fun s ↦ hc.lift { pt := s.pt, π.app j := s.proj j })
+ (by subsingleton) (by subsingleton)
+ invFun h := { lift s := Fan.IsLimit.lift h s.π.app }
+
+/-- If `K : J ⥤ C` is a diagram with `C` thin, a cone for `K` is limiting
+if and only if the cone point is the product of the components. -/
+def isColimitEquivCofanOfIsThin (c : Cocone K) :
+ IsColimit c ≃ IsColimit (Cofan.mk c.pt c.ι.app) where
+ toFun hc := Cofan.IsColimit.mk _ (fun s ↦ hc.desc { pt := s.pt, ι.app j := s.inj j })
+ (by subsingleton) (by subsingleton)
+ invFun h := { desc s := Cofan.IsColimit.desc h s.ι.app }
+
+end Thin
+
section Fubini
variable {ι ι' : Type*} {X : ι → ι' → C}
diff --git a/Mathlib/CategoryTheory/Limits/Shapes/Pullback/IsPullback/Basic.lean b/Mathlib/CategoryTheory/Limits/Shapes/Pullback/IsPullback/Basic.lean
index e598b03ba20ed7..842a249e0832c2 100644
--- a/Mathlib/CategoryTheory/Limits/Shapes/Pullback/IsPullback/Basic.lean
+++ b/Mathlib/CategoryTheory/Limits/Shapes/Pullback/IsPullback/Basic.lean
@@ -943,6 +943,54 @@ lemma IsPushout.iff_app [HasPushouts D] {F₁ F₂ F₃ F₄ : C ⥤ D}
end Functor
+section Thin
+
+variable [Quiver.IsThin C]
+
+lemma isPullback_iff_isLimit_binaryFan_of_isThin {P X Y Z : C}
+ {fst : P ⟶ X} {snd : P ⟶ Y} {f : X ⟶ Z} {g : Y ⟶ Z} :
+ IsPullback fst snd f g ↔ Nonempty (IsLimit (BinaryFan.mk fst snd)) := by
+ refine ⟨fun h ↦ ?_, fun h ↦ ?_⟩
+ · exact ⟨BinaryFan.IsLimit.mk _ (fun u v ↦ h.lift u v (by subsingleton))
+ (by subsingleton) (by subsingleton) (by subsingleton)⟩
+ · exact ⟨⟨by subsingleton⟩,
+ ⟨PullbackCone.IsLimit.mk _ (fun s ↦ BinaryFan.IsLimit.lift h.some s.fst s.snd)
+ (by subsingleton) (by subsingleton) (by subsingleton)⟩⟩
+
+lemma isPushout_iff_isColimit_binaryCofan_of_isThin {P X Y Z : C}
+ {f : Z ⟶ X} {g : Z ⟶ Y} {inl : X ⟶ P} {inr : Y ⟶ P} :
+ IsPushout f g inl inr ↔ Nonempty (IsColimit (BinaryCofan.mk inl inr)) := by
+ refine ⟨fun h ↦ ?_, fun h ↦ ?_⟩
+ · exact ⟨BinaryCofan.IsColimit.mk _ (fun u v ↦ h.desc u v (by subsingleton))
+ (by subsingleton) (by subsingleton) (by subsingleton)⟩
+ · exact ⟨⟨by subsingleton⟩,
+ ⟨PushoutCocone.IsColimit.mk _ (fun s ↦ BinaryCofan.IsColimit.desc h.some s.inl s.inr)
+ (by subsingleton) (by subsingleton) (by subsingleton)⟩⟩
+
+variable {D : Type*} [Category* D] [Quiver.IsThin D] (F : C ⥤ D)
+
+instance (priority := low) [PreservesLimitsOfShape (Discrete WalkingPair) F] :
+ PreservesLimitsOfShape WalkingCospan F := by
+ refine preservesLimitsOfShape_walkingCospan_of_forall_isPullback fun X Y Z f g hfg ↦ ?_
+ use pullback f g, pullback.fst f g, pullback.snd f g, .of_hasPullback f g
+ rw [isPullback_iff_isLimit_binaryFan_of_isThin]
+ refine ⟨(BinaryFan.mk (pullback.fst f g) (pullback.snd f g)).isLimitMapConeEquiv ?_⟩
+ apply isLimitOfPreserves _ (Nonempty.some ?_)
+ rw [← CategoryTheory.isPullback_iff_isLimit_binaryFan_of_isThin (f := f) (g := g)]
+ exact .of_hasPullback f g
+
+instance (priority := low) [PreservesColimitsOfShape (Discrete WalkingPair) F] :
+ PreservesColimitsOfShape WalkingSpan F := by
+ refine preservesColimitsOfShape_walkingCospan_of_forall_isPushout fun X Y Z f g hfg ↦ ?_
+ use pushout f g, pushout.inl f g, pushout.inr f g, .of_hasPushout f g
+ rw [isPushout_iff_isColimit_binaryCofan_of_isThin]
+ refine ⟨(BinaryCofan.mk (pushout.inl f g) (pushout.inr f g)).isColimitMapConeEquiv ?_⟩
+ apply isColimitOfPreserves _ (Nonempty.some ?_)
+ rw [← CategoryTheory.isPushout_iff_isColimit_binaryCofan_of_isThin (f := f) (g := g)]
+ exact .of_hasPushout f g
+
+end Thin
+
section IsPullbackOverPullback
open Limits
diff --git a/Mathlib/Topology/Category/TopCat/Opens.lean b/Mathlib/Topology/Category/TopCat/Opens.lean
index 24e13f01622724..96f6645978055a 100644
--- a/Mathlib/Topology/Category/TopCat/Opens.lean
+++ b/Mathlib/Topology/Category/TopCat/Opens.lean
@@ -7,6 +7,8 @@ module
public import Mathlib.CategoryTheory.Category.GaloisConnection
public import Mathlib.CategoryTheory.EqToHom
+public import Mathlib.CategoryTheory.Limits.Preorder
+public import Mathlib.CategoryTheory.Limits.Preserves.Shapes.Products
public import Mathlib.Topology.Category.TopCat.EpiMono
public import Mathlib.Topology.Sets.Opens
@@ -341,6 +343,15 @@ lemma Topology.IsOpenEmbedding.functor_obj_injective {X Y : TopCat.{u}} {f : X
(hf : IsOpenEmbedding f) : Function.Injective hf.functor.obj :=
fun _ _ e ↦ Opens.ext (Set.image_injective.mpr hf.injective (congr_arg (↑· : Opens Y → Set Y) e))
+lemma Topology.IsOpenEmbedding.functor_obj_iInf {X Y : TopCat.{u}} (f : X ⟶ Y)
+ (hf : Topology.IsOpenEmbedding f) {ι : Type*} [Nonempty ι] [Finite ι]
+ (g : ι → TopologicalSpace.Opens X) :
+ hf.functor.obj (⨅ i, g i) = ⨅ i, hf.functor.obj (g i) := by
+ ext : 1
+ simp only [IsOpenMap.coe_functor_obj, TopologicalSpace.Opens.coe_iInf]
+ rw [Set.InjOn.image_iInter_eq]
+ exact hf.injective.injOn
+
namespace Topology.IsInducing
/-- Given an inducing map `X ⟶ Y` and some `U : Opens X`, this is the union of all open sets
@@ -463,4 +474,15 @@ theorem adjunction_counit_map_functor {X : TopCat.{u}} {U : Opens X} (V : Opens
eqToHom (by dsimp; rw [map_functor_eq V]) := by
subsingleton
+open Limits in
+instance {X Y : TopCat.{u}} (f : X ⟶ Y) (hf : Topology.IsOpenEmbedding f) {ι : Type*}
+ [Nonempty ι] [Finite ι] :
+ PreservesLimitsOfShape (Discrete ι) hf.functor := by
+ apply +allowSynthFailures preservesLimitsOfShape_of_discrete
+ intro g
+ refine preservesLimit_of_preserves_limit_cone (Preorder.isLimitIInf g) ?_
+ refine (Limits.Fan.isLimitMapConeEquiv _ _ _).symm (Preorder.isLimitOfIsGLB _ _ ?_)
+ simp only [Discrete.range_functor, homOfLE_leOfHom, Fan.mk_pt, hf.functor_obj_iInf]
+ apply isGLB_iInf
+
end TopologicalSpace.Opens
diff --git a/Mathlib/Topology/Sets/Opens.lean b/Mathlib/Topology/Sets/Opens.lean
index 8ceb5f00263fea..c6593fd745e47b 100644
--- a/Mathlib/Topology/Sets/Opens.lean
+++ b/Mathlib/Topology/Sets/Opens.lean
@@ -5,6 +5,7 @@ Authors: Johannes Hölzl, Mario Carneiro, Floris van Doorn
-/
module
+public import Mathlib.Data.Fintype.Option
public import Mathlib.Order.Hom.CompleteLattice
public import Mathlib.Topology.Compactness.Bases
public import Mathlib.Topology.ContinuousMap.Basic
@@ -232,6 +233,13 @@ instance [Nonempty α] : Nontrivial (Opens α) where
theorem coe_iSup {ι} (s : ι → Opens α) : ((⨆ i, s i : Opens α) : Set α) = ⋃ i, s i := by
simp [iSup]
+lemma coe_iInf {ι : Type*} [Finite ι] (U : ι → TopologicalSpace.Opens α) :
+ (((⨅ i, U i) : Opens α) : Set α) = ⋂ i, U i := by
+ induction ι using Finite.induction_empty_option with
+ | of_equiv e ih => rw [← e.iInf_comp, ← e.surjective.iInter_comp, ih]
+ | h_empty => simp
+ | h_option ih => rw [iInf_option, Set.iInter_option, Opens.coe_inf, ih]
+
theorem iSup_def {ι} (s : ι → Opens α) : ⨆ i, s i = ⟨⋃ i, s i, isOpen_iUnion fun i => (s i).2⟩ :=
ext <| coe_iSup s
From b5f56a63146c6411ebff5ef24898ca212445dbc8 Mon Sep 17 00:00:00 2001
From: "Filippo A. E. Nuccio" <65080144+faenuccio@users.noreply.github.com>
Date: Wed, 24 Jun 2026 17:13:11 +0000
Subject: [PATCH 0327/1300] feat(Mathlib.Topology.Algebra.Module.Equiv): add
results on IsHomeomorph (#39476)
Add the construction of a `ContinuousLinearEquiv` from a `LinearEquiv` that `IsHomeomorph`, and two basic API lemmas. Also remove a `simp` tag from a lemma in about `Function.Bijective`, and change its signature a bit.
Co-authored-by: faenuccio
---
Mathlib/Topology/Algebra/Module/Equiv.lean | 68 ++++++++++++++++------
1 file changed, 50 insertions(+), 18 deletions(-)
diff --git a/Mathlib/Topology/Algebra/Module/Equiv.lean b/Mathlib/Topology/Algebra/Module/Equiv.lean
index afc135bda2246b..1c48c97bd88454 100644
--- a/Mathlib/Topology/Algebra/Module/Equiv.lean
+++ b/Mathlib/Topology/Algebra/Module/Equiv.lean
@@ -12,8 +12,26 @@ public import Mathlib.Topology.Algebra.Module.ContinuousLinearMap.Restrict
/-!
# Continuous linear equivalences
+## Notation
Continuous semilinear / linear / star-linear equivalences between topological modules are denoted
by `M ≃SL[σ] M₂`, `M ≃L[R] M₂` and `M ≃L⋆[R] M₂`.
+
+## Main Definitions
+* `toHomeomorph` is the homeomorphism induced by a continuous (semi)linear equivalence.
+* `symm` is the inverse of a continuous linear equivalence as a continuous linear equivalence.
+* `equivOfInverse` creates a `ContinuousLinearEquiv` from two `ContinuousLinearMap`s that are
+ inverse of each other (as functions). See also `equivOfInverse'` when they're inverse to each
+ other as continuous linear maps.
+* `ofUnit` is the `ContinuousLinearEquiv` corresponding to a unit in the ring of continuous
+ endomorphisms. See `toUnit` for the inverse direction.
+* `IsInvertible`: a continuous linear map is invertible if it is the forward direction of a
+ continuous linear equivalence.
+* `ofIsHomeomorph`: a linear equivalence that is a homeomorphism is a continuous linear equivalence.
+
+## Main Results
+* `prodComm`: the product of topological modules is commutative up to continuous linear isomorphism.
+* `LinearEquiv.isHomeomorph_iff`: A linear equivalence between topological modules is a
+ homeomorphism if and only if it is continuous in both directions.
-/
@[expose] public section
@@ -26,8 +44,6 @@ open scoped Ring
universe u v w u'
-section
-
/-- Continuous linear equivalences between modules. We only put the type classes that are necessary
for the definition, although in applications `M` and `M₂` will be topological modules over the
topological semiring `R`. -/
@@ -223,7 +239,6 @@ theorem isClosed_image (e : M₁ ≃SL[σ₁₂] M₂) {s : Set M₁} : IsClosed
theorem map_nhds_eq (e : M₁ ≃SL[σ₁₂] M₂) (x : M₁) : map e (𝓝 x) = 𝓝 (e x) :=
e.toHomeomorph.map_nhds_eq x
--- Make some straightforward lemmas available to `simp`.
theorem map_zero (e : M₁ ≃SL[σ₁₂] M₂) : e (0 : M₁) = 0 :=
(e : M₁ →SL[σ₁₂] M₂).map_zero
@@ -280,10 +295,6 @@ def toContinuousAddEquiv (e : M₁ ≃L[R₁] M) : M₁ ≃ₜ+ M :=
@[simp]
lemma toContinuousAddEquiv_coe (e : M₁ ≃L[R₁] M) : ⇑e.toContinuousAddEquiv = e := rfl
-end
-
-section
-
variable (R₁ M₁)
/-- The identity map as a continuous linear equivalence. -/
@@ -378,7 +389,7 @@ theorem prodCongr_symm [Module R₁ M₂] [Module R₁ M₃] [Module R₁ M₄]
variable (R₁ M₁ M₂)
set_option backward.defeqAttrib.useBackward true in
-/-- Product of modules is commutative up to continuous linear isomorphism. -/
+/-- Product of topological modules is commutative up to continuous linear isomorphism. -/
@[simps! apply toLinearEquiv]
def prodComm [Module R₁ M₂] : (M₁ × M₂) ≃L[R₁] M₂ × M₁ where
__ := LinearEquiv.prodComm R₁ M₁ M₂
@@ -611,7 +622,8 @@ protected theorem _root_.LinearEquiv.isUniformEmbedding {E₁ E₂ : Type*} [Uni
E₁ ≃SL[σ₁₂] E₂)
/-- Create a `ContinuousLinearEquiv` from two `ContinuousLinearMap`s that are
-inverse of each other. See also `equivOfInverse'`. -/
+inverse of each other. See also `equivOfInverse'`.
+*ToDo*: Improve the naiming to make it match `LinearMap.ofLinear`. -/
def equivOfInverse (f₁ : M₁ →SL[σ₁₂] M₂) (f₂ : M₂ →SL[σ₂₁] M₁) (h₁ : Function.LeftInverse f₂ f₁)
(h₂ : Function.RightInverse f₂ f₁) : M₁ ≃SL[σ₁₂] M₂ :=
{ f₁ with
@@ -630,7 +642,8 @@ theorem symm_equivOfInverse (f₁ : M₁ →SL[σ₁₂] M₂) (f₂ h₁ h₂)
rfl
/-- Create a `ContinuousLinearEquiv` from two `ContinuousLinearMap`s that are
-inverse of each other, in the `ContinuousLinearMap.comp` sense. See also `equivOfInverse`. -/
+inverse of each other, in the `ContinuousLinearMap.comp` sense. See also `equivOfInverse`.
+*ToDo*: Improve the naiming to make it match `LinearMap.ofLinear` -/
def equivOfInverse' (f₁ : M₁ →SL[σ₁₂] M₂) (f₂ : M₂ →SL[σ₂₁] M₁)
(h₁ : f₁.comp f₂ = .id R₂ M₂) (h₂ : f₂.comp f₁ = .id R₁ M₁) : M₁ ≃SL[σ₁₂] M₂ :=
equivOfInverse f₁ f₂
@@ -1412,15 +1425,34 @@ theorem smul_trans [SMulCommClass R S V] [IsScalarTower S R G] (α : Sˣ) (e : G
theorem trans_smul [IsScalarTower S R G] (α : Sˣ) (e : G ≃L[R] V) (f : V ≃L[R] W) :
e.trans (α • f) = α • (e.trans f) := by ext; simp
-end ContinuousLinearEquiv
+section IsHomeomorph
+
+variable {S₁ M M₁ : Type*} [Semiring S₁] {σ : S →+* S₁} {σ' : S₁ →+* S}
+ [RingHomInvPair σ σ'] [RingHomInvPair σ' σ] [TopologicalSpace M] [AddCommMonoid M] [Module S M]
+ [TopologicalSpace M₁] [AddCommMonoid M₁] [Module S₁ M₁]
+
+/-- A linear equivalence that is a homeomorphism is a continuous linear equivalence. -/
+def ofIsHomeomorph (f : M ≃ₛₗ[σ] M₁) (hf : IsHomeomorph f) : M ≃SL[σ] M₁ where
+ __ := f
+ continuous_toFun := hf.continuous
+ continuous_invFun := (f.isHomeomorph_iff.mp hf).2
+
+theorem isHomeomorph (f : M ≃SL[σ] M₁) : IsHomeomorph f := ⟨f.continuous, isOpenMap f, f.bijective⟩
+
+variable {f : M ≃ₛₗ[σ] M₁} (hf : IsHomeomorph f)
+
+@[simp]
+lemma toLinearquiv_ofIsHomeomorph : (ofIsHomeomorph f hf).toLinearEquiv = f := by
+ dsimp only [ofIsHomeomorph]
+
+@[simp]
+lemma coe_ofIsHomeomorph : (ofIsHomeomorph f hf : M → M₁) = f := by dsimp [ofIsHomeomorph]
/-- A linear equivalence between topological modules is a homeomorphism if and only if it is
continuous in both directions. -/
-theorem LinearEquiv.isHomeomorph_iff {R S : Type*} [Semiring R] [Semiring S]
- {σ : R →+* S} {σ' : S →+* R} [RingHomInvPair σ σ'] [RingHomInvPair σ' σ]
- {M : Type*} [TopologicalSpace M] [AddCommMonoid M] [Module R M]
- {N : Type*} [TopologicalSpace N] [AddCommMonoid N] [Module S N]
- (e : M ≃ₛₗ[σ] N) : IsHomeomorph e ↔ Continuous e ∧ Continuous e.symm :=
- e.toEquiv.isHomeomorph_iff
+theorem _root_.LinearEquiv.isHomeomorph_iff (e : M ≃ₛₗ[σ] M₁) :
+ IsHomeomorph e ↔ Continuous e ∧ Continuous e.symm := e.toEquiv.isHomeomorph_iff
-end
+end IsHomeomorph
+
+end ContinuousLinearEquiv
From a53f9216345ba66b3a21ec82f01c008b1bbef989 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ga=C3=ABtan=20Serr=C3=A9?=
<56162277+gaetanserre@users.noreply.github.com>
Date: Wed, 24 Jun 2026 17:51:59 +0000
Subject: [PATCH 0328/1300] feat(CStarAlgebra): `IsometricCFC` instance for
square `RCLike` matrices (#40272)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Following @j-loreaux advices (see [#mathlib4 > `CFC.sqrt` continuous on positive (real) matrices](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/.60CFC.2Esqrt.60.20continuous.20on.20positive.20.28real.29.20matrices/with/600698185)), defines an instance of `IsometricContinuousFunctionalCalculus` for `n × n` matrices with `RCLike` coefficients.
---
Mathlib/Analysis/Matrix/Order.lean | 25 +++++++++++++++++++
.../Analysis/Normed/Group/Constructions.lean | 25 +++++++++++++++++++
Mathlib/Topology/ContinuousMap/Compact.lean | 7 ++++++
Mathlib/Topology/MetricSpace/Isometry.lean | 4 +++
4 files changed, 61 insertions(+)
diff --git a/Mathlib/Analysis/Matrix/Order.lean b/Mathlib/Analysis/Matrix/Order.lean
index e07208296f2cec..20eec4b66f66bc 100644
--- a/Mathlib/Analysis/Matrix/Order.lean
+++ b/Mathlib/Analysis/Matrix/Order.lean
@@ -12,6 +12,7 @@ public import Mathlib.Analysis.Matrix.PosDef
public import Mathlib.Analysis.RCLike.Sqrt
public import Mathlib.Analysis.SpecialFunctions.ContinuousFunctionalCalculus.Abs
public import Mathlib.LinearAlgebra.Matrix.Vec
+public import Mathlib.Analysis.CStarAlgebra.Matrix
/-!
# The partial order on matrices
@@ -339,4 +340,28 @@ def toMatrixInnerProductSpace (M : Matrix n n 𝕜) (hM : M.PosSemidef) :
@[deprecated (since := "2025-11-18")] alias PosDef.matrixNormedAddCommGroup :=
toMatrixNormedAddCommGroup
+
+open scoped Norms.L2Operator in
+set_option backward.isDefEq.respectTransparency false in
+/-- The isometric continuous functional calculus on `Matrix n n 𝕜` arising from the operator norm
+given by the identification with (continuous) linear endomorphisms of `EuclideanSpace 𝕜 n`. -/
+instance instIsometricContinuousFunctionalCalculus [DecidableEq n] :
+ IsometricContinuousFunctionalCalculus ℝ (Matrix n n 𝕜) IsSelfAdjoint where
+ isometric A hA := by
+ rw [← isHermitian_iff_isSelfAdjoint] at hA
+ rw [IsHermitian.cfcHom_eq_cfcAux hA, AddMonoidHomClass.isometry_iff_norm]
+ intro f
+ simp only [IsHermitian.cfcAux_apply, Unitary.conjStarAlgAut_apply, ← Unitary.coe_star,
+ CStarRing.norm_mul_coe_unitary, CStarRing.norm_coe_unitary_mul, l2_opNorm_diagonal]
+ rw [((algebraMap_isometry ℝ 𝕜).postcomp_pi).norm_map_of_map_zero (by ext; simp)]
+ let : Fintype (spectrum ℝ A) := .ofFinite _
+ rw [ContinuousMap.norm_eq_norm_coeFn]
+ refine Function.Surjective.pi_norm_comp ?_ _
+ rw [← Function.Surjective.of_comp_iff'
+ (Equiv.setCongr hA.spectrum_real_eq_range_eigenvalues).bijective]
+ exact Set.codRestrict_range_surjective hA.eigenvalues
+
+scoped[Matrix.Norms.L2Operator] attribute [instance]
+ Matrix.instIsometricContinuousFunctionalCalculus
+
end Matrix
diff --git a/Mathlib/Analysis/Normed/Group/Constructions.lean b/Mathlib/Analysis/Normed/Group/Constructions.lean
index 6ce5fd0c8b9fd0..030fcae76666bb 100644
--- a/Mathlib/Analysis/Normed/Group/Constructions.lean
+++ b/Mathlib/Analysis/Normed/Group/Constructions.lean
@@ -366,6 +366,31 @@ lemma pi_norm_const' [Nonempty ι] (a : E) : ‖fun _i : ι => a‖ = ‖a‖ :=
lemma pi_nnnorm_const' [Nonempty ι] (a : E) : ‖fun _i : ι => a‖₊ = ‖a‖₊ :=
NNReal.eq <| pi_norm_const' a
+@[to_additive pi_norm_comp_le]
+lemma pi_norm_comp_le' [Fintype F] (g : ι → E) (f : F → ι) : ‖g ∘ f‖ ≤ ‖g‖ := by
+ rw [pi_norm_le_iff_of_nonneg' (by positivity)]
+ exact fun x ↦ norm_le_pi_norm' g (f x)
+
+@[to_additive IsGreatest.pi_norm]
+lemma IsGreatest.pi_norm' [Nonempty ι] (f : ι → E) : IsGreatest (Set.range (‖f ·‖)) ‖f‖ := by
+ constructor
+ · rw [Pi.norm_def' f]
+ obtain ⟨x, -, hx⟩ := (Finset.univ (α := ι)).exists_mem_eq_sup (by simp) (‖f ·‖₊)
+ simp [hx]
+ · rintro - ⟨x, rfl⟩
+ exact norm_le_pi_norm' f x
+
+@[to_additive Function.Surjective.pi_norm_comp]
+lemma Function.Surjective.pi_norm_comp' [Fintype F] {f : ι → F} (hf : Function.Surjective f)
+ (g : F → E) : ‖g ∘ f‖ = ‖g‖ := by
+ obtain (h | h) := isEmpty_or_nonempty F
+ · have : IsEmpty ι := f.isEmpty
+ simp [Subsingleton.elim g 1]
+ apply le_antisymm (pi_norm_comp_le' g f)
+ obtain ⟨⟨x, h⟩, -⟩ := IsGreatest.pi_norm' g
+ obtain ⟨y, rfl⟩ := hf x
+ exact h ▸ norm_le_pi_norm' (g ∘ f) y
+
/-- The $L^1$ norm is less than the $L^\infty$ norm scaled by the cardinality. -/
@[to_additive Pi.sum_norm_apply_le_norm /-- The $L^1$ norm is less than the $L^\infty$ norm scaled
by the cardinality. -/]
diff --git a/Mathlib/Topology/ContinuousMap/Compact.lean b/Mathlib/Topology/ContinuousMap/Compact.lean
index 221cbe425e19d7..83583586041b6c 100644
--- a/Mathlib/Topology/ContinuousMap/Compact.lean
+++ b/Mathlib/Topology/ContinuousMap/Compact.lean
@@ -243,6 +243,13 @@ theorem norm_restrict_mono_set {X : Type*} [TopologicalSpace X] (f : C(X, E))
{K L : TopologicalSpace.Compacts X} (hKL : K ≤ L) : ‖f.restrict K‖ ≤ ‖f.restrict L‖ :=
(norm_le _ (norm_nonneg _)).mpr fun x => norm_coe_le_norm (f.restrict L) <| Set.inclusion hKL x
+lemma norm_eq_norm_coeFn [Fintype α] : ‖f‖ = ‖(f : α → E)‖ := by
+ apply le_antisymm
+ · rw [ContinuousMap.norm_le _ (by positivity)]
+ exact norm_le_pi_norm _
+ · rw [pi_norm_le_iff_of_nonneg (by positivity)]
+ exact f.norm_coe_le_norm
+
end
section
diff --git a/Mathlib/Topology/MetricSpace/Isometry.lean b/Mathlib/Topology/MetricSpace/Isometry.lean
index 0fdbb686f4ca3f..c89bf11c768a84 100644
--- a/Mathlib/Topology/MetricSpace/Isometry.lean
+++ b/Mathlib/Topology/MetricSpace/Isometry.lean
@@ -121,6 +121,10 @@ protected lemma inr [AddZeroClass α] [AddZeroClass β] : Isometry (AddMonoidHom
theorem comp {g : β → γ} {f : α → β} (hg : Isometry g) (hf : Isometry f) : Isometry (g ∘ f) :=
fun _ _ => (hg _ _).trans (hf _ _)
+omit [PseudoEMetricSpace α] in
+lemma postcomp_pi [Fintype α] {g : β → γ} (hg : Isometry g) : Isometry (fun f : α → β ↦ g ∘ f) :=
+ fun _ _ ↦ by simp [edist_pi_def, hg.edist_eq]
+
/-- An isometry from a metric space is a uniform continuous map -/
protected theorem uniformContinuous (hf : Isometry f) : UniformContinuous f :=
hf.lipschitz.uniformContinuous
From 2929a789898c9465220a5e2361032eb9edc5c928 Mon Sep 17 00:00:00 2001
From: Salvatore Mercuri <47568553+smmercuri@users.noreply.github.com>
Date: Wed, 24 Jun 2026 18:12:48 +0000
Subject: [PATCH 0329/1300] chore(Algebra): `coe_algHom` -> `coe_toAlgHom`
(#38950)
---
Mathlib/Algebra/Algebra/Equiv.lean | 17 ++++++++-----
Mathlib/Algebra/Algebra/Spectrum/Basic.lean | 2 +-
.../Algebra/Subalgebra/Centralizer.lean | 2 +-
Mathlib/Algebra/Azumaya/Basic.lean | 2 +-
Mathlib/Algebra/MvPolynomial/Equiv.lean | 6 ++---
Mathlib/AlgebraicGeometry/AffineSpace.lean | 2 +-
.../Analysis/CStarAlgebra/GelfandDuality.lean | 2 +-
Mathlib/FieldTheory/Extension.lean | 2 +-
Mathlib/FieldTheory/Galois/Basic.lean | 2 +-
Mathlib/FieldTheory/Isaacs.lean | 2 +-
Mathlib/FieldTheory/KummerExtension.lean | 6 ++---
Mathlib/FieldTheory/LinearDisjoint.lean | 2 +-
Mathlib/FieldTheory/Minpoly/Field.lean | 2 +-
Mathlib/FieldTheory/SeparableDegree.lean | 2 +-
Mathlib/FieldTheory/SeparablyGenerated.lean | 4 +--
Mathlib/LinearAlgebra/Charpoly/Basic.lean | 2 +-
.../TensorProduct/Subalgebra.lean | 2 +-
Mathlib/NumberTheory/Cyclotomic/Gal.lean | 8 +++---
.../RingTheory/Algebraic/MvPolynomial.lean | 4 +--
Mathlib/RingTheory/Bialgebra/Equiv.lean | 4 +--
Mathlib/RingTheory/Bialgebra/Hom.lean | 4 ++-
.../RingTheory/DividedPowerAlgebra/Init.lean | 4 +--
.../Extension/Presentation/Basic.lean | 2 +-
.../Extension/Presentation/Core.lean | 2 +-
Mathlib/RingTheory/GradedAlgebra/AlgHom.lean | 25 ++++++++++++-------
.../GradedAlgebra/TensorProduct.lean | 4 +--
.../RingTheory/Ideal/Quotient/Operations.lean | 2 +-
.../Symmetric/FundamentalTheorem.lean | 2 +-
Mathlib/RingTheory/NoetherNormalization.lean | 2 +-
.../Polynomial/Cyclotomic/Factorization.lean | 4 +--
Mathlib/RingTheory/Smooth/Basic.lean | 4 +--
.../RingTheory/Smooth/IntegralClosure.lean | 6 ++---
Mathlib/RingTheory/TensorProduct/Maps.lean | 6 ++---
33 files changed, 78 insertions(+), 64 deletions(-)
diff --git a/Mathlib/Algebra/Algebra/Equiv.lean b/Mathlib/Algebra/Algebra/Equiv.lean
index 5fad57bfc7b9b8..9f592f5c7127d0 100644
--- a/Mathlib/Algebra/Algebra/Equiv.lean
+++ b/Mathlib/Algebra/Algebra/Equiv.lean
@@ -187,12 +187,14 @@ theorem toAlgHom_apply (x : A₁) : e.toAlgHom x = e x :=
rfl
@[simp, norm_cast]
-theorem coe_algHom : DFunLike.coe e.toAlgHom = DFunLike.coe e :=
- rfl
+theorem coe_toAlgHom : DFunLike.coe e.toAlgHom = e := rfl
-theorem coe_algHom_injective : Function.Injective ((↑) : (A₁ ≃ₐ[R] A₂) → A₁ →ₐ[R] A₂) :=
+theorem coe_toAlgHom_injective : Function.Injective ((↑) : (A₁ ≃ₐ[R] A₂) → A₁ →ₐ[R] A₂) :=
fun _ _ h => ext <| AlgHom.congr_fun h
+@[deprecated (since := "2026-05-05")] alias coe_algHom := coe_toAlgHom
+@[deprecated (since := "2026-05-05")] alias coe_algHom_injective := coe_toAlgHom_injective
+
@[simp, norm_cast]
lemma toAlgHom_toRingHom : ((e : A₁ →ₐ[R] A₂) : A₁ →+* A₂) = e :=
rfl
@@ -476,15 +478,18 @@ def ofAlgHom (f : A₁ →ₐ[R] A₂) (g : A₂ →ₐ[R] A₁) (h₁ : f.comp
left_inv := AlgHom.ext_iff.1 h₂
right_inv := AlgHom.ext_iff.1 h₁ }
-theorem coe_algHom_ofAlgHom (f : A₁ →ₐ[R] A₂) (g : A₂ →ₐ[R] A₁) (h₁ h₂) :
+theorem toAlgHom_ofAlgHom (f : A₁ →ₐ[R] A₂) (g : A₂ →ₐ[R] A₁) (h₁ h₂) :
↑(ofAlgHom f g h₁ h₂) = f :=
rfl
@[simp]
-theorem ofAlgHom_coe_algHom (f : A₁ ≃ₐ[R] A₂) (g : A₂ →ₐ[R] A₁) (h₁ h₂) :
+theorem ofAlgHom_toAlgHom (f : A₁ ≃ₐ[R] A₂) (g : A₂ →ₐ[R] A₁) (h₁ h₂) :
ofAlgHom (↑f) g h₁ h₂ = f :=
ext fun _ => rfl
+@[deprecated (since := "2026-05-05")] alias coe_algHom_ofAlgHom := toAlgHom_ofAlgHom
+@[deprecated (since := "2026-05-05")] alias ofAlgHom_coe_algHom := ofAlgHom_toAlgHom
+
theorem ofAlgHom_symm (f : A₁ →ₐ[R] A₂) (g : A₂ →ₐ[R] A₁) (h₁ h₂) :
(ofAlgHom f g h₁ h₂).symm = ofAlgHom g f h₂ h₁ :=
rfl
@@ -761,7 +766,7 @@ def algHomUnitsEquiv (R S : Type*) [CommSemiring R] [Semiring S] [Algebra R S] :
/-- See also `Finite.algHom` -/
instance _root_.Finite.algEquiv [Finite (A₁ →ₐ[R] A₂)] : Finite (A₁ ≃ₐ[R] A₂) :=
- Finite.of_injective _ AlgEquiv.coe_algHom_injective
+ Finite.of_injective _ AlgEquiv.coe_toAlgHom_injective
-- TODO Morally this is just `isLocalHom_equiv`: can we obviate the need for this instance?
instance : IsLocalHom e.toAlgHom := by
diff --git a/Mathlib/Algebra/Algebra/Spectrum/Basic.lean b/Mathlib/Algebra/Algebra/Spectrum/Basic.lean
index 90ae07b320afb9..16755c4c407be2 100644
--- a/Mathlib/Algebra/Algebra/Spectrum/Basic.lean
+++ b/Mathlib/Algebra/Algebra/Spectrum/Basic.lean
@@ -431,7 +431,7 @@ theorem AlgEquiv.spectrum_eq {F R A B : Type*} [CommSemiring R] [Ring A] [Ring B
[Algebra R B] [EquivLike F A B] [AlgEquivClass F R A B] (f : F) (a : A) :
spectrum R (f a) = spectrum R a :=
Set.Subset.antisymm (AlgHom.spectrum_apply_subset _ _) <| by
- simpa only [AlgEquiv.coe_algHom, AlgEquiv.coe_coe_symm_apply_coe_apply] using
+ simpa only [AlgEquiv.coe_toAlgHom, AlgEquiv.coe_coe_symm_apply_coe_apply] using
AlgHom.spectrum_apply_subset (AlgEquivClass.toAlgEquiv f : A ≃ₐ[R] B).symm (f a)
section ConjugateUnits
diff --git a/Mathlib/Algebra/Algebra/Subalgebra/Centralizer.lean b/Mathlib/Algebra/Algebra/Subalgebra/Centralizer.lean
index 50fffbc6381b3c..85f0870d237eb7 100644
--- a/Mathlib/Algebra/Algebra/Subalgebra/Centralizer.lean
+++ b/Mathlib/Algebra/Algebra/Subalgebra/Centralizer.lean
@@ -117,7 +117,7 @@ lemma centralizer_coe_image_includeRight_eq_center_tensorProduct
⟨fun h b hb ↦ (Algebra.TensorProduct.comm R A B).symm.injective <| by aesop, fun h b hb ↦
(Algebra.TensorProduct.comm R A B).injective <| by aesop⟩
· ext x
- simp only [AlgHom.mem_range, mem_comap, AlgEquiv.coe_algHom]
+ simp only [AlgHom.mem_range, mem_comap, AlgEquiv.coe_toAlgHom]
constructor
· rintro ⟨x, rfl⟩
exact ⟨(Algebra.TensorProduct.comm R _ _) x,
diff --git a/Mathlib/Algebra/Azumaya/Basic.lean b/Mathlib/Algebra/Azumaya/Basic.lean
index cf682e3afca522..8f5035854b7b00 100644
--- a/Mathlib/Algebra/Azumaya/Basic.lean
+++ b/Mathlib/Algebra/Azumaya/Basic.lean
@@ -79,7 +79,7 @@ theorem of_AlgEquiv (e : A ≃ₐ[R] B) [IsAzumaya R A] : IsAzumaya R B :=
let _ : Module.Finite R B := .equiv e.toLinearEquiv
⟨Function.Bijective.of_comp_iff (AlgHom.mulLeftRight R B)
(Algebra.TensorProduct.congr e e.op).bijective |>.1 <| by
- rw [← AlgEquiv.coe_algHom, ← AlgHom.coe_comp, mulLeftRight_comp_congr]
+ rw [← AlgEquiv.coe_toAlgHom, ← AlgHom.coe_comp, mulLeftRight_comp_congr]
simp [AlgHom.mulLeftRight_bij]⟩
end IsAzumaya
diff --git a/Mathlib/Algebra/MvPolynomial/Equiv.lean b/Mathlib/Algebra/MvPolynomial/Equiv.lean
index 543505a067ec91..3a90eea82c3a26 100644
--- a/Mathlib/Algebra/MvPolynomial/Equiv.lean
+++ b/Mathlib/Algebra/MvPolynomial/Equiv.lean
@@ -502,7 +502,7 @@ theorem optionEquivLeft_elim_eval (s : S₁ → R) (y : R) (f : MvPolynomial (Op
apply MvPolynomial.algHom_ext
rw [Option.forall]
simp only [aeval_X, Option.elim_none, AlgHom.coe_comp, Polynomial.coe_aeval_eq_eval,
- AlgHom.coe_mk, Polynomial.coe_mapRingHom, AlgEquiv.coe_algHom, comp_apply,
+ AlgHom.coe_mk, Polynomial.coe_mapRingHom, AlgEquiv.coe_toAlgHom, comp_apply,
optionEquivLeft_apply, Polynomial.map_X, Polynomial.eval_X, Option.elim_some,
Polynomial.map_C, eval_X, Polynomial.eval_C, implies_true, and_self, φ]
@@ -684,7 +684,7 @@ theorem eval_eq_eval_mv_eval' (s : Fin n → R) (y : R) (f : MvPolynomial (Fin (
apply MvPolynomial.algHom_ext
rw [Fin.forall_iff_succ]
simp only [aeval_X, Fin.cons_zero, AlgHom.coe_comp, Polynomial.coe_aeval_eq_eval,
- AlgHom.coe_mk, Polynomial.coe_mapRingHom, AlgEquiv.coe_algHom,
+ AlgHom.coe_mk, Polynomial.coe_mapRingHom, AlgEquiv.coe_toAlgHom,
comp_apply, finSuccEquiv_apply, eval₂Hom_X', Fin.cases_zero, Polynomial.map_X,
Polynomial.eval_X, Fin.cons_succ, Fin.cases_succ, Polynomial.map_C, eval_X, Polynomial.eval_C,
implies_true, and_self, φ]
@@ -864,7 +864,7 @@ lemma Polynomial.toMvPolynomial_eq_rename_comp (i : σ) :
lemma Polynomial.toMvPolynomial_injective (i : σ) :
Function.Injective (toMvPolynomial (R := R) i) := by
- simp only [toMvPolynomial_eq_rename_comp, AlgHom.coe_comp, AlgEquiv.coe_algHom,
+ simp only [toMvPolynomial_eq_rename_comp, AlgHom.coe_comp, AlgEquiv.coe_toAlgHom,
EquivLike.injective_comp]
exact MvPolynomial.rename_injective (fun x ↦ i) fun _ _ _ ↦ rfl
diff --git a/Mathlib/AlgebraicGeometry/AffineSpace.lean b/Mathlib/AlgebraicGeometry/AffineSpace.lean
index 2ec9cee81b52b1..c886fc0dfce1a6 100644
--- a/Mathlib/AlgebraicGeometry/AffineSpace.lean
+++ b/Mathlib/AlgebraicGeometry/AffineSpace.lean
@@ -457,7 +457,7 @@ lemma isIntegralHom_over_iff_isEmpty : IsIntegralHom (𝔸(n; S) ↘ S) ↔ IsEm
have : (rename fun _ ↦ i).comp (uniqueAlgEquiv.{_, u} _ PUnit).symm.toAlgHom p = 0 := by
simp [← hp', ← algebraMap_eq]
rw [AlgHom.comp_apply, map_eq_zero_iff _ (rename_injective _ (fun _ _ _ ↦ rfl))] at this
- simp only [AlgEquiv.coe_algHom, EmbeddingLike.map_eq_zero_iff] at this
+ simp only [AlgEquiv.coe_toAlgHom, EmbeddingLike.map_eq_zero_iff] at this
simp [this] at hp
· rintro (_ | _) <;> infer_instance
diff --git a/Mathlib/Analysis/CStarAlgebra/GelfandDuality.lean b/Mathlib/Analysis/CStarAlgebra/GelfandDuality.lean
index 8fa4b863c83f0b..68ae5a326fbd8f 100644
--- a/Mathlib/Analysis/CStarAlgebra/GelfandDuality.lean
+++ b/Mathlib/Analysis/CStarAlgebra/GelfandDuality.lean
@@ -91,7 +91,7 @@ noncomputable def Ideal.toCharacterSpace : characterSpace ℂ A :=
theorem Ideal.toCharacterSpace_apply_eq_zero_of_mem {a : A} (ha : a ∈ I) :
I.toCharacterSpace a = 0 := by
unfold Ideal.toCharacterSpace
- simp only [CharacterSpace.equivAlgHom_symm_coe, AlgHom.coe_comp, AlgEquiv.coe_algHom,
+ simp only [CharacterSpace.equivAlgHom_symm_coe, AlgHom.coe_comp, AlgEquiv.coe_toAlgHom,
Quotient.mkₐ_eq_mk, Function.comp_apply, NormedRing.algEquivComplexOfComplete_symm_apply]
simp_rw [Quotient.eq_zero_iff_mem.mpr ha, spectrum.zero_eq]
exact Set.eq_of_mem_singleton (Set.singleton_nonempty (0 : ℂ)).some_mem
diff --git a/Mathlib/FieldTheory/Extension.lean b/Mathlib/FieldTheory/Extension.lean
index 7221fe4410be1e..7406aef78c527b 100644
--- a/Mathlib/FieldTheory/Extension.lean
+++ b/Mathlib/FieldTheory/Extension.lean
@@ -59,7 +59,7 @@ noncomputable instance : OrderBot (Lifts F E K) where
bot := ⟨⊥, (Algebra.ofId F K).comp (botEquiv F E)⟩
bot_le L := ⟨bot_le, fun x ↦ by
obtain ⟨x, rfl⟩ := (botEquiv F E).symm.surjective x
- simp_rw [AlgHom.comp_apply, AlgEquiv.coe_algHom, AlgEquiv.apply_symm_apply]
+ simp_rw [AlgHom.comp_apply, AlgEquiv.coe_toAlgHom, AlgEquiv.apply_symm_apply]
exact L.emb.commutes x⟩
noncomputable instance : Inhabited (Lifts F E K) :=
diff --git a/Mathlib/FieldTheory/Galois/Basic.lean b/Mathlib/FieldTheory/Galois/Basic.lean
index 9a8f0a6a0aed22..494394ceaf6ee4 100644
--- a/Mathlib/FieldTheory/Galois/Basic.lean
+++ b/Mathlib/FieldTheory/Galois/Basic.lean
@@ -447,7 +447,7 @@ open scoped Pointwise
theorem map_fixingSubgroup (σ : Gal(L/K)) :
(E.map σ).fixingSubgroup = (MulAut.conj σ) • E.fixingSubgroup := by
ext τ
- simp only [coe_map, AlgEquiv.coe_algHom, Set.mem_image, SetLike.mem_coe, AlgEquiv.smul_def,
+ simp only [coe_map, AlgEquiv.coe_toAlgHom, Set.mem_image, SetLike.mem_coe, AlgEquiv.smul_def,
forall_exists_index, and_imp, forall_apply_eq_imp_iff₂,
Subgroup.mem_pointwise_smul_iff_inv_smul_mem, ← symm_apply_eq,
IntermediateField.fixingSubgroup, mem_fixingSubgroup_iff]
diff --git a/Mathlib/FieldTheory/Isaacs.lean b/Mathlib/FieldTheory/Isaacs.lean
index 3f214bd9523737..e29985c0cc2fe2 100644
--- a/Mathlib/FieldTheory/Isaacs.lean
+++ b/Mathlib/FieldTheory/Isaacs.lean
@@ -62,7 +62,7 @@ theorem nonempty_algHom_of_exists_root (h : ∀ x : E, ∃ y : K, aeval y (minpo
have ⟨ω, hω⟩ := exists_algHom_adjoin_of_splits
(fun s hs ↦ ⟨(alg.isIntegral).1 _, splits s hs⟩) ϕ (adjoin_simple_le_iff.mpr hα)
refine ⟨ω, β, ((DFunLike.congr_fun hω <| AdjoinSimple.gen F α).trans ?_).symm⟩
- rw [AlgHom.comp_apply, AlgHom.comp_apply, AlgEquiv.coe_algHom,
+ rw [AlgHom.comp_apply, AlgHom.comp_apply, AlgEquiv.coe_toAlgHom,
adjoinRootEquivAdjoin_symm_apply_gen, AdjoinRoot.liftAlgHom_root]
rfl
have ω : ∃ ω : Ω, ⊤ ≤ M ω := by
diff --git a/Mathlib/FieldTheory/KummerExtension.lean b/Mathlib/FieldTheory/KummerExtension.lean
index 593f21e400895f..d20b29ba748424 100644
--- a/Mathlib/FieldTheory/KummerExtension.lean
+++ b/Mathlib/FieldTheory/KummerExtension.lean
@@ -269,10 +269,10 @@ def autAdjoinRootXPowSubCEquiv [NeZero n] :
intro e
have := Fact.mk H
letI : Algebra K K[n√a] := inferInstance
- apply AlgEquiv.coe_algHom_injective
+ apply AlgEquiv.coe_toAlgHom_injective
apply AdjoinRoot.algHom_ext
simp only [AdjoinRootXPowSubCEquivToRootsOfUnity, AdjoinRoot.algebraMap_eq, OneHom.toFun_eq_coe,
- MonoidHom.toOneHom_coe, AlgEquiv.coe_algHom, autAdjoinRootXPowSubC_root, Algebra.smul_def]
+ MonoidHom.toOneHom_coe, AlgEquiv.coe_toAlgHom, autAdjoinRootXPowSubC_root, Algebra.smul_def]
rw [rootsOfUnityEquivOfPrimitiveRoots_symm_apply, rootsOfUnity.val_mkOfPowEq_coe]
split_ifs with h
· obtain rfl := not_imp_not.mp (fun hn ↦ ne_zero_of_irreducible_X_pow_sub_C' hn H) h
@@ -356,7 +356,7 @@ lemma Algebra.adjoin_root_eq_top_of_isSplittingField :
(adjoinRootXPowSubCEquiv hζ H hα).symm.injective
rw [Algebra.map_top, (AlgHom.range_eq_top _).mpr
(adjoinRootXPowSubCEquiv hζ H hα).symm.surjective, AlgHom.map_adjoin,
- Set.image_singleton, AlgEquiv.coe_algHom, adjoinRootXPowSubCEquiv_symm_eq_root,
+ Set.image_singleton, AlgEquiv.coe_toAlgHom, adjoinRootXPowSubCEquiv_symm_eq_root,
adjoinRoot_eq_top]
include hζ H hα in
diff --git a/Mathlib/FieldTheory/LinearDisjoint.lean b/Mathlib/FieldTheory/LinearDisjoint.lean
index a9035e67cc1299..5894be8689642f 100644
--- a/Mathlib/FieldTheory/LinearDisjoint.lean
+++ b/Mathlib/FieldTheory/LinearDisjoint.lean
@@ -685,7 +685,7 @@ theorem isField_of_forall (A : Type v) [Field A] (B : Type w) [Field B]
(AlgEquiv.ofInjective fa fa.injective) (AlgEquiv.ofInjective fb fb.injective)) := by
ext <;> simp [fa, fb]
replace H : Function.Injective i := by simpa only
- [hi, AlgHom.coe_comp, AlgEquiv.coe_algHom, EquivLike.injective_comp, fa, this, K, fb]
+ [hi, AlgHom.coe_comp, AlgEquiv.coe_toAlgHom, EquivLike.injective_comp, fa, this, K, fb]
change Function.Injective (Ideal.Quotient.mk M) at H
rwa [RingHom.injective_iff_ker_eq_bot, Ideal.mk_ker] at H
diff --git a/Mathlib/FieldTheory/Minpoly/Field.lean b/Mathlib/FieldTheory/Minpoly/Field.lean
index a530c39880c1a3..f4759385159d6a 100644
--- a/Mathlib/FieldTheory/Minpoly/Field.lean
+++ b/Mathlib/FieldTheory/Minpoly/Field.lean
@@ -333,7 +333,7 @@ lemma minpoly_algEquiv_toLinearMap (σ : L ≃ₐ[K] L) (hσ : IsOfFinOrder σ)
simp_rw [← AlgEquiv.pow_toLinearMap] at hs
apply hq.ne_zero
simpa using Fintype.linearIndependent_iff.mp
- (((linearIndependent_algHom_toLinearMap' K L L).comp _ AlgEquiv.coe_algHom_injective).comp _
+ (((linearIndependent_algHom_toLinearMap' K L L).comp _ AlgEquiv.coe_toAlgHom_injective).comp _
(Subtype.val_injective.comp ((finEquivPowers hσ).injective)))
(q.coeff ∘ (↑)) hs ⟨_, H⟩
diff --git a/Mathlib/FieldTheory/SeparableDegree.lean b/Mathlib/FieldTheory/SeparableDegree.lean
index 8f092165f30c70..cd80f415c6cef7 100644
--- a/Mathlib/FieldTheory/SeparableDegree.lean
+++ b/Mathlib/FieldTheory/SeparableDegree.lean
@@ -159,7 +159,7 @@ def embEquivOfEquiv (i : E ≃ₐ[F] K) :
intro x
have h := isAlgebraic_algebraMap (R := E) (A := K) (i.symm.toAlgHom x)
rw [show ∀ y : E, (algebraMap E K) y = i.toAlgHom y from fun y ↦ rfl] at h
- simpa only [AlgEquiv.coe_algHom, AlgEquiv.apply_symm_apply] using h
+ simpa only [AlgEquiv.coe_toAlgHom, AlgEquiv.apply_symm_apply] using h
apply AlgEquiv.restrictScalars (R := F) (S := E)
exact IsAlgClosure.equivOfAlgebraic E K (AlgebraicClosure K) (AlgebraicClosure E)
diff --git a/Mathlib/FieldTheory/SeparablyGenerated.lean b/Mathlib/FieldTheory/SeparablyGenerated.lean
index 5301cf752be973..1ae054816114f6 100644
--- a/Mathlib/FieldTheory/SeparablyGenerated.lean
+++ b/Mathlib/FieldTheory/SeparablyGenerated.lean
@@ -61,7 +61,7 @@ theorem aeval_toPolynomialAdjoinImageCompl_eq_zero
{a : ι → K} {F : MvPolynomial ι k} (hFa : F.aeval a = 0) (i : ι) :
(toPolynomialAdjoinImageCompl F a i).aeval (a i) = 0 := by
rw [← hFa, ← AlgHom.restrictScalars_apply k]
- simp_rw [toPolynomialAdjoinImageCompl, ← AlgEquiv.coe_algHom, ← AlgHom.comp_apply]
+ simp_rw [toPolynomialAdjoinImageCompl, ← AlgEquiv.coe_toAlgHom, ← AlgHom.comp_apply]
congr; ext; aesop (add simp optionEquivLeft_X_some) (add simp optionEquivLeft_X_none)
set_option backward.isDefEq.respectTransparency false in
@@ -74,7 +74,7 @@ theorem irreducible_toPolynomialAdjoinImageCompl {F : MvPolynomial ι k} (hF : I
hF.map (renameEquiv k (Equiv.optionSubtypeNe i).symm) |>.map (optionEquivLeft k _) |>.map
(Polynomial.mapAlgEquiv
(H.aevalEquiv.trans (Subalgebra.equivOfEq _ _ congr(Algebra.adjoin k $this.symm))))
- rw [← AlgEquiv.coe_algHom]
+ rw [← AlgEquiv.coe_toAlgHom]
congr
aesop
diff --git a/Mathlib/LinearAlgebra/Charpoly/Basic.lean b/Mathlib/LinearAlgebra/Charpoly/Basic.lean
index e7e2ce6bd931a1..c3e7afb498ccc3 100644
--- a/Mathlib/LinearAlgebra/Charpoly/Basic.lean
+++ b/Mathlib/LinearAlgebra/Charpoly/Basic.lean
@@ -88,7 +88,7 @@ to the linear map itself, is zero.
See `Matrix.aeval_self_charpoly` for the equivalent statement about matrices. -/
theorem aeval_self_charpoly : aeval f f.charpoly = 0 := by
apply (LinearEquiv.map_eq_zero_iff (algEquivMatrix (chooseBasis R M)).toLinearEquiv).1
- rw [AlgEquiv.toLinearEquiv_apply, ← AlgEquiv.coe_algHom, ← Polynomial.aeval_algHom_apply _ _ _,
+ rw [AlgEquiv.toLinearEquiv_apply, ← AlgEquiv.coe_toAlgHom, ← Polynomial.aeval_algHom_apply _ _ _,
charpoly_def]
exact Matrix.aeval_self_charpoly _
diff --git a/Mathlib/LinearAlgebra/TensorProduct/Subalgebra.lean b/Mathlib/LinearAlgebra/TensorProduct/Subalgebra.lean
index 7ec7f4c133f8f9..174859ad12e7f1 100644
--- a/Mathlib/LinearAlgebra/TensorProduct/Subalgebra.lean
+++ b/Mathlib/LinearAlgebra/TensorProduct/Subalgebra.lean
@@ -248,7 +248,7 @@ variable {A B} in
theorem val_mulMap'_tmul (a : A) (b : B) : (mulMap' A B (a ⊗ₜ[R] b) : S) = a.1 * b.1 := rfl
theorem mulMap'_surjective : Function.Surjective (mulMap' A B) := by
- simp_rw [mulMap', AlgHom.coe_comp, AlgEquiv.coe_algHom,
+ simp_rw [mulMap', AlgHom.coe_comp, AlgEquiv.coe_toAlgHom,
EquivLike.comp_surjective, AlgHom.rangeRestrict_surjective]
end Subalgebra
diff --git a/Mathlib/NumberTheory/Cyclotomic/Gal.lean b/Mathlib/NumberTheory/Cyclotomic/Gal.lean
index a1953f31cffa15..fb8e0532ac4489 100644
--- a/Mathlib/NumberTheory/Cyclotomic/Gal.lean
+++ b/Mathlib/NumberTheory/Cyclotomic/Gal.lean
@@ -58,9 +58,9 @@ theorem autToPow_injective : Function.Injective <| hμ.autToPow K := by
intro f g hfg
have : f.toAlgHom = g.toAlgHom := by
apply (hμ.powerBasis K).algHom_ext
- rw [AlgEquiv.coe_algHom, AlgEquiv.coe_algHom, powerBasis_gen,
+ rw [AlgEquiv.coe_toAlgHom, AlgEquiv.coe_toAlgHom, powerBasis_gen,
← autToPow_spec K hμ g, ← autToPow_spec K hμ f, hfg]
- exact AlgEquiv.coe_algHom_injective this
+ exact AlgEquiv.coe_toAlgHom_injective this
end IsPrimitiveRoot
@@ -89,9 +89,9 @@ noncomputable def autEquivPow (h : Irreducible (cyclotomic n K)) : Gal(L/K) ≃*
exact ((zeta_spec n K L).minpoly_eq_cyclotomic_of_irreducible h).symm.trans hr)
left_inv := fun f => by
simp only [MonoidHom.toFun_eq_coe]
- apply AlgEquiv.coe_algHom_injective
+ apply AlgEquiv.coe_toAlgHom_injective
apply (hζ.powerBasis K).algHom_ext
- simp only [AlgEquiv.coe_algHom]
+ simp only [AlgEquiv.coe_toAlgHom]
rw [PowerBasis.equivOfMinpoly_gen]
simp only [IsPrimitiveRoot.powerBasis_gen, IsPrimitiveRoot.autToPow_spec]
right_inv := fun x => by
diff --git a/Mathlib/RingTheory/Algebraic/MvPolynomial.lean b/Mathlib/RingTheory/Algebraic/MvPolynomial.lean
index 632a314528adee..40baac310e5252 100644
--- a/Mathlib/RingTheory/Algebraic/MvPolynomial.lean
+++ b/Mathlib/RingTheory/Algebraic/MvPolynomial.lean
@@ -44,7 +44,7 @@ theorem transcendental_supported_polynomial_aeval_X {i : σ} {s : Set σ} (h : i
(Polynomial.mapAlgEquiv (supportedEquivMvPolynomial s).symm).toAlgHom
replace hf : Function.Injective u := by
simp only [AlgHom.coe_comp, Subalgebra.coe_val,
- AlgEquiv.coe_algHom, AlgEquiv.coe_trans, Function.comp_assoc, u]
+ AlgEquiv.coe_toAlgHom, AlgEquiv.coe_trans, Function.comp_assoc, u]
apply Subtype.val_injective.comp
simp only [EquivLike.comp_injective]
apply hf.comp
@@ -59,7 +59,7 @@ theorem transcendental_supported_polynomial_aeval_X {i : σ} {s : Set σ} (h : i
· ext1
simp [Set.subtypeInsertEquivOption, Subalgebra.algebraMap_eq, optionEquivLeft_symm_apply]
· simp [Set.subtypeInsertEquivOption, h1, optionEquivLeft_symm_apply]
- simpa only [h2, v, AlgHom.coe_comp, AlgEquiv.coe_algHom,
+ simpa only [h2, v, AlgHom.coe_comp, AlgEquiv.coe_toAlgHom,
EquivLike.injective_comp, AlgHom.coe_restrictScalars'] using hf
theorem transcendental_polynomial_aeval_X (i : σ) {f : R[X]} (hf : Transcendental R f) :
diff --git a/Mathlib/RingTheory/Bialgebra/Equiv.lean b/Mathlib/RingTheory/Bialgebra/Equiv.lean
index 819381736fc847..877a4943aae75e 100644
--- a/Mathlib/RingTheory/Bialgebra/Equiv.lean
+++ b/Mathlib/RingTheory/Bialgebra/Equiv.lean
@@ -280,10 +280,10 @@ lemma apply_symm_apply (e : A ≃ₐc[R] B) : ∀ x, e (e.symm x) = x := e.toEqu
lemma symm_apply_apply (e : A ≃ₐc[R] B) : ∀ x, e.symm (e x) = x := e.toEquiv.symm_apply_apply
@[simp] lemma comp_symm (e : A ≃ₐc[R] B) : (e : A →ₐc[R] B).comp e.symm = .id R B :=
- BialgHom.coe_algHom_injective e.toAlgEquiv.comp_symm
+ BialgHom.coe_toAlgHom_injective e.toAlgEquiv.comp_symm
@[simp] lemma symm_comp (e : A ≃ₐc[R] B) : (e.symm : B →ₐc[R] A).comp e = .id R A :=
- BialgHom.coe_algHom_injective e.toAlgEquiv.symm_comp
+ BialgHom.coe_toAlgHom_injective e.toAlgEquiv.symm_comp
@[simp] lemma toRingEquiv_toRingHom (e : A ≃ₐc[R] B) : ((e : A ≃+* B) : A →+* B) = e := rfl
@[simp] lemma toAlgEquiv_toRingHom (e : A ≃ₐc[R] B) : ((e : A ≃ₐ[R] B) : A →+* B) = e := rfl
diff --git a/Mathlib/RingTheory/Bialgebra/Hom.lean b/Mathlib/RingTheory/Bialgebra/Hom.lean
index 7598da37a0d9ea..2e23be000a224b 100644
--- a/Mathlib/RingTheory/Bialgebra/Hom.lean
+++ b/Mathlib/RingTheory/Bialgebra/Hom.lean
@@ -194,10 +194,12 @@ theorem coe_coalgHom_injective : Function.Injective ((↑) : (A →ₐc[R] B)
fun φ₁ φ₂ H => coe_fn_injective <|
show ((φ₁ : A →ₗc[R] B) : A → B) = ((φ₂ : A →ₗc[R] B) : A → B) from congr_arg _ H
-theorem coe_algHom_injective : Function.Injective ((↑) : (A →ₐc[R] B) → A →ₐ[R] B) :=
+theorem coe_toAlgHom_injective : Function.Injective ((↑) : (A →ₐc[R] B) → A →ₐ[R] B) :=
fun φ₁ φ₂ H => coe_fn_injective <|
show ((φ₁ : A →ₐ[R] B) : A → B) = ((φ₂ : A →ₐ[R] B) : A → B) from congr_arg _ H
+@[deprecated (since := "2026-05-05")] alias coe_algHom_injective := coe_toAlgHom_injective
+
theorem coe_linearMap_injective : Function.Injective ((↑) : (A →ₐc[R] B) → A →ₗ[R] B) :=
CoalgHom.coe_linearMap_injective.comp coe_coalgHom_injective
diff --git a/Mathlib/RingTheory/DividedPowerAlgebra/Init.lean b/Mathlib/RingTheory/DividedPowerAlgebra/Init.lean
index 3549203e60bb03..c36b5a97e98dfb 100644
--- a/Mathlib/RingTheory/DividedPowerAlgebra/Init.lean
+++ b/Mathlib/RingTheory/DividedPowerAlgebra/Init.lean
@@ -495,11 +495,11 @@ theorem LinearEquiv.coe_lift_symm (g : M ≃ₗ[R] N) :
(mapEquiv g).symm = map R g.symm.toLinearMap := rfl
theorem mapEquiv_refl : mapEquiv (LinearEquiv.refl R M) = AlgEquiv.refl :=
- AlgEquiv.coe_algHom_injective map_id
+ AlgEquiv.coe_toAlgHom_injective map_id
theorem mapEquiv_trans (g : M ≃ₗ[R] N) (h : N ≃ₗ[R] P) :
(mapEquiv g).trans (mapEquiv h) = mapEquiv (g.trans h) :=
- AlgEquiv.coe_algHom_injective (map_comp _ _).symm
+ AlgEquiv.coe_toAlgHom_injective (map_comp _ _).symm
end IsScalarTower
diff --git a/Mathlib/RingTheory/Extension/Presentation/Basic.lean b/Mathlib/RingTheory/Extension/Presentation/Basic.lean
index b501c0723b700f..075657d1a4168f 100644
--- a/Mathlib/RingTheory/Extension/Presentation/Basic.lean
+++ b/Mathlib/RingTheory/Extension/Presentation/Basic.lean
@@ -210,7 +210,7 @@ lemma _root_.Algebra.Generators.ker_localizationAway :
(Ideal.Quotient.mkₐ R (Ideal.span {C r * X () - 1})) := by
ext x
simp only [aeval_X, Generators.localizationAway_val, AlgHom.coe_comp,
- AlgEquiv.coe_algHom, Ideal.Quotient.mkₐ_eq_mk, Function.comp_apply]
+ AlgEquiv.coe_toAlgHom, Ideal.Quotient.mkₐ_eq_mk, Function.comp_apply]
rw [IsLocalization.Away.mvPolynomialQuotientEquiv_apply, aeval_X]
rw [Generators.ker_eq_ker_aeval_val, this, ← RingHom.ker_coe_toRingHom, AlgHom.comp_toRingHom,
← RingHom.comap_ker]
diff --git a/Mathlib/RingTheory/Extension/Presentation/Core.lean b/Mathlib/RingTheory/Extension/Presentation/Core.lean
index 9ed6f073826e2c..14d4919f4ff948 100644
--- a/Mathlib/RingTheory/Extension/Presentation/Core.lean
+++ b/Mathlib/RingTheory/Extension/Presentation/Core.lean
@@ -160,7 +160,7 @@ noncomputable def tensorModelOfHasCoeffsInv : S →ₐ[R] R ⊗[R₀] P.ModelOfH
rw [← P.span_range_relation_eq_ker, Ideal.span_le]
rintro a ⟨i, rfl⟩
simp only [SetLike.mem_coe, RingHom.mem_ker, AlgHom.coe_comp,
- AlgEquiv.coe_algHom, Function.comp_apply, algebraTensorAlgEquiv_symm_relation]
+ AlgEquiv.coe_toAlgHom, Function.comp_apply, algebraTensorAlgEquiv_symm_relation]
simp only [TensorProduct.map_tmul, AlgHom.coe_id, id_eq, Ideal.Quotient.mkₐ_eq_mk,
Ideal.Quotient.mk_span_range, tmul_zero]).comp
(P.quotientEquiv.restrictScalars R).symm.toAlgHom
diff --git a/Mathlib/RingTheory/GradedAlgebra/AlgHom.lean b/Mathlib/RingTheory/GradedAlgebra/AlgHom.lean
index baacd12d69fcf3..6b6c5faa288492 100644
--- a/Mathlib/RingTheory/GradedAlgebra/AlgHom.lean
+++ b/Mathlib/RingTheory/GradedAlgebra/AlgHom.lean
@@ -98,9 +98,11 @@ initialize_simps_projections GradedAlgHom (toFun → apply)
theorem coe_mks {f : A → B} (h₁ h₂ h₃ h₄ h₅ h₆) :
⇑(⟨⟨⟨⟨⟨f, h₁⟩, h₂⟩, h₃, h₄⟩, h₅⟩, h₆⟩ : 𝒜 →ₐᵍ[R] ℬ) = f := rfl
-theorem coe_algHom_mk {f : A →ₐ[R] B} (h) : ((⟨f, h⟩ : 𝒜 →ₐᵍ[R] ℬ) : A →ₐ[R] B) = f := by
+theorem coe_toAlgHom_mk {f : A →ₐ[R] B} (h) : ((⟨f, h⟩ : 𝒜 →ₐᵍ[R] ℬ) : A →ₐ[R] B) = f := by
dsimp only
+@[deprecated (since := "2026-05-05")] alias coe_algHom_mk := coe_toAlgHom_mk
+
variable (f : 𝒜 →ₐᵍ[R] ℬ)
theorem coe_fn_injective : Function.Injective ((↑) : (𝒜 →ₐᵍ[R] ℬ) → (A → B)) :=
@@ -109,23 +111,25 @@ theorem coe_fn_injective : Function.Injective ((↑) : (𝒜 →ₐᵍ[R] ℬ)
theorem coe_fn_inj {f₁ f₂ : 𝒜 →ₐᵍ[R] ℬ} : (f₁ : A → B) = f₂ ↔ f₁ = f₂ :=
DFunLike.coe_fn_eq
-theorem coe_algHom_injective : Function.Injective ((↑) : (𝒜 →ₐᵍ[R] ℬ) → A →ₐ[R] B) :=
+theorem coe_toAlgHom_injective : Function.Injective ((↑) : (𝒜 →ₐᵍ[R] ℬ) → A →ₐ[R] B) :=
fun _ _ h ↦ coe_fn_injective congr($h)
+@[deprecated (since := "2026-05-05")] alias coe_algHom_injective := coe_toAlgHom_injective
+
theorem toGradedRingHom_injective : Function.Injective (toGradedRingHom (𝒜 := 𝒜) (ℬ := ℬ)) :=
fun _ _ h ↦ coe_fn_injective congr($h)
theorem coe_linearMap_injective : Function.Injective ((↑) : (𝒜 →ₐᵍ[R] ℬ) → A →ₗ[R] B) :=
- AlgHom.toLinearMap_injective.comp coe_algHom_injective
+ AlgHom.toLinearMap_injective.comp coe_toAlgHom_injective
theorem coe_ringHom_injective : Function.Injective ((↑) : (𝒜 →ₐᵍ[R] ℬ) → A →+* B) :=
- AlgHom.coe_ringHom_injective.comp coe_algHom_injective
+ AlgHom.coe_ringHom_injective.comp coe_toAlgHom_injective
theorem coe_monoidHom_injective : Function.Injective ((↑) : (𝒜 →ₐᵍ[R] ℬ) → A →* B) :=
- AlgHom.coe_monoidHom_injective.comp coe_algHom_injective
+ AlgHom.coe_monoidHom_injective.comp coe_toAlgHom_injective
theorem coe_addMonoidHom_injective : Function.Injective ((↑) : (𝒜 →ₐᵍ[R] ℬ) → A →+ B) :=
- AlgHom.coe_addMonoidHom_injective.comp coe_algHom_injective
+ AlgHom.coe_addMonoidHom_injective.comp coe_toAlgHom_injective
/-- Consider using `congr($H x)` instead. -/
protected theorem congr_fun {f₁ f₂ : 𝒜 →ₐᵍ[R] ℬ} (H : f₁ = f₂) (x : A) : f₁ x = f₂ x :=
@@ -215,11 +219,11 @@ instance : Monoid (𝒜 →ₐᵍ[R] 𝒜) where
lemma cancel_right {g₁ g₂ : ℬ →ₐᵍ[R] 𝒞} {f : 𝒜 →ₐᵍ[R] ℬ} (hf : Function.Surjective f) :
g₁.comp f = g₂.comp f ↔ g₁ = g₂ :=
- ⟨fun h ↦ coe_algHom_injective <| (AlgHom.cancel_right hf).1 congr($h), fun h ↦ h ▸ rfl⟩
+ ⟨fun h ↦ coe_toAlgHom_injective <| (AlgHom.cancel_right hf).1 congr($h), fun h ↦ h ▸ rfl⟩
lemma cancel_left {g₁ g₂ : 𝒜 →ₐᵍ[R] ℬ} {f : ℬ →ₐᵍ[R] 𝒞} (hf : Function.Injective f) :
f.comp g₁ = f.comp g₂ ↔ g₁ = g₂ :=
- ⟨fun h ↦ coe_algHom_injective <| (AlgHom.cancel_left hf).1 congr($h), fun h ↦ h ▸ rfl⟩
+ ⟨fun h ↦ coe_toAlgHom_injective <| (AlgHom.cancel_left hf).1 congr($h), fun h ↦ h ▸ rfl⟩
/-- We enrich the existing function `toAlgHom` with the structure of a `MonoidHom`, to produce a
bundled function that we now call `toEnd`. -/
@@ -256,9 +260,12 @@ variable (R₀ : Type*) [CommSemiring R₀] [Algebra R₀ R]
@[simp] lemma coe_restrictScalars : ⇑(f.restrictScalars R₀) = f := rfl
-@[simp] lemma restrictScalars_coe_algHom :
+@[simp] lemma restrictScalars_toAlgHom :
(f : A →ₐ[R] B).restrictScalars R₀ = f.restrictScalars R₀ := rfl
+@[deprecated (since := "2026-05-05")]
+alias restrictScalars_coe_algHom := restrictScalars_toAlgHom
+
@[simp] lemma restrictScalars_coe_linearMap :
(f : A →ₗ[R] B).restrictScalars R₀ = f.restrictScalars R₀ := rfl
diff --git a/Mathlib/RingTheory/GradedAlgebra/TensorProduct.lean b/Mathlib/RingTheory/GradedAlgebra/TensorProduct.lean
index 46e82e06a69fe6..5a6b85e0106a28 100644
--- a/Mathlib/RingTheory/GradedAlgebra/TensorProduct.lean
+++ b/Mathlib/RingTheory/GradedAlgebra/TensorProduct.lean
@@ -128,8 +128,8 @@ def liftEquiv : (𝒜 →ₐᵍ[R] (ℬ · |>.restrictScalars R)) ≃ ((𝒜 ·
invFun f :=
{ AlgHom.liftEquiv R S A B |>.symm f with
map_mem hx := f.map_mem <| tmul_mem_baseChange_of_mem _ hx }
- left_inv f := coe_algHom_injective <| by simp
- right_inv f := coe_algHom_injective <| by simp
+ left_inv f := coe_toAlgHom_injective <| by simp
+ right_inv f := coe_toAlgHom_injective <| by simp
variable {𝒜 ℬ}
diff --git a/Mathlib/RingTheory/Ideal/Quotient/Operations.lean b/Mathlib/RingTheory/Ideal/Quotient/Operations.lean
index 996128bf8ca4ef..49867700683d63 100644
--- a/Mathlib/RingTheory/Ideal/Quotient/Operations.lean
+++ b/Mathlib/RingTheory/Ideal/Quotient/Operations.lean
@@ -566,7 +566,7 @@ lemma _root_.AlgHom.liftOfSurjective_apply (f : A →ₐ[R] B) (hf : Function.Su
(g : A →ₐ[R] C) (H : RingHom.ker f.toRingHom ≤ RingHom.ker g.toRingHom) (x) :
AlgHom.liftOfSurjective f hf g H (f x) = g x := by
dsimp [AlgHom.liftOfSurjective]
- erw [AlgEquiv.coe_algHom] -- fixed after #21031
+ erw [AlgEquiv.coe_toAlgHom] -- fixed after #21031
rw [Ideal.quotientKerAlgEquivOfSurjective_symm_apply]
rfl
diff --git a/Mathlib/RingTheory/MvPolynomial/Symmetric/FundamentalTheorem.lean b/Mathlib/RingTheory/MvPolynomial/Symmetric/FundamentalTheorem.lean
index a20511d3dcb445..512daafeff3a99 100644
--- a/Mathlib/RingTheory/MvPolynomial/Symmetric/FundamentalTheorem.lean
+++ b/Mathlib/RingTheory/MvPolynomial/Symmetric/FundamentalTheorem.lean
@@ -142,7 +142,7 @@ lemma esymmAlgHom_apply (p : MvPolynomial (Fin n) R) :
lemma rename_esymmAlgHom (e : σ ≃ τ) :
(renameSymmetricSubalgebra e).toAlgHom.comp (esymmAlgHom σ R n) = esymmAlgHom τ R n := by
ext i : 2
- simp_rw [AlgHom.comp_apply, esymmAlgHom, aeval_X, AlgEquiv.coe_algHom,
+ simp_rw [AlgHom.comp_apply, esymmAlgHom, aeval_X, AlgEquiv.coe_toAlgHom,
renameSymmetricSubalgebra_apply_coe, rename_esymm]
variable (σ) in
diff --git a/Mathlib/RingTheory/NoetherNormalization.lean b/Mathlib/RingTheory/NoetherNormalization.lean
index 2e99d7ec0ca2a8..c3ffd0befae1f6 100644
--- a/Mathlib/RingTheory/NoetherNormalization.lean
+++ b/Mathlib/RingTheory/NoetherNormalization.lean
@@ -278,7 +278,7 @@ theorem exists_integral_inj_algHom_of_fg : ∃ s, ∃ g : (MvPolynomial (Fin s)
set ϕ := quotientKerAlgEquivOfSurjective fsurj
obtain ⟨s, _, g, injg, intg⟩ := exists_integral_inj_algHom_of_quotient (ker f) (ker_ne_top _)
use s, ϕ.toAlgHom.comp g
- simp only [AlgHom.coe_comp, AlgEquiv.coe_algHom, EmbeddingLike.comp_injective,
+ simp only [AlgHom.coe_comp, AlgEquiv.coe_toAlgHom, EmbeddingLike.comp_injective,
AlgHom.toRingHom_eq_coe]
exact ⟨injg, intg.trans _ _ (isIntegral_of_surjective _ ϕ.surjective)⟩
diff --git a/Mathlib/RingTheory/Polynomial/Cyclotomic/Factorization.lean b/Mathlib/RingTheory/Polynomial/Cyclotomic/Factorization.lean
index cb48fd6da6f6c2..2d1410358a3ebb 100644
--- a/Mathlib/RingTheory/Polynomial/Cyclotomic/Factorization.lean
+++ b/Mathlib/RingTheory/Polynomial/Cyclotomic/Factorization.lean
@@ -64,9 +64,9 @@ private theorem natDegree_of_dvd_cyclotomic_of_irreducible_of_monic (hP : P ∣
⟨n, pos_of_ne_zero (fun h0 ↦ by simp [h0, hp.out.ne_one] at hn),
hζ.pow_eq_one⟩
refine dvd_antisymm
- (orderOf_dvd_iff_pow_eq_one.mpr <| AlgEquiv.coe_algHom_injective <| pB.algHom_ext ?_)
+ (orderOf_dvd_iff_pow_eq_one.mpr <| AlgEquiv.coe_toAlgHom_injective <| pB.algHom_ext ?_)
(orderOf_dvd_iff_pow_eq_one.mpr <| Units.ext ?_)
- · simp only [AlgEquiv.coe_algHom, AlgEquiv.coe_pow, AlgEquiv.one_apply,
+ · simp only [AlgEquiv.coe_toAlgHom, AlgEquiv.coe_pow, AlgEquiv.one_apply,
coe_frobeniusAlgEquivOfAlgebraic, pow_iterate, hK]
nth_rewrite 2 [← pow_one pB.gen]
rw [powerBasis_gen hPirr.ne_zero, hζ'.pow_eq_pow_iff_modEq, ← hζ.eq_orderOf,
diff --git a/Mathlib/RingTheory/Smooth/Basic.lean b/Mathlib/RingTheory/Smooth/Basic.lean
index 1ba66377ae655e..54640dfe993ab0 100644
--- a/Mathlib/RingTheory/Smooth/Basic.lean
+++ b/Mathlib/RingTheory/Smooth/Basic.lean
@@ -177,7 +177,7 @@ theorem liftOfSurjective_apply [FormallySmooth R A] (f : A →ₐ[R] C) (g : B
(hg : Function.Surjective g) (hg' : IsNilpotent <| RingHom.ker g) (x : A) :
g (FormallySmooth.liftOfSurjective f g hg hg' x) = f x := by
apply (Ideal.quotientKerAlgEquivOfSurjective hg).symm.injective
- conv_rhs => rw [← AlgEquiv.coe_algHom, ← AlgHom.comp_apply,
+ conv_rhs => rw [← AlgEquiv.coe_toAlgHom, ← AlgHom.comp_apply,
← FormallySmooth.mk_lift (A := A) _ hg']
apply (Ideal.quotientKerAlgEquivOfSurjective hg).injective
rw [AlgEquiv.apply_symm_apply, Ideal.quotientKerAlgEquivOfSurjective_apply]
@@ -361,7 +361,7 @@ theorem of_comp_surjective
refine ⟨g, AlgHom.ext fun x ↦ congr(f.kerSquareLift.kerLift ($hg x)).trans ?_⟩
obtain ⟨x, rfl⟩ := (Ideal.quotientKerAlgEquivOfSurjective surj).surjective x
obtain ⟨x, rfl⟩ := Ideal.Quotient.mk_surjective x
- simp only [AlgHom.toRingHom_eq_coe, AlgEquiv.coe_algHom, AlgEquiv.symm_apply_apply,
+ simp only [AlgHom.toRingHom_eq_coe, AlgEquiv.coe_toAlgHom, AlgEquiv.symm_apply_apply,
AlgHom.coe_id, id_eq]
simp only [Ideal.quotientKerAlgEquivOfSurjective_apply]
diff --git a/Mathlib/RingTheory/Smooth/IntegralClosure.lean b/Mathlib/RingTheory/Smooth/IntegralClosure.lean
index a412ca4415d48f..614a4369212324 100644
--- a/Mathlib/RingTheory/Smooth/IntegralClosure.lean
+++ b/Mathlib/RingTheory/Smooth/IntegralClosure.lean
@@ -66,7 +66,7 @@ lemma TensorProduct.toIntegralClosure_bijective_of_tower
(AlgEquiv.ofBijective _ H').trans <|
(AlgEquiv.mapIntegralClosure (Algebra.TensorProduct.cancelBaseChange ..))
convert! e.bijective
- rw [← e.coe_algHom]
+ rw [← e.coe_toAlgHom]
congr 1
ext; simp [e, toIntegralClosure]
@@ -175,7 +175,7 @@ lemma TensorProduct.toIntegralClosure_bijective_of_isLocalization
convert!
(IsLocalization.algEquiv (Algebra.algebraMapSubmonoid (integralClosure R B) M)
(S ⊗[R] integralClosure R B) (integralClosure S (S ⊗[R] B))).bijective
- rw [← AlgHom.coe_restrictScalars' R, ← AlgEquiv.coe_restrictScalars' R, ← AlgEquiv.coe_algHom]
+ rw [← AlgHom.coe_restrictScalars' R, ← AlgEquiv.coe_restrictScalars' R, ← AlgEquiv.coe_toAlgHom]
congr 1
ext1
· apply IsLocalization.algHom_ext M; ext
@@ -354,7 +354,7 @@ theorem mem_adjoin_map_integralClosure_of_isStandardEtale [Algebra.IsStandardEta
AlgEquiv.apply_symm_apply, map_pow, heg]
simp_rw [mul_assoc, ← map_pow, show 𝓟.g.map (algebraMap R B) = 𝓟'.g from rfl,
IsLocalization.mk'_spec'_mk, ← derivative_map]; rfl
- · simp only [← AlgEquiv.coe_algHom, ← AlgHom.coe_toRingHom, ← RingHom.comp_apply,
+ · simp only [← AlgEquiv.coe_toAlgHom, ← AlgHom.coe_toRingHom, ← RingHom.comp_apply,
← coe_eval₂RingHom]
congr 1
ext <;> simp [e, StandardEtalePair.equivAwayAdjoinRoot]; rfl
diff --git a/Mathlib/RingTheory/TensorProduct/Maps.lean b/Mathlib/RingTheory/TensorProduct/Maps.lean
index 36f0c51b51f6f2..0e7c4b8e3b4116 100644
--- a/Mathlib/RingTheory/TensorProduct/Maps.lean
+++ b/Mathlib/RingTheory/TensorProduct/Maps.lean
@@ -583,12 +583,12 @@ theorem congr_symm_apply (f : A ≃ₐ[S] C) (g : B ≃ₐ[R] D) (x) :
@[simp]
theorem congr_refl : congr (.refl : A ≃ₐ[S] A) (.refl : B ≃ₐ[R] B) = .refl :=
- AlgEquiv.coe_algHom_injective <| map_id
+ AlgEquiv.coe_toAlgHom_injective <| map_id
theorem congr_trans
(f₁ : A ≃ₐ[S] C) (f₂ : C ≃ₐ[S] E) (g₁ : B ≃ₐ[R] D) (g₂ : D ≃ₐ[R] F) :
congr (f₁.trans f₂) (g₁.trans g₂) = (congr f₁ g₁).trans (congr f₂ g₂) :=
- AlgEquiv.coe_algHom_injective <| map_comp f₂.toAlgHom f₁.toAlgHom g₂.toAlgHom g₁.toAlgHom
+ AlgEquiv.coe_toAlgHom_injective <| map_comp f₂.toAlgHom f₁.toAlgHom g₂.toAlgHom g₁.toAlgHom
theorem congr_symm (f : A ≃ₐ[S] C) (g : B ≃ₐ[R] D) : congr f.symm g.symm = (congr f g).symm := rfl
@@ -746,7 +746,7 @@ def lmulEquiv [CompatibleSMul R S S S] : S ⊗[R] S ≃ₐ[S] S :=
theorem lmulEquiv_eq_lidOfCompatibleSMul [CompatibleSMul R S S S] :
lmulEquiv R S = lidOfCompatibleSMul R S S :=
- AlgEquiv.coe_algHom_injective <| by ext; rfl
+ AlgEquiv.coe_toAlgHom_injective <| by ext; rfl
/-- If `S` is commutative, for a pair of morphisms `f : A →ₐ[R] S`, `g : B →ₐ[R] S`,
We obtain a map `A ⊗[R] B →ₐ[R] S` that commutes with `f`, `g` via `a ⊗ b ↦ f(a) * g(b)`.
From d8f28212f72cc6595762261e85c0ba70a792c794 Mon Sep 17 00:00:00 2001
From: Chris Henson <46805207+chenson2018@users.noreply.github.com>
Date: Wed, 24 Jun 2026 20:59:37 +0000
Subject: [PATCH 0330/1300] feat: well founded relations on a nonempty type are
not left/right total (#41006)
Discussed in [this thread](https://leanprover.zulipchat.com/#narrow/channel/217875-Is-there-code-for-X.3F/topic/finite.2C.20nonempty.20strict.20order.20is.20not.20left.2Fright.20total/with/606017061). I use `Function.swap` for the dual (as opposed to `flip`) because having this be reducible is nice.
---
Mathlib/Order/WellFounded.lean | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/Mathlib/Order/WellFounded.lean b/Mathlib/Order/WellFounded.lean
index aa94d70693c099..b3bae400954623 100644
--- a/Mathlib/Order/WellFounded.lean
+++ b/Mathlib/Order/WellFounded.lean
@@ -95,6 +95,21 @@ theorem has_min {α} {r : α → α → Prop} (H : WellFounded r) (s : Set α) :
not_imp_not.1 fun hne hx => hne <| ⟨x, hx, fun y hy hyx => hne <| IH y hyx hy⟩)
ha
+theorem not_rightTotal (wf : WellFounded r) [Nonempty α] : ¬ Relator.RightTotal r := by
+ intro h
+ obtain ⟨a, -, ha⟩ := wf.has_min Set.univ Set.univ_nonempty
+ obtain ⟨b, hba⟩ := h a
+ specialize ha b (Set.mem_univ b)
+ contradiction
+
+theorem not_leftTotal (wf : WellFounded (Function.swap r)) [Nonempty α] :
+ ¬ Relator.LeftTotal r := by
+ intro h
+ obtain ⟨a, -, ha⟩ := wf.has_min Set.univ Set.univ_nonempty
+ obtain ⟨b, hab⟩ := h a
+ specialize ha b (Set.mem_univ b)
+ contradiction
+
/-- A minimal element of a nonempty set in a well-founded order.
If you're working with a nonempty linear order, consider defining a
From ee89f2d4639074a91449870b8f7a0d0e401faba8 Mon Sep 17 00:00:00 2001
From: Felix Pernegger <188575194+felixpernegger@users.noreply.github.com>
Date: Wed, 24 Jun 2026 22:06:35 +0000
Subject: [PATCH 0331/1300] chore: remove redundant exceptions of
`linter.style.emptyline` (#41004)
Co-authored-by: Batixx
---
Mathlib/Analysis/Polynomial/MahlerMeasure.lean | 1 -
Mathlib/Geometry/Manifold/Notation.lean | 2 --
2 files changed, 3 deletions(-)
diff --git a/Mathlib/Analysis/Polynomial/MahlerMeasure.lean b/Mathlib/Analysis/Polynomial/MahlerMeasure.lean
index f3abb35d18507e..71d466e6a1089c 100644
--- a/Mathlib/Analysis/Polynomial/MahlerMeasure.lean
+++ b/Mathlib/Analysis/Polynomial/MahlerMeasure.lean
@@ -293,7 +293,6 @@ theorem mahlerMeasure_le_sum_norm_coeff (p : ℂ[X]) : p.mahlerMeasure ≤ p.sum
apply norm_sum_le_of_le p.support
simp
-set_option linter.style.emptyLine false in
open MeasureTheory Set in
/-- **Landau's inequality**: the Mahler measure of a polynomial is at most the ℓ² norm
of its coefficient vector, `√(∑ ‖coeff i‖²)`.
diff --git a/Mathlib/Geometry/Manifold/Notation.lean b/Mathlib/Geometry/Manifold/Notation.lean
index 4165465c5cf227..3c34b70a359791 100644
--- a/Mathlib/Geometry/Manifold/Notation.lean
+++ b/Mathlib/Geometry/Manifold/Notation.lean
@@ -354,7 +354,6 @@ where findFromLocalInstance (e : Expr) : TermElabM <| Option Expr := do
else return none
| _ => pure none
-set_option linter.style.emptyLine false in -- linter false positive
/-- Try to find a `ModelWithCorners` instance on a type (represented by an expression `e`),
using the local context to infer the appropriate instance. This supports the following cases:
- the model with corners on the total space of a vector bundle
@@ -730,7 +729,6 @@ where
let iTerm : Term ← ``(𝓘($eT, $eT))
Term.elabTerm iTerm none
-set_option linter.style.emptyLine false in -- linter false positive
/-- Try to find a `ModelWithCorners` instance on a type (represented by an expression `e`),
using the local context to infer the appropriate instance.
This supports all `ModelWithCorners` instances that are currently defined in mathlib.
From 2da222063cdf16dd6df95e950e5bc8ed7d4be7ae Mon Sep 17 00:00:00 2001
From: "Yi.Yuan"
Date: Thu, 25 Jun 2026 05:38:43 +0000
Subject: [PATCH 0332/1300] chore(AlgebraicGeometry/AffineScheme): golf using
`simp` (#40991)
As recommended in the style guide. Extracted from https://github.com/leanprover-community/mathlib4/pull/40793.
---
Mathlib/AlgebraicGeometry/AffineScheme.lean | 49 +++++----------------
Mathlib/AlgebraicGeometry/Restrict.lean | 6 +++
2 files changed, 17 insertions(+), 38 deletions(-)
diff --git a/Mathlib/AlgebraicGeometry/AffineScheme.lean b/Mathlib/AlgebraicGeometry/AffineScheme.lean
index 9a1138dac3fd1c..fb0f92073d1e68 100644
--- a/Mathlib/AlgebraicGeometry/AffineScheme.lean
+++ b/Mathlib/AlgebraicGeometry/AffineScheme.lean
@@ -964,47 +964,20 @@ set_option backward.isDefEq.respectTransparency false in
open _root_.PrimeSpectrum in
/-- The restriction of `Spec.map f` to a basic open `D(r)` is isomorphic to `Spec.map` of the
localization of `f` away from `r`. -/
-noncomputable
-def SpecMapRestrictBasicOpenIso {R S : CommRingCat} (f : R ⟶ S) (r : R) :
+noncomputable def SpecMapRestrictBasicOpenIso {R S : CommRingCat} (f : R ⟶ S) (r : R) :
Arrow.mk (Spec.map f ∣_ (PrimeSpectrum.basicOpen r)) ≅
Arrow.mk (Spec.map <| CommRingCat.ofHom (Localization.awayMap f.hom r)) := by
- letI e₁ : Localization.Away r ≃ₐ[R] Γ(Spec R, basicOpen r) :=
- IsLocalization.algEquiv (Submonoid.powers r) _ _
- letI e₂ : Localization.Away (f.hom r) ≃ₐ[S] Γ(Spec S, basicOpen (f.hom r)) :=
- IsLocalization.algEquiv (Submonoid.powers (f.hom r)) _ _
refine Arrow.isoMk ?_ ?_ ?_
- · exact (Spec _).isoOfEq (comap_basicOpen _ _) ≪≫
- (IsAffineOpen.Spec_basicOpen (f.hom r)).isoSpec ≪≫ Scheme.Spec.mapIso e₂.toCommRingCatIso.op
- · exact (IsAffineOpen.Spec_basicOpen r).isoSpec ≪≫ Scheme.Spec.mapIso e₁.toCommRingCatIso.op
- · have := AlgebraicGeometry.IsOpenImmersion.of_isLocalization
- (S := (Localization.Away r)) r
- rw [← cancel_mono (Spec.map (CommRingCat.ofHom (algebraMap R (Localization.Away r))))]
- simp only [Arrow.mk_left, Arrow.mk_right, Scheme.isoOfEq_rfl, Iso.refl_trans,
- Iso.trans_hom, Functor.mapIso_hom, Iso.op_hom, Scheme.Spec_map, Quiver.Hom.unop_op,
- Arrow.mk_hom, Category.assoc, ← Spec.map_comp]
- conv =>
- congr
- · enter [2, 1]; tactic =>
- change _ =
- (f ≫ (Scheme.ΓSpecIso S).inv ≫ (Spec S).presheaf.map (homOfLE le_top).op)
- ext
- simp only [Localization.awayMap, IsLocalization.Away.map,
- RingEquiv.toCommRingCatIso_hom, AlgEquiv.toRingEquiv_toRingHom, CommRingCat.hom_comp,
- CommRingCat.hom_ofHom, RingHom.comp_apply, IsLocalization.map_eq, RingHom.coe_coe,
- AlgEquiv.commutes, IsAffineOpen.algebraMap_Spec_obj]
- · enter [2, 2, 1]; tactic =>
- change _ = (Scheme.ΓSpecIso R).inv ≫ (Spec R).presheaf.map (homOfLE le_top).op
- ext
- simp only [RingEquiv.toCommRingCatIso_hom,
- AlgEquiv.toRingEquiv_toRingHom, CommRingCat.hom_comp, CommRingCat.hom_ofHom,
- RingHom.coe_comp, RingHom.coe_coe, Function.comp_apply, AlgEquiv.commutes,
- IsAffineOpen.algebraMap_Spec_obj, homOfLE_leOfHom]
- simp only [IsAffineOpen.isoSpec_hom, homOfLE_leOfHom, Spec.map_comp, Category.assoc,
- Scheme.Opens.toSpecΓ_SpecMap_presheaf_map_assoc, Scheme.Opens.toSpecΓ_top,
- Scheme.homOfLE_ι_assoc, morphismRestrict_ι_assoc]
- simp only [← SpecMap_ΓSpecIso_hom, ← Spec.map_comp, Category.assoc, Iso.inv_hom_id,
- Category.comp_id, Category.id_comp]
- rfl
+ · exact (Spec _).isoOfEq (comap_basicOpen _ _) ≪≫ basicOpenIsoSpecAway (f.hom r)
+ · exact basicOpenIsoSpecAway r
+ · have hcomp : CommRingCat.ofHom (algebraMap R (Localization.Away r)) ≫
+ CommRingCat.ofHom (Localization.awayMap f.hom r) =
+ f ≫ CommRingCat.ofHom (algebraMap S (Localization.Away (f.hom r))) := by
+ ext x
+ simp [Localization.awayMap, IsLocalization.Away.map]
+ rw [← cancel_mono (Spec.map (CommRingCat.ofHom (algebraMap R _)))]
+ simp only [Arrow.mk_hom, Category.assoc, ← Spec.map_comp]
+ simp [hcomp]
lemma stalkMap_injective_of_isAffine {X Y : Scheme} (f : X ⟶ Y) [IsAffine Y] (x : X)
(h : ∀ g, f.stalkMap x (Y.presheaf.Γgerm (f x) g) = 0 → Y.presheaf.Γgerm (f x) g = 0) :
diff --git a/Mathlib/AlgebraicGeometry/Restrict.lean b/Mathlib/AlgebraicGeometry/Restrict.lean
index 2ac6b65fe87143..bb23bc5250d9e7 100644
--- a/Mathlib/AlgebraicGeometry/Restrict.lean
+++ b/Mathlib/AlgebraicGeometry/Restrict.lean
@@ -517,6 +517,12 @@ def basicOpenIsoSpecAway {R : CommRingCat.{u}} (f : R) :
simp only [Scheme.Opens.range_ι]
exact (PrimeSpectrum.localization_away_comap_range _ _).symm)
+@[reassoc (attr := simp)]
+lemma basicOpenIsoSpecAway_hom_SpecMap {R : CommRingCat.{u}} (f : R) :
+ (basicOpenIsoSpecAway f).hom ≫ Spec.map (CommRingCat.ofHom (algebraMap R _)) =
+ Scheme.Opens.ι (X := Spec R) (PrimeSpectrum.basicOpen f) := by
+ simp [basicOpenIsoSpecAway]
+
set_option backward.isDefEq.respectTransparency false in
@[reassoc]
lemma basicOpenIsoSpecAway_inv_homOfLE {R : CommRingCat.{u}} (f g x : R) (hx : x = f * g) :
From f8fd74f95c7a5f33eac4cbd4312a28a7492bda36 Mon Sep 17 00:00:00 2001
From: "Thomas R. Murrills" <68410468+thorimur@users.noreply.github.com>
Date: Thu, 25 Jun 2026 07:54:34 +0000
Subject: [PATCH 0333/1300] chore: remove unused instances (#41013)
Removes unused instances in theorems found by fixing the unused arguments linter to handle theorems in leanprover-community/batteries#1879.
---
Mathlib/Algebra/Module/Submodule/Map.lean | 4 ++--
.../Algebra/Module/Submodule/RestrictScalars.lean | 2 +-
Mathlib/Algebra/Module/Torsion/Basic.lean | 2 +-
Mathlib/Algebra/Order/Ring/IsNonarchimedean.lean | 3 ++-
Mathlib/Algebra/SkewPolynomial/Basic.lean | 6 ++----
Mathlib/AlgebraicGeometry/Morphisms/Finite.lean | 3 +--
Mathlib/Analysis/LocallyConvex/HahnBanach.lean | 4 ++--
Mathlib/Analysis/Normed/Operator/NormedSpace.lean | 2 +-
Mathlib/Analysis/Seminorm.lean | 2 +-
.../LiftingProperties/PushoutProduct.lean | 8 ++++----
Mathlib/FieldTheory/Galois/Basic.lean | 2 +-
Mathlib/FieldTheory/Galois/IsGaloisGroup.lean | 2 +-
Mathlib/Geometry/Manifold/Algebra/SMul.lean | 3 +--
Mathlib/Geometry/Manifold/MFDeriv/Basic.lean | 2 +-
.../LinearAlgebra/AffineSpace/Simplex/Basic.lean | 8 ++++----
.../Constructions/BorelSpace/Metrizable.lean | 2 +-
Mathlib/ModelTheory/Satisfiability.lean | 2 +-
.../NumberField/Discriminant/Different.lean | 2 +-
Mathlib/Probability/HasCondDistrib.lean | 2 +-
Mathlib/Probability/Independence/Basic.lean | 2 +-
Mathlib/Probability/Independence/Integration.lean | 14 +++++++-------
.../Probability/Independence/Kernel/IndepFun.lean | 2 +-
Mathlib/Probability/Martingale/Centering.lean | 7 +++----
Mathlib/RingTheory/Ideal/Operations.lean | 2 +-
Mathlib/RingTheory/IsGaloisGroup/Basic.lean | 2 +-
Mathlib/RingTheory/MvPowerSeries/GaussNorm.lean | 2 +-
Mathlib/RingTheory/RamificationInertia/Basic.lean | 2 +-
Mathlib/RingTheory/TwoSidedIdeal/BigOperators.lean | 6 ++----
Mathlib/Topology/Algebra/Module/Complement.lean | 2 +-
Mathlib/Topology/Algebra/Order/Floor.lean | 2 +-
Mathlib/Topology/Order/Completion.lean | 3 +--
Mathlib/Topology/Order/ScottTopology.lean | 3 +--
.../Topology/UniformSpace/ProdApproximation.lean | 5 ++---
33 files changed, 53 insertions(+), 62 deletions(-)
diff --git a/Mathlib/Algebra/Module/Submodule/Map.lean b/Mathlib/Algebra/Module/Submodule/Map.lean
index c41af3f34468ea..494d9e4cd303f1 100644
--- a/Mathlib/Algebra/Module/Submodule/Map.lean
+++ b/Mathlib/Algebra/Module/Submodule/Map.lean
@@ -697,8 +697,8 @@ theorem map_restrict [RingHomSurjective σ₂₁] {p : Submodule R₂ M₂} {q :
map (f.restrict h) p' = comap q.subtype (map f (map p.subtype p')) := by
rw [restrict_eq_codRestrict_domRestrict, map_codRestrict, map_domRestrict]
-theorem comap_restrict [RingHomSurjective σ₂₁] {p : Submodule R₂ M₂} {q : Submodule R M}
- {f : M₂ →ₛₗ[σ₂₁] M} (h : ∀ x ∈ p, f x ∈ q) (p') :
+theorem comap_restrict {p : Submodule R₂ M₂} {q : Submodule R M} {f : M₂ →ₛₗ[σ₂₁] M}
+ (h : ∀ x ∈ p, f x ∈ q) (p') :
comap (f.restrict h) p' = comap p.subtype (comap f (map q.subtype p')) := by
rw [restrict_eq_codRestrict_domRestrict, comap_codRestrict, comap_domRestrict]
diff --git a/Mathlib/Algebra/Module/Submodule/RestrictScalars.lean b/Mathlib/Algebra/Module/Submodule/RestrictScalars.lean
index 26aca7cdc39ede..a1d249200d4eee 100644
--- a/Mathlib/Algebra/Module/Submodule/RestrictScalars.lean
+++ b/Mathlib/Algebra/Module/Submodule/RestrictScalars.lean
@@ -56,7 +56,7 @@ theorem restrictScalars_self (V : Submodule R M) : V.restrictScalars R = V :=
SetLike.coe_injective rfl
@[simp] theorem restrictScalars_restrictScalars
- (T : Type*) [Semiring T] [SMul T R] [SMul S T] [IsScalarTower S T R]
+ (T : Type*) [Semiring T] [SMul T R] [SMul S T]
[Module T M] [IsScalarTower S T M] [IsScalarTower T R M]
(V : Submodule R M) :
(V.restrictScalars T).restrictScalars S = V.restrictScalars S :=
diff --git a/Mathlib/Algebra/Module/Torsion/Basic.lean b/Mathlib/Algebra/Module/Torsion/Basic.lean
index 73f3ab0e224fe2..6719c23989257c 100644
--- a/Mathlib/Algebra/Module/Torsion/Basic.lean
+++ b/Mathlib/Algebra/Module/Torsion/Basic.lean
@@ -577,7 +577,7 @@ theorem IsTorsionBy.mk_smul [(Ideal.span {r}).IsTwoSided] (hM : IsTorsionBy R M
def IsTorsionBySet.module [I.IsTwoSided] (hM : IsTorsionBySet R M I) : Module (R ⧸ I) M :=
letI := hM.hasSMul; fast_instance% I.mkQ_surjective.moduleLeft _ (IsTorsionBySet.mk_smul hM)
-instance IsTorsionBySet.isScalarTower [I.IsTwoSided] (hM : IsTorsionBySet R M I)
+instance IsTorsionBySet.isScalarTower (hM : IsTorsionBySet R M I)
{S : Type*} [SMul S R] [SMul S M] [IsScalarTower S R M] [IsScalarTower S R R] :
@IsScalarTower S (R ⧸ I) M _ hM.hasSMul _ :=
-- Porting note: still needed to be fed the Module R / I M instance
diff --git a/Mathlib/Algebra/Order/Ring/IsNonarchimedean.lean b/Mathlib/Algebra/Order/Ring/IsNonarchimedean.lean
index 92d41588b31803..6de7d61e93f446 100644
--- a/Mathlib/Algebra/Order/Ring/IsNonarchimedean.lean
+++ b/Mathlib/Algebra/Order/Ring/IsNonarchimedean.lean
@@ -118,7 +118,7 @@ theorem add_eq_max_of_ne {F α : Type*} [AddGroup α] [FunLike F α R]
/- TODO: Remove the funlike conditions on the lemmas required for add_max_of_ne, this will allow us
to remove the CommGroup part in the below which is unnecessary. -/
-lemma add_eq_max_of_ne' {α S : Type*} [Semiring S] [LinearOrder S] [AddCommGroup α]
+lemma add_eq_max_of_ne' {α S : Type*} [LinearOrder S] [AddCommGroup α]
(f : α → S) (fna : IsNonarchimedean f) (Neg : ∀ a, f a = f (-a)) {a b : α}
(hne : f a ≠ f b) : f (a + b) = max (f a) (f b) := by
wlog hab : f a > f b generalizing a b with H
@@ -223,6 +223,7 @@ theorem finset_powerset_image_add [IsStrictOrderedRing R]
g (powersetCard (s.card - m) s)
exact ⟨⟨b, hb_in (powersetCard_nonempty.mpr (Nat.sub_le s.card m))⟩, hb⟩
+omit [Semiring R] in
lemma apply_sum_eq_of_lt {α β : Type*} [AddCommGroup α] {f : α → R} (fna : IsNonarchimedean f)
(f_neg : ∀ a, f a = f (-a)) {s : Finset β} {l : β → α} {k : β} (hk : k ∈ s)
(hmax : ∀ j ∈ s, j ≠ k → f (l j) < f (l k)) : f (∑ i ∈ s, l i) = f (l k) := by
diff --git a/Mathlib/Algebra/SkewPolynomial/Basic.lean b/Mathlib/Algebra/SkewPolynomial/Basic.lean
index 700145186964c9..0e1369bec1cfe0 100644
--- a/Mathlib/Algebra/SkewPolynomial/Basic.lean
+++ b/Mathlib/Algebra/SkewPolynomial/Basic.lean
@@ -173,8 +173,7 @@ def monomial : R →ₗ[R] SkewPolynomial R := lsingle R (ofAdd n)
lemma monomial_zero_right : monomial n (0 : R) = 0 := single_zero _
-lemma monomial_zero_one [MulSemiringAction (Multiplicative ℕ) R] : monomial 0 (1 : R) = 1 :=
- rfl
+lemma monomial_zero_one : monomial 0 (1 : R) = 1 := rfl
lemma monomial_def (a : R) : monomial n a = single (ofAdd n) a := rfl
@@ -348,8 +347,7 @@ lemma coeff_monomial : coeff (monomial n a) m = if n = m then a else 0 :=
@[simp] lemma coeff_zero (n : ℕ) : coeff (0 : SkewPolynomial R) n = 0 := rfl
-@[simp] lemma coeff_one_zero [MulSemiringAction (Multiplicative ℕ) R] :
- coeff (1 : SkewPolynomial R) 0 = 1 := coeff_monomial
+@[simp] lemma coeff_one_zero : coeff (1 : SkewPolynomial R) 0 = 1 := coeff_monomial
lemma coeff_one [MulSemiringAction (Multiplicative ℕ) R] (n : ℕ) :
coeff (1 : SkewPolynomial R) n = if 0 = n then 1 else 0 := by
diff --git a/Mathlib/AlgebraicGeometry/Morphisms/Finite.lean b/Mathlib/AlgebraicGeometry/Morphisms/Finite.lean
index 86a83253b590f5..e57a1ed93c8f18 100644
--- a/Mathlib/AlgebraicGeometry/Morphisms/Finite.lean
+++ b/Mathlib/AlgebraicGeometry/Morphisms/Finite.lean
@@ -154,8 +154,7 @@ 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] :
+lemma Scheme.Hom.finite_appTop {X Y : Scheme.{u}} (f : X ⟶ Y) [IsAffine Y] [IsFinite f] :
f.appTop.hom.Finite :=
(HasAffineProperty.iff_of_isAffine (P := @IsFinite).mp inferInstance).2
diff --git a/Mathlib/Analysis/LocallyConvex/HahnBanach.lean b/Mathlib/Analysis/LocallyConvex/HahnBanach.lean
index 22754bba9a8f0f..1082d18d304e88 100644
--- a/Mathlib/Analysis/LocallyConvex/HahnBanach.lean
+++ b/Mathlib/Analysis/LocallyConvex/HahnBanach.lean
@@ -68,8 +68,8 @@ variable [TopologicalSpace E]
/-- **Hahn-Banach theorem** for linear functionals dominated by a continuous seminorm on
polynormable spaces over `ℝ`. -/
-theorem Module.Dual.exists_continuous_extension_of_le_seminorm_real [IsTopologicalAddGroup E]
- [Module ℝ E] [ContinuousSMul ℝ E] [PolynormableSpace ℝ E] (S : Subspace ℝ E) (f : Dual ℝ S)
+theorem Module.Dual.exists_continuous_extension_of_le_seminorm_real
+ [Module ℝ E] [PolynormableSpace ℝ E] (S : Subspace ℝ E) (f : Dual ℝ S)
{p : Seminorm ℝ E} (hp_cont : Continuous p) (hp : ∀ x, f x ≤ p x) :
∃ g : StrongDual ℝ E, (∀ x : S, g x = f x) ∧ ∀ x, |g x| ≤ p x := by
obtain ⟨g, hg, hl⟩ := f.exists_extension_of_le_seminorm_real S hp
diff --git a/Mathlib/Analysis/Normed/Operator/NormedSpace.lean b/Mathlib/Analysis/Normed/Operator/NormedSpace.lean
index 360f0ccdb8be29..74ba95d31cf2bd 100644
--- a/Mathlib/Analysis/Normed/Operator/NormedSpace.lean
+++ b/Mathlib/Analysis/Normed/Operator/NormedSpace.lean
@@ -438,7 +438,7 @@ lemma ContinuousLinearMap.norm_inl [SeminormedAddCommGroup E] [NontrivialTopolog
‖ContinuousLinearMap.inl 𝕜 E F‖ = 1 :=
(LinearIsometry.inl 𝕜 E F).norm_toContinuousLinearMap
-lemma ContinuousLinearMap.norm_inr [SeminormedAddCommGroup E] [NontrivialTopology E]
+lemma ContinuousLinearMap.norm_inr [SeminormedAddCommGroup E]
[NormedSpace 𝕜 E] [SeminormedAddCommGroup F] [NormedSpace 𝕜 F] [NontrivialTopology F] :
‖ContinuousLinearMap.inr 𝕜 E F‖ = 1 :=
(LinearIsometry.inr 𝕜 E F).norm_toContinuousLinearMap
diff --git a/Mathlib/Analysis/Seminorm.lean b/Mathlib/Analysis/Seminorm.lean
index 5cb671c016ff74..7e79c81ce21402 100644
--- a/Mathlib/Analysis/Seminorm.lean
+++ b/Mathlib/Analysis/Seminorm.lean
@@ -1175,7 +1175,7 @@ theorem continuous_of_le [TopologicalSpace E] [IsTopologicalAddGroup E]
exact isOpen_lt hq continuous_const
/-- The sum over a finite set of continuous seminorms is continuous. -/
-theorem continuous_finsetSum [TopologicalSpace E] [IsTopologicalAddGroup E]
+theorem continuous_finsetSum [TopologicalSpace E]
{p : ι → Seminorm 𝕝 E} {s : Finset ι} (hp : ∀ i ∈ s, Continuous (p i)) :
Continuous ((∑ i ∈ s, p i : Seminorm 𝕝 E) : E → ℝ) := by
change Continuous (fun x ↦ coeFnAddMonoidHom _ _ (∑ i ∈ s, p i) x)
diff --git a/Mathlib/CategoryTheory/LiftingProperties/PushoutProduct.lean b/Mathlib/CategoryTheory/LiftingProperties/PushoutProduct.lean
index 6be256b72cd9bc..6f6fecc53e31cd 100644
--- a/Mathlib/CategoryTheory/LiftingProperties/PushoutProduct.lean
+++ b/Mathlib/CategoryTheory/LiftingProperties/PushoutProduct.lean
@@ -68,7 +68,7 @@ lemma hasLiftingProperty_mk_iff' [HasPushouts C] [HasPullbacks C]
set_option backward.defeqAttrib.useBackward true in
/-- `(∅ ⟶ B) □ g` lifts against `X ⟶ Y` if and only if `g` lifts against `B ⟹ X ⟶ B ⟹ Y`. -/
-lemma hasLiftingProperty_mk_isInitial_iff [HasPushouts C] [HasPullbacks C]
+lemma hasLiftingProperty_mk_isInitial_iff [HasPushouts C]
[CartesianMonoidalCategory C] [MonoidalClosed C] [BraidedCategory C]
{A B K L X Y : C} {g : K ⟶ L} {h : X ⟶ Y}
(i : IsInitial A) :
@@ -80,7 +80,7 @@ lemma hasLiftingProperty_mk_isInitial_iff [HasPushouts C] [HasPullbacks C]
exact Adjunction.hasLiftingProperty_iff (ihom.adjunction B) g h
/-- `f □ (∅ ⟶ L)` lifts against `X ⟶ Y` if and only if `f` lifts against `L ⟹ X ⟶ L ⟹ Y`. -/
-lemma hasLiftingProperty_mk_isInitial_iff' [HasPushouts C] [HasPullbacks C]
+lemma hasLiftingProperty_mk_isInitial_iff' [HasPushouts C]
[CartesianMonoidalCategory C] [MonoidalClosed C] [BraidedCategory C]
{A B K L X Y : C} {f : A ⟶ B} {h : X ⟶ Y}
(i : IsInitial K) :
@@ -100,7 +100,7 @@ lemma hasLiftingProperty_mk_isTerminal_iff [HasPushouts C] [HasPullbacks C]
exact HasLiftingProperty.iff_of_arrow_iso_right g (PullbackHom.isTerminalIso _ t)
/-- `(∅ ⟶ B) □ g` lifts against `X ⟶ ⋆` if and only if `g` lifts against `(B ⟹ X) ⟶ ⋆`. -/
-lemma hasLiftingProperty_mk_isInitial_isTerminal_iff [HasPushouts C] [HasPullbacks C]
+lemma hasLiftingProperty_mk_isInitial_isTerminal_iff [HasPushouts C]
[CartesianMonoidalCategory C] [MonoidalClosed C] [BraidedCategory C]
{A B K L X Y : C} {g : K ⟶ L}
(i : IsInitial A) (t : IsTerminal Y) :
@@ -112,7 +112,7 @@ lemma hasLiftingProperty_mk_isInitial_isTerminal_iff [HasPushouts C] [HasPullbac
(t.hom_ext _ _))
/-- `f □ (∅ ⟶ L)` lifts against `X ⟶ ⋆` if and only if `f` lifts against `(L ⟹ X) ⟶ ⋆`. -/
-lemma hasLiftingProperty_mk_isInitial_isTerminal_iff' [HasPushouts C] [HasPullbacks C]
+lemma hasLiftingProperty_mk_isInitial_isTerminal_iff' [HasPushouts C]
[CartesianMonoidalCategory C] [MonoidalClosed C] [BraidedCategory C]
{A B K L X Y : C} {f : A ⟶ B}
(i : IsInitial K) (t : IsTerminal Y) :
diff --git a/Mathlib/FieldTheory/Galois/Basic.lean b/Mathlib/FieldTheory/Galois/Basic.lean
index 494394ceaf6ee4..eb5609d304f505 100644
--- a/Mathlib/FieldTheory/Galois/Basic.lean
+++ b/Mathlib/FieldTheory/Galois/Basic.lean
@@ -181,7 +181,7 @@ instance isGalois_bot : IsGalois F (⊥ : IntermediateField F E) :=
(IntermediateField.botEquiv F E).transfer_galois.mpr (IsGalois.self F)
theorem IsGalois.of_equiv_equiv {M N : Type*} [Field N] [Field M] [Algebra M N]
- [Algebra.IsAlgebraic F E] [h : IsGalois F E] {f : F ≃+* M} {g : E ≃+* N}
+ [h : IsGalois F E] {f : F ≃+* M} {g : E ≃+* N}
(hcomp : (algebraMap M N).comp f = (g : E →+* N).comp (algebraMap F E)) :
IsGalois M N :=
isGalois_iff.mpr ⟨Algebra.IsSeparable.of_equiv_equiv f g hcomp, Normal.of_equiv_equiv hcomp⟩
diff --git a/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean b/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean
index 431a89fbdd99a2..dd462f855e2f75 100644
--- a/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean
+++ b/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean
@@ -317,7 +317,7 @@ theorem fixedPoints_fixingSubgroup [Finite G] :
/-- If `G` acts as a Galois group on `L/K` and the subgroup `H` acts as a Galois group on `L/B`,
then the fixed points of `H` equals the range of `algebraMap B L`. -/
-theorem fixedPoints_eq_range_algebraMap [Finite G] (B : Type*)
+theorem fixedPoints_eq_range_algebraMap (B : Type*)
[CommSemiring B] [Algebra B L] [IsGaloisGroup H B L] :
(FixedPoints.intermediateField H : IntermediateField K L) = Set.range (algebraMap B L) := by
ext
diff --git a/Mathlib/Geometry/Manifold/Algebra/SMul.lean b/Mathlib/Geometry/Manifold/Algebra/SMul.lean
index b9680bc87af096..0f2d1913bd6bb9 100644
--- a/Mathlib/Geometry/Manifold/Algebra/SMul.lean
+++ b/Mathlib/Geometry/Manifold/Algebra/SMul.lean
@@ -173,8 +173,7 @@ instance {n : ℕ∞ω} : ContMDiffSMul 𝓘(𝕜) 𝓘(𝕜, E) n 𝕜 E where
exact contDiff_smul.contMDiff.comp h
/-- The monoid `E →L[𝕜] E` of continuous linear endomorphisms of `E` acts smoothly on `E`. -/
-instance [CompleteSpace E] {n : ℕ∞ω} :
- ContMDiffSMul 𝓘(𝕜, E →L[𝕜] E) 𝓘(𝕜, E) n (E →L[𝕜] E) E where
+instance {n : ℕ∞ω} : ContMDiffSMul 𝓘(𝕜, E →L[𝕜] E) 𝓘(𝕜, E) n (E →L[𝕜] E) E where
contMDiff_smul := by
have h : ContMDiff (𝓘(𝕜, E →L[𝕜] E).prod 𝓘(𝕜, E)) 𝓘(𝕜, (E →L[𝕜] E) × E) n
(@id ((E →L[𝕜] E) × E)) := by
diff --git a/Mathlib/Geometry/Manifold/MFDeriv/Basic.lean b/Mathlib/Geometry/Manifold/MFDeriv/Basic.lean
index c1c0e5ecbf1239..04fc8658e10cff 100644
--- a/Mathlib/Geometry/Manifold/MFDeriv/Basic.lean
+++ b/Mathlib/Geometry/Manifold/MFDeriv/Basic.lean
@@ -242,7 +242,7 @@ variable {e : OpenPartialHomeomorph M H} {e' : OpenPartialHomeomorph M' H'}
open IsManifold
theorem mdifferentiableWithinAt_iff_source_of_mem_maximalAtlas
- [IsManifold I 1 M] (he : e ∈ maximalAtlas I 1 M) (hx : x ∈ e.source) :
+ (he : e ∈ maximalAtlas I 1 M) (hx : x ∈ e.source) :
MDifferentiableWithinAt I I' f s x ↔
MDifferentiableWithinAt 𝓘(𝕜, E) I' (f ∘ (e.extend I).symm) ((e.extend I).symm ⁻¹' s ∩ range I)
(e.extend I x) := by
diff --git a/Mathlib/LinearAlgebra/AffineSpace/Simplex/Basic.lean b/Mathlib/LinearAlgebra/AffineSpace/Simplex/Basic.lean
index 4e8ea4567aa25a..8ec160506c7636 100644
--- a/Mathlib/LinearAlgebra/AffineSpace/Simplex/Basic.lean
+++ b/Mathlib/LinearAlgebra/AffineSpace/Simplex/Basic.lean
@@ -643,7 +643,7 @@ theorem closedInterior_face_ssubset_closedInterior [Nontrivial k] [ZeroLEOneClas
apply (Set.ssubset_iff_of_subset (s.closedInterior_face_subset_closedInterior h)).mpr
exact ⟨s.points a, s.point_mem_closedInterior a, fun hs ↦ ha (by simpa using hs)⟩
-theorem disjoint_interior_closedInterior_face [Nontrivial k] [ZeroLEOneClass k] {n : ℕ}
+theorem disjoint_interior_closedInterior_face {n : ℕ}
(s : Simplex k P n) {fs : Finset (Fin (n + 1))} (hfs : fs ≠ .univ) {m : ℕ} (h : #fs = m + 1) :
Disjoint s.interior (s.face h).closedInterior := by
refine Set.disjoint_left.mpr fun p hleft hright ↦ ?_
@@ -669,8 +669,8 @@ theorem closedInterior_faceOpposite_ssubset_closedInterior [Nontrivial k] [ZeroL
(s.faceOpposite i).closedInterior ⊂ s.closedInterior :=
s.closedInterior_face_ssubset_closedInterior (by simp) _
-theorem disjoint_interior_closedInterior_faceOpposite [Nontrivial k] [ZeroLEOneClass k] {n : ℕ}
- [NeZero n] (s : Simplex k P n) (i : Fin (n + 1)) :
+theorem disjoint_interior_closedInterior_faceOpposite {n : ℕ} [NeZero n]
+ (s : Simplex k P n) (i : Fin (n + 1)) :
Disjoint s.interior (s.faceOpposite i).closedInterior :=
s.disjoint_interior_closedInterior_face (by simp) _
@@ -705,7 +705,7 @@ theorem closedInterior_eq_interior_union [IsOrderedAddMonoid k] [ZeroLEOneClass
· refine Set.union_subset s.interior_subset_closedInterior (Set.iUnion_subset fun i ↦ ?_)
exact s.closedInterior_faceOpposite_subset_closedInterior i
-theorem closedInterior_sdiff_interior [Nontrivial k] [IsOrderedAddMonoid k] [ZeroLEOneClass k]
+theorem closedInterior_sdiff_interior [IsOrderedAddMonoid k] [ZeroLEOneClass k]
{n : ℕ} [NeZero n] (s : Simplex k P n) :
s.closedInterior \ s.interior = ⋃ i : Fin (n + 1), (s.faceOpposite i).closedInterior := by
simpa [closedInterior_eq_interior_union] using
diff --git a/Mathlib/MeasureTheory/Constructions/BorelSpace/Metrizable.lean b/Mathlib/MeasureTheory/Constructions/BorelSpace/Metrizable.lean
index b96f72d46a923a..69fa66fdf6c2eb 100644
--- a/Mathlib/MeasureTheory/Constructions/BorelSpace/Metrizable.lean
+++ b/Mathlib/MeasureTheory/Constructions/BorelSpace/Metrizable.lean
@@ -103,7 +103,7 @@ theorem measurable_of_tendsto_metrizable_ae {μ : Measure α} [μ.IsComplete] {f
aemeasurable_iff_measurable.mp
(aemeasurable_of_tendsto_metrizable_ae' (fun i => (hf i).aemeasurable) h_ae_tendsto)
-theorem measurable_limit_of_tendsto_metrizable_ae {ι} [Countable ι] [Nonempty ι] {μ : Measure α}
+theorem measurable_limit_of_tendsto_metrizable_ae {ι} [Nonempty ι] {μ : Measure α}
{f : ι → α → β} {L : Filter ι} [L.IsCountablyGenerated] (hf : ∀ n, AEMeasurable (f n) μ)
(h_ae_tendsto : ∀ᵐ x ∂μ, ∃ l : β, Tendsto (fun n => f n x) L (𝓝 l)) :
∃ f_lim : α → β, Measurable f_lim ∧ ∀ᵐ x ∂μ, Tendsto (fun n => f n x) L (𝓝 (f_lim x)) := by
diff --git a/Mathlib/ModelTheory/Satisfiability.lean b/Mathlib/ModelTheory/Satisfiability.lean
index ff95887b9bc350..c36c9f809fde4c 100644
--- a/Mathlib/ModelTheory/Satisfiability.lean
+++ b/Mathlib/ModelTheory/Satisfiability.lean
@@ -196,7 +196,7 @@ variable (L)
into `M`, but is not by type a substructure of `M`, and thus can be chosen to belong to the universe
of the cardinal `κ`.
-/
-theorem exists_elementaryEmbedding_card_eq_of_le (M : Type w') [L.Structure M] [Nonempty M]
+theorem exists_elementaryEmbedding_card_eq_of_le (M : Type w') [L.Structure M]
(κ : Cardinal.{w}) (h1 : ℵ₀ ≤ κ) (h2 : lift.{w} L.card ≤ Cardinal.lift.{max u v} κ)
(h3 : lift.{w'} κ ≤ Cardinal.lift.{w} #M) :
∃ N : Bundled L.Structure, Nonempty (N ↪ₑ[L] M) ∧ #N = κ := by
diff --git a/Mathlib/NumberTheory/NumberField/Discriminant/Different.lean b/Mathlib/NumberTheory/NumberField/Discriminant/Different.lean
index ea87dce51ac262..ae67c5f9b4a1d0 100644
--- a/Mathlib/NumberTheory/NumberField/Discriminant/Different.lean
+++ b/Mathlib/NumberTheory/NumberField/Discriminant/Different.lean
@@ -87,7 +87,7 @@ lemma discr_mem_differentIdeal : ↑(discr K) ∈ differentIdeal ℤ 𝒪 := by
attribute [local instance] FractionRing.liftAlgebra in
theorem natAbs_discr_eq_absNorm_differentIdeal_mul_natAbs_discr_pow (L 𝒪' : Type*) [Field L]
- [NumberField L] [CommRing 𝒪'] [Algebra 𝒪' L] [IsFractionRing 𝒪' L] [IsIntegralClosure 𝒪' ℤ L]
+ [NumberField L] [CommRing 𝒪'] [Algebra 𝒪' L] [IsFractionRing 𝒪' L]
[IsDedekindDomain 𝒪'] [CharZero 𝒪'] [Algebra K L] [Algebra 𝒪 𝒪'] [Algebra 𝒪 L]
[IsScalarTower 𝒪 K L] [IsScalarTower 𝒪 𝒪' L] [IsTorsionFree 𝒪 𝒪'] [Free ℤ 𝒪']
[Module.Finite ℤ 𝒪'] [Module.Finite 𝒪 𝒪'] :
diff --git a/Mathlib/Probability/HasCondDistrib.lean b/Mathlib/Probability/HasCondDistrib.lean
index b70e76eb7ace7b..68b6d95b1793bb 100644
--- a/Mathlib/Probability/HasCondDistrib.lean
+++ b/Mathlib/Probability/HasCondDistrib.lean
@@ -49,7 +49,7 @@ lemma HasCondDistrib.aemeasurable_fst (h : HasCondDistrib Y X κ P) :
lemma HasCondDistrib.aemeasurable_snd (h : HasCondDistrib Y X κ P) :
AEMeasurable Y P := h.aemeasurable.snd
-lemma HasLaw.prodMk_of_hasCondDistrib {Q : Measure 𝓧} [IsSFiniteKernel κ]
+lemma HasLaw.prodMk_of_hasCondDistrib {Q : Measure 𝓧}
(h1 : HasLaw X Q P) (h2 : HasCondDistrib Y X κ P) :
HasLaw (fun ω ↦ (X ω, Y ω)) (Q ⊗ₘ κ) P := by rwa [← h1.map_eq]
diff --git a/Mathlib/Probability/Independence/Basic.lean b/Mathlib/Probability/Independence/Basic.lean
index 680f9930fa7ed5..41ce7f824cf9d6 100644
--- a/Mathlib/Probability/Independence/Basic.lean
+++ b/Mathlib/Probability/Independence/Basic.lean
@@ -1036,7 +1036,7 @@ theorem iIndepSet.iIndepFun_indicator [Zero β] [One β] {m : MeasurableSpace β
Kernel.iIndepSet.iIndepFun_indicator hs
lemma Indep.indicator_indepFun {m : MeasurableSpace Ω} {M 𝓧 : Type*}
- [Zero M] [MeasurableSpace M] (c : M) [NeZero c] {m𝓧 : MeasurableSpace 𝓧} {A : Set Ω}
+ [Zero M] [MeasurableSpace M] (c : M) {m𝓧 : MeasurableSpace 𝓧} {A : Set Ω}
{X : Ω → 𝓧} (hA : MeasurableSet[m] A) (h : Indep m (m𝓧.comap X) μ) :
(A.indicator (fun _ ↦ c)) ⟂ᵢ[μ] X :=
Kernel.Indep.indicator_const_indepFun c hA h
diff --git a/Mathlib/Probability/Independence/Integration.lean b/Mathlib/Probability/Independence/Integration.lean
index b3e84ccd379466..dde2dfa2836440 100644
--- a/Mathlib/Probability/Independence/Integration.lean
+++ b/Mathlib/Probability/Independence/Integration.lean
@@ -312,11 +312,11 @@ theorem IndepFun.integral_bilin_comp_comp'
/-- If `X` and `Y` are independent and integrable random variables and `B`
is a continuous bilinear map, then `∫ ω, B (X ω) (Y ω) ∂μ = B μ[X] μ[Y].` -/
theorem IndepFun.integral_bilin
- [NormedAddCommGroup E] [NormedSpace ℝ E] [NormedSpace 𝕜 E] [CompleteSpace E]
+ [NormedAddCommGroup E] [NormedSpace ℝ E] [CompleteSpace E]
[MeasurableSpace E] [BorelSpace E]
- [NormedAddCommGroup F] [NormedSpace ℝ F] [NormedSpace 𝕜 F] [CompleteSpace F]
+ [NormedAddCommGroup F] [NormedSpace ℝ F] [CompleteSpace F]
[MeasurableSpace F] [BorelSpace F]
- [NormedAddCommGroup G] [NormedSpace ℝ G] [NormedSpace 𝕜 G] [CompleteSpace G]
+ [NormedAddCommGroup G] [NormedSpace ℝ G] [CompleteSpace G]
{X : Ω → E} {Y : Ω → F} (hXY : X ⟂ᵢ[μ] Y) (hX : Integrable X μ) (hY : Integrable Y μ)
(B : E →L[ℝ] F →L[ℝ] G) :
∫ ω, B (X ω) (Y ω) ∂μ = B μ[X] μ[Y] :=
@@ -333,11 +333,11 @@ The assumption on `B` allows to drop the integrability condition in
`IndepFun.integral_bilin'`, which is useful for the versions where `B` is the scalar
multiplication or the multiplication. -/
theorem IndepFun.integral_bilin'
- [NormedAddCommGroup E] [NormedSpace ℝ E] [NormedSpace 𝕜 E] [CompleteSpace E]
+ [NormedAddCommGroup E] [NormedSpace ℝ E] [CompleteSpace E]
[MeasurableSpace E] [BorelSpace E]
- [NormedAddCommGroup F] [NormedSpace ℝ F] [NormedSpace 𝕜 F] [CompleteSpace F]
+ [NormedAddCommGroup F] [NormedSpace ℝ F] [CompleteSpace F]
[MeasurableSpace F] [BorelSpace F]
- [NormedAddCommGroup G] [NormedSpace ℝ G] [NormedSpace 𝕜 G] [CompleteSpace G]
+ [NormedAddCommGroup G] [NormedSpace ℝ G] [CompleteSpace G]
{X : Ω → E} {Y : Ω → F} (hXY : X ⟂ᵢ[μ] Y) (hX : AEStronglyMeasurable X μ)
(hY : AEStronglyMeasurable Y μ)
(B : E →L[ℝ] F →L[ℝ] G) (c : ℝ≥0) (hc : c ≠ 0) (hB : ∀ x y, c * ‖x‖ * ‖y‖ ≤ ‖B x y‖) :
@@ -405,7 +405,7 @@ lemma IndepFun.integral_smul_eq_smul_integral
(hX : AEStronglyMeasurable X μ) (hY : AEStronglyMeasurable Y μ) :
μ[X • Y] = μ[X] • μ[Y] := by
by_cases hE : CompleteSpace E
- · exact hXY.integral_bilin' (𝕜 := 𝕜) hX hY (.lsmul ℝ 𝕜) 1 (by simp) (by simp [norm_smul])
+ · exact hXY.integral_bilin' hX hY (.lsmul ℝ 𝕜) 1 (by simp) (by simp [norm_smul])
· simp [integral, hE]
lemma IndepFun.integral_mul_eq_mul_integral
diff --git a/Mathlib/Probability/Independence/Kernel/IndepFun.lean b/Mathlib/Probability/Independence/Kernel/IndepFun.lean
index bd59b14882e064..56935576fa75f9 100644
--- a/Mathlib/Probability/Independence/Kernel/IndepFun.lean
+++ b/Mathlib/Probability/Independence/Kernel/IndepFun.lean
@@ -695,7 +695,7 @@ theorem iIndepSet.iIndepFun_indicator [Zero β] [One β] {m : MeasurableSpace β
· exact @MeasurableSet.empty _ (generateFrom {s i})
lemma Indep.indicator_const_indepFun {m : MeasurableSpace Ω} {M 𝓧 : Type*}
- [Zero M] [MeasurableSpace M] (c : M) [NeZero c] {m𝓧 : MeasurableSpace 𝓧} {A : Set Ω}
+ [Zero M] [MeasurableSpace M] (c : M) {m𝓧 : MeasurableSpace 𝓧} {A : Set Ω}
{X : Ω → 𝓧} (hA : MeasurableSet[m] A) (h : Indep m (m𝓧.comap X) κ μ) :
IndepFun (A.indicator (fun _ ↦ c)) X κ μ :=
indep_of_indep_of_le_left h (measurable_const.indicator hA).comap_le
diff --git a/Mathlib/Probability/Martingale/Centering.lean b/Mathlib/Probability/Martingale/Centering.lean
index 4fdfd483e7f7a0..0581c5562ca25a 100644
--- a/Mathlib/Probability/Martingale/Centering.lean
+++ b/Mathlib/Probability/Martingale/Centering.lean
@@ -99,8 +99,8 @@ lemma Submartingale.predictablePart_nonneg
filter_upwards [hf.monotone_predictablePart] with ω hω n
simpa [predictablePart_zero] using hω (Nat.zero_le n)
-lemma IsStronglyPredictable.predictablePart_eq [SecondCountableTopology E] [MeasurableSpace E]
- [BorelSpace E] [SigmaFiniteFiltration μ ℱ] (hf : IsStronglyPredictable ℱ f)
+lemma IsStronglyPredictable.predictablePart_eq
+ [SigmaFiniteFiltration μ ℱ] (hf : IsStronglyPredictable ℱ f)
(hfint : ∀ n, Integrable (f n) μ) (n : ℕ) :
predictablePart f ℱ μ n =ᵐ[μ] f n - f 0 := by
simp only [predictablePart, ← Finset.sum_range_sub]
@@ -148,8 +148,7 @@ lemma Martingale.martingalePart_eq [CompleteSpace E] (hf : Martingale f ℱ μ)
filter_upwards [hf.predictablePart_eq_zero n] with ω hω
simp [martingalePart, hω]
-lemma IsPredictable.martingalePart_eq [SecondCountableTopology E] [MeasurableSpace E]
- [BorelSpace E] [SigmaFiniteFiltration μ ℱ] (hf : IsStronglyPredictable ℱ f)
+lemma IsPredictable.martingalePart_eq [SigmaFiniteFiltration μ ℱ] (hf : IsStronglyPredictable ℱ f)
(hfint : ∀ n, Integrable (f n) μ) (n : ℕ) :
martingalePart f ℱ μ n =ᵐ[μ] f 0 := by
filter_upwards [hf.predictablePart_eq (μ := μ) hfint n] with ω hω
diff --git a/Mathlib/RingTheory/Ideal/Operations.lean b/Mathlib/RingTheory/Ideal/Operations.lean
index 58c363276417d0..7017583d9e6b72 100644
--- a/Mathlib/RingTheory/Ideal/Operations.lean
+++ b/Mathlib/RingTheory/Ideal/Operations.lean
@@ -438,7 +438,7 @@ theorem span_singleton_mul_left_inj [IsDomain R] [I.IsTwoSided] [J.IsTwoSided]
theorem mul_le_inf [I.IsTwoSided] : I * J ≤ I ⊓ J :=
mul_le.2 fun r hri s hsj => ⟨I.mul_mem_right s hri, J.mul_mem_left r hsj⟩
-lemma inf_ne_bot_of_ne_bot [NoZeroDivisors R] {I J : Ideal R} [I.IsTwoSided] [J.IsTwoSided]
+lemma inf_ne_bot_of_ne_bot [NoZeroDivisors R] {I J : Ideal R} [I.IsTwoSided]
(hI : I ≠ ⊥) (hJ : J ≠ ⊥) :
I ⊓ J ≠ ⊥ := by
grw [← bot_lt_iff_ne_bot, ← mul_le_inf, bot_lt_iff_ne_bot, Ne, mul_eq_bot]
diff --git a/Mathlib/RingTheory/IsGaloisGroup/Basic.lean b/Mathlib/RingTheory/IsGaloisGroup/Basic.lean
index 19bd8b54b351a6..f2df00cd008376 100644
--- a/Mathlib/RingTheory/IsGaloisGroup/Basic.lean
+++ b/Mathlib/RingTheory/IsGaloisGroup/Basic.lean
@@ -116,7 +116,7 @@ If `G` is finite and `IsGaloisGroup G A B` with `A` and `B` domains, then `G` is
a Galois group for `FractionRing B / FractionRing A` for the action defined by
`IsFractionRing.mulSemiringAction`.
-/
-instance IsGaloisGroup.toFractionRing [IsDomain A] [IsDomain B] [IsTorsionFree A B] [Finite G]
+instance IsGaloisGroup.toFractionRing [IsDomain A] [IsDomain B] [Finite G]
[IsGaloisGroup G A B] [Algebra (FractionRing A) (FractionRing B)]
[IsScalarTower A (FractionRing A) (FractionRing B)] :
letI := IsFractionRing.mulSemiringAction G B (FractionRing B)
diff --git a/Mathlib/RingTheory/MvPowerSeries/GaussNorm.lean b/Mathlib/RingTheory/MvPowerSeries/GaussNorm.lean
index db2ea3a8a78c2e..b26a1d6e4c6903 100644
--- a/Mathlib/RingTheory/MvPowerSeries/GaussNorm.lean
+++ b/Mathlib/RingTheory/MvPowerSeries/GaussNorm.lean
@@ -180,7 +180,7 @@ lemma ultrametric_strict (na : IsNonarchimedean f)
variable [Semiring S]
lemma Finset.Nonempty.map_sum_le_sup'_map
- {α S : Type*} [Semiring S] [LinearOrder S] [AddCommMonoid α] (g : α → S)
+ {α S : Type*} [LinearOrder S] [AddCommMonoid α] (g : α → S)
{ι : Type*} {s : Finset ι} (hs : s.Nonempty) (f : ι → α)
(na : ∀ a b, g (a + b) ≤ max (g a) (g b)) :
g (∑ i ∈ s, f i) ≤ s.sup' hs fun x ↦ g (f x) := by
diff --git a/Mathlib/RingTheory/RamificationInertia/Basic.lean b/Mathlib/RingTheory/RamificationInertia/Basic.lean
index eba20d69cb7d25..e9529d9886d084 100644
--- a/Mathlib/RingTheory/RamificationInertia/Basic.lean
+++ b/Mathlib/RingTheory/RamificationInertia/Basic.lean
@@ -42,7 +42,7 @@ variable {R : Type*} [CommRing R] (p : Ideal R) [p.IsPrime] (S : Type*) [CommRin
open IsLocalRing Module OrderIso PrimeSpectrum in
theorem sum_ramification_inertia_eq_finrank_fiber
- [Algebra.QuasiFinite R S] [Flat R S] [Fintype (p.primesOver S)] :
+ [Algebra.QuasiFinite R S] [Fintype (p.primesOver S)] :
∑ q : p.primesOver S, q.1.ramificationIdx' R * q.1.inertiaDeg' R =
finrank p.ResidueField (p.Fiber S) := by
let := Fintype.ofFinite (PrimeSpectrum (p.Fiber S))
diff --git a/Mathlib/RingTheory/TwoSidedIdeal/BigOperators.lean b/Mathlib/RingTheory/TwoSidedIdeal/BigOperators.lean
index 1fc23328611068..31958c93682821 100644
--- a/Mathlib/RingTheory/TwoSidedIdeal/BigOperators.lean
+++ b/Mathlib/RingTheory/TwoSidedIdeal/BigOperators.lean
@@ -81,10 +81,8 @@ lemma finsetProd_mem {ι : Type*} (s : Finset ι) (f : ι → R) (hs : ∃ x ∈
rcases s
simpa using multiSetProd_mem (hs := hs)
-lemma finsuppProd_mem {ι : Type*} {β : Type*} {M : Type*}
- [Add M] [CommMonoid M] [Zero β]
- (h : ι → β → R) {f : ι →₀ β} (H : ∃ i ∈ f.support, h i (f i) ∈ I) :
- f.prod h ∈ I :=
+lemma finsuppProd_mem {ι : Type*} {β : Type*} [Zero β]
+ (h : ι → β → R) {f : ι →₀ β} (H : ∃ i ∈ f.support, h i (f i) ∈ I) : f.prod h ∈ I :=
finsetProd_mem _ _ _ H
lemma dfinsuppProd_mem {ι : Type*} {β : ι → Type*}
diff --git a/Mathlib/Topology/Algebra/Module/Complement.lean b/Mathlib/Topology/Algebra/Module/Complement.lean
index 74c43d16d84cce..68b2429b536667 100644
--- a/Mathlib/Topology/Algebra/Module/Complement.lean
+++ b/Mathlib/Topology/Algebra/Module/Complement.lean
@@ -284,7 +284,7 @@ lemma projectionL_eq_id_sub_projectionL [IsTopologicalAddGroup M] (h : IsTopComp
ContinuousLinearMap.ext <| projectionL_eq_self_sub_projectionL h
/-- The projection to `p` along `q` of `x` equals `x` if and only if `x ∈ p`. -/
-lemma projectionL_eq_self_iff [ContinuousSub M] (h : IsTopCompl p q) (x : M) :
+lemma projectionL_eq_self_iff (h : IsTopCompl p q) (x : M) :
p.projectionL q h x = x ↔ x ∈ p :=
projection_eq_self_iff h.isCompl x
diff --git a/Mathlib/Topology/Algebra/Order/Floor.lean b/Mathlib/Topology/Algebra/Order/Floor.lean
index fca55d68eb8cb1..5f59c6fa74b19c 100644
--- a/Mathlib/Topology/Algebra/Order/Floor.lean
+++ b/Mathlib/Topology/Algebra/Order/Floor.lean
@@ -86,7 +86,7 @@ theorem continuousOn_floor (n : ℤ) :
ContinuousOn (fun x => floor x : α → α) (Ico n (n + 1) : Set α) :=
(continuousOn_congr <| floor_eq_on_Ico' n).mpr continuousOn_const
-theorem continuousOn_ceil [IsStrictOrderedRing α] (n : ℤ) :
+theorem continuousOn_ceil (n : ℤ) :
ContinuousOn (fun x => ceil x : α → α) (Ioc (n - 1) n : Set α) :=
(continuousOn_congr <| ceil_eq_on_Ioc' n).mpr continuousOn_const
diff --git a/Mathlib/Topology/Order/Completion.lean b/Mathlib/Topology/Order/Completion.lean
index 050b3668cc531f..a7521c3468d4dd 100644
--- a/Mathlib/Topology/Order/Completion.lean
+++ b/Mathlib/Topology/Order/Completion.lean
@@ -63,8 +63,7 @@ namespace Fill
instance : TopologicalSpace (Fill α) := Preorder.topology _
-instance [TopologicalSpace α] [OrderTopology α] : OrderTopology (Fill α) :=
- ⟨rfl⟩
+instance : OrderTopology (Fill α) := ⟨rfl⟩
/-- A continuous embedding of `α` into `Fill α`. -/
def some : α ↪o Fill α where
diff --git a/Mathlib/Topology/Order/ScottTopology.lean b/Mathlib/Topology/Order/ScottTopology.lean
index 583db890a253ff..5e77f371f9f738 100644
--- a/Mathlib/Topology/Order/ScottTopology.lean
+++ b/Mathlib/Topology/Order/ScottTopology.lean
@@ -101,8 +101,7 @@ and closed sets are `DirSupClosedOn D`. -/
class IsScottHausdorff (α) (D : Set (Set α)) [Preorder α] [TopologicalSpace α] : Prop where
topology_eq_scottHausdorff : ‹TopologicalSpace α› = scottHausdorff α D
-instance (α) (D : Set (Set α)) [Preorder α] [TopologicalSpace α] :
- @IsScottHausdorff α D _ (scottHausdorff α D) :=
+instance (α) (D : Set (Set α)) [Preorder α] : @IsScottHausdorff α D _ (scottHausdorff α D) :=
@IsScottHausdorff.mk _ _ _ (scottHausdorff α D) rfl
namespace IsScottHausdorff
diff --git a/Mathlib/Topology/UniformSpace/ProdApproximation.lean b/Mathlib/Topology/UniformSpace/ProdApproximation.lean
index 9975b76d1389d5..7ee1f6bc4a8a3c 100644
--- a/Mathlib/Topology/UniformSpace/ProdApproximation.lean
+++ b/Mathlib/Topology/UniformSpace/ProdApproximation.lean
@@ -95,9 +95,8 @@ lemma tensorHom_tmul (f : C(X, R)) (g : C(Y, R)) :
tensorHom (f ⊗ₜ g) = prodMul f g := by
rw [tensorHom, TensorProduct.lift.tmul]
-lemma denseRange_tensorHom [CompactSpace X] [T2Space X] [CompactSpace Y] [T2Space Y]
- [TotallyDisconnectedSpace X] :
- DenseRange (tensorHom : C(X, R) ⊗[R] C(Y, R) → C(X × Y, R)) := by
+lemma denseRange_tensorHom [CompactSpace X] [T2Space X] [CompactSpace Y]
+ [TotallyDisconnectedSpace X] : DenseRange (tensorHom : C(X, R) ⊗[R] C(Y, R) → C(X × Y, R)) := by
let : UniformSpace R := IsTopologicalAddGroup.rightUniformSpace R
let : IsUniformAddGroup R := isUniformAddGroup_of_addCommGroup
intro f
From 96aedd613e0924fd4519f64cf7c67c6f9d73d751 Mon Sep 17 00:00:00 2001
From: Felix Pernegger <188575194+felixpernegger@users.noreply.github.com>
Date: Thu, 25 Jun 2026 08:33:39 +0000
Subject: [PATCH 0334/1300] chore(Geometry/Euclidean/Angle): fix an `erw`
(#41014)
This one is easy
Co-authored-by: Batixx
---
Mathlib/Geometry/Euclidean/Angle/Unoriented/RightAngle.lean | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Mathlib/Geometry/Euclidean/Angle/Unoriented/RightAngle.lean b/Mathlib/Geometry/Euclidean/Angle/Unoriented/RightAngle.lean
index c059c3844d66a2..e0887fd418d0f1 100644
--- a/Mathlib/Geometry/Euclidean/Angle/Unoriented/RightAngle.lean
+++ b/Mathlib/Geometry/Euclidean/Angle/Unoriented/RightAngle.lean
@@ -328,8 +328,8 @@ variable {V : Type*} {P : Type*} [NormedAddCommGroup V] [InnerProductSpace ℝ V
theorem dist_sq_eq_dist_sq_add_dist_sq_iff_angle_eq_pi_div_two (p₁ p₂ p₃ : P) :
dist p₁ p₃ * dist p₁ p₃ = dist p₁ p₂ * dist p₁ p₂ + dist p₃ p₂ * dist p₃ p₂ ↔
∠ p₁ p₂ p₃ = π / 2 := by
- erw [dist_comm p₃ p₂, dist_eq_norm_vsub V p₁ p₃, dist_eq_norm_vsub V p₁ p₂,
- dist_eq_norm_vsub V p₂ p₃, ← norm_sub_sq_eq_norm_sq_add_norm_sq_iff_angle_eq_pi_div_two,
+ rw [dist_comm p₃ p₂, dist_eq_norm_vsub V p₁ p₃, dist_eq_norm_vsub V p₁ p₂,
+ dist_eq_norm_vsub V p₂ p₃, angle, ← norm_sub_sq_eq_norm_sq_add_norm_sq_iff_angle_eq_pi_div_two,
vsub_sub_vsub_cancel_right p₁, ← neg_vsub_eq_vsub_rev p₂ p₃, norm_neg]
/-- An angle in a right-angled triangle expressed using `arccos`. -/
From 609d880957e96da2065c4bbcc5ca9cf87fadf079 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=ABl=20Riou?=
<37772949+joelriou@users.noreply.github.com>
Date: Thu, 25 Jun 2026 08:42:39 +0000
Subject: [PATCH 0335/1300] chore: deprecating module
LinearAlgebra.PiTensorProduct (#26987)
---
Mathlib.lean | 1 +
Mathlib/LinearAlgebra/PiTensorProduct.lean | 5 +++++
2 files changed, 6 insertions(+)
create mode 100644 Mathlib/LinearAlgebra/PiTensorProduct.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 881db5f16663e8..be8188b36cd456 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -5154,6 +5154,7 @@ public import Mathlib.LinearAlgebra.PID
public import Mathlib.LinearAlgebra.PerfectPairing.Basic
public import Mathlib.LinearAlgebra.PerfectPairing.Restrict
public import Mathlib.LinearAlgebra.Pi
+public import Mathlib.LinearAlgebra.PiTensorProduct
public import Mathlib.LinearAlgebra.PiTensorProduct.Basic
public import Mathlib.LinearAlgebra.PiTensorProduct.Basis
public import Mathlib.LinearAlgebra.PiTensorProduct.DFinsupp
diff --git a/Mathlib/LinearAlgebra/PiTensorProduct.lean b/Mathlib/LinearAlgebra/PiTensorProduct.lean
new file mode 100644
index 00000000000000..68ff7ebd53b1ad
--- /dev/null
+++ b/Mathlib/LinearAlgebra/PiTensorProduct.lean
@@ -0,0 +1,5 @@
+module
+
+public import Mathlib.LinearAlgebra.PiTensorProduct.Basic
+
+deprecated_module (since := "2026-06-18")
From b12383cf6da7a4bf69e11b5ebaa6c4037c5c545e Mon Sep 17 00:00:00 2001
From: "Yi.Yuan"
Date: Thu, 25 Jun 2026 08:42:42 +0000
Subject: [PATCH 0336/1300] chore(RingTheory/Derivation/DifferentialRing):
unsqueeze terminal `simp`s (#40837)
As recommended in the style guide. Extracted from #40793.
---
Mathlib/RingTheory/Derivation/DifferentialRing.lean | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/Mathlib/RingTheory/Derivation/DifferentialRing.lean b/Mathlib/RingTheory/Derivation/DifferentialRing.lean
index b1d7fb96ad0c88..94b6c54691de99 100644
--- a/Mathlib/RingTheory/Derivation/DifferentialRing.lean
+++ b/Mathlib/RingTheory/Derivation/DifferentialRing.lean
@@ -93,8 +93,4 @@ lemma DifferentialAlgebra.equiv {A : Type*} [CommRing A] [Differential A]
letI := Differential.equiv h.toRingEquiv
⟨fun a ↦ by
change (LinearMap.comp ..) _ = _
- simp only [RingHom.toAddMonoidHom_eq_coe,
- RingEquiv.toRingHom_eq_coe, AlgEquiv.toRingEquiv_toRingHom, LinearMap.coe_comp,
- AddMonoidHom.coe_toIntLinearMap, AddMonoidHom.coe_coe, RingHom.coe_coe, Derivation.coeFn_coe,
- Function.comp_apply, AlgEquiv.commutes, deriv_algebraMap]
- apply h.symm.commutes⟩
+ simp [deriv_algebraMap]⟩
From d64a465b46a637c851ca8035ce55f0a2ecd45992 Mon Sep 17 00:00:00 2001
From: Christian Merten <136261474+chrisflav@users.noreply.github.com>
Date: Thu, 25 Jun 2026 08:51:58 +0000
Subject: [PATCH 0337/1300] feat(AlgebraicGeometry): essential image of tilde
is quasi-coherent modules (#40052)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
We show that for a quasi-coherent module `M` over `Spec R`, the counit `M.fromTildeΓ` is an isomorphism, i.e., `M` is isomorphic to the tilde of the global sections of `M`.
Co-authored-by: Andrew Yang
Co-authored-by: Andrew Yang <36414270+erdOne@users.noreply.github.com>
---
Mathlib/AlgebraicGeometry/Modules/Tilde.lean | 260 +++++++++++++++++++
1 file changed, 260 insertions(+)
diff --git a/Mathlib/AlgebraicGeometry/Modules/Tilde.lean b/Mathlib/AlgebraicGeometry/Modules/Tilde.lean
index cdd5f8c98f175d..f8c2feffe37612 100644
--- a/Mathlib/AlgebraicGeometry/Modules/Tilde.lean
+++ b/Mathlib/AlgebraicGeometry/Modules/Tilde.lean
@@ -10,6 +10,7 @@ public import Mathlib.Algebra.Category.ModuleCat.Localization
public import Mathlib.Algebra.Category.ModuleCat.Sheaf.Quasicoherent
public import Mathlib.Algebra.Module.LocalizedModule.Away
public import Mathlib.AlgebraicGeometry.Modules.Sheaf
+public import Mathlib.Data.Fintype.Order
/-!
@@ -595,6 +596,265 @@ lemma Scheme.Modules.exists_affineOpenCover_presentation {X : Scheme.{u}} (M : X
exact SheafOfModules.Presentation.ofIsIso.{u, u, u} ((restrictFunctorComp _ _).app M).inv <|
(presentationRestrict (hU' i).isoSpec.inv (pres i))
+namespace QuasicoherentTilde
+
+variable (M : (Spec R).Modules)
+
+/-- Auxiliary structure used in the proof of `Scheme.Modules.isIso_fromTildeΓ_of_isQuasicoherent`.
+These are conditions d1) and d2) from [Theoreme 1.4.1, grothendieck-1971]. -/
+-- TODO: Generalise this to a general scheme, replacing `f : R` by sections over a suitable set.
+private structure Aux (V : (Spec R).Opens) where
+ existence (f : R) (hf : basicOpen f ≤ V) (s : Γ(M, basicOpen f)) :
+ ∃ (n : ℕ) (t : Γ(M, V)), M.presheaf.map (homOfLE hf).op t = f ^ n • s
+ uniqueness (f : R) (hf : basicOpen f ≤ V) (t : Γ(M, V)) :
+ M.presheaf.map (.op <| homOfLE hf) t = (0 : Γ(M, basicOpen f)) →
+ ∃ (n : ℕ), f ^ n • t = 0
+
+set_option backward.isDefEq.respectTransparency false in
+private lemma Aux.of_le {M : (Spec R).Modules} {V : (Spec R).Opens} (g : R) (hg : basicOpen g ≤ V)
+ (hV : Aux M V) :
+ Aux M (basicOpen g) where
+ existence f hfg s := by
+ obtain ⟨n, t, ht⟩ := hV.existence f (le_trans hfg hg) s
+ use n, M.presheaf.map (homOfLE hg).op t
+ simp [← M.presheaf.map_comp_apply, ← op_comp, homOfLE_comp, ht]
+ uniqueness f hfg t ht := by
+ obtain ⟨n, t', ht'⟩ := hV.existence g hg t
+ obtain ⟨m, hm⟩ := hV.uniqueness _ (le_trans hfg hg) t' <| by
+ rw [← homOfLE_comp hfg hg, op_comp, M.presheaf.map_comp_apply, ht', M.map_smul_Spec, ht]
+ simp
+ refine ⟨m, ((M.isSMulRegular_of_le_basicOpen le_rfl).pow n).right_eq_zero_of_smul ?_⟩
+ simp [smul_comm, ← ht', ← M.map_smul_Spec, hm]
+
+set_option backward.isDefEq.respectTransparency false in
+/-- This is the key computation for the proof of
+`Scheme.Modules.isQuasicoherent_iff_isIso_fromTildeΓ`.
+ [Lemme 1.4.1.1][grothendieck-1971] -/
+private lemma Aux.of_eq_iSup_basicOpen {M : (Spec R).Modules} (V : (Spec R).Opens)
+ {ι : Type*} [Finite ι] (g : ι → R) (hg : V = ⨆ i, basicOpen (g i))
+ (h₁ : ∀ (i : ι), Aux M (basicOpen (g i))) :
+ Aux M V := by
+ have h₂ (i j : ι) : Aux M (basicOpen (g i * g j)) :=
+ .of_le _ (basicOpen_mul_le_left _ _) (h₁ i)
+ have hgle (i : ι) : basicOpen (g i) ≤ V := by rw [hg]; exact le_iSup_of_le _ le_rfl
+ have hug (i : ι) (m : ℕ) :
+ IsUnit (algebraMap R (Module.End R Γ(M, basicOpen (g i))) (g i ^ m)) := by
+ rw [map_pow]
+ exact (Scheme.Modules.isUnit_algebraMap_end_of_le_basicOpen (g i) le_rfl).pow m
+ -- We show existence and uniqueness separately.
+ refine ⟨fun f hf s ↦ ?_, fun f hf t hs ↦ ?_⟩
+ · have hfgi (i : ι) : basicOpen (f * g i) ≤ basicOpen (g i) := basicOpen_mul_le_right f (g i)
+ let s' (i : ι) : Γ(M, basicOpen (f * g i)) :=
+ M.presheaf.map (homOfLE <| basicOpen_mul_le_left f (g i)).op s
+ /- By `h₁`, up to a factor of `f ^ N`, the restrictions of `s` to `D(f) ∩ D(gᵢ)` lift
+ to sections `tᵢ` over `D(gᵢ)`. -/
+ obtain ⟨N, t, ht⟩ : ∃ (N : ℕ) (t : ∀ i, Γ(M, basicOpen (g i))),
+ ∀ i, f ^ N • s' i = M.presheaf.map (homOfLE (basicOpen_mul_le_right f (g i))).op (t i) := by
+ have (i : ι) : ∃ (n : ℕ) (t : Γ(M, basicOpen (g i))),
+ f ^ n • s' i = M.presheaf.map (homOfLE (hfgi i)).op t := by
+ obtain ⟨n, t', ht'⟩ := (h₁ i).existence (f * g i) (hfgi i) (s' i)
+ rw [mul_pow, mul_smul, smul_comm] at ht'
+ obtain ⟨ψ, hψ⟩ := IsUnit.exists_right_inv (hug i n)
+ use n, ψ t'
+ apply (M.isSMulRegular_of_le_basicOpen (basicOpen_mul_le_right f (g i))).pow n
+ dsimp
+ rw [← ht', ← Scheme.Modules.map_smul_Spec]
+ congr 1
+ exact congr($hψ t').symm
+ choose n t' ht' using this
+ have (i : ι) : n i ≤ ⨆ i, n i := le_ciSup (Finite.bddAbove_range _) _
+ have hN (i : ι) : ⨆ i, n i = ((⨆ i, n i) - n i) + n i := by grind
+ refine ⟨⨆ i, n i, fun i ↦ f ^ ((⨆ i, n i) - n i) • t' i, fun i ↦ ?_⟩
+ conv_lhs => rw [hN i]
+ rw [pow_add, mul_smul, ht', M.map_smul_Spec]
+ /- By `h₂`, up to a factor of `f ^ K`, the restrictions of `tᵢ` and `tⱼ` to
+ to `D(gᵢ) ∩ D(gⱼ)` agree. -/
+ obtain ⟨K, hK⟩ : ∃ (K : ℕ), ∀ (i j : ι),
+ M.presheaf.map (homOfLE (basicOpen_mul_le_left (g i) (g j))).op (f ^ K • t i) =
+ M.presheaf.map (homOfLE (basicOpen_mul_le_right (g i) (g j))).op (f ^ K • t j) := by
+ have (i j : ι) : ∃ (m : ℕ),
+ M.presheaf.map (homOfLE (basicOpen_mul_le_left (g i) (g j))).op (f ^ m • t i) =
+ M.presheaf.map (homOfLE (basicOpen_mul_le_right (g i) (g j))).op (f ^ m • t j) := by
+ have := (h₂ i j).uniqueness (f * (g i * g j)) (basicOpen_mul_le_right _ _)
+ (M.presheaf.map (homOfLE (basicOpen_mul_le_left (g i) (g j))).op (t i) -
+ M.presheaf.map (homOfLE (basicOpen_mul_le_right (g i) (g j))).op (t j)) ?_
+ · obtain ⟨m, hm⟩ := this
+ use m
+ apply (M.isSMulRegular_of_le_basicOpen le_rfl).pow m
+ simpa [M.map_smul_Spec _ (f ^ m), ← mul_smul, ← mul_smul, ← mul_pow, ← mul_comm f,
+ smul_sub, sub_eq_zero] using hm
+ · have hfgigi : basicOpen (f * (g i * g j)) ≤ basicOpen (f * g i) := by
+ rw [← mul_assoc]
+ exact basicOpen_mul_le_left _ _
+ have hfgigj : basicOpen (f * (g i * g j)) ≤ basicOpen (f * g j) := by
+ rw [mul_comm (g i) (g j), ← mul_assoc]
+ exact basicOpen_mul_le_left _ _
+ rw [map_sub, ← M.presheaf.map_comp_apply, ← op_comp, ← M.presheaf.map_comp_apply,
+ ← op_comp, homOfLE_comp, homOfLE_comp, ← homOfLE_comp hfgigi (hfgi i),
+ ← homOfLE_comp hfgigj (hfgi j), op_comp, M.presheaf.map_comp_apply, ← ht i,
+ M.map_smul_Spec, ← M.presheaf.map_comp_apply, ← op_comp, homOfLE_comp, op_comp,
+ M.presheaf.map_comp_apply, ← ht j, M.map_smul_Spec, ← M.presheaf.map_comp_apply,
+ ← op_comp, homOfLE_comp]
+ simp
+ choose m hm using this
+ let K := ⨆ i, ⨆ j, m i j
+ refine ⟨K, fun i j ↦ ?_⟩
+ have : m i j ≤ K :=
+ le_ciSup_of_le (Finite.bddAbove_range _) i (le_ciSup (Finite.bddAbove_range _) _)
+ have : K = (K - m i j) + m i j := by lia
+ rw [this, pow_add, mul_smul, mul_smul, M.map_smul_Spec, M.map_smul_Spec _ (f ^ (K - m i j)),
+ hm i j]
+ -- So up to a factor of `f ^ (N + K)`, the `tᵢ` glue.
+ refine ⟨N + K, ?_⟩
+ have := TopCat.Sheaf.existsUnique_gluing' ⟨_, M.isSheaf⟩ (fun i ↦ basicOpen (g i)) V
+ (fun i ↦ homOfLE (by rw [hg]; exact le_iSup_of_le _ le_rfl)) (by simp [hg])
+ (fun i ↦ f ^ K • t i) ?_
+ · obtain ⟨a, ha, -⟩ := this
+ use a
+ refine TopCat.Sheaf.eq_of_locally_eq' ⟨_, M.isSheaf⟩ (fun i ↦ basicOpen (f * g i)) _
+ (fun i ↦ homOfLE (basicOpen_mul_le_left f (g i))) ?_ _ _ ?_
+ · rw [left_eq_inf.mpr hf, hg, inf_iSup_eq]
+ simp_rw [basicOpen_mul]
+ exact le_rfl
+ · intro i
+ rw [← M.presheaf.map_comp_apply, ← op_comp, homOfLE_comp,
+ ← homOfLE_comp (basicOpen_mul_le_right _ _) (hgle i), op_comp, M.presheaf.map_comp_apply,
+ M.map_smul_Spec, ha, M.map_smul_Spec, pow_add, mul_smul, smul_comm, ht i]
+ · intro i j
+ have : Function.Injective (M.presheaf.map (eqToHom <| (basicOpen_mul (g i) (g j))).op) :=
+ ConcreteCategory.injective_of_mono_of_preservesPullback _
+ apply this
+ dsimp [Opens.infLELeft, Opens.infLERight]
+ simp_rw [← M.presheaf.map_comp_apply, ← op_comp, eqToHom_comp_homOfLE]
+ exact hK i j
+ · have (i : ι) : ∃ (n : ℕ), M.presheaf.map (homOfLE (hgle i)).op (f ^ n • t) = 0 := by
+ have := (h₁ i).uniqueness (f * g i) (basicOpen_mul_le_right f (g i))
+ (M.presheaf.map (homOfLE (hgle i)).op t) ?_
+ · obtain ⟨n, hn⟩ := this
+ use n
+ rw [mul_pow, mul_comm, mul_smul, ← Scheme.Modules.map_smul_Spec] at hn
+ exact ((M.isSMulRegular_of_le_basicOpen le_rfl).pow n).right_eq_zero_of_smul hn
+ · rw [← M.presheaf.map_comp_apply, ← op_comp, homOfLE_comp,
+ ← homOfLE_comp ((basicOpen_mul_le_left f (g i))) hf, op_comp, M.presheaf.map_comp_apply]
+ simp [hs]
+ choose n hn using this
+ use ⨆ i, n i
+ apply TopCat.Sheaf.eq_of_locally_eq' ⟨_, M.isSheaf⟩ (fun i ↦ basicOpen (g i)) _
+ (fun i ↦ homOfLE (by rw [hg]; exact le_iSup_of_le _ le_rfl))
+ · simp [hg]
+ · intro i
+ have : n i ≤ ⨆ i, n i := le_ciSup (Finite.bddAbove_range _) _
+ have : ⨆ i, n i = ((⨆ i, n i) - n i) + n i := by lia
+ rw [this, pow_add, mul_smul, Scheme.Modules.map_smul_Spec, hn i]
+ simp
+
+private lemma isLocalizing_iff_aux (M : (Spec R).Modules) :
+ IsLocalizing (modulesSpecToSheaf.obj M) ↔ Aux M ⊤ := by
+ let φ (f : R) := ((modulesSpecToSheaf.obj M).obj.map (basicOpen f).leTop.op).hom
+ refine ⟨fun h ↦ ?_, fun h f ↦ IsLocalizedModule.Away.mk_of_addCommGroup ?_ ?_ ?_⟩
+ · have hf (f : R) : IsLocalizedModule.Away f (φ f) := h f
+ refine ⟨fun f hle s ↦ ?_, fun f hle s hs ↦ ?_⟩
+ · obtain ⟨n, y, hy⟩ := (hf f).surj _ _ s
+ use n, y, hy.symm
+ · obtain ⟨⟨_, n, rfl⟩, hn⟩ := (IsLocalizedModule.eq_zero_iff (.powers f) (φ f)).mp hs
+ use n, hn
+ · exact Scheme.Modules.isUnit_algebraMap_end_of_le_basicOpen f le_rfl
+ · intro x
+ obtain ⟨n, t, ht⟩ := h.existence _ _ x
+ use n, t, ht.symm
+ · intro x hx
+ obtain ⟨n, hn⟩ := h.uniqueness _ _ _ hx
+ use n, hn
+
+set_option backward.defeqAttrib.useBackward true in
+set_option backward.isDefEq.respectTransparency false in
+private lemma aux_basicOpen_of_aux_restrict (M : (Spec R).Modules) (g : R)
+ (h : Aux (M.restrict <|
+ Spec.map <| CommRingCat.ofHom <| algebraMap R <| Localization.Away g) ⊤) :
+ Aux M (basicOpen g) := by
+ let a : R ⟶ CommRingCat.of (Localization.Away g) :=
+ CommRingCat.ofHom <| algebraMap R _
+ set ψ : Spec (.of <| Localization.Away g) ⟶ Spec (.of R) := Spec.map a
+ set M' : (Spec (.of <| Localization.Away g)).Modules := M.restrict ψ
+ have heq (f : R) (hf : basicOpen f ≤ basicOpen g) :
+ basicOpen f = ψ ''ᵁ basicOpen (a f) := by
+ rw [← SpecMap_preimage_basicOpen, Scheme.Hom.image_preimage_eq_opensRange_inf]
+ simp [a, ψ, hf]
+ let iso : Γ(M.restrict ψ, ⊤) ≅ Γ(M, basicOpen g) :=
+ M.restrictAppIso _ _ ≪≫ M.presheaf.mapIso (eqToIso <| by simp [ψ, a]).op
+ let e (f : R) (hf : basicOpen f ≤ basicOpen g) : Γ(M', basicOpen (a f)) ≅ Γ(M, basicOpen f) :=
+ M.restrictAppIso ψ (basicOpen (a f)) ≪≫ M.presheaf.mapIso (eqToIso <| heq f hf).op
+ refine ⟨fun f hf s ↦ ?_, fun f hf t ht ↦ ?_⟩
+ · obtain ⟨n, t, ht⟩ := h.existence (a f) le_top ((e _ hf).inv s)
+ use n, iso.hom t
+ have := congr((e _ hf).hom $ht)
+ dsimp [M'] at this
+ rw [← ConcreteCategory.comp_apply] at this
+ simp only [homOfLE_leOfHom, Iso.trans_hom, Functor.mapIso_hom, Iso.op_hom, eqToIso.hom,
+ eqToHom_op, Iso.trans_inv, Functor.mapIso_inv, Iso.op_inv, eqToIso.inv, e, iso] at this ⊢
+ simp only [homOfLE_leOfHom, Scheme.Modules.map_restrictAppIso_hom_assoc, AddCommGrpCat.hom_comp,
+ AddMonoidHom.coe_comp, Function.comp_apply, ← map_pow, ψ] at this
+ rw [Scheme.Modules.restrictAppIso_smul_Spec] at this
+ simpa [← Functor.map_comp_apply, eqToHom_comp_homOfLE_op, homOfLE_op_comp_eqToHom] using this
+ · obtain ⟨n, hn⟩ := h.uniqueness (a f) le_top (iso.inv t) <| by
+ simpa [M', iso, ← M.presheaf.map_comp_apply, homOfLE_op_comp_eqToHom, e] using
+ congr((e _ hf).inv $ht)
+ use n
+ have := congr(iso.hom $hn)
+ dsimp [iso, ψ] at this
+ rw [eqToHom_op, map_zero, ← map_pow, Scheme.Modules.restrictAppIso_smul_Spec,
+ M.map_smul_Spec, Iso.inv_hom_id_apply] at this
+ simpa using this
+
+end QuasicoherentTilde
+
+open QuasicoherentTilde in
+set_option backward.defeqAttrib.useBackward true in
+set_option backward.isDefEq.respectTransparency false in
+/-- If `M` is a quasi-coherent `𝒪_{Spec R}` module, it is isomorphic to `Γ(M)^~`. -/
+instance Scheme.Modules.isIso_fromTildeΓ_of_isQuasicoherent (M : (Spec R).Modules)
+ [M.IsQuasicoherent] : IsIso M.fromTildeΓ := by
+ rw [isIso_fromTildeΓ_iff_isLocalizing, isLocalizing_iff_aux]
+ obtain ⟨ι, U, pres, hU, hU'⟩ := M.exists_isOpenCover_presentation
+ obtain ⟨s, hs⟩ := hU.exists_finite_of_compactSpace
+ choose κ hκ a ha using fun i : s ↦
+ PrimeSpectrum.isBasis_basic_opens.exists_iSup_eq_of_isCompact (U i) (hU' i).isCompact
+ refine Aux.of_eq_iSup_basicOpen _ (fun i : Sigma κ ↦ a _ i.2) ?_ ?_
+ · rw [IsOpenCover] at hs
+ rw [eq_comm, iSup_sigma, ← hs]
+ exact iSup_congr fun i ↦ (ha i).symm
+ · intro i
+ let t := (Spec R).homOfLE (U := PrimeSpectrum.basicOpen (a _ i.2)) (V := U i.1)
+ (by rw [ha]; exact le_iSup_of_le _ le_rfl)
+ let iso : restrictFunctor (U i.1).ι ⋙ restrictFunctor ((basicOpenIsoSpecAway _).inv ≫ t) ≅
+ restrictFunctor (Spec.map (CommRingCat.ofHom <| algebraMap _ _)) :=
+ (restrictFunctorComp _ _).symm ≪≫
+ restrictFunctorCongr (by simp [t, basicOpenIsoSpecAway])
+ let pres := SheafOfModules.Presentation.ofIsIso.{u, u, u} (iso.app M).hom <|
+ presentationRestrict ((basicOpenIsoSpecAway _).inv ≫ t) (pres i.1)
+ have : IsIso _ := isIso_fromTildeΓ_of_presentation (M.restrict _) pres
+ rw [isIso_fromTildeΓ_iff_isLocalizing, isLocalizing_iff_aux] at this
+ exact aux_basicOpen_of_aux_restrict _ _ this
+
+set_option backward.isDefEq.respectTransparency false in
+/-- An `𝒪_{Spec R}` module `M` is quasicoherent if and only if it is isomorphic to `Γ(M)^~`. -/
+theorem isQuasicoherent_iff_isIso_fromTildeΓ (M : (Spec R).Modules) :
+ M.IsQuasicoherent ↔ IsIso M.fromTildeΓ := by
+ refine ⟨fun h ↦ inferInstance, fun h ↦ ?_⟩
+ exact (SheafOfModules.isQuasicoherent (Spec R).ringCatSheaf).prop_of_iso
+ (asIso <| M.fromTildeΓ) inferInstance
+
+set_option backward.defeqAttrib.useBackward true in
+set_option backward.isDefEq.respectTransparency false in
+lemma essImage_tilde : (tilde.functor R).essImage =
+ SheafOfModules.isQuasicoherent (Spec R).ringCatSheaf := by
+ refine le_antisymm ?_ ?_
+ · intro M ⟨N, ⟨e⟩⟩
+ exact (SheafOfModules.isQuasicoherent (Spec R).ringCatSheaf).prop_of_iso e
+ (by dsimp; infer_instance)
+ · intro M (h : M.IsQuasicoherent)
+ exact ⟨((modulesSpecToSheaf.obj M).presheaf.obj (.op ⊤)), ⟨asIso <| M.fromTildeΓ⟩⟩
+
end IsQuasicoherent
end AlgebraicGeometry
From 22a6ad55f8437ddf229103cd067d7411feb6038b Mon Sep 17 00:00:00 2001
From: Anatole Dedecker
Date: Thu, 25 Jun 2026 09:00:38 +0000
Subject: [PATCH 0338/1300] feat: define LinearMap.IsQuasiInverse (#39475)
If the name "QuasiInverse" is considered too vague, I am open to suggestions.
This was written by @PatrickMassot and @CoolRmal at the May 2026 ICERM workshop as part of the project on Fredholm operators.
Co-authored-by: Rmal <97214596+CoolRmal@users.noreply.github.com>
---
.../Algebra/Module/LinearMap/FiniteRange.lean | 172 +++++++++++++++++-
1 file changed, 169 insertions(+), 3 deletions(-)
diff --git a/Mathlib/Algebra/Module/LinearMap/FiniteRange.lean b/Mathlib/Algebra/Module/LinearMap/FiniteRange.lean
index ec403ae2982f64..a04166c0c38750 100644
--- a/Mathlib/Algebra/Module/LinearMap/FiniteRange.lean
+++ b/Mathlib/Algebra/Module/LinearMap/FiniteRange.lean
@@ -1,7 +1,7 @@
/-
Copyright (c) 2026 Patrick Massot. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
-Authors: Patrick Massot, Anatole Dedecker
+Authors: Patrick Massot, Anatole Dedecker, Yongxi Lin
-/
module
@@ -32,6 +32,9 @@ In this file, we define:
noetherian ring, in which case the two notions agree.
This is an instance in the scope `LinearMap.FiniteRangeSetoid`,
so opening this scope allows this relation to be denoted by `≈`.
+* `LinearMap.IsQuasiInverse`: two linear maps `u` and `v` are **quasi-inverses** if we have
+ `u ∘ₗ v ≈ id` and `v ∘ₗ u ≈ id` modulo linear maps with noetherian ranges.
+
-/
@[expose] public section
@@ -50,10 +53,12 @@ variable [Semiring K]
[AddCommMonoid V₃] [Module K V₃]
/-- A linear map **has Noetherian range** if its range is a Noetherian module. -/
-def HasNoetherianRange (f : V →ₗ[K] V₂) := IsNoetherian K f.range
+def HasNoetherianRange (f : V →ₗ[K] V₂) : Prop :=
+ IsNoetherian K f.range
/-- A linear map **has finite range** if its range is finitely generated. -/
-def HasFiniteRange (f : V →ₗ[K] V₂) := f.range.FG
+def HasFiniteRange (f : V →ₗ[K] V₂) : Prop :=
+ f.range.FG
lemma hasNoetherianRange_iff_range {f : V →ₗ[K] V₂} :
f.HasNoetherianRange ↔ IsNoetherian K f.range :=
@@ -268,4 +273,165 @@ end FiniteRangeSetoid
end Setoid
+section QuasiInverse
+
+variable [CommRing K]
+ [AddCommGroup V] [Module K V]
+ [AddCommGroup V₂] [Module K V₂]
+ [AddCommGroup V₃] [Module K V₃]
+
+open scoped LinearMap.FiniteRangeSetoid
+
+/-- `u` is a **left quasi-inverse** to `v` if `u ∘ₗ v ≈ id` modulo
+linear maps with noetherian ranges. Recall that if the scalar ring is noetherian
+(e.g a field), then "noetherian range" can be replaced by "finitely generated range". -/
+def IsLeftQuasiInverse (u : V →ₗ[K] V₂) (v : V₂ →ₗ[K] V) : Prop :=
+ u ∘ₗ v ≈ .id
+
+/-- `u` is a **right quasi-inverse** to `v` if `v ∘ₗ u ≈ id` modulo
+linear maps with noetherian ranges. Recall that if the scalar ring is noetherian
+(e.g a field), then "noetherian range" can be replaced by "finitely generated range". -/
+def IsRightQuasiInverse (u : V₃ →ₗ[K] V₂) (v : V₂ →ₗ[K] V₃) : Prop :=
+ v ∘ₗ u ≈ .id
+
+/-- `u` is a **quasi-inverse** to `v` if `u ∘ₗ v ≈ id` and `v ∘ₗ u ≈ id` modulo
+linear maps with noetherian ranges. Recall that if the scalar ring is noetherian
+(e.g a field), then "noetherian range" can be replaced by "finitely generated range". -/
+def IsQuasiInverse (u : V₃ →ₗ[K] V₂) (v : V₂ →ₗ[K] V₃) : Prop :=
+ u.IsLeftQuasiInverse v ∧ u.IsRightQuasiInverse v
+
+lemma isLeftQuasiInverse_iff_isRightQuasiInverse_swap {u : V₃ →ₗ[K] V₂} {v : V₂ →ₗ[K] V₃} :
+ u.IsLeftQuasiInverse v ↔ v.IsRightQuasiInverse u := Iff.rfl
+
+alias ⟨IsLeftQuasiInverse.isRightQuasiInverse, IsRightQuasiInverse.isLeftQuasiInverse⟩ :=
+ isLeftQuasiInverse_iff_isRightQuasiInverse_swap
+
+lemma IsLeftQuasiInverse.equiv {u : V₃ →ₗ[K] V₂} {v : V₂ →ₗ[K] V₃}
+ (h : u.IsLeftQuasiInverse v) : u ∘ₗ v ≈ .id := h
+
+lemma IsRightQuasiInverse.equiv {u : V₃ →ₗ[K] V₂} {v : V₂ →ₗ[K] V₃}
+ (h : u.IsRightQuasiInverse v) : v ∘ₗ u ≈ .id := h
+
+@[symm]
+lemma IsQuasiInverse.symm {u : V₃ →ₗ[K] V₂} {v : V₂ →ₗ[K] V₃}
+ (h : u.IsQuasiInverse v) : v.IsQuasiInverse u :=
+ And.symm h
+
+@[gcongr]
+lemma IsLeftQuasiInverse.congr {u u' : V₃ →ₗ[K] V₂} {v v' : V₂ →ₗ[K] V₃}
+ (h : u.IsLeftQuasiInverse v) (hu : u' ≈ u) (hv : v' ≈ v) :
+ u'.IsLeftQuasiInverse v' := by
+ unfold IsLeftQuasiInverse at *
+ grw [hu, hv]
+ assumption
+
+@[gcongr]
+lemma isLeftQuasiInverse_congr {u u' : V₃ →ₗ[K] V₂} {v v' : V₂ →ₗ[K] V₃}
+ (hu : u' ≈ u) (hv : v' ≈ v) :
+ u.IsLeftQuasiInverse v ↔ u'.IsLeftQuasiInverse v' :=
+ ⟨fun H ↦ H.congr hu hv, fun H ↦ H.congr (Setoid.symm hu) (Setoid.symm hv)⟩
+
+@[gcongr]
+lemma IsRightQuasiInverse.congr {u u' : V₃ →ₗ[K] V₂} {v v' : V₂ →ₗ[K] V₃}
+ (h : u.IsRightQuasiInverse v) (hu : u' ≈ u) (hv : v' ≈ v) :
+ u'.IsRightQuasiInverse v' :=
+ h.isLeftQuasiInverse.congr hv hu |>.isRightQuasiInverse
+
+lemma isRightQuasiInverse_congr {u u' : V₃ →ₗ[K] V₂} {v v' : V₂ →ₗ[K] V₃}
+ (hu : u' ≈ u) (hv : v' ≈ v) :
+ u.IsRightQuasiInverse v ↔ u'.IsRightQuasiInverse v' :=
+ ⟨fun H ↦ H.congr hu hv, fun H ↦ H.congr (Setoid.symm hu) (Setoid.symm hv)⟩
+
+@[gcongr]
+lemma IsQuasiInverse.congr {u u' : V₃ →ₗ[K] V₂} {v v' : V₂ →ₗ[K] V₃}
+ (h : u.IsQuasiInverse v) (hu : u' ≈ u) (hv : v' ≈ v) :
+ u'.IsQuasiInverse v' :=
+ ⟨h.1.congr hu hv, h.2.congr hu hv⟩
+
+lemma isQuasiInverse_congr {u u' : V₃ →ₗ[K] V₂} {v v' : V₂ →ₗ[K] V₃}
+ (hu : u' ≈ u) (hv : v' ≈ v) :
+ u.IsQuasiInverse v ↔ u'.IsQuasiInverse v' := by
+ simp [IsQuasiInverse, isLeftQuasiInverse_congr hu hv, isRightQuasiInverse_congr hu hv]
+
+lemma IsQuasiInverse.equiv_of_left {u u' : V₃ →ₗ[K] V₂} {v v' : V₂ →ₗ[K] V₃}
+ (h : u.IsQuasiInverse v) (h' : u'.IsQuasiInverse v') (hu : u ≈ u') :
+ v ≈ v' := by
+ calc
+ v = v ∘ₗ .id := by simp
+ _ ≈ v ∘ₗ (u' ∘ₗ v') := by grw [h'.1.equiv]
+ _ ≈ v ∘ₗ (u ∘ₗ v') := by grw [hu]
+ _ = (v ∘ₗ u) ∘ₗ v' := by rw [comp_assoc]
+ _ ≈ .id ∘ₗ v' := by grw [h.2.equiv]
+ _ = v' := by simp
+
+lemma IsQuasiInverse.equiv_of_right {u u' : V₃ →ₗ[K] V₂} {v v' : V₂ →ₗ[K] V₃}
+ (h : u.IsQuasiInverse v) (h' : u'.IsQuasiInverse v') (hv : v ≈ v') :
+ u ≈ u' :=
+ h.symm.equiv_of_left h'.symm hv
+
+/-- Left quasi-inverses compose in the opposite order. -/
+lemma IsLeftQuasiInverse.comp {u : V →ₗ[K] V₂} {v : V₂ →ₗ[K] V₃} {u' : V₂ →ₗ[K] V}
+ {v' : V₃ →ₗ[K] V₂} (hu : u'.IsLeftQuasiInverse u) (hv : v'.IsLeftQuasiInverse v) :
+ (u' ∘ₗ v').IsLeftQuasiInverse (v ∘ₗ u) :=
+ calc
+ _ = u' ∘ₗ (v' ∘ₗ v) ∘ₗ u := rfl
+ _ ≈ u' ∘ₗ .id ∘ₗ u := by grw [hv.equiv]
+ _ ≈ .id := hu.equiv
+
+/-- Right quasi-inverses compose in the opposite order. -/
+lemma IsRightQuasiInverse.comp {u : V →ₗ[K] V₂} {v : V₂ →ₗ[K] V₃} {u' : V₂ →ₗ[K] V}
+ {v' : V₃ →ₗ[K] V₂} (hu : u'.IsRightQuasiInverse u) (hv : v'.IsRightQuasiInverse v) :
+ (u' ∘ₗ v').IsRightQuasiInverse (v ∘ₗ u) :=
+ hv.isLeftQuasiInverse.comp hu.isLeftQuasiInverse |>.isRightQuasiInverse
+
+/-- Quasi-inverses compose in the opposite order. -/
+lemma IsQuasiInverse.comp {u : V →ₗ[K] V₂} {v : V₂ →ₗ[K] V₃} {u' : V₂ →ₗ[K] V}
+ {v' : V₃ →ₗ[K] V₂} (hu : u'.IsQuasiInverse u) (hv : v'.IsQuasiInverse v) :
+ (u' ∘ₗ v').IsQuasiInverse (v ∘ₗ u) :=
+ ⟨hu.1.comp hv.1, hu.2.comp hv.2⟩
+
+/-- If `u'` is a right quasi-inverse of `u` and `w` is a left quasi-inverse of `v ∘ₗ u`,
+then `u ∘ₗ w` is a left quasi-inverse of `v`. -/
+lemma IsLeftQuasiInverse.of_comp_left {u : V →ₗ[K] V₂} {v : V₂ →ₗ[K] V₃}
+ {u' : V₂ →ₗ[K] V} {w : V₃ →ₗ[K] V} (hu : u'.IsRightQuasiInverse u)
+ (hw : w.IsLeftQuasiInverse (v ∘ₗ u)) :
+ (u ∘ₗ w).IsLeftQuasiInverse v := by
+ calc
+ _ = ((u ∘ₗ w) ∘ₗ v) ∘ₗ .id := rfl
+ _ ≈ ((u ∘ₗ w) ∘ₗ v) ∘ₗ (u ∘ₗ u') := by grw [hu.equiv]
+ _ = u ∘ₗ (w ∘ₗ (v ∘ₗ u)) ∘ₗ u' := rfl
+ _ ≈ u ∘ₗ .id ∘ₗ u' := by grw [hw.equiv]
+ _ ≈ .id := hu.equiv
+
+/-- If `u'` is a quasi-inverse of `u` and `w` is a quasi-inverse of `v ∘ₗ u`, then
+`u ∘ₗ w` is a quasi-inverse of `v`. -/
+lemma IsQuasiInverse.of_comp_left {u : V →ₗ[K] V₂} {v : V₂ →ₗ[K] V₃}
+ {u' : V₂ →ₗ[K] V} {w : V₃ →ₗ[K] V} (hu : u'.IsQuasiInverse u)
+ (hw : w.IsQuasiInverse (v ∘ₗ u)) :
+ (u ∘ₗ w).IsQuasiInverse v :=
+ ⟨.of_comp_left hu.2 hw.1, hw.2⟩
+
+/-- If `v'` is a left quasi-inverse of `v` and `w` is a right quasi-inverse of `v ∘ₗ u`,
+then `w ∘ₗ v` is a right quasi-inverse of `u`. -/
+lemma IsRightQuasiInverse.of_comp_right {u : V →ₗ[K] V₂} {v : V₂ →ₗ[K] V₃}
+ {v' : V₃ →ₗ[K] V₂} {w : V₃ →ₗ[K] V} (hv : v'.IsLeftQuasiInverse v)
+ (hw : w.IsRightQuasiInverse (v ∘ₗ u)) :
+ (w ∘ₗ v).IsRightQuasiInverse u := by
+ calc
+ _ = .id ∘ₗ (u ∘ₗ (w ∘ₗ v)) := rfl
+ _ ≈ (v' ∘ₗ v) ∘ₗ (u ∘ₗ (w ∘ₗ v)) := by grw [hv.equiv]
+ _ = v' ∘ₗ ((v ∘ₗ u) ∘ₗ w) ∘ₗ v := rfl
+ _ ≈ v' ∘ₗ .id ∘ₗ v := by grw [hw.equiv]
+ _ ≈ .id := hv.equiv
+
+/-- If `v'` is a quasi-inverse of `v` and `w` is a quasi-inverse of `v ∘ₗ u`, then
+`w ∘ₗ v` is a quasi-inverse of `u`. -/
+lemma IsQuasiInverse.of_comp_right {u : V →ₗ[K] V₂} {v : V₂ →ₗ[K] V₃}
+ {v' : V₃ →ₗ[K] V₂} {w : V₃ →ₗ[K] V} (hv : v'.IsQuasiInverse v)
+ (hw : w.IsQuasiInverse (v ∘ₗ u)) :
+ (w ∘ₗ v).IsQuasiInverse u :=
+ ⟨hw.1, IsRightQuasiInverse.of_comp_right hv.1 hw.2⟩
+
+end QuasiInverse
+
end LinearMap
From b8ddb2552a4c4746d01b48ccbfc3549d7f2f9627 Mon Sep 17 00:00:00 2001
From: Leo Diedering <129694072+ldiedering@users.noreply.github.com>
Date: Thu, 25 Jun 2026 09:10:25 +0000
Subject: [PATCH 0339/1300]
refactor(MeasureTheory/Measure/Typeclasses/NoAtoms): add deprecation for
`NoAtoms` (#40815)
Add module deprecation for `MeasureTheory/Measure/Typeclasses/NoAtoms`.
---
Mathlib.lean | 1 +
Mathlib/MeasureTheory/Measure/Typeclasses/NoAtoms.lean | 9 +++++++++
2 files changed, 10 insertions(+)
create mode 100644 Mathlib/MeasureTheory/Measure/Typeclasses/NoAtoms.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index be8188b36cd456..26884b65969460 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -5595,6 +5595,7 @@ public import Mathlib.MeasureTheory.Measure.TightNormed
public import Mathlib.MeasureTheory.Measure.Tilted
public import Mathlib.MeasureTheory.Measure.Trim
public import Mathlib.MeasureTheory.Measure.Typeclasses.Finite
+public import Mathlib.MeasureTheory.Measure.Typeclasses.NoAtoms
public import Mathlib.MeasureTheory.Measure.Typeclasses.NullSingletonClass
public import Mathlib.MeasureTheory.Measure.Typeclasses.Probability
public import Mathlib.MeasureTheory.Measure.Typeclasses.SFinite
diff --git a/Mathlib/MeasureTheory/Measure/Typeclasses/NoAtoms.lean b/Mathlib/MeasureTheory/Measure/Typeclasses/NoAtoms.lean
new file mode 100644
index 00000000000000..10b58e0c999fe1
--- /dev/null
+++ b/Mathlib/MeasureTheory/Measure/Typeclasses/NoAtoms.lean
@@ -0,0 +1,9 @@
+module
+
+/-! # NoAtoms
+This file is deprecated. Please use `Mathlib.MeasureTheory.Measure.Typeclasses.NullSingletonClass`
+instead.
+-/
+
+deprecated_module "use Mathlib.MeasureTheory.Measure.Typeclasses.NullSingletonClass instead"
+ (since := "2026-06-19")
From 25e35a45b208f060b7165770fc7f723b8b92a53e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=ABl=20Riou?=
<37772949+joelriou@users.noreply.github.com>
Date: Thu, 25 Jun 2026 09:10:27 +0000
Subject: [PATCH 0340/1300] feat(Algebra/Homology): the bounded below derived
category (#40944)
In this PR, we show that the bounded below derived category is the localization of the bounded below homotopy category with respect to quasi-isomorphisms.
---
Mathlib.lean | 1 +
.../Homology/DerivedCategory/Basic.lean | 2 +-
.../DerivedCategory/HomologySequence.lean | 16 ++
.../Homology/DerivedCategory/Plus.lean | 228 ++++++++++++++++++
.../Homology/DerivedCategory/TStructure.lean | 1 -
.../Homology/HomotopyCategory/Plus.lean | 60 ++++-
6 files changed, 305 insertions(+), 3 deletions(-)
create mode 100644 Mathlib/Algebra/Homology/DerivedCategory/Plus.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 26884b65969460..4017194f141030 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -589,6 +589,7 @@ public import Mathlib.Algebra.Homology.DerivedCategory.HomologySequence
public import Mathlib.Algebra.Homology.DerivedCategory.KInjective
public import Mathlib.Algebra.Homology.DerivedCategory.KProjective
public import Mathlib.Algebra.Homology.DerivedCategory.Linear
+public import Mathlib.Algebra.Homology.DerivedCategory.Plus
public import Mathlib.Algebra.Homology.DerivedCategory.ShortExact
public import Mathlib.Algebra.Homology.DerivedCategory.SingleTriangle
public import Mathlib.Algebra.Homology.DerivedCategory.SmallShiftedHom
diff --git a/Mathlib/Algebra/Homology/DerivedCategory/Basic.lean b/Mathlib/Algebra/Homology/DerivedCategory/Basic.lean
index 72369bf49d4c35..d78c2f9f096135 100644
--- a/Mathlib/Algebra/Homology/DerivedCategory/Basic.lean
+++ b/Mathlib/Algebra/Homology/DerivedCategory/Basic.lean
@@ -237,7 +237,7 @@ their compatibilities with shifts. -/
def singleFunctors : SingleFunctors C (DerivedCategory C) ℤ :=
(HomotopyCategory.singleFunctors C).postcomp Qh
-/-- The shift functor `C ⥤ DerivedCategory C` which sends `X : C` to the
+/-- The single functor `C ⥤ DerivedCategory C` which sends `X : C` to the
single cochain complex with `X` sitting in degree `n : ℤ`. -/
abbrev singleFunctor (n : ℤ) := (singleFunctors C).functor n
diff --git a/Mathlib/Algebra/Homology/DerivedCategory/HomologySequence.lean b/Mathlib/Algebra/Homology/DerivedCategory/HomologySequence.lean
index 8ca61a1c2e2c22..0a5406598d9864 100644
--- a/Mathlib/Algebra/Homology/DerivedCategory/HomologySequence.lean
+++ b/Mathlib/Algebra/Homology/DerivedCategory/HomologySequence.lean
@@ -85,6 +85,22 @@ lemma isIso_Qh_map_iff {X Y : HomotopyCategory C (ComplexShape.up ℤ)} (f : X
infer_instance
· exact Localization.inverts Qh (HomotopyCategory.quasiIso _ _) _
+lemma isIso_iff {K L : DerivedCategory C} (f : K ⟶ L) :
+ IsIso f ↔ ∀ (n : ℤ), IsIso ((homologyFunctor C n).map f) := by
+ refine ⟨fun hf n ↦ inferInstance, fun hf ↦ ?_⟩
+ refine ((MorphismProperty.isomorphisms (DerivedCategory C)).arrow_iso_iff
+ (Qh.mapArrow.objObjPreimageIso (Arrow.mk f))).1 ?_
+ let g := Qh.mapArrow.objPreimage (Arrow.mk f)
+ change IsIso (Qh.map g.hom)
+ rw [isIso_Qh_map_iff, HomotopyCategory.mem_quasiIso_iff]
+ intro n
+ have e : Arrow.mk ((homologyFunctor C n).map f) ≅
+ Arrow.mk ((HomotopyCategory.homologyFunctor _ _ n).map g.hom) :=
+ ((homologyFunctor C n).mapArrow.mapIso
+ ((Qh.mapArrow.objObjPreimageIso (Arrow.mk f)).symm)) ≪≫
+ ((Functor.mapArrowFunctor _ _).mapIso (homologyFunctorFactorsh C n)).app (Arrow.mk g.hom)
+ exact ((MorphismProperty.isomorphisms C).arrow_iso_iff e).1 (hf n)
+
instance (n : ℤ) : (homologyFunctor C n).IsHomological :=
Functor.isHomological_of_localization Qh
(homologyFunctor C n) _ (homologyFunctorFactorsh C n)
diff --git a/Mathlib/Algebra/Homology/DerivedCategory/Plus.lean b/Mathlib/Algebra/Homology/DerivedCategory/Plus.lean
new file mode 100644
index 00000000000000..72b30ddd52edf2
--- /dev/null
+++ b/Mathlib/Algebra/Homology/DerivedCategory/Plus.lean
@@ -0,0 +1,228 @@
+/-
+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.Homology.DerivedCategory.KInjective
+public import Mathlib.Algebra.Homology.DerivedCategory.TStructure
+public import Mathlib.Algebra.Homology.HomotopyCategory.Plus
+public import Mathlib.CategoryTheory.Triangulated.LocalizingSubcategory
+public import Mathlib.CategoryTheory.Triangulated.TStructure.Induced
+
+/-!
+# The bounded below derived category
+
+Let `C` be an abelian category. In this file, we show that
+the bounded below derived category `DerivedCategory.Plus C` (defined
+as a full subcategory of `DerivedCategory C`) is the localization
+of the bounded below homotopy category `HomotopyCategory.Plus C`
+with respect to quasi-isomorphisms.
+
+-/
+
+@[expose] public section
+
+open CategoryTheory Category Triangulated Limits
+
+variable {C : Type*} [Category* C] [Abelian C]
+
+namespace HomotopyCategory.Plus
+
+variable (C)
+
+/-- The property of objects in `HomotopyCategory.Plus C` that is satisfied
+by acyclic complexes. -/
+abbrev subcategoryAcyclic :
+ ObjectProperty (HomotopyCategory.Plus C) :=
+ (HomotopyCategory.subcategoryAcyclic C).inverseImage (HomotopyCategory.Plus.ι C)
+
+set_option backward.defeqAttrib.useBackward true in
+lemma quasiIso_eq_subcategoryAcyclic_trW :
+ HomotopyCategory.Plus.quasiIso C = (subcategoryAcyclic C).trW := by
+ ext K L f
+ obtain ⟨M, g, h, mem⟩ := CategoryTheory.Pretriangulated.distinguished_cocone_triangle f
+ have := (HomotopyCategory.subcategoryAcyclic C).trW_iff_of_distinguished _
+ ((HomotopyCategory.Plus.ι C).map_distinguished _ mem)
+ rw [← HomotopyCategory.quasiIso_eq_trW_subcategoryAcyclic] at this
+ rwa [dsimp% (subcategoryAcyclic C).trW_iff_of_distinguished _ mem]
+
+end HomotopyCategory.Plus
+
+namespace DerivedCategory
+
+open TStructure
+
+variable [HasDerivedCategory C]
+
+namespace Plus
+
+/-- The localization functor `HomotopyCategory.Plus C ⥤ DerivedCategory.Plus C`. -/
+noncomputable def Qh : HomotopyCategory.Plus C ⥤ Plus C :=
+ t.plus.lift (HomotopyCategory.Plus.ι _ ⋙ DerivedCategory.Qh) (by
+ rintro ⟨K, hK⟩
+ obtain ⟨K, rfl⟩ := HomotopyCategory.quotient_obj_surjective K
+ obtain ⟨n, _⟩ := (HomotopyCategory.plus_quotient_obj_iff _).mp hK
+ exact ⟨n, t.isGE_of_iso ((quotientCompQhIso C).symm.app K) n⟩)
+
+noncomputable instance : (Qh : _ ⥤ Plus C).CommShift ℤ := by
+ dsimp only [Qh]
+ infer_instance
+
+set_option backward.isDefEq.respectTransparency false in
+instance : (Qh : _ ⥤ Plus C).IsTriangulated := by
+ dsimp only [Qh]
+ infer_instance
+
+set_option backward.isDefEq.respectTransparency false in
+lemma Qh_map_bijective_of_isKInjective (K L : HomotopyCategory.Plus C)
+ (_ : CochainComplex.IsKInjective L.1.as) : Function.Bijective (Qh.map : (K ⟶ L) → _) := by
+ have := CochainComplex.IsKInjective.Qh_map_bijective K.1 L.1.as
+ rw [← Function.Bijective.of_comp_iff _
+ ((HomotopyCategory.Plus.fullyFaithfulι C).map_bijective _ _)] at this
+ rwa [← Function.Bijective.of_comp_iff' (t.plus.fullyFaithfulι.map_bijective _ _)]
+
+instance : (HomotopyCategory.plus C).IsVerdierRightLocalizing
+ (HomotopyCategory.subcategoryAcyclic C) where
+ fac {K L} φ hK hL := by
+ obtain ⟨K : CochainComplex _ _, rfl⟩ := HomotopyCategory.quotient_obj_surjective K
+ obtain ⟨L : CochainComplex _ _, rfl⟩ := HomotopyCategory.quotient_obj_surjective L
+ simp only [HomotopyCategory.plus_quotient_obj_iff] at hL
+ obtain ⟨n, hn⟩ := hL
+ obtain ⟨φ, rfl⟩ := (HomotopyCategory.quotient _ _).map_surjective φ
+ rw [HomotopyCategory.quotient_obj_mem_subcategoryAcyclic_iff_acyclic] at hK
+ refine ⟨(HomotopyCategory.quotient _ _).obj (K.truncGE n),
+ (HomotopyCategory.quotient _ _).map (K.πTruncGE n),
+ (HomotopyCategory.quotient _ _).map (CochainComplex.truncGEMap φ n ≫ inv (L.πTruncGE n)),
+ ?_, ?_, by simp [← Functor.map_comp]⟩
+ · simp only [HomotopyCategory.plus_quotient_obj_iff]
+ exact ⟨n, inferInstance⟩
+ · rw [HomotopyCategory.quotient_obj_mem_subcategoryAcyclic_iff_acyclic]
+ exact hK.truncGE _
+
+variable (C)
+
+/-- The functor `DerivedCategory.Plus.Qh : HomotopyCategory.Plus C ⥤ DerivedCategory.Plus C`
+is induced by `DerivedCategory.Qh : HomotopyCategory C (.up ℤ) ⥤ DerivedCategory C`. -/
+noncomputable def QhCompιIsoιCompQh :
+ Qh ⋙ Plus.ι ≅ HomotopyCategory.Plus.ι C ⋙ DerivedCategory.Qh := Iso.refl _
+
+instance : (Qh (C := C)).EssSurj where
+ mem_essImage := by
+ intro ⟨X, n, K, e, h⟩
+ refine ⟨⟨(HomotopyCategory.quotient C (ComplexShape.up ℤ)).obj K, ?_⟩,
+ ⟨Plus.ι.preimageIso ((quotientCompQhIso C).app _ ≪≫ e.symm)⟩⟩
+ simp only [HomotopyCategory.plus_quotient_obj_iff]
+ exact ⟨n, h⟩
+
+instance : Qh.IsLocalization (HomotopyCategory.Plus.subcategoryAcyclic C).trW :=
+ ((HomotopyCategory.plus C).triangulatedLocalizerMorphism
+ (HomotopyCategory.subcategoryAcyclic C)).isLocalization_of_isLocalizedFullyFaithful
+ (QhCompιIsoιCompQh C).symm
+
+instance : Qh.IsLocalization (HomotopyCategory.Plus.quasiIso C) := by
+ rw [HomotopyCategory.Plus.quasiIso_eq_subcategoryAcyclic_trW]
+ infer_instance
+
+/-- The single functors `C ⥤ DerivedCategory.Plus C` for all `n : ℤ` along with
+their compatibilities with shifts. -/
+noncomputable def singleFunctors : SingleFunctors C (Plus C) ℤ :=
+ SingleFunctors.lift (DerivedCategory.singleFunctors C) Plus.ι
+ (fun n => t.plus.lift (DerivedCategory.singleFunctor C n)
+ (fun _ => ⟨n, inferInstance⟩))
+ (fun _ => Iso.refl _)
+
+/-- The single functor `C ⥤ DerivedCategory.Plus C` which sends `X : C` to the
+single cochain complex with `X` sitting in degree `n : ℤ`. -/
+noncomputable abbrev singleFunctor (n : ℤ) : C ⥤ Plus C := (singleFunctors C).functor n
+
+/-- The single functors on `DerivedCategory.Plus C` are induced by the
+single functors on `DerivedCategory C`. -/
+noncomputable def singleFunctorιIso (n : ℤ) :
+ singleFunctor C n ⋙ Plus.ι ≅ DerivedCategory.singleFunctor C n :=
+ Iso.refl _
+
+instance (n : ℤ) : (singleFunctor C n).Additive := by
+ dsimp [singleFunctor, singleFunctors]
+ infer_instance
+
+/-- The homology functor `DerivedCategory.Plus C ⥤ C` in degree `n : ℤ`. -/
+noncomputable def homologyFunctor (n : ℤ) : Plus C ⥤ C :=
+ Plus.ι ⋙ DerivedCategory.homologyFunctor C n
+deriving Functor.IsHomological
+
+instance : (Qh (C := C)).mapArrow.EssSurj :=
+ Localization.essSurj_mapArrow _
+ (HomotopyCategory.Plus.subcategoryAcyclic C).trW
+
+variable {C}
+
+/-- The canonical t-structure on `DerivedCategory.Plus C`. -/
+noncomputable abbrev TStructure.t : TStructure (DerivedCategory.Plus C) :=
+ (DerivedCategory.TStructure.t (C := C)).plus.tStructure DerivedCategory.TStructure.t
+
+/-- Given `X : DerivedCategory.Plus C` and `n : ℤ`, this property means
+that `X` is `≥ n` for the canonical t-structure. -/
+abbrev IsGE (X : Plus C) (n : ℤ) : Prop := Plus.TStructure.t.IsGE X n
+
+/-- Given `X : DerivedCategory.Plus C` and `n : ℤ`, this property means
+that `X` is `≤ n` for the canonical t-structure. -/
+abbrev IsLE (X : Plus C) (n : ℤ) : Prop := Plus.TStructure.t.IsLE X n
+
+lemma isGE_ι_obj_iff (X : Plus C) (n : ℤ) :
+ (ι.obj X).IsGE n ↔ X.IsGE n := by
+ constructor
+ all_goals exact fun h ↦ ⟨h.1⟩
+
+lemma isLE_ι_obj_iff (X : Plus C) (n : ℤ) :
+ (ι.obj X).IsLE n ↔ X.IsLE n := by
+ constructor
+ all_goals exact fun h ↦ ⟨h.1⟩
+
+instance (X : Plus C) (n : ℤ) [X.IsGE n] : (ι.obj X).IsGE n := by
+ rw [isGE_ι_obj_iff]
+ infer_instance
+
+instance (X : Plus C) (n : ℤ) [X.IsLE n] : (ι.obj X).IsLE n := by
+ rw [isLE_ι_obj_iff]
+ infer_instance
+
+noncomputable instance : (DerivedCategory.Plus.homologyFunctor C 0).ShiftSequence ℤ :=
+ inferInstanceAs ((ι ⋙ DerivedCategory.homologyFunctor C 0).ShiftSequence ℤ)
+
+instance (X : C) (n : ℤ) : ((singleFunctor C n).obj X).IsGE n := by
+ rw [← isGE_ι_obj_iff]
+ change DerivedCategory.TStructure.t.IsGE ((DerivedCategory.singleFunctor C n).obj X) n
+ infer_instance
+
+instance (X : C) (n : ℤ) : ((singleFunctor C n).obj X).IsLE n := by
+ rw [← isLE_ι_obj_iff]
+ change DerivedCategory.TStructure.t.IsLE ((DerivedCategory.singleFunctor C n).obj X) n
+ infer_instance
+
+lemma isZero_homology_of_isGE
+ (X : Plus C) (n : ℤ) [X.IsGE n] (i : ℤ) (hi : i < n) :
+ IsZero ((homologyFunctor C i).obj X) :=
+ (ι.obj X).isZero_of_isGE n i hi
+
+lemma isZero_homology_of_isLE
+ (X : Plus C) (n : ℤ) [X.IsLE n] (i : ℤ) (hi : n < i) :
+ IsZero ((homologyFunctor C i).obj X) :=
+ (ι.obj X).isZero_of_isLE n i hi
+
+lemma isIso_iff {X Y : Plus C} (f : X ⟶ Y) :
+ IsIso f ↔ ∀ (n : ℤ), IsIso ((homologyFunctor C n).map f) := by
+ refine ⟨fun _ _ ↦ inferInstance, fun _ ↦ ?_⟩
+ have : IsIso (ι.map f) := by rwa [DerivedCategory.isIso_iff]
+ exact isIso_of_fully_faithful ι _
+
+/-- The localization functor `CochainComplex.Plus C ⥤ DerivedCategory.Plus C`. -/
+noncomputable def Q : CochainComplex.Plus C ⥤ DerivedCategory.Plus C :=
+ HomotopyCategory.Plus.quotient C ⋙ Qh
+
+-- TODO: show that `Q` is indeed a localization functor with respect to quasi-isomorphisms
+
+end Plus
+
+end DerivedCategory
diff --git a/Mathlib/Algebra/Homology/DerivedCategory/TStructure.lean b/Mathlib/Algebra/Homology/DerivedCategory/TStructure.lean
index 8ee57cc096ea31..1cc81f41cfa053 100644
--- a/Mathlib/Algebra/Homology/DerivedCategory/TStructure.lean
+++ b/Mathlib/Algebra/Homology/DerivedCategory/TStructure.lean
@@ -193,7 +193,6 @@ lemma exists_iso_singleFunctor_obj_of_isGE_of_isLE
obtain ⟨Y, ⟨e'⟩⟩ := CochainComplex.exists_iso_single K n
exact ⟨Y, ⟨e ≪≫ Q.mapIso e'⟩⟩
-
open DerivedCategory.TStructure
variable (C)
diff --git a/Mathlib/Algebra/Homology/HomotopyCategory/Plus.lean b/Mathlib/Algebra/Homology/HomotopyCategory/Plus.lean
index 505b25c297c77f..352d3a8ee1bca5 100644
--- a/Mathlib/Algebra/Homology/HomotopyCategory/Plus.lean
+++ b/Mathlib/Algebra/Homology/HomotopyCategory/Plus.lean
@@ -23,7 +23,7 @@ of `HomotopyCategory C (.up ℤ)` consisting of bounded below cochain complexes.
open CategoryTheory Limits ZeroObject Pretriangulated HomotopicalAlgebra
-variable (C : Type*) [Category* C] [Preadditive C]
+variable (C D : Type*) [Category* C] [Category* D] [Preadditive C] [Preadditive D]
(A : Type*) [Category* A] [Abelian A]
namespace CochainComplex
@@ -248,3 +248,61 @@ end
end Plus
end HomotopyCategory
+
+namespace CategoryTheory
+
+namespace Functor
+
+variable {C D}
+variable (F : C ⥤ D) [F.Additive]
+
+set_option backward.defeqAttrib.useBackward true in
+/-- The functor between bounded below homotopy categories that is induced
+by an additive functor. -/
+def mapHomotopyCategoryPlus : HomotopyCategory.Plus C ⥤ HomotopyCategory.Plus D :=
+ (HomotopyCategory.plus D).lift
+ (HomotopyCategory.Plus.ι C ⋙ F.mapHomotopyCategory (ComplexShape.up ℤ)) (by
+ rintro ⟨X, hX⟩
+ obtain ⟨K, rfl⟩ := HomotopyCategory.quotient_obj_surjective X
+ dsimp
+ simp only [HomotopyCategory.plus_quotient_obj_iff] at hX ⊢
+ obtain ⟨n, _⟩ := hX
+ exact ⟨n, inferInstanceAs (CochainComplex.IsStrictlyGE
+ ((F.mapHomologicalComplex _).obj K) n)⟩)
+
+noncomputable instance :
+ F.mapHomotopyCategoryPlus.CommShift ℤ :=
+ inferInstanceAs (((HomotopyCategory.plus D).lift (HomotopyCategory.Plus.ι C ⋙
+ F.mapHomotopyCategory (.up ℤ)) _).CommShift ℤ)
+
+set_option backward.isDefEq.respectTransparency false in
+instance [HasZeroObject C] [HasBinaryBiproducts C] [HasZeroObject D] [HasBinaryBiproducts D] :
+ (F.mapHomotopyCategoryPlus).IsTriangulated := by
+ dsimp only [mapHomotopyCategoryPlus]
+ infer_instance
+
+instance [Full F] [Faithful F] : Full F.mapHomotopyCategoryPlus where
+ map_surjective f :=
+ ⟨ObjectProperty.homMk ((F.mapHomotopyCategory _).preimage f.hom), by
+ ext
+ exact (F.mapHomotopyCategory _).map_preimage f.hom⟩
+
+instance [Full F] [Faithful F] : Faithful F.mapHomotopyCategoryPlus where
+ map_injective h := by
+ ext
+ exact (F.mapHomotopyCategory _).map_injective ((ObjectProperty.ι _).congr_map h)
+
+/-- Given additive functors that are related by an isomorphism `F ⋙ G ≅ H`, this is
+the corresponding isomorphism on the corresponding functor between
+the bounded below homotopy categories. -/
+def mapHomotopyCategoryPlusCompIso {E : Type*} [Category* E] [Preadditive E]
+ {F : C ⥤ D} {G : D ⥤ E} {H : C ⥤ E} (e : F ⋙ G ≅ H)
+ [F.Additive] [G.Additive] [H.Additive] :
+ F.mapHomotopyCategoryPlus ⋙ G.mapHomotopyCategoryPlus ≅ H.mapHomotopyCategoryPlus :=
+ ((HomotopyCategory.plus _).fullyFaithfulι.whiskeringRight _).preimageIso
+ (isoWhiskerLeft (HomotopyCategory.Plus.ι C)
+ (mapHomotopyCategoryCompIso e (.up ℤ)))
+
+end Functor
+
+end CategoryTheory
From a8d8ebb9327970a699d0d5eb3ce0ddf275bfd4f6 Mon Sep 17 00:00:00 2001
From: smorel394 <67864981+smorel394@users.noreply.github.com>
Date: Thu, 25 Jun 2026 09:58:27 +0000
Subject: [PATCH 0341/1300]
chore(CategoryTheory/Limits/Shapes/Pullbacks/HasPullback): fix typo (#41024)
Change definition `pullback.desc'` (which is about pushouts) to `pushout.desc'`.
Co-authored-by: morel
---
.../Limits/Shapes/Pullback/HasPullback.lean | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/Mathlib/CategoryTheory/Limits/Shapes/Pullback/HasPullback.lean b/Mathlib/CategoryTheory/Limits/Shapes/Pullback/HasPullback.lean
index 3cf75a355296f5..ad855e72608a3d 100644
--- a/Mathlib/CategoryTheory/Limits/Shapes/Pullback/HasPullback.lean
+++ b/Mathlib/CategoryTheory/Limits/Shapes/Pullback/HasPullback.lean
@@ -194,16 +194,19 @@ theorem pushout.inr_desc {W X Y Z : C} {f : X ⟶ Y} {g : X ⟶ Z} [HasPushout f
`l : W ⟶ pullback f g` such that `l ≫ pullback.fst = h` and `l ≫ pullback.snd = k`. -/
def pullback.lift' {W X Y Z : C} {f : X ⟶ Z} {g : Y ⟶ Z} [HasPullback f g] (h : W ⟶ X) (k : W ⟶ Y)
(w : h ≫ f = k ≫ g) :
- { l : W ⟶ pullback f g // l ≫ pullback.fst f g = h ∧ l ≫ pullback.snd f g = k } :=
+ { l : W ⟶ pullback f g // l ≫ pullback.fst f g = h ∧ l ≫ pullback.snd f g = k } :=
⟨pullback.lift h k w, pullback.lift_fst _ _ _, pullback.lift_snd _ _ _⟩
/-- A pair of morphisms `h : Y ⟶ W` and `k : Z ⟶ W` satisfying `f ≫ h = g ≫ k` induces a morphism
`l : pushout f g ⟶ W` such that `pushout.inl _ _ ≫ l = h` and `pushout.inr _ _ ≫ l = k`. -/
-def pullback.desc' {W X Y Z : C} {f : X ⟶ Y} {g : X ⟶ Z} [HasPushout f g] (h : Y ⟶ W) (k : Z ⟶ W)
+def pushout.desc' {W X Y Z : C} {f : X ⟶ Y} {g : X ⟶ Z} [HasPushout f g] (h : Y ⟶ W) (k : Z ⟶ W)
(w : f ≫ h = g ≫ k) :
- { l : pushout f g ⟶ W // pushout.inl _ _ ≫ l = h ∧ pushout.inr _ _ ≫ l = k } :=
+ { l : pushout f g ⟶ W // pushout.inl _ _ ≫ l = h ∧ pushout.inr _ _ ≫ l = k } :=
⟨pushout.desc h k w, pushout.inl_desc _ _ _, pushout.inr_desc _ _ _⟩
+@[deprecated (since := "2026-06-25")]
+alias CategoryTheory.Limits.pullback.desc' := pushout.desc'
+
@[reassoc]
theorem pullback.condition {X Y Z : C} {f : X ⟶ Z} {g : Y ⟶ Z} [HasPullback f g] :
pullback.fst f g ≫ f = pullback.snd f g ≫ g :=
From 50a441759cb137dbfc5ee2b83a8b42115e4ce0e4 Mon Sep 17 00:00:00 2001
From: Christian Merten <136261474+chrisflav@users.noreply.github.com>
Date: Thu, 25 Jun 2026 10:21:48 +0000
Subject: [PATCH 0342/1300] chore(CategoryTheory): lemmas for morphisms into
colimits (#41011)
We add some variants of lemmas with presentability replaced by `Hom(X, _)` preserving certain colimits.
From Proetale.
---
Mathlib/CategoryTheory/Presentable/Basic.lean | 61 ++++++++++++++++---
1 file changed, 53 insertions(+), 8 deletions(-)
diff --git a/Mathlib/CategoryTheory/Presentable/Basic.lean b/Mathlib/CategoryTheory/Presentable/Basic.lean
index 36fd4d00df525e..f1d14fbe781e7c 100644
--- a/Mathlib/CategoryTheory/Presentable/Basic.lean
+++ b/Mathlib/CategoryTheory/Presentable/Basic.lean
@@ -237,35 +237,80 @@ lemma isCardinalPresentable_iff_of_isEquivalence
· intro
infer_instance
+section
+
+variable {J : Type*} [Category* J] {D : J ⥤ C}
+
+lemma Limits.exists_hom_of_preservesColimit_coyoneda {c : Cocone D} (hc : IsColimit c) {X : C}
+ [PreservesColimit D (coyoneda.obj (.op X))] (f : X ⟶ c.pt) :
+ ∃ (j : J) (p : X ⟶ D.obj j), p ≫ c.ι.app j = f :=
+ Types.jointly_surjective_of_isColimit (isColimitOfPreserves (coyoneda.obj (.op X)) hc) f
+
+lemma Limits.exists_eq_of_preservesColimit_coyoneda [IsFiltered J] {c : Cocone D}
+ (hc : IsColimit c) {X : C} [PreservesColimit D (coyoneda.obj (.op X))]
+ {i j : J} (f : X ⟶ D.obj i) (g : X ⟶ D.obj j) (h : f ≫ c.ι.app i = g ≫ c.ι.app j) :
+ ∃ (k : J) (u : i ⟶ k) (v : j ⟶ k), f ≫ D.map u = g ≫ D.map v :=
+ (Types.FilteredColimit.isColimit_eq_iff _ (isColimitOfPreserves (coyoneda.obj (.op X)) hc)).mp h
+
+lemma Limits.exists_eq_of_preservesColimit_coyoneda_self [IsFiltered J] {c : Cocone D}
+ (hc : IsColimit c) {X : C} [PreservesColimit D (coyoneda.obj (.op X))]
+ {i : J} (f g : X ⟶ D.obj i) (h : f ≫ c.ι.app i = g ≫ c.ι.app i) :
+ ∃ (j : J) (a : i ⟶ j), f ≫ D.map a = g ≫ D.map a :=
+ (Types.FilteredColimit.isColimit_eq_iff'
+ (isColimitOfPreserves (coyoneda.obj (.op X)) hc) f g).mp h
+
+lemma Limits.exists_hom_of_preservesColimit_yoneda {c : Cone D} (hc : IsLimit c) {X : C}
+ [PreservesColimit D.op (yoneda.obj X)] (f : c.pt ⟶ X) :
+ ∃ (j : J) (p : D.obj j ⟶ X), c.π.app j ≫ p = f := by
+ obtain ⟨j, p, hp⟩ := Types.jointly_surjective_of_isColimit
+ (isColimitOfPreserves (yoneda.obj X) hc.op) f
+ exact ⟨j.unop, p, hp⟩
+
+lemma Limits.exists_eq_of_preservesColimit_yoneda [IsCofiltered J] {c : Cone D} (hc : IsLimit c)
+ {X : C} [PreservesColimit D.op (yoneda.obj X)]
+ {i j : J} (f : D.obj i ⟶ X) (g : D.obj j ⟶ X) (h : c.π.app i ≫ f = c.π.app j ≫ g) :
+ ∃ (k : J) (u : k ⟶ i) (v : k ⟶ j), D.map u ≫ f = D.map v ≫ g := by
+ obtain ⟨k, u, v, huv⟩ :=
+ (Types.FilteredColimit.isColimit_eq_iff _ (isColimitOfPreserves (yoneda.obj X) hc.op)).mp h
+ exact ⟨k.unop, u.unop, v.unop, huv⟩
+
+lemma Limits.exists_eq_of_preservesColimit_yoneda_self [IsCofiltered J] {c : Cone D}
+ (hc : IsLimit c) {X : C} [PreservesColimit D.op (yoneda.obj X)]
+ {i : J} (f g : D.obj i ⟶ X) (h : c.π.app i ≫ f = c.π.app i ≫ g) :
+ ∃ (j : J) (a : j ⟶ i), D.map a ≫ f = D.map a ≫ g := by
+ obtain ⟨j, a, ha⟩ := (Types.FilteredColimit.isColimit_eq_iff'
+ (isColimitOfPreserves (yoneda.obj X) hc.op) f g).mp h
+ exact ⟨j.unop, a.unop, ha⟩
+
variable {X} in
lemma IsCardinalPresentable.exists_hom_of_isColimit [IsCardinalPresentable X κ]
- {J : Type u₂} [Category.{v₂} J] [EssentiallySmall.{w} J] [IsCardinalFiltered J κ]
+ [EssentiallySmall.{w} J] [IsCardinalFiltered J κ]
{F : J ⥤ C} {c : Cocone F} (hc : IsColimit c) (f : X ⟶ c.pt) :
∃ (j : J) (f' : X ⟶ F.obj j), f' ≫ c.ι.app j = f := by
have := preservesColimitsOfShape_of_isCardinalPresentable_of_essentiallySmall X κ J
- exact Types.jointly_surjective_of_isColimit (isColimitOfPreserves (coyoneda.obj (op X)) hc) f
+ exact exists_hom_of_preservesColimit_coyoneda hc f
variable {X} in
lemma IsCardinalPresentable.exists_eq_of_isColimit [IsCardinalPresentable X κ]
- {J : Type u₂} [Category.{v₂} J] [EssentiallySmall.{w} J] [IsCardinalFiltered J κ]
+ [EssentiallySmall.{w} J] [IsCardinalFiltered J κ]
{F : J ⥤ C} {c : Cocone F} (hc : IsColimit c) {i₁ i₂ : J} (f₁ : X ⟶ F.obj i₁)
(f₂ : X ⟶ F.obj i₂) (hf : f₁ ≫ c.ι.app i₁ = f₂ ≫ c.ι.app i₂) :
∃ (j : J) (u : i₁ ⟶ j) (v : i₂ ⟶ j), f₁ ≫ F.map u = f₂ ≫ F.map v := by
have := preservesColimitsOfShape_of_isCardinalPresentable_of_essentiallySmall X κ J
have := isFiltered_of_isCardinalFiltered J κ
- exact (Types.FilteredColimit.isColimit_eq_iff _
- (isColimitOfPreserves (coyoneda.obj (op X)) hc)).1 hf
+ exact exists_eq_of_preservesColimit_coyoneda hc f₁ f₂ hf
variable {X} in
lemma IsCardinalPresentable.exists_eq_of_isColimit' [IsCardinalPresentable X κ]
- {J : Type u₂} [Category.{v₂} J] [EssentiallySmall.{w} J] [IsCardinalFiltered J κ]
+ [EssentiallySmall.{w} J] [IsCardinalFiltered J κ]
{F : J ⥤ C} {c : Cocone F} (hc : IsColimit c) {i : J} (f₁ f₂ : X ⟶ F.obj i)
(hf : f₁ ≫ c.ι.app i = f₂ ≫ c.ι.app i) :
∃ (j : J) (u : i ⟶ j), f₁ ≫ F.map u = f₂ ≫ F.map u := by
have := preservesColimitsOfShape_of_isCardinalPresentable_of_essentiallySmall X κ J
have := isFiltered_of_isCardinalFiltered J κ
- exact (Types.FilteredColimit.isColimit_eq_iff'
- (isColimitOfPreserves (coyoneda.obj (op X)) hc) f₁ f₂).1 hf
+ exact exists_eq_of_preservesColimit_coyoneda_self hc f₁ f₂ hf
+
+end
lemma isCardinalPresentable_iff_isCardinalAccessible_uliftCoyoneda_obj :
IsCardinalPresentable X κ ↔ (uliftCoyoneda.{t}.obj (op X)).IsCardinalAccessible κ := by
From c3bd83c0a77fbb61e1417b2974ac87cd93970be6 Mon Sep 17 00:00:00 2001
From: William Coram
Date: Thu, 25 Jun 2026 10:54:58 +0000
Subject: [PATCH 0343/1300] feat: final lemmas needed for showing gaussNorm on
MvPowerSeries is an absolute value (#40997)
We finish our section on showing that the gaussNorm on MvRestricted power series will be an absolute value by giving the neg and mul_eq_mul lemmas.
Co-authored-by: WilliamCoram
---
.../RingTheory/MvPowerSeries/GaussNorm.lean | 37 +++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/Mathlib/RingTheory/MvPowerSeries/GaussNorm.lean b/Mathlib/RingTheory/MvPowerSeries/GaussNorm.lean
index b26a1d6e4c6903..37d55774ebae66 100644
--- a/Mathlib/RingTheory/MvPowerSeries/GaussNorm.lean
+++ b/Mathlib/RingTheory/MvPowerSeries/GaussNorm.lean
@@ -33,6 +33,13 @@ the set of all values of `v (coeff t f) * ∏ i : t.support, c i` for all `t :
* `MvPowerSeries.gaussNorm_add_le_max`: if `v` is a non-negative non-archimedean function and the
set of values `v (coeff t f) * ∏ i : t.support, c i` is bounded above (similarly for `g`), then
the Gauss norm has the non-archimedean property.
+
+* `MvPowerSeries.AchievesGaussNorm`: a type `i` is said to achieve gauss norm if
+ `v (coeff i f) * i.prod (c · ^ ·) = gaussNorm v c f`.
+
+* `MvPowerSeries.gaussNorm_neg`: if `v` has the property that `∀ i, v i = v (-i)` then
+ `gaussNorm v c (-f) = gaussNorm v c f `.
+
-/
@[expose] public section
@@ -164,6 +171,12 @@ variable [Ring R]
abbrev AchievesGaussNorm (i : σ →₀ ℕ) : Prop :=
v (coeff i f) * i.prod (c · ^ ·) = gaussNorm v c f
+lemma gaussNorm_neg (vNeg : ∀ x, v (-x) = v x) (f : MvPowerSeries σ R) :
+ gaussNorm v c (-f) = gaussNorm v c f := by
+ simp_rw [gaussNorm]
+ have (t : σ →₀ ℕ) : (coeff t) (-f) = - (coeff t) f := by rfl
+ simp_rw [this, vNeg]
+
section absoluteValue
variable {α S : Type*} [LinearOrder S] [AddCommGroup α] (f : α → S)
@@ -226,6 +239,30 @@ lemma gaussNorm_le_mul (vMulEq : ∀ a b, v (a * b) = v a * v b)
rw [antidiagonal_dominant v f g i₀ j₀ vna vMulEq vNeg hdom']
_ ≤ gaussNorm v c (f * g) := le_gaussNorm v c (f * g) hbfg (i₀ + j₀)
+lemma gaussNorm_mul_eq_mul (f g : MvPowerSeries σ R) (hf : HasGaussNorm v c f)
+ (hg : HasGaussNorm v c g) (hfg : HasGaussNorm v c (f * g))
+ (vNonneg : ∀ a, v a ≥ 0) (vZero : v 0 = 0) (vNA : IsNonarchimedean v)
+ (vMulEq : ∀ (a b : R), v (a * b) = v a * v b) (vNeg : ∀ (a : R), v (-a) = v a)
+ (h_eq_zero : ∀ (x : R), v x = 0 → x = 0) (hc : ∀ (i : σ), 0 < c i)
+ (hdom : ∃ i j, AchievesGaussNorm v c f i ∧ AchievesGaussNorm v c g j ∧
+ ∀ p ∈ Finset.antidiagonal (i + j), p ≠ (i, j) → v (coeff p.1 f * coeff p.2 g) <
+ v (coeff i f) * v (coeff j g)) :
+ gaussNorm v c (f * g) = gaussNorm v c f * gaussNorm v c g := by
+ by_cases hf' : f = 0
+ · simp [hf', gaussNorm_zero v c vZero]
+ by_cases hg' : g = 0
+ · simp [hg', gaussNorm_zero v c vZero]
+ have hf1 : gaussNorm v c f ≠ 0 := by
+ convert gaussNorm_eq_zero_iff v c f vZero vNonneg h_eq_zero hc hf
+ grind
+ have hg1 : gaussNorm v c g ≠ 0 := by
+ convert gaussNorm_eq_zero_iff v c g vZero vNonneg h_eq_zero hc hg
+ grind
+ apply ge_antisymm_iff.mpr
+ constructor
+ · exact gaussNorm_le_mul v c f g vMulEq vNA (by grind) hfg hdom
+ · exact gaussNorm_mul_le v c f g (StrongLT.le hc) vNonneg (by grind) vNA vZero hf hg
+
end absoluteValue
end MvPowerSeries
From 008653f6c054a0cfcc8b43992bcffe5b7c20b410 Mon Sep 17 00:00:00 2001
From: Christian Merten <136261474+chrisflav@users.noreply.github.com>
Date: Thu, 25 Jun 2026 11:32:15 +0000
Subject: [PATCH 0344/1300] feat(AlgebraicGeometry): restriction of
quasi-coherent sheaf is quasi-coherent (#37766)
---
Mathlib/AlgebraicGeometry/Modules/Tilde.lean | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/Mathlib/AlgebraicGeometry/Modules/Tilde.lean b/Mathlib/AlgebraicGeometry/Modules/Tilde.lean
index f8c2feffe37612..a7fa7625846a7f 100644
--- a/Mathlib/AlgebraicGeometry/Modules/Tilde.lean
+++ b/Mathlib/AlgebraicGeometry/Modules/Tilde.lean
@@ -554,6 +554,21 @@ theorem isIso_fromTildeΓ_pushforward (M : (Spec S).Modules) [h : IsIso M.fromTi
end IsLocalizing
+set_option backward.isDefEq.respectTransparency false in
+instance Scheme.Modules.isQuasicoherent_restrictFunctor {X Y : Scheme.{u}} (f : X ⟶ Y)
+ [IsOpenImmersion f] (M : Y.Modules) [M.IsQuasicoherent] :
+ ((restrictFunctor f).obj M).IsQuasicoherent := by
+ let α : X.presheaf ⟶ f.opensFunctor.op ⋙ Y.presheaf := { app U := (f.appIso U.unop).inv }
+ have hα : IsIso α := NatIso.isIso_of_isIso_app _
+ let φ : X.ringCatSheaf ⟶ (f.opensFunctor.sheafPushforwardContinuous _ _ _).obj Y.ringCatSheaf :=
+ ⟨Functor.whiskerRight α (forget₂ CommRingCat RingCat)⟩
+ have : IsIso φ := by
+ rw [← isIso_iff_of_reflects_iso _ (ObjectProperty.ι _)]
+ dsimp [φ]
+ infer_instance
+ exact SheafOfModules.isQuasicoherent_pushforward_of_isLeftAdjoint.{u}
+ f.opensFunctor φ (Scheme.Modules.restrictUnitIso _)
+
set_option backward.isDefEq.respectTransparency false in
/-- The presentation of `M.restrict f` by restricting a presentation of `M`. -/
def Scheme.Modules.presentationRestrict {X Y : Scheme.{u}} (f : Y ⟶ X)
From 0eabedc61dc068bde900285b55b7a174278ebf3e Mon Sep 17 00:00:00 2001
From: Anatole Dedecker
Date: Thu, 25 Jun 2026 12:00:33 +0000
Subject: [PATCH 0345/1300] feat: if S is disjoint from a finite-codim closed
submodule T, then S is complemented (#41033)
On the road towards Fredholm operators
---
Mathlib/Topology/Algebra/Module/FiniteDimension.lean | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/Mathlib/Topology/Algebra/Module/FiniteDimension.lean b/Mathlib/Topology/Algebra/Module/FiniteDimension.lean
index 67c5ce664b99c6..9e16cef2540d80 100644
--- a/Mathlib/Topology/Algebra/Module/FiniteDimension.lean
+++ b/Mathlib/Topology/Algebra/Module/FiniteDimension.lean
@@ -10,6 +10,7 @@ public import Mathlib.Analysis.LocallyConvex.Bounded
public import Mathlib.Analysis.Normed.Module.Basic
public import Mathlib.Analysis.SpecificLimits.Normed
public import Mathlib.LinearAlgebra.FiniteDimensional.Lemmas
+public import Mathlib.RingTheory.Finiteness.Cofinite
public import Mathlib.RingTheory.LocalRing.Basic
public import Mathlib.Topology.Algebra.Module.Determinant
public import Mathlib.Topology.Algebra.Module.ModuleTopology
@@ -739,6 +740,14 @@ theorem Submodule.ClosedComplemented.of_finiteDimensional_quotient {p : Submodul
alias Submodule.ClosedComplemented.of_quotient_finiteDimensional :=
Submodule.ClosedComplemented.of_finiteDimensional_quotient
+theorem Submodule.ClosedComplemented.of_disjoint_of_finiteDimensional_quotient
+ {A B : Submodule 𝕜 E} [B_cofg : FiniteDimensional 𝕜 (E ⧸ B)] (hB : IsClosed (B : Set E))
+ (hAB : Disjoint A B) : A.ClosedComplemented := by
+ obtain ⟨C, B_le_C, C_compl_A⟩ := hAB.symm.exists_isCompl
+ have C_cofg : FiniteDimensional 𝕜 (E ⧸ C) := CoFG.of_le B_le_C B_cofg
+ have hC : IsClosed (C : Set E) := isClosed_mono_of_finiteDimensional_quotient hB B_le_C
+ exact C_compl_A.isTopCompl_of_finiteDimensional_quotient hC |>.symm.closedComplemented
+
lemma Submodule.ClosedComplemented.of_finiteDimensional_of_le
{A B : Submodule 𝕜 E} [FiniteDimensional 𝕜 A] (hA : A.ClosedComplemented) [T2Space A]
(hB : B ≤ A) : B.ClosedComplemented := by
From 918d46759650d493222b403b05dae2d7ecfd04b8 Mon Sep 17 00:00:00 2001
From: Anatole Dedecker
Date: Thu, 25 Jun 2026 12:17:16 +0000
Subject: [PATCH 0346/1300] feat: a submodule disjoint from a coFG module is FG
(#41008)
... over a noetherian ring.
Also, over an arbitrary ring, a submodule codisjoint from a fg module is cofg.
This is a prerequisite for Fredholm operators.
---
Mathlib/RingTheory/Finiteness/Cofinite.lean | 16 +++++++++++++---
Mathlib/RingTheory/Noetherian/Basic.lean | 6 ++++++
2 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/Mathlib/RingTheory/Finiteness/Cofinite.lean b/Mathlib/RingTheory/Finiteness/Cofinite.lean
index 7e23470116e2b4..98924e5a4a6fcd 100644
--- a/Mathlib/RingTheory/Finiteness/Cofinite.lean
+++ b/Mathlib/RingTheory/Finiteness/Cofinite.lean
@@ -49,10 +49,20 @@ theorem _root_.Module.Finite.iff_cofg_bot : (⊥ : Submodule R M).CoFG ↔ Modul
theorem CoFG.fg_of_isCompl {S T : Submodule R M} (hST : IsCompl S T) (hS : S.CoFG) : T.FG :=
Module.Finite.iff_fg.mp <| Module.Finite.equiv <| quotientEquivOfIsCompl S T hST
+/-- Over a noetherian ring, if `S` and `T` are disjoint and `T` is CoFG, then `S` is FG. -/
+theorem CoFG.fg_of_disjoint [IsNoetherianRing R] {S T : Submodule R M} (hST : Disjoint S T)
+ (hT : T.CoFG) : S.FG :=
+ .of_disjoint_of_isNoetherian_quotient hST
+
+/-- If `S` and `T` are co-disjoint and `S` is FG, then `T` is CoFG. -/
+theorem FG.cofg_of_codisjoint {S T : Submodule R M} (hST : Codisjoint S T) (hS : S.FG) :
+ T.CoFG :=
+ have := Module.Finite.iff_fg.mpr hS
+ .of_surjective (T.mkQ.domRestrict S) (by simp [← LinearMap.range_eq_top, hST.symm.eq_top])
+
/-- A complement of an FG submodule is CoFG. -/
-theorem FG.cofg_of_isCompl {S T : Submodule R M} (hST : IsCompl S T) (hS : S.FG) : T.CoFG := by
- haveI := Module.Finite.iff_fg.mpr hS
- exact Module.Finite.equiv (quotientEquivOfIsCompl T S hST.symm).symm
+theorem FG.cofg_of_isCompl {S T : Submodule R M} (hST : IsCompl S T) (hS : S.FG) : T.CoFG :=
+ hS.cofg_of_codisjoint hST.codisjoint
/-- A submodule that contains a CoFG submodule is CoFG. -/
theorem CoFG.of_le {S T : Submodule R M} (hT : S ≤ T) (hS : S.CoFG) : T.CoFG := by
diff --git a/Mathlib/RingTheory/Noetherian/Basic.lean b/Mathlib/RingTheory/Noetherian/Basic.lean
index 8e31fb6142f586..324118d0f9e31a 100644
--- a/Mathlib/RingTheory/Noetherian/Basic.lean
+++ b/Mathlib/RingTheory/Noetherian/Basic.lean
@@ -382,6 +382,12 @@ lemma FG.of_le [IsNoetherianRing R] {S T : Submodule R M} (hT : T.FG) (hST : S
rw [← Module.Finite.iff_fg] at hT
exact FG.of_le_of_isNoetherian hST
+/-- If `S` is disjoint from `T` and `M ⧸ T` is a noetherian module, then `S` is FG.
+See also `Submodule.CoFG.fg_of_disjoint`. -/
+theorem FG.of_disjoint_of_isNoetherian_quotient {S T : Submodule R M} [IsNoetherian R (M ⧸ T)]
+ (hST : Disjoint S T) : S.FG :=
+ Module.Finite.iff_fg.mp <| .of_injective (T.mkQ.domRestrict S) (by simp [hST.eq_bot])
+
end Submodule
universe w v u
From 9e80b1a3dbfb369c229f1f0e62fbf48032cf9c9b Mon Sep 17 00:00:00 2001
From: Michael Rothgang <10105016+grunweg@users.noreply.github.com>
Date: Thu, 25 Jun 2026 12:17:18 +0000
Subject: [PATCH 0347/1300] fix(Geometry/Manifold/VectorBundle/Hom): fix typo
in doc-string (#41025)
Discovered while working on #36036.
---
Mathlib/Geometry/Manifold/VectorBundle/Hom.lean | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/Mathlib/Geometry/Manifold/VectorBundle/Hom.lean b/Mathlib/Geometry/Manifold/VectorBundle/Hom.lean
index 654aab97adf6ee..661dd8ab0a85c0 100644
--- a/Mathlib/Geometry/Manifold/VectorBundle/Hom.lean
+++ b/Mathlib/Geometry/Manifold/VectorBundle/Hom.lean
@@ -360,7 +360,7 @@ section TwoVariables
variable [∀ x, IsTopologicalAddGroup (E₃ x)] [∀ x, ContinuousSMul 𝕜 (E₃ x)]
{ψ : ∀ x, (E₁ (b x) →L[𝕜] E₂ (b x) →L[𝕜] E₃ (b x))} {w : ∀ x, E₂ (b x)}
-/-- Consider `C^n` maps `v : M → E₁` and `v : M → E₂` to vector bundles, over a base map
+/-- Consider `C^n` maps `v : M → E₁` and `w : M → E₂` to vector bundles, over a base map
`b : M → B`, and bilinear maps `ψ m : E₁ (b m) → E₂ (b m) → E₃ (b m)` depending smoothly on `m`.
One can apply `ψ m` to `v m` and `w m`, and the resulting map is `C^n`.
@@ -373,7 +373,7 @@ lemma ContMDiffWithinAt.clm_bundle_apply₂
CMDiffAt[s] n (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) x :=
hψ.clm_bundle_apply hv |>.clm_bundle_apply hw
-/-- Consider `C^n` maps `v : M → E₁` and `v : M → E₂` to vector bundles, over a base map
+/-- Consider `C^n` maps `v : M → E₁` and `w : M → E₂` to vector bundles, over a base map
`b : M → B`, and bilinear maps `ψ m : E₁ (b m) → E₂ (b m) → E₃ (b m)` depending smoothly on `m`.
One can apply `ψ m` to `v m` and `w m`, and the resulting map is `C^n`.
@@ -386,7 +386,7 @@ lemma ContMDiffAt.clm_bundle_apply₂
CMDiffAt n (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) x :=
ContMDiffWithinAt.clm_bundle_apply₂ hψ hv hw
-/-- Consider `C^n` maps `v : M → E₁` and `v : M → E₂` to vector bundles, over a base map
+/-- Consider `C^n` maps `v : M → E₁` and `w : M → E₂` to vector bundles, over a base map
`b : M → B`, and bilinear maps `ψ m : E₁ (b m) → E₂ (b m) → E₃ (b m)` depending smoothly on `m`.
One can apply `ψ m` to `v m` and `w m`, and the resulting map is `C^n`.
@@ -399,7 +399,7 @@ lemma ContMDiffOn.clm_bundle_apply₂
CMDiff[s] n (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) :=
fun x hx ↦ (hψ x hx).clm_bundle_apply₂ (hv x hx) (hw x hx)
-/-- Consider `C^n` maps `v : M → E₁` and `v : M → E₂` to vector bundles, over a base map
+/-- Consider `C^n` maps `v : M → E₁` and `w : M → E₂` to vector bundles, over a base map
`b : M → B`, and bilinear maps `ψ m : E₁ (b m) → E₂ (b m) → E₃ (b m)` depending smoothly on `m`.
One can apply `ψ m` to `v m` and `w m`, and the resulting map is `C^n`. -/
lemma ContMDiff.clm_bundle_apply₂
@@ -417,7 +417,7 @@ section TwoVariables'
variable [∀ x, IsTopologicalAddGroup (E₃ x)] [∀ x, ContinuousSMul 𝕜 (E₃ x)]
{ψ : ∀ x, (E₁ (b x) →L[𝕜] E₂ (b x) →L[𝕜] E₃ (b x))} {w : ∀ x, E₂ (b x)}
-/-- Consider differentiable maps `v : M → E₁` and `v : M → E₂` to vector bundles, over a base map
+/-- Consider differentiable maps `v : M → E₁` and `w : M → E₂` to vector bundles, over a base map
`b : M → B`, and bilinear maps `ψ m : E₁ (b m) → E₂ (b m) → E₃ (b m)` depending smoothly on `m`.
One can apply `ψ m` to `v m` and `w m`, and the resulting map is differentiable.
@@ -430,7 +430,7 @@ lemma MDifferentiableWithinAt.clm_bundle_apply₂
MDiffAt[s] (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) x :=
hψ.clm_bundle_apply hv |>.clm_bundle_apply hw
-/-- Consider differentiable maps `v : M → E₁` and `v : M → E₂` to vector bundles, over a base map
+/-- Consider differentiable maps `v : M → E₁` and `w : M → E₂` to vector bundles, over a base map
`b : M → B`, and bilinear maps `ψ m : E₁ (b m) → E₂ (b m) → E₃ (b m)` depending smoothly on `m`.
One can apply `ψ m` to `v m` and `w m`, and the resulting map is differentiable.
@@ -443,7 +443,7 @@ lemma MDifferentiableAt.clm_bundle_apply₂
MDiffAt (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) x :=
MDifferentiableWithinAt.clm_bundle_apply₂ hψ hv hw
-/-- Consider differentiable maps `v : M → E₁` and `v : M → E₂` to vector bundles, over a base map
+/-- Consider differentiable maps `v : M → E₁` and `w : M → E₂` to vector bundles, over a base map
`b : M → B`, and bilinear maps `ψ m : E₁ (b m) → E₂ (b m) → E₃ (b m)` depending smoothly on `m`.
One can apply `ψ m` to `v m` and `w m`, and the resulting map is differentiable.
@@ -456,7 +456,7 @@ lemma MDifferentiableOn.clm_bundle_apply₂
MDiff[s] (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) :=
fun x hx ↦ (hψ x hx).clm_bundle_apply₂ (hv x hx) (hw x hx)
-/-- Consider differentiable maps `v : M → E₁` and `v : M → E₂` to vector bundles, over a base map
+/-- Consider differentiable maps `v : M → E₁` and `w : M → E₂` to vector bundles, over a base map
`b : M → B`, and bilinear maps `ψ m : E₁ (b m) → E₂ (b m) → E₃ (b m)` depending smoothly on `m`.
One can apply `ψ m` to `v m` and `w m`, and the resulting map is differentiable. -/
lemma MDifferentiable.clm_bundle_apply₂
From 38f43abaf3ed4853a783dd1f99f9ef28884ea77c Mon Sep 17 00:00:00 2001
From: Wenrong Zou <141128015+WenrongZou@users.noreply.github.com>
Date: Thu, 25 Jun 2026 12:42:13 +0000
Subject: [PATCH 0348/1300] feat(Date/Choose): add some lemmas about choose of
prime pow (#38317)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
In this PR, I add some lemma about choose of prime pow.
1) For primes `p` and positive integer `n`, assume that for all `i ∈ Icc 1 (n - 1)`, `choose n i` congruent to `0` module `p`, then `n = p ^ multiplicity p n`.
2) For a prime power `n`, the greatest common divisor of `choose n 1, ⋯, choose n (n - 1)` is actually the minimal prime factor of `n`.
3) For a natural number `n` greater than `1`, assume that `n` is not a prime power, then the greatest common divisor of `choose n 1, ⋯, choose n (n - 1)` is `1`.
Co-authored-by: WenrongZou
---
Mathlib/Data/Nat/Choose/Lucas.lean | 93 ++++++++++++++++++++++++++++++
1 file changed, 93 insertions(+)
diff --git a/Mathlib/Data/Nat/Choose/Lucas.lean b/Mathlib/Data/Nat/Choose/Lucas.lean
index 5b7ca9df72bacb..cc7f66fdd02fa5 100644
--- a/Mathlib/Data/Nat/Choose/Lucas.lean
+++ b/Mathlib/Data/Nat/Choose/Lucas.lean
@@ -132,4 +132,97 @@ theorem choose_pow_mul_pow_mul_modEq_choose_nat :
rw [← Int.natCast_modEq_iff]
exact_mod_cast choose_pow_mul_pow_mul_modEq_choose
+/-- For primes `p` and positive integer `n`, assume that for all `i ∈ Icc 1 (n - 1)`,
+`choose n i` congruent to `0` module `p`, then `n = p ^ multiplicity p n`.
+Also see `eq_pow_multiplicity_of_choose_modEq_zero_nat` for the version with `MOD`. -/
+theorem eq_pow_multiplicity_of_choose_modEq_zero (hn : 0 < n)
+ (h : ∀ i ∈ Icc 1 (n - 1), n.choose i ≡ 0 [ZMOD p]) : n = p ^ multiplicity p n := by
+ rename_i hp
+ by_contra! hn₀
+ obtain ⟨m, hm⟩ := pow_multiplicity_dvd p n
+ specialize h (p ^ multiplicity p n) (by grind [le_of_dvd hn (pow_multiplicity_dvd p n)])
+ nth_grw 1 [← mul_one (p ^ _), hm, choose_pow_mul_pow_mul_modEq_choose, choose_one_right] at h
+ suffices multiplicity p n + 1 ≤ multiplicity p n by lia
+ rw [← FiniteMultiplicity.pow_dvd_iff_le_multiplicity]
+ · nth_rw 2 [hm]
+ simpa [pow_add] using Nat.mul_dvd_mul_left _ (dvd_iff_mod_eq_zero.mpr (by exact_mod_cast h))
+ · exact finiteMultiplicity_iff.mpr ⟨hp.out.ne_one, hn⟩
+
+/-- For primes `p` and positive integer `n`, assume that for all `i ∈ Icc 1 (n - 1)`,
+`choose n i` congruent to `0` module `p`, then `n = p ^ multiplicity p n`.
+Also see `eq_pow_multiplicity_of_choose_modEq_zero` for the version with `ZMOD`. -/
+theorem eq_pow_multiplicity_of_choose_modEq_zero_nat (hn : 0 < n)
+ (h : ∀ i ∈ Icc 1 (n - 1), n.choose i ≡ 0 [MOD p]) : n = p ^ multiplicity p n :=
+ eq_pow_multiplicity_of_choose_modEq_zero hn (by exact_mod_cast h)
+
+/-- For a prime power `n`, the minimal prime factor divides the greatest common divisor of
+`choose n 1, ⋯, choose n (n - 1)`. -/
+theorem minFac_dvd_gcd_choose_of_isPrimePow (h : IsPrimePow n) :
+ n.minFac ∣ (Icc 1 (n - 1)).gcd n.choose := by
+ obtain ⟨k, _, _, hn₁⟩ := (isPrimePow_nat_iff_bounded_log_minFac _).mp h
+ exact dvd_gcd_iff.mpr fun i hi => by
+ nth_rw 2 [hn₁]
+ exact Prime.dvd_choose_pow (minFac_prime_iff.mpr h.ne_one) (by grind) (by grind)
+
+lemma minFac_sq_ndvd_gcd_choose_of_isPrimePow (h : IsPrimePow n) :
+ ¬ n.minFac ^ 2 ∣ (Icc 1 (n - 1)).gcd n.choose := by
+ obtain ⟨k, _, k_pos, hn₁⟩ := (isPrimePow_nat_iff_bounded_log_minFac _).mp h
+ have isPrime := minFac_prime_iff.mpr (IsPrimePow.ne_one h)
+ refine mt Finset.dvd_gcd_iff.mp ?_
+ simp only [mem_Icc, not_forall]
+ have : n.minFac ^ (k - 1) ≤ n.minFac ^ k := Nat.pow_le_pow_right (minFac_pos n) (sub_le k 1)
+ refine ⟨n.minFac ^ (k - 1), ⟨one_le_pow _ _ (minFac_pos n), ?_⟩, ?_⟩
+ · refine le_sub_one_of_lt ?_
+ nth_rw 2 [hn₁]
+ exact Nat.pow_lt_pow_of_lt (Prime.one_lt isPrime) (sub_one_lt_of_lt k_pos)
+ · refine emultiplicity_lt_iff_not_dvd.mp ?_
+ nth_rw 2 [hn₁]
+ rw [Nat.Prime.emultiplicity_choose_prime_pow isPrime this (pow_ne_zero _
+ (Nat.Prime.ne_zero isPrime)), multiplicity_pow_self_of_prime (prime_iff.mp isPrime)]
+ norm_cast
+ grind
+
+lemma primeFactors_gcd_choose_of_isPrimePow (h : IsPrimePow n) :
+ ((Icc 1 (n - 1)).gcd n.choose).primeFactors = {n.minFac} := by
+ have ne_zero : (Icc 1 (n - 1)).gcd n.choose ≠ 0 :=
+ gcd_ne_zero_iff.mpr ⟨1, by simp; grind [IsPrimePow.two_le h]⟩
+ have isPrime := minFac_prime_iff.mpr (IsPrimePow.ne_one h)
+ refine eq_singleton_iff_unique_mem.mpr ⟨isPrime.mem_primeFactors
+ (minFac_dvd_gcd_choose_of_isPrimePow h) ne_zero, ?_⟩
+ intro p hp
+ simp only [mem_primeFactors, ne_eq] at hp
+ obtain ⟨hp₁, hp₂, hp₃⟩ := hp
+ haveI : Fact (Nat.Prime p) := ⟨hp₁⟩
+ simp_rw [Finset.dvd_gcd_iff, ← modEq_zero_iff_dvd] at hp₂
+ have := eq_pow_multiplicity_of_choose_modEq_zero_nat h.pos hp₂
+ have dvd_pow : n.minFac ∣ p ^ multiplicity p n := this ▸ minFac_dvd _
+ exact (Nat.prime_dvd_prime_iff_eq isPrime hp₁).mp (isPrime.dvd_of_dvd_pow dvd_pow)|>.symm
+
+/-- For a prime power `n`, the greatest common divisor of `choose n 1, ⋯, choose n (n - 1)`
+is actually the minimal prime factor of `n`. -/
+theorem gcd_choose_eq_minFac_of_isPrimePow (h : IsPrimePow n) :
+ (Icc 1 (n - 1)).gcd n.choose = n.minFac := by
+ have ne_zero : (Icc 1 (n - 1)).gcd n.choose ≠ 0 :=
+ gcd_ne_zero_iff.mpr ⟨1, by simp; grind [IsPrimePow.two_le h]⟩
+ have isPrime := minFac_prime_iff.mpr (IsPrimePow.ne_one h)
+ have : multiplicity n.minFac ((Icc 1 (n - 1)).gcd n.choose) = 1 := by
+ refine multiplicity_eq_of_dvd_of_not_dvd ?_ (minFac_sq_ndvd_gcd_choose_of_isPrimePow h)
+ simpa using minFac_dvd_gcd_choose_of_isPrimePow h
+ rw [Nat.prod_pow_primeFactors_factorization ne_zero, primeFactors_gcd_choose_of_isPrimePow h]
+ simp [← Nat.multiplicity_eq_factorization isPrime ne_zero, this]
+
+/-- For a natural number `n` greater than `1`, assume that `n` is not a prime power, then
+the greatest common divisor of `choose n 1, ⋯, choose n (n - 1)` is `1`. -/
+theorem gcd_choose_eq_one_of_not_isPrimePow (hn : 1 < n) (hpn : ¬ IsPrimePow n) :
+ (Icc 1 (n - 1)).gcd n.choose = 1 := by
+ contrapose! hpn
+ obtain ⟨q, hq, h⟩ := Nat.exists_prime_and_dvd hpn
+ simp_rw [Finset.dvd_gcd_iff, ← modEq_zero_iff_dvd] at h
+ haveI : Fact (Nat.Prime q) := ⟨hq⟩
+ have := eq_pow_multiplicity_of_choose_modEq_zero_nat (zero_lt_of_lt hn) h
+ refine (isPrimePow_nat_iff n).mpr ⟨q, _, hq, Dvd.multiplicity_pos ?_, this.symm⟩
+ specialize h 1 (by grind)
+ rw [choose_one_right, modEq_zero_iff_dvd] at h
+ exact h
+
end Choose
From 23f4bd67e1dc779023290f1189e088c46ced3a45 Mon Sep 17 00:00:00 2001
From: Oliver Nash <7734364+ocfnash@users.noreply.github.com>
Date: Thu, 25 Jun 2026 12:42:15 +0000
Subject: [PATCH 0349/1300] feat: local homeomorphisms are covering maps for
compact domains (#41031)
---
Mathlib/Analysis/Complex/CoveringMap.lean | 2 +-
Mathlib/Topology/Covering/Basic.lean | 42 ++++++++++++++++++-----
2 files changed, 35 insertions(+), 9 deletions(-)
diff --git a/Mathlib/Analysis/Complex/CoveringMap.lean b/Mathlib/Analysis/Complex/CoveringMap.lean
index 95872f8372379f..2d5b9fa2902f01 100644
--- a/Mathlib/Analysis/Complex/CoveringMap.lean
+++ b/Mathlib/Analysis/Complex/CoveringMap.lean
@@ -53,7 +53,7 @@ variable {𝕜 : Type*} [NontriviallyNormedField 𝕜] [ProperSpace 𝕜]
theorem Polynomial.isCoveringMapOn_eval (p : 𝕜[X]) :
IsCoveringMapOn p.eval (p.eval '' {k | p.derivative.eval k = 0})ᶜ := by
- refine p.isClosedMap_eval.isCoveringMapOn_of_openPartialHomeomorph (fun x hx ↦ ?_)
+ refine p.isClosedMap_eval.isCoveringMapOn_of_isLocalHomeomorphOn (fun x hx ↦ ?_)
fun x hx ↦ ⟨_, ((p.hasStrictDerivAt x).hasStrictFDerivAt_equiv
fun h ↦ hx ⟨x, h, rfl⟩).mem_toOpenPartialHomeomorph_source, by simp⟩
obtain rfl | ne := eq_or_ne p (C x)
diff --git a/Mathlib/Topology/Covering/Basic.lean b/Mathlib/Topology/Covering/Basic.lean
index 8400516ab95c2c..ad257b2dbc7685 100644
--- a/Mathlib/Topology/Covering/Basic.lean
+++ b/Mathlib/Topology/Covering/Basic.lean
@@ -555,11 +555,18 @@ theorem IsClosedMap.isEvenlyCovered_of_openPartialHomeomorph [T2Space E] {x : X}
/-- If `f : E → X` is a closed map between topological spaces with `E` Hausdorff, and `s` is
a subset of `X` on which `f` has finite fibers, such that `f` restricts to a homeomorphism on
a neighborhood of every point of `f ⁻¹' s`, then `f` is a covering map on `s`. -/
-theorem IsClosedMap.isCoveringMapOn_of_openPartialHomeomorph [T2Space E]
+theorem IsClosedMap.isCoveringMapOn_of_isLocalHomeomorphOn [T2Space E]
(hf : IsClosedMap f) (hs : ∀ x ∈ s, (f ⁻¹' {x}).Finite)
- (h : ∀ e ∈ f ⁻¹' s, ∃ φ : OpenPartialHomeomorph E X, e ∈ φ.source ∧ φ = f) :
- IsCoveringMapOn f s :=
- fun x hx ↦ hf.isEvenlyCovered_of_openPartialHomeomorph (hs x hx) fun e he ↦ h e (by apply he ▸ hx)
+ (h : IsLocalHomeomorphOn f (f ⁻¹' s)) :
+ IsCoveringMapOn f s := by
+ intro x hx
+ refine hf.isEvenlyCovered_of_openPartialHomeomorph (hs x hx) fun e he ↦ ?_
+ obtain ⟨φ, hφ, rfl⟩ := h e (by aesop)
+ aesop
+
+@[deprecated (since := "2026-06-25")]
+alias IsClosedMap.isCoveringMapOn_of_openPartialHomeomorph :=
+ IsClosedMap.isCoveringMapOn_of_isLocalHomeomorphOn
/-- If `f : E → X` is a continuous map between Hausdorff spaces with `E` compact,
and `f` restricts to a homeomorphism on a neighborhood of every point of a fiber `f ⁻¹' {x}`,
@@ -578,8 +585,27 @@ then `f` is a covering map on `s`.
For example, `s` can be taken to be the set of regular values of a C¹ map `f : E → X`
where `E` and `X` are manifolds of the same dimension with `E` compact, according to
the inverse function theorem (see `ContDiffAt.toOpenPartialHomeomorph`). -/
-theorem IsCoveringMapOn.of_openPartialHomeomorph
+theorem IsCoveringMapOn.of_isLocalHomeomorphOn
[T2Space E] [T2Space X] [CompactSpace E] (hf : Continuous f)
- (h : ∀ e ∈ f ⁻¹' s, ∃ φ : OpenPartialHomeomorph E X, e ∈ φ.source ∧ φ = f) :
- IsCoveringMapOn f s :=
- fun x hx ↦ .of_openPartialHomeomorph hf fun e he ↦ h e (by apply he ▸ hx)
+ (h : IsLocalHomeomorphOn f (f ⁻¹' s)) :
+ IsCoveringMapOn f s := by
+ intro x hx
+ refine .of_openPartialHomeomorph hf fun e he ↦ ?_
+ obtain ⟨φ, hφ, rfl⟩ := h e (by aesop)
+ aesop
+
+@[deprecated (since := "2026-06-25")]
+alias IsCoveringMapOn.of_openPartialHomeomorph := IsCoveringMapOn.of_isLocalHomeomorphOn
+
+@[simp]
+lemma isLocalHomeomorph_iff_isCoveringMap [T2Space E] [T2Space X] [CompactSpace E] :
+ IsLocalHomeomorph f ↔ IsCoveringMap f := by
+ refine ⟨fun h ↦ ?_, IsCoveringMap.isLocalHomeomorph⟩
+ have hf : Continuous f := by
+ rw [continuous_iff_continuousAt]
+ intro e
+ obtain ⟨φ, hφ, rfl⟩ := h e
+ exact φ.continuousAt hφ
+ rw [isCoveringMap_iff_isCoveringMapOn_univ]
+ apply IsCoveringMapOn.of_isLocalHomeomorphOn hf
+ simpa [← isLocalHomeomorph_iff_isLocalHomeomorphOn_univ]
From bb39487d795eef69f1218b2d28abc5eb3dcad0c5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=ABl=20Riou?=
<37772949+joelriou@users.noreply.github.com>
Date: Thu, 25 Jun 2026 13:10:36 +0000
Subject: [PATCH 0350/1300] feat(CategoryTheory/ObjectProperty): preservation
of limits is closed under isomorphisms (#41036)
This is only the translation of this statement in terms of some `ObjectProperty` in functor categories.
---
.../ObjectProperty/CompleteLattice.lean | 7 +++++++
.../FunctorCategory/PreservesLimits.lean | 20 +++++++++++++++++++
2 files changed, 27 insertions(+)
diff --git a/Mathlib/CategoryTheory/ObjectProperty/CompleteLattice.lean b/Mathlib/CategoryTheory/ObjectProperty/CompleteLattice.lean
index 51dd57c087bcb7..6152f84f2e3d72 100644
--- a/Mathlib/CategoryTheory/ObjectProperty/CompleteLattice.lean
+++ b/Mathlib/CategoryTheory/ObjectProperty/CompleteLattice.lean
@@ -97,6 +97,13 @@ instance [∀ a, (P a).IsClosedUnderIsomorphisms] :
simp only [isClosedUnderIsomorphisms_iff_isoClosure_eq_self,
isoClosure_iSup, isoClosure_eq_self]
+instance [∀ a, (P a).IsClosedUnderIsomorphisms] :
+ ((⨅ (a : α), P a)).IsClosedUnderIsomorphisms where
+ of_iso e h := by
+ simp only [iInf_apply, iInf_Prop_eq] at h ⊢
+ intro a
+ exact (P a).prop_of_iso e (h a)
+
end
@[push]
diff --git a/Mathlib/CategoryTheory/ObjectProperty/FunctorCategory/PreservesLimits.lean b/Mathlib/CategoryTheory/ObjectProperty/FunctorCategory/PreservesLimits.lean
index a160c062fe8b4a..cceb19b3174f4c 100644
--- a/Mathlib/CategoryTheory/ObjectProperty/FunctorCategory/PreservesLimits.lean
+++ b/Mathlib/CategoryTheory/ObjectProperty/FunctorCategory/PreservesLimits.lean
@@ -45,6 +45,9 @@ lemma congr_preservesLimit {F F' : K ⥤ J} (e : F ≅ F') :
exact ⟨fun h ↦ preservesLimit_of_iso_diagram _ e,
fun h ↦ preservesLimit_of_iso_diagram _ e.symm⟩
+instance (F : K ⥤ J) : (preservesLimit (C := C) F).IsClosedUnderIsomorphisms where
+ of_iso e _ := preservesLimit_of_natIso _ e
+
variable {K} in
/-- The property of objects in the functor category `J ⥤ C`
which preserves the colimit of a functor `F : K ⥤ J`. -/
@@ -61,6 +64,9 @@ lemma congr_preservesColimit {F F' : K ⥤ J} (e : F ≅ F') :
exact ⟨fun h ↦ preservesColimit_of_iso_diagram _ e,
fun h ↦ preservesColimit_of_iso_diagram _ e.symm⟩
+instance (F : K ⥤ J) : (preservesColimit (C := C) F).IsClosedUnderIsomorphisms where
+ of_iso e _ := preservesColimit_of_natIso _ e
+
/-- The property of objects in the functor category `J ⥤ C`
which preserves limits of shape `K`. -/
abbrev preservesLimitsOfShape : ObjectProperty (J ⥤ C) := PreservesLimitsOfShape K
@@ -84,6 +90,10 @@ lemma congr_preservesLimitsOfShape (e : K ≌ K') :
exact ⟨fun _ ↦ preservesLimitsOfShape_of_equiv e _,
fun _ ↦ preservesLimitsOfShape_of_equiv e.symm _⟩
+instance : (preservesLimitsOfShape (J := J) (C := C) K).IsClosedUnderIsomorphisms := by
+ rw [preservesLimitsOfShape_eq_iSup]
+ infer_instance
+
/-- The property of objects in the functor category `J ⥤ C`
which preserves colimits of shape `K`. -/
abbrev preservesColimitsOfShape : ObjectProperty (J ⥤ C) := PreservesColimitsOfShape K
@@ -107,6 +117,10 @@ lemma congr_preservesColimitsOfShape (e : K ≌ K') :
exact ⟨fun _ ↦ preservesColimitsOfShape_of_equiv e _,
fun _ ↦ preservesColimitsOfShape_of_equiv e.symm _⟩
+instance : (preservesColimitsOfShape (J := J) (C := C) K).IsClosedUnderIsomorphisms := by
+ rw [preservesColimitsOfShape_eq_iSup]
+ infer_instance
+
/-- The property of objects in the functor category `J ⥤ C`
which preserves finite limits. -/
abbrev preservesFiniteLimits : ObjectProperty (J ⥤ C) := PreservesFiniteLimits
@@ -115,10 +129,16 @@ abbrev preservesFiniteLimits : ObjectProperty (J ⥤ C) := PreservesFiniteLimits
lemma preservesFiniteLimits_iff (F : J ⥤ C) :
preservesFiniteLimits F ↔ PreservesFiniteLimits F := Iff.rfl
+instance : (preservesFiniteLimits (J := J) (C := C)).IsClosedUnderIsomorphisms where
+ of_iso e _ := preservesFiniteLimits_of_natIso e
+
/-- The property of objects in the functor category `J ⥤ C`
which preserves finite colimits. -/
abbrev preservesFiniteColimits : ObjectProperty (J ⥤ C) := PreservesFiniteColimits
+instance : (preservesFiniteColimits (J := J) (C := C)).IsClosedUnderIsomorphisms where
+ of_iso e _ := preservesFiniteColimits_of_natIso e
+
@[simp]
lemma preservesFiniteColimits_iff (F : J ⥤ C) :
preservesFiniteColimits F ↔ PreservesFiniteColimits F := Iff.rfl
From d31b5731def4cfc9061d0f273418b3d1c0df2101 Mon Sep 17 00:00:00 2001
From: Raphael Douglas Giles <77658801+Raph-DG@users.noreply.github.com>
Date: Thu, 25 Jun 2026 13:30:18 +0000
Subject: [PATCH 0351/1300] feat(AlgebraicGeometry): define algebraic cycles
(#37901)
In this PR we define the notion of algebraic cycles on a scheme, and define the pushforward of an algebraic cycle by a quasicompact morhphism. This was originally defined in #26304, but after some refactoring it was decided that it would be best to split this definition into a separate PR.
Co-authored-by: Raph-DG
---
Mathlib.lean | 1 +
.../AlgebraicCycle/Basic.lean | 79 +++++++++++++++++++
Mathlib/AlgebraicGeometry/ResidueField.lean | 14 ++++
3 files changed, 94 insertions(+)
create mode 100644 Mathlib/AlgebraicGeometry/AlgebraicCycle/Basic.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 4017194f141030..766e023cdb5735 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -1346,6 +1346,7 @@ public import Mathlib.AlgebraicGeometry.AffineScheme
public import Mathlib.AlgebraicGeometry.AffineSpace
public import Mathlib.AlgebraicGeometry.AffineTransitionLimit
public import Mathlib.AlgebraicGeometry.AlgClosed.Basic
+public import Mathlib.AlgebraicGeometry.AlgebraicCycle.Basic
public import Mathlib.AlgebraicGeometry.Artinian
public import Mathlib.AlgebraicGeometry.Birational.Dominant
public import Mathlib.AlgebraicGeometry.Birational.RationalMap
diff --git a/Mathlib/AlgebraicGeometry/AlgebraicCycle/Basic.lean b/Mathlib/AlgebraicGeometry/AlgebraicCycle/Basic.lean
new file mode 100644
index 00000000000000..22d69227a54f40
--- /dev/null
+++ b/Mathlib/AlgebraicGeometry/AlgebraicCycle/Basic.lean
@@ -0,0 +1,79 @@
+/-
+Copyright (c) 2026 Raphael Douglas Giles. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Raphael Douglas Giles
+-/
+module
+
+public import Mathlib.AlgebraicGeometry.Morphisms.QuasiCompact
+public import Mathlib.AlgebraicGeometry.Properties
+public import Mathlib.Topology.LocallyFinsupp.Pushforward
+public import Mathlib.AlgebraicGeometry.ResidueField
+
+/-!
+# Algebraic Cycles
+
+In this file we define algebraic cycles on a scheme `X` with coefficients in a type `R` and provide
+some basic API for working with them. We define an algebraic cycle on a scheme `X` with
+coefficients in a type `R` to be functions `c : X → R` whose support is locally finite.
+
+## Implementation notes
+
+Here we're making use of the equivalence between irreducible closed subsets of a scheme and their
+generic points in order to reuse the API in `Function.locallyFinsupp`, hence the slightly
+nonstandard definition.
+-/
+
+@[expose] public section
+
+namespace AlgebraicGeometry
+
+open CategoryTheory
+
+universe u v
+variable {X Y : Scheme.{u}} {R : Type*}
+
+/--
+Algebraic cycle on a scheme `X` with coefficients in a type `Z` is just a function from `X` to `Z`
+with locally finite support (see the module docstring for more details).
+
+Note: currently this is an abbrev to save some effort in duplicating API. This seems fine for now,
+but be aware of this if there is ever an instance clash involving algebraic cycles.
+-/
+@[stacks 02QR]
+abbrev AlgebraicCycle (X : Scheme.{u}) (R : Type*) [Zero R] :=
+ Function.locallyFinsupp X R
+
+variable (f : X ⟶ Y) [Semiring R] (c : AlgebraicCycle X R) (x : X) (z : Y)
+namespace AlgebraicCycle
+
+/--
+Implementation detail for `AlgebraicCycle.map`: function used to define the coefficient of the
+pushforward of a cycle `c` at a point `z = f x`.
+-/
+@[stacks 02R3]
+noncomputable def mapCoeff {N : Type*} [DecidableEq N] {Y : Scheme} (f : X ⟶ Y) (wx : X → N)
+ (wy : Y → N) (x : X) : ℕ := if wx x = wy (f.base x) then f.residueDegree x else 0
+
+/--
+The pushforward of algebraic cycles with respect to a quasicompact morphism of schemes. The
+arguments `wx` and `wy` are certain weight functions used to calculate how the weights of the
+algebraic cycle should be adjusted to make the pushforward operation functorial. Typically in
+applications these will be some notions of dimension or codimension. The most common notion of
+dimension is `Order.height`, and the most common notion of codimension is `Order.coheight`, though
+more sophisticated notions exist in the literature which are useful when sufficient
+equidimensionality hypotheses cannot be assumed.
+-/
+@[stacks 02R3]
+noncomputable
+def map [QuasiCompact f] {N : Type*} [DecidableEq N] (wx : X → N) (wy : Y → N)
+ (c : AlgebraicCycle X R) : AlgebraicCycle Y R :=
+ Function.locallyFinsupp.map f (Nat.cast (R := R) <| mapCoeff f wx wy ·) f.isSpectralMap c
+
+@[simp]
+lemma map_id {N : Type*} [DecidableEq N] (wx : X → N) (c : AlgebraicCycle X R) :
+ map (𝟙 _) wx wx c = c := by
+ apply Function.locallyFinsupp.map_id
+ simp [mapCoeff]
+
+end AlgebraicGeometry.AlgebraicCycle
diff --git a/Mathlib/AlgebraicGeometry/ResidueField.lean b/Mathlib/AlgebraicGeometry/ResidueField.lean
index 3aa4861abd5d33..20c400e69b5ca9 100644
--- a/Mathlib/AlgebraicGeometry/ResidueField.lean
+++ b/Mathlib/AlgebraicGeometry/ResidueField.lean
@@ -140,6 +140,20 @@ lemma residueFieldMap_comp {Z : Scheme.{u}} (g : Y ⟶ Z) (x : X) :
(f ≫ g).residueFieldMap x = g.residueFieldMap (f x) ≫ f.residueFieldMap x :=
LocallyRingedSpace.residueFieldMap_comp _ _ _
+/--
+Degree of `f` at a point `x` is defined to be the degree of the associated field extension
+from `κ(f x)` to `κ(x)`. We return a default value of zero when this degree is infinite.
+-/
+def Hom.residueDegree (f : X ⟶ Y) (x : X) : ℕ :=
+ letI := (f.residueFieldMap x).hom.toAlgebra
+ Module.finrank (Y.residueField (f x)) (X.residueField x)
+
+@[simp]
+lemma Hom.residueDegree_id (x : X) : (𝟙 _ : X ⟶ X).residueDegree x = 1 := by
+ dsimp [residueDegree]
+ rw [residueFieldMap_id]
+ exact CommSemiring.finrank_self _
+
@[reassoc]
lemma evaluation_naturality {V : Opens Y} (x : X) (hx : f x ∈ V) :
Y.evaluation V (f x) hx ≫ f.residueFieldMap x =
From 0cd556a586391216f097067ee3785af582e78fb3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20=C4=8Cert=C3=ADk?=
Date: Thu, 25 Jun 2026 13:30:21 +0000
Subject: [PATCH 0352/1300] feat: generalize `ae_hasDerivAt_integral` to Banach
spaces (#40976)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Generalize both interval-version Lebesgue differentiation theorems `LocallyIntegrable.ae_hasDerivAt_integral` and
`IntervalIntegrable.ae_hasDerivAt_integral` from real-valued functions `f : ℝ → ℝ` to functions `f : ℝ → E` valued in a Banach space `E`. The existing proof already goes through the vector-valued averaging theorem `VitaliFamily.ae_tendsto_average`, so the only change is replacing scalar multiplication `*` by `•` in the slope computation.
This is a prerequisite for https://github.com/leanprover-community/mathlib4/pull/40973.
AI usage disclosure: I used Claude Opus 4.8 to implement this and manually tested it with the other PR and my other separate project.
---
.../LebesgueDifferentiationThm.lean | 24 ++++++++++---------
1 file changed, 13 insertions(+), 11 deletions(-)
diff --git a/Mathlib/MeasureTheory/Integral/IntervalIntegral/LebesgueDifferentiationThm.lean b/Mathlib/MeasureTheory/Integral/IntervalIntegral/LebesgueDifferentiationThm.lean
index 2654b47e2e7e3b..23dad80410b6b8 100644
--- a/Mathlib/MeasureTheory/Integral/IntervalIntegral/LebesgueDifferentiationThm.lean
+++ b/Mathlib/MeasureTheory/Integral/IntervalIntegral/LebesgueDifferentiationThm.lean
@@ -15,11 +15,11 @@ public import Mathlib.MeasureTheory.Integral.IntervalIntegral.Basic
This file proves the interval version of the Lebesgue Differentiation Theorem. There are two
versions in this file.
-* `LocallyIntegrable.ae_hasDerivAt_integral` is the global version. It states that if `f : ℝ → ℝ`
- is locally integrable, then for almost every `x`, for any `c : ℝ`, the derivative of
- `∫ (t : ℝ) in c..x, f t` at `x` is equal to `f x`.
+* `LocallyIntegrable.ae_hasDerivAt_integral` is the global version. It states that if `f : ℝ → E`
+ is locally integrable (`E` a Banach space), then for almost every `x`, for any `c : ℝ`, the
+ derivative of `∫ (t : ℝ) in c..x, f t` at `x` is equal to `f x`.
-* `IntervalIntegrable.ae_hasDerivAt_integral` is the local version. It states that if `f : ℝ → ℝ`
+* `IntervalIntegrable.ae_hasDerivAt_integral` is the local version. It states that if `f : ℝ → E`
is interval integrable on `a..b`, then for almost every `x ∈ uIcc a b`, for any `c ∈ uIcc a b`,
the derivative of `∫ (t : ℝ) in c..x, f t` at `x` is equal to `f x`.
-/
@@ -30,10 +30,12 @@ open MeasureTheory Set Filter Function IsUnifLocDoublingMeasure
open scoped Topology
-/-- The (global) interval version of the *Lebesgue Differentiation Theorem*: if `f : ℝ → ℝ` is
+variable {E : Type*} [NormedAddCommGroup E] [NormedSpace ℝ E] [CompleteSpace E]
+
+/-- The (global) interval version of the *Lebesgue Differentiation Theorem*: if `f : ℝ → E` is
locally integrable, then for almost every `x`, for any `c : ℝ`, the derivative of
`∫ (t : ℝ) in c..x, f t` at `x` is equal to `f x`. -/
-theorem LocallyIntegrable.ae_hasDerivAt_integral {f : ℝ → ℝ} (hf : LocallyIntegrable f volume) :
+theorem LocallyIntegrable.ae_hasDerivAt_integral {f : ℝ → E} (hf : LocallyIntegrable f volume) :
∀ᵐ x, ∀ c, HasDerivAt (fun x => ∫ (t : ℝ) in c..x, f t) (f x) x := by
have hg (x y : ℝ) : IntervalIntegrable f volume x y :=
intervalIntegrable_iff.mpr <|
@@ -48,24 +50,24 @@ theorem LocallyIntegrable.ae_hasDerivAt_integral {f : ℝ → ℝ} (hf : Locally
· refine Filter.tendsto_congr' ?_ |>.mpr (hx.comp x.tendsto_Icc_vitaliFamily_left)
filter_upwards [self_mem_nhdsWithin] with y hy
replace hy : y ≤ x := hy.le
- suffices -((y - x)⁻¹ * ∫ (t : ℝ) in Icc y x, f t) = (x - y)⁻¹ * ∫ (t : ℝ) in Icc y x, f t by
+ suffices -((y - x)⁻¹ • ∫ (t : ℝ) in Icc y x, f t) = (x - y)⁻¹ • ∫ (t : ℝ) in Icc y x, f t by
simpa [slope, average, intervalIntegral.integral_interval_sub_left, hg,
intervalIntegral.integral_of_ge, hy, h]
- rw [← neg_mul, neg_inv, neg_sub]
+ rw [← neg_smul, neg_inv, neg_sub]
· refine Filter.tendsto_congr' ?_ |>.mpr (hx.comp x.tendsto_Icc_vitaliFamily_right)
filter_upwards [self_mem_nhdsWithin] with y hy
replace hy : x ≤ y := hy.le
simp [slope, average, intervalIntegral.integral_interval_sub_left, hg,
intervalIntegral.integral_of_le, hy, h]
-/-- The (local) interval version of the *Lebesgue Differentiation Theorem*: if `f : ℝ → ℝ` is
+/-- The (local) interval version of the *Lebesgue Differentiation Theorem*: if `f : ℝ → E` is
interval integrable on `a..b`, then for almost every `x ∈ uIcc a b`, for any `c ∈ uIcc a b`, the
derivative of `∫ (t : ℝ) in c..x, f t` at `x` is equal to `f x`. -/
-theorem IntervalIntegrable.ae_hasDerivAt_integral {f : ℝ → ℝ} {a b : ℝ}
+theorem IntervalIntegrable.ae_hasDerivAt_integral {f : ℝ → E} {a b : ℝ}
(hf : IntervalIntegrable f volume a b) :
∀ᵐ x, x ∈ uIcc a b → ∀ c ∈ uIcc a b, HasDerivAt (fun x => ∫ (t : ℝ) in c..x, f t) (f x) x := by
wlog hab : a ≤ b
- · exact uIcc_comm b a ▸ @this f b a hf.symm (by linarith)
+ · exact uIcc_comm b a ▸ this hf.symm (by linarith)
rw [uIcc_of_le hab]
have h₁ : ∀ᵐ x, x ≠ a := by simp [ae_iff, measure_singleton]
have h₂ : ∀ᵐ x, x ≠ b := by simp [ae_iff, measure_singleton]
From aea5d67c4f9b8f29ea3e43c6540b3d69b8be7501 Mon Sep 17 00:00:00 2001
From: Moritz Doll <21366319+mcdoll@users.noreply.github.com>
Date: Thu, 25 Jun 2026 13:57:32 +0000
Subject: [PATCH 0353/1300] chore(MeasureTheory): minor clean up of seminorm
lemmas (#40986)
Remove a duplicate lemma and generalize some lemmas to support two different codomains.
---
.../Function/LpSeminorm/Basic.lean | 23 ++++++++-----------
.../Function/LpSeminorm/Indicator.lean | 8 +++----
2 files changed, 14 insertions(+), 17 deletions(-)
diff --git a/Mathlib/MeasureTheory/Function/LpSeminorm/Basic.lean b/Mathlib/MeasureTheory/Function/LpSeminorm/Basic.lean
index 96fc1608bff08b..1e18c385d5293c 100644
--- a/Mathlib/MeasureTheory/Function/LpSeminorm/Basic.lean
+++ b/Mathlib/MeasureTheory/Function/LpSeminorm/Basic.lean
@@ -285,17 +285,17 @@ theorem eLpNorm'_mono_ae {f : α → F} {g : α → G} (hq : 0 ≤ q) (h : ∀
eLpNorm' f q μ ≤ eLpNorm' g q μ :=
eLpNorm'_mono_enorm_ae hq (by simpa only [enorm_le_iff_norm_le] using h)
-theorem eLpNorm'_congr_enorm_ae {f g : α → ε} (hfg : ∀ᵐ x ∂μ, ‖f x‖ₑ = ‖g x‖ₑ) :
+theorem eLpNorm'_congr_enorm_ae {f : α → ε} {g : α → ε'} (hfg : ∀ᵐ x ∂μ, ‖f x‖ₑ = ‖g x‖ₑ) :
eLpNorm' f q μ = eLpNorm' g q μ := by
have : (‖f ·‖ₑ ^ q) =ᵐ[μ] (‖g ·‖ₑ ^ q) := hfg.mono fun x hx ↦ by simp [hx]
simp only [eLpNorm'_eq_lintegral_enorm, lintegral_congr_ae this]
-theorem eLpNorm'_congr_nnnorm_ae {f g : α → F} (hfg : ∀ᵐ x ∂μ, ‖f x‖₊ = ‖g x‖₊) :
+theorem eLpNorm'_congr_nnnorm_ae {f : α → F} {g : α → G} (hfg : ∀ᵐ x ∂μ, ‖f x‖₊ = ‖g x‖₊) :
eLpNorm' f q μ = eLpNorm' g q μ := by
have : (‖f ·‖ₑ ^ q) =ᵐ[μ] (‖g ·‖ₑ ^ q) := hfg.mono fun x hx ↦ by simp [enorm, hx]
simp only [eLpNorm'_eq_lintegral_enorm, lintegral_congr_ae this]
-theorem eLpNorm'_congr_norm_ae {f g : α → F} (hfg : ∀ᵐ x ∂μ, ‖f x‖ = ‖g x‖) :
+theorem eLpNorm'_congr_norm_ae {f : α → F} {g : α → G} (hfg : ∀ᵐ x ∂μ, ‖f x‖ = ‖g x‖) :
eLpNorm' f q μ = eLpNorm' g q μ :=
eLpNorm'_congr_nnnorm_ae <| hfg.mono fun _x hx => NNReal.eq hx
@@ -306,11 +306,11 @@ theorem eLpNormEssSup_congr_ae {f g : α → ε} (hfg : f =ᵐ[μ] g) :
eLpNormEssSup f μ = eLpNormEssSup g μ :=
essSup_congr_ae (hfg.fun_comp enorm)
-theorem eLpNormEssSup_mono_enorm_ae {f g : α → ε} (hfg : ∀ᵐ x ∂μ, ‖f x‖ₑ ≤ ‖g x‖ₑ) :
+theorem eLpNormEssSup_mono_enorm_ae {f : α → ε} {g : α → ε'} (hfg : ∀ᵐ x ∂μ, ‖f x‖ₑ ≤ ‖g x‖ₑ) :
eLpNormEssSup f μ ≤ eLpNormEssSup g μ :=
essSup_mono_ae <| hfg
-theorem eLpNormEssSup_mono_nnnorm_ae {f g : α → F} (hfg : ∀ᵐ x ∂μ, ‖f x‖₊ ≤ ‖g x‖₊) :
+theorem eLpNormEssSup_mono_nnnorm_ae {f : α → F} {g : α → G} (hfg : ∀ᵐ x ∂μ, ‖f x‖₊ ≤ ‖g x‖₊) :
eLpNormEssSup f μ ≤ eLpNormEssSup g μ :=
essSup_mono_ae <| hfg.mono fun _x hx => ENNReal.coe_le_coe.mpr hx
@@ -319,7 +319,7 @@ theorem eLpNorm_mono_enorm_ae {f : α → ε} {g : α → ε'} (h : ∀ᵐ x ∂
simp only [eLpNorm]
split_ifs
· exact le_rfl
- · exact essSup_mono_ae h
+ · exact eLpNormEssSup_mono_enorm_ae h
· exact eLpNorm'_mono_enorm_ae ENNReal.toReal_nonneg h
theorem eLpNorm_mono_nnnorm_ae {f : α → F} {g : α → G} (h : ∀ᵐ x ∂μ, ‖f x‖₊ ≤ ‖g x‖₊) :
@@ -330,10 +330,7 @@ theorem eLpNorm_mono_ae {f : α → F} {g : α → G} (h : ∀ᵐ x ∂μ, ‖f
eLpNorm f p μ ≤ eLpNorm g p μ :=
eLpNorm_mono_enorm_ae (by simpa only [enorm_le_iff_norm_le] using h)
-theorem eLpNorm_mono_ae' {ε' : Type*} [ENorm ε']
- {f : α → ε} {g : α → ε'} (h : ∀ᵐ x ∂μ, ‖f x‖ₑ ≤ ‖g x‖ₑ) :
- eLpNorm f p μ ≤ eLpNorm g p μ :=
- eLpNorm_mono_enorm_ae (by simpa only [enorm_le_iff_norm_le] using h)
+@[deprecated (since := "2026-06-24")] alias eLpNorm_mono_ae' := eLpNorm_mono_enorm_ae
theorem eLpNorm_mono_ae_real {f : α → F} {g : α → ℝ} (h : ∀ᵐ x ∂μ, ‖f x‖ ≤ g x) :
eLpNorm f p μ ≤ eLpNorm g p μ :=
@@ -501,7 +498,7 @@ variable {ε ε' : Type*}
theorem MemLp.of_le_enorm {f : α → ε} {g : α → ε'} (hg : MemLp g p μ)
(hf : AEStronglyMeasurable f μ) (hfg : ∀ᵐ x ∂μ, ‖f x‖ₑ ≤ ‖g x‖ₑ) : MemLp f p μ :=
- ⟨hf, (eLpNorm_mono_ae' hfg).trans_lt (by finiteness)⟩
+ ⟨hf, (eLpNorm_mono_enorm_ae hfg).trans_lt (by finiteness)⟩
theorem MemLp.of_le {f : α → E} {g : α → F} (hg : MemLp g p μ) (hf : AEStronglyMeasurable f μ)
(hfg : ∀ᵐ x ∂μ, ‖f x‖ ≤ ‖g x‖) : MemLp f p μ :=
@@ -762,8 +759,8 @@ theorem eLpNorm_eq_zero_of_ae_zero {f : α → ε} (hf : f =ᵐ[μ] 0) : eLpNorm
theorem eLpNorm'_eq_zero_of_ae_eq_zero {f : α → ε} {p : ℝ} (hp : 0 < p)
(hf : ∀ᵐ (x : α) ∂μ, ‖f x‖ₑ = 0) : eLpNorm' f p μ = 0 := by
- rw [← eLpNorm'_zero hp (μ := μ) (ε := ε), eLpNorm'_congr_enorm_ae]
- simp only [hf, Pi.zero_apply, enorm_zero]
+ rw [← eLpNorm'_zero hp (μ := μ) (ε := ε)]
+ exact eLpNorm'_congr_enorm_ae (by simp [hf])
variable {ε : Type*} [ENorm ε] in
theorem ae_le_eLpNormEssSup {f : α → ε} : ∀ᵐ y ∂μ, ‖f y‖ₑ ≤ eLpNormEssSup f μ :=
diff --git a/Mathlib/MeasureTheory/Function/LpSeminorm/Indicator.lean b/Mathlib/MeasureTheory/Function/LpSeminorm/Indicator.lean
index 7a4da8882b40b4..4bfab0e33c6d3a 100644
--- a/Mathlib/MeasureTheory/Function/LpSeminorm/Indicator.lean
+++ b/Mathlib/MeasureTheory/Function/LpSeminorm/Indicator.lean
@@ -60,13 +60,13 @@ lemma eLpNorm_restrict_le (f : α → ε') (p : ℝ≥0∞) (μ : Measure α) (s
lemma eLpNorm_indicator_le (f : α → ε) :
eLpNorm (s.indicator f) p μ ≤ eLpNorm f p μ := by
- refine eLpNorm_mono_ae' <| .of_forall fun x ↦ ?_
- rw [enorm_indicator_eq_indicator_enorm]
- exact s.indicator_le_self _ x
+ apply eLpNorm_mono_enorm
+ simp_rw [enorm_indicator_eq_indicator_enorm]
+ exact s.indicator_le_self _
lemma eLpNormEssSup_indicator_le (s : Set α) (f : α → ε) :
eLpNormEssSup (s.indicator f) μ ≤ eLpNormEssSup f μ := by
- refine essSup_mono_ae (Eventually.of_forall fun x => ?_)
+ refine essSup_mono_ae (.of_forall fun x => ?_)
simp_rw [enorm_indicator_eq_indicator_enorm]
exact Set.indicator_le_self s _ x
From 83a37978d0b810119c3f74efe0c2b0a6264a5de9 Mon Sep 17 00:00:00 2001
From: Eric Wieser <425260+eric-wieser@users.noreply.github.com>
Date: Thu, 25 Jun 2026 14:57:46 +0000
Subject: [PATCH 0354/1300] chore: add star lemmas for congruence relations
(#40919)
This can in future replace a similar lemma for `RingQuot`.
The old copyright is because this was derived from `Mathlib/Algebra/Star/RingQuot.lean`.
---
Mathlib.lean | 2 +
Mathlib/GroupTheory/Congruence/Star.lean | 53 ++++++++++++++++++++++++
Mathlib/RingTheory/Congruence/Star.lean | 38 +++++++++++++++++
3 files changed, 93 insertions(+)
create mode 100644 Mathlib/GroupTheory/Congruence/Star.lean
create mode 100644 Mathlib/RingTheory/Congruence/Star.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 766e023cdb5735..92067dc839f781 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -4715,6 +4715,7 @@ public import Mathlib.GroupTheory.Congruence.BigOperators
public import Mathlib.GroupTheory.Congruence.Defs
public import Mathlib.GroupTheory.Congruence.Hom
public import Mathlib.GroupTheory.Congruence.Opposite
+public import Mathlib.GroupTheory.Congruence.Star
public import Mathlib.GroupTheory.Coprod.Basic
public import Mathlib.GroupTheory.CoprodI
public import Mathlib.GroupTheory.Coset.Basic
@@ -6474,6 +6475,7 @@ public import Mathlib.RingTheory.Congruence.BigOperators
public import Mathlib.RingTheory.Congruence.Defs
public import Mathlib.RingTheory.Congruence.Hom
public import Mathlib.RingTheory.Congruence.Opposite
+public import Mathlib.RingTheory.Congruence.Star
public import Mathlib.RingTheory.Coprime.Basic
public import Mathlib.RingTheory.Coprime.Ideal
public import Mathlib.RingTheory.Coprime.Lemmas
diff --git a/Mathlib/GroupTheory/Congruence/Star.lean b/Mathlib/GroupTheory/Congruence/Star.lean
new file mode 100644
index 00000000000000..dc4db4b4432140
--- /dev/null
+++ b/Mathlib/GroupTheory/Congruence/Star.lean
@@ -0,0 +1,53 @@
+/-
+Copyright (c) 2020 Eric Wieser. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Eric Wieser
+-/
+module
+
+public import Mathlib.GroupTheory.Congruence.Basic
+public import Mathlib.Algebra.Star.Basic
+
+/-!
+# Helpers for working with star operators on quotients.
+
+TODO: consider defining `Star` versions of `Con` and `AddCon`.
+-/
+
+@[expose] public section
+
+section Mul
+variable {M : Type*} [Mul M] [StarMul M] {r : M → M → Prop}
+
+theorem ConGen.Rel.star (hr : ∀ a b, r a b → r (star a) (star b))
+ ⦃a b : M⦄ : Rel r a b → Rel r (star a) (star b)
+ | refl _ => .refl _
+ | symm h => .symm <| h.star hr
+ | trans h1 h2 => .trans (h1.star hr) (h2.star hr)
+ | of _ _ h => .of _ _ (hr _ _ h)
+ | mul h1 h2 => by
+ rw [star_mul, star_mul]
+ exact (h2.star hr).mul (h1.star hr)
+
+theorem conGen_star (hr : ∀ a b, r a b → r (star a) (star b)) ⦃a b : M⦄ :
+ conGen r a b → conGen r (star a) (star b) := (ConGen.Rel.star hr ·)
+
+end Mul
+
+section Add
+variable {A : Type*} [AddMonoid A] [StarAddMonoid A] {r : A → A → Prop}
+
+theorem AddConGen.Rel.star (hr : ∀ a b, r a b → r (star a) (star b))
+ ⦃a b : A⦄ : Rel r a b → Rel r (star a) (star b)
+ | refl _ => .refl _
+ | symm h => .symm <| h.star hr
+ | trans h1 h2 => .trans (h1.star hr) (h2.star hr)
+ | of _ _ h => .of _ _ (hr _ _ h)
+ | add h1 h2 => by
+ rw [star_add, star_add]
+ exact (h1.star hr).add (h2.star hr)
+
+theorem addConGen_star (hr : ∀ a b, r a b → r (star a) (star b)) ⦃a b : A⦄ :
+ addConGen r a b → addConGen r (star a) (star b) := (AddConGen.Rel.star hr ·)
+
+end Add
diff --git a/Mathlib/RingTheory/Congruence/Star.lean b/Mathlib/RingTheory/Congruence/Star.lean
new file mode 100644
index 00000000000000..b8ed4c30fc26b2
--- /dev/null
+++ b/Mathlib/RingTheory/Congruence/Star.lean
@@ -0,0 +1,38 @@
+/-
+Copyright (c) 2020 Eric Wieser. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Eric Wieser
+-/
+module
+
+public import Mathlib.RingTheory.Congruence.Basic
+public import Mathlib.Algebra.Star.Basic
+
+/-!
+# Helpers for working with star operators on quotients.
+
+TODO: consider defining `Star` versions of `RingCon`.
+-/
+
+@[expose] public section
+
+section Ring
+variable {R : Type*} [NonUnitalNonAssocSemiring R] [StarRing R] {r : R → R → Prop}
+
+theorem RingConGen.Rel.star (hr : ∀ a b, r a b → r (star a) (star b))
+ ⦃a b : R⦄ : Rel r a b → Rel r (star a) (star b)
+ | refl _ => .refl _
+ | symm h => .symm <| h.star hr
+ | trans h1 h2 => .trans (h1.star hr) (h2.star hr)
+ | of _ _ h => .of _ _ (hr _ _ h)
+ | mul h1 h2 => by
+ rw [star_mul, star_mul]
+ exact (h2.star hr).mul (h1.star hr)
+ | add h1 h2 => by
+ rw [star_add, star_add]
+ exact (h1.star hr).add (h2.star hr)
+
+theorem ringConGen_star (hr : ∀ a b, r a b → r (star a) (star b)) ⦃a b : R⦄ :
+ ringConGen r a b → ringConGen r (star a) (star b) := (RingConGen.Rel.star hr ·)
+
+end Ring
From 20711864f1a4827da127b722f1e2fa5e90486f6d Mon Sep 17 00:00:00 2001
From: Arend Mellendijk <8759745+amellendijk@users.noreply.github.com>
Date: Thu, 25 Jun 2026 15:06:05 +0000
Subject: [PATCH 0355/1300] feat(Tactic): `polynomial(_nf)` tactics (#31513)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Implement tactic for proving equality of polynomials.
This tactic is part of a larger suite (see https://github.com/leanprover-community/mathlib4/pull/30374).
Also generalize the preprocessing step for `algebra` so that it always replaces `algebraMap R A r` with `r • 1` instead of only when `R` is `Nat` or `Int`. This was an oversight in `algebra` that broke `polynomial`.
- [x] depends on: #31508
---
Mathlib.lean | 3 +
Mathlib/Algebra/MvPolynomial/Basic.lean | 20 ++-
Mathlib/RingTheory/Polynomial/Basic.lean | 2 +-
Mathlib/Tactic.lean | 3 +
Mathlib/Tactic/Algebra/AlgebraNF.lean | 77 ++++++++++
Mathlib/Tactic/Algebra/Basic.lean | 51 +------
Mathlib/Tactic/Polynomial/Basic.lean | 179 +++++++++++++++++++++++
Mathlib/Tactic/Polynomial/Core.lean | 100 +++++++++++++
MathlibTest/Tactic/Polynomial.lean | 82 +++++++++++
9 files changed, 468 insertions(+), 49 deletions(-)
create mode 100644 Mathlib/Tactic/Algebra/AlgebraNF.lean
create mode 100644 Mathlib/Tactic/Polynomial/Basic.lean
create mode 100644 Mathlib/Tactic/Polynomial/Core.lean
create mode 100644 MathlibTest/Tactic/Polynomial.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 92067dc839f781..66d8dccb727666 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -7165,6 +7165,7 @@ public import Mathlib.SetTheory.ZFC.VonNeumann
public import Mathlib.Tactic
public import Mathlib.Tactic.Abel
public import Mathlib.Tactic.AdaptationNote
+public import Mathlib.Tactic.Algebra.AlgebraNF
public import Mathlib.Tactic.Algebra.Basic
public import Mathlib.Tactic.Algebra.Lemmas
public import Mathlib.Tactic.Algebraize
@@ -7421,6 +7422,8 @@ public import Mathlib.Tactic.Order.ToInt
public import Mathlib.Tactic.PNatToNat
public import Mathlib.Tactic.PPWithUniv
public import Mathlib.Tactic.Peel
+public import Mathlib.Tactic.Polynomial.Basic
+public import Mathlib.Tactic.Polynomial.Core
public import Mathlib.Tactic.Polyrith
public import Mathlib.Tactic.Positivity
public import Mathlib.Tactic.Positivity.Basic
diff --git a/Mathlib/Algebra/MvPolynomial/Basic.lean b/Mathlib/Algebra/MvPolynomial/Basic.lean
index 74f35d21f7e3d5..8c0bd485a58ea7 100644
--- a/Mathlib/Algebra/MvPolynomial/Basic.lean
+++ b/Mathlib/Algebra/MvPolynomial/Basic.lean
@@ -15,6 +15,7 @@ public import Mathlib.Algebra.Regular.Pow
public import Mathlib.Data.Finsupp.Antidiagonal
public import Mathlib.Data.Finsupp.Order
public import Mathlib.Order.SymmDiff
+public meta import Mathlib.Tactic.Polynomial.Core
/-!
# Multivariate polynomials
@@ -106,10 +107,14 @@ def C : R →+* MvPolynomial σ R :=
variable (R σ)
-@[simp]
+@[simp, polynomial_post]
theorem algebraMap_eq : algebraMap R (MvPolynomial σ R) = C :=
rfl
+@[polynomial_pre]
+theorem C_eq_algebraMap : MvPolynomial.C = algebraMap R (MvPolynomial σ R) :=
+ rfl
+
variable {R σ}
@[simp]
@@ -1078,4 +1083,17 @@ end coeffsIn
end CommSemiring
+meta section Meta
+
+open Mathlib.Tactic.Polynomial in
+/-- Infer base ring for `MvPolynomial _ R`. Used by the `polynomial` tactic. -/
+@[polynomial_infer_base]
+def mvPolynomialInferBaseImpl : PolynomialExt where
+ infer e := do
+ match_expr e with
+ | MvPolynomial _ R _ => pure R
+ | _ => failure
+
+end Meta
+
end MvPolynomial
diff --git a/Mathlib/RingTheory/Polynomial/Basic.lean b/Mathlib/RingTheory/Polynomial/Basic.lean
index efa4f23011e935..96e51ee43704b3 100644
--- a/Mathlib/RingTheory/Polynomial/Basic.lean
+++ b/Mathlib/RingTheory/Polynomial/Basic.lean
@@ -926,7 +926,7 @@ lemma aeval_natDegree_le {R : Type*} [CommSemiring R] {m n : ℕ}
apply (Polynomial.natDegree_sum_le _ _).trans
apply Finset.sup_le
intro d hd
- simp_rw [Function.comp_apply, ← C_eq_algebraMap]
+ simp_rw [Function.comp_apply, ← Polynomial.C_eq_algebraMap]
apply (Polynomial.natDegree_C_mul_le _ _).trans
apply (Polynomial.natDegree_prod_le _ _).trans
have : ∑ i ∈ d.support, (d i) * n ≤ m * n := by
diff --git a/Mathlib/Tactic.lean b/Mathlib/Tactic.lean
index 02d86792ef5678..b09c506d6e6a86 100644
--- a/Mathlib/Tactic.lean
+++ b/Mathlib/Tactic.lean
@@ -2,6 +2,7 @@ module -- shake: keep-all --deprecated_module: ignore
public import Mathlib.Tactic.Abel
public import Mathlib.Tactic.AdaptationNote
+public import Mathlib.Tactic.Algebra.AlgebraNF
public import Mathlib.Tactic.Algebra.Basic
public import Mathlib.Tactic.Algebra.Lemmas
public import Mathlib.Tactic.Algebraize
@@ -258,6 +259,8 @@ public import Mathlib.Tactic.Order.ToInt
public import Mathlib.Tactic.PNatToNat
public import Mathlib.Tactic.PPWithUniv
public import Mathlib.Tactic.Peel
+public import Mathlib.Tactic.Polynomial.Basic
+public import Mathlib.Tactic.Polynomial.Core
public import Mathlib.Tactic.Polyrith
public import Mathlib.Tactic.Positivity
public import Mathlib.Tactic.Positivity.Basic
diff --git a/Mathlib/Tactic/Algebra/AlgebraNF.lean b/Mathlib/Tactic/Algebra/AlgebraNF.lean
new file mode 100644
index 00000000000000..04730c671c32ba
--- /dev/null
+++ b/Mathlib/Tactic/Algebra/AlgebraNF.lean
@@ -0,0 +1,77 @@
+/-
+Copyright (c) 2025 Arend Mellendijk. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Arend Mellendijk
+-/
+module
+
+public import Mathlib.Tactic.Algebra.Basic
+
+/-! # The `algebra_nf` tactic
+
+This file contains helper functions for the (currently unimplemented) `algebra_nf` tactic.
+
+The defnitions in this file are currently only used by `polynomial_nf`.
+-/
+
+public meta section
+
+open Lean Meta Qq Mathlib.Tactic.Ring
+
+namespace Mathlib.Tactic.Algebra
+
+/-- Clean up the normal form into a more human-friendly format. This does everything
+ `RingNF.cleanup` does and also pulls the scalar multiplication from the end of of each term to
+ the start. i.e. x * y * (r • 1) → r • (x * y)
+ Used by `cleanup`. -/
+def cleanupSMul (cfg : RingNF.Config) (r : Simp.Result) : MetaM Simp.Result := do
+ let thms : SimpTheorems := {}
+ let thms ← [``add_zero, ``add_assoc_rev, ``_root_.mul_one, ``mul_assoc_rev, ``_root_.pow_one,
+ ``mul_neg, ``add_neg, ``one_smul, ``mul_smul_comm, ``Algebra.algebraMap_eq_smul_one
+ ].foldlM (·.addConst ·) thms
+ let thms ← [``nat_rawCast_0, ``nat_rawCast_1, ``nat_rawCast_2, ``int_rawCast_neg,
+ ``nnrat_rawCast, ``rat_rawCast_neg].foldlM (·.addConst · (post := false)) thms
+ let ctx ← Simp.mkContext { zetaDelta := cfg.zetaDelta }
+ (simpTheorems := #[thms])
+ (congrTheorems := ← getSimpCongrTheorems)
+ pure <| ←
+ r.mkEqTrans (← Simp.main r.expr ctx (methods := Lean.Meta.Simp.mkDefaultMethodsCore {})).1
+
+/-- Turn scalar multiplication by an explicit constant in `R` into multiplication in `A`.
+
+e.g. `(4 : ℚ) • x` becomes `4 * x` but `↑n • x` stays `↑n • x`.
+-/
+def cleanupConsts (cfg : RingNF.Config) (r : Simp.Result) : MetaM Simp.Result := do
+ let thms : SimpTheorems := {}
+ let thms ← [``add_zero, ``_root_.one_mul, ``_root_.mul_one,
+ ``neg_mul, ``add_neg].foldlM (·.addConst ·) thms
+ let thms ← [``ofNat_smul, ``neg_ofNat_smul, ``neg_1_smul, ``nnRat_ofNat_smul_1,
+ ``nnRat_ofNat_smul_2, ``rat_ofNat_smul_1, ``rat_ofNat_smul_2
+ ].foldlM (·.addConst · (post := false)) thms
+ let ctx ← Simp.mkContext { zetaDelta := cfg.zetaDelta }
+ (simpTheorems := #[thms])
+ (congrTheorems := ← getSimpCongrTheorems)
+ pure <| ←
+ r.mkEqTrans (← Simp.main r.expr ctx (methods := Lean.Meta.Simp.mkDefaultMethodsCore {})).1
+
+/-- The core of `algebra_nf with R` - normalize the expression `e` over the base ring `R`
+Also used internally in `polynomial_nf`. -/
+meta def evalExpr {u : Lean.Level} (R : Q(Type u)) (e : Expr) : AtomM Simp.Result := do
+ let e ← withReducible <| whnf e
+ guard e.isApp -- all interesting ring expressions are applications
+ let ⟨v, A, e⟩ ← inferTypeQ' e
+ let sA ← synthInstanceQ q(CommSemiring $A)
+ let sR ← synthInstanceQ q(CommSemiring $R)
+ let sAlg ← synthInstanceQ q(Algebra $R $A)
+ let cr ← Algebra.mkCache sR
+ let ca ← Algebra.mkCache sA
+ assumeInstancesCommute
+ let ⟨a, _, pa⟩ ← match
+ ← Common.isAtomOrDerivable (Algebra.ringCompute q($sAlg) cr ca) ca.toCache q($e) with
+ -- `none` indicates that `eval` will find something algebraic.
+ | none => Common.eval rcℕ (Algebra.ringCompute sAlg cr ca) ca.toCache e
+ | some none => failure -- No point rewriting atoms
+ | some (some r) => pure r -- Nothing algebraic for `eval` to use, but `norm_num` simplifies.
+ pure { expr := a, proof? := pa }
+
+end Mathlib.Tactic.Algebra
diff --git a/Mathlib/Tactic/Algebra/Basic.lean b/Mathlib/Tactic/Algebra/Basic.lean
index cda5ca25b7cf19..a01cf768db41cd 100644
--- a/Mathlib/Tactic/Algebra/Basic.lean
+++ b/Mathlib/Tactic/Algebra/Basic.lean
@@ -9,7 +9,6 @@ public meta import Lean.Meta.Tactic.NormCast
public import Mathlib.Tactic.Algebra.Lemmas
public import Mathlib.Tactic.Ring.RingNF
-
/-!
# The `algebra` tactic
A suite of three tactics for solving equations in commutative algebras over commutative (semi)rings,
@@ -295,59 +294,17 @@ open Lean Parser.Tactic Elab Command Elab.Tactic Meta Qq
theorem Nat.cast_eq_algebraMap (A : Type*) [CommSemiring A] (n : ℕ) :
Nat.cast n = algebraMap ℕ A n := rfl
-theorem Nat.algebraMap_eq_cast (A : Type*) [CommSemiring A] (n : ℕ) :
- algebraMap ℕ A n = Nat.cast n := rfl
-
theorem Int.cast_eq_algebraMap (A : Type*) [CommRing A] (n : ℤ) :
Int.cast n = algebraMap ℤ A n := rfl
-theorem Int.algebraMap_eq_cast (A : Type*) [CommRing A] (n : ℤ) :
- algebraMap ℤ A n = Int.cast n := rfl
-
/-- Remove some nonstandard spellings of `algebraMap` such as `Nat.cast` -/
-def preprocess (mvarId : MVarId) : MetaM MVarId := do
+def preprocess (e : Expr) : MetaM Simp.Result := do
-- collect the available `push_cast` lemmas
let thms : SimpTheorems := {}
let thms ← [``Nat.cast_eq_algebraMap, ``Int.cast_eq_algebraMap,
``Algebra.algebraMap_eq_smul_one].foldlM (·.addConst ·) thms
let ctx ← Simp.mkContext { failIfUnchanged := false } (simpTheorems := #[thms])
- let (some r, _) ← simpTarget mvarId ctx (simprocs := #[]) |
- throwError "internal error in polynomial tactic: preprocessing should not close goals"
- return r
-
-/-- Clean up the normal form into a more human-friendly format. This does everything
- `RingNF.cleanup` does and also pulls the scalar multiplication from the end of of each term to
- the start. i.e. x * y * (r • 1) → r • (x * y)
- Used by `cleanup`. -/
-def cleanupSMul (cfg : RingNF.Config) (r : Simp.Result) : MetaM Simp.Result := do
- let thms : SimpTheorems := {}
- let thms ← [``add_zero, ``add_assoc_rev, ``_root_.mul_one, ``mul_assoc_rev, ``_root_.pow_one,
- ``mul_neg, ``add_neg, ``one_smul, ``mul_smul_comm, ``Nat.algebraMap_eq_cast,
- ``Int.algebraMap_eq_cast].foldlM (·.addConst ·) thms
- let thms ← [``nat_rawCast_0, ``nat_rawCast_1, ``nat_rawCast_2, ``int_rawCast_neg,
- ``nnrat_rawCast, ``rat_rawCast_neg].foldlM (·.addConst · (post := false)) thms
- let ctx ← Simp.mkContext { zetaDelta := cfg.zetaDelta }
- (simpTheorems := #[thms])
- (congrTheorems := ← getSimpCongrTheorems)
- pure <| ←
- r.mkEqTrans (← Simp.main r.expr ctx (methods := Lean.Meta.Simp.mkDefaultMethodsCore {})).1
-
-/-- Turn scalar multiplication by an explicit constant in `R` into multiplication in `A`.
-
-e.g. `(4 : ℚ) • x` becomes `4 * x` but `↑n • x` stays `↑n • x`.
--/
-def cleanupConsts (cfg : RingNF.Config) (r : Simp.Result) : MetaM Simp.Result := do
- let thms : SimpTheorems := {}
- let thms ← [``add_zero, ``_root_.one_mul, ``_root_.mul_one,
- ``neg_mul, ``add_neg].foldlM (·.addConst ·) thms
- let thms ← [``ofNat_smul, ``neg_ofNat_smul, ``neg_1_smul, ``nnRat_ofNat_smul_1,
- ``nnRat_ofNat_smul_2, ``rat_ofNat_smul_1, ``rat_ofNat_smul_2
- ].foldlM (·.addConst · (post := false)) thms
- let ctx ← Simp.mkContext { zetaDelta := cfg.zetaDelta }
- (simpTheorems := #[thms])
- (congrTheorems := ← getSimpCongrTheorems)
- pure <| ←
- r.mkEqTrans (← Simp.main r.expr ctx (methods := Lean.Meta.Simp.mkDefaultMethodsCore {})).1
+ return (← Simp.main e ctx (methods := Lean.Meta.Simp.mkDefaultMethodsCore {})).1
/-- Collect all scalar rings from scalar multiplications using a state monad for performance.
@@ -477,14 +434,14 @@ automatically.
-/
elab (name := algebra) "algebra":tactic =>
withMainContext do
- liftMetaTactic' preprocess
+ liftMetaTactic1 (transformAtTarget (fun e _ ↦ preprocess e) "algebra" .silent · default)
let g ← getMainGoal
AtomM.run .default (proveEq none g)
@[tactic_alt algebra]
elab (name := algebraWith) "algebra" " with " R:term : tactic =>
withMainContext do
- liftMetaTactic' preprocess
+ liftMetaTactic1 (transformAtTarget (fun e _ ↦ preprocess e) "algebra" .silent · default)
let ⟨u, R⟩ ← getLevelQ' (← elabTerm R none)
let g ← getMainGoal
AtomM.run .default (proveEq (some ⟨u, R⟩) g)
diff --git a/Mathlib/Tactic/Polynomial/Basic.lean b/Mathlib/Tactic/Polynomial/Basic.lean
new file mode 100644
index 00000000000000..c2c74e5d77e67f
--- /dev/null
+++ b/Mathlib/Tactic/Polynomial/Basic.lean
@@ -0,0 +1,179 @@
+/-
+Copyright (c) 2025 Arend Mellendijk. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Arend Mellendijk
+-/
+
+module
+
+public import Mathlib.Algebra.Polynomial.AlgebraMap
+public import Mathlib.Algebra.Polynomial.Coeff
+public import Mathlib.Tactic.Algebra.Basic
+public import Mathlib.Tactic.Algebra.AlgebraNF
+public import Mathlib.Tactic.Polynomial.Core
+
+/-!
+# Polynomial
+An extensible tactic for proving equality of polynomial expressions implemented using `algebra`.
+To add support for a new polynomial-like type, one needs to do three things:
+* Implement a polynomial extension that lets `polynomial` infer the base ring from the algebraic
+ type. For example:
+```
+@[polynomial_infer_base]
+def polynomialInferBase : PolynomialExt where
+ infer e := do
+ match_expr e with
+ | Polynomial R _ => pure R
+ | _ => failure
+```
+* Tag any preprocessing lemmas with @[polynomial_pre]. This would include a lemma saying that
+`C = algebraMap _ _` so that `algebra` knows how to normalize it.
+* Tag any postprocessing lemmas with @[polynomial_post], so that `polynomial_nf` produces a pretty
+expression.
+-/
+
+open Lean Mathlib.Tactic Mathlib.Tactic.Algebra Parser.Tactic Elab Meta Qq
+
+public meta section
+
+namespace Mathlib.Tactic.Polynomial
+
+/-- Infer base ring for `Polynomial R` -/
+@[polynomial_infer_base]
+def polynomialInferBase : PolynomialExt where
+ infer e := do
+ match_expr e with
+ | Polynomial R _ => pure R
+ | _ => failure
+
+section Lemmas
+
+variable {σ R A : Type*} [CommSemiring R] [CommSemiring A] [Algebra R A]
+
+attribute [polynomial_post] mul_one Algebra.smul_def Polynomial.algebraMap_eq
+
+@[polynomial_pre]
+theorem monomial_eq_smul (a : R) (n : ℕ) : Polynomial.monomial n a = a • (.X ^ n) := by
+ rw [← Polynomial.C_mul_X_pow_eq_monomial, Polynomial.smul_eq_C_mul]
+
+-- `polynomial_pre` contains a lemma sending `C -> algebraMap`, so `C` is not simp normal form.
+@[polynomial_pre]
+theorem map_algebraMap (r : R) :
+ Polynomial.map (algebraMap R A) (algebraMap R (Polynomial R) r) =
+ algebraMap A (Polynomial A) (algebraMap R A r) := by
+ simp
+
+end Lemmas
+
+open Mathlib.Meta AtomM
+
+attribute [polynomial_pre] Polynomial.C_eq_algebraMap
+ Polynomial.monomial_eq_smul Polynomial.map_add Polynomial.map_mul Polynomial.map_pow
+ Polynomial.map_X Polynomial.map_natCast Polynomial.map_intCast
+
+/- TODO: we don't currently have a good way to normalize monomials of MvPolynomials. These are
+indexed by finsupps, making it difficult to turn into the appropriate normal form. -/
+/-- Run the `polynomial_pre` simpset to turn nonstandard spellings of `algebraMap` such as
+`Polynomial.C` into `algebraMap` -/
+def preprocess (e : Expr) : MetaM Simp.Result := do
+ let preThms ← polynomialPreExt.getTheorems
+ let ctx ← Simp.mkContext { failIfUnchanged := false } (simpTheorems := #[preThms])
+ pure (← Simp.main e ctx (methods := Lean.Meta.Simp.mkDefaultMethodsCore {})).1
+
+open Tactic
+
+/-- `polynomial` solves equalities of `Polynomial`s and similar types.
+
+Given a goal which is an equality in `Polynomial R` with a commutative ring `R`, `polynomial`
+turns both sides of the equation into a normal form by expanding out the brackets. It then closes
+the goal if both sides contain the same terms and fails otherwise. `polynomial_nf` normalizes all
+subexpressions at a given location.
+
+Variants of `polynomial` include:
+* `polynomial`: normalize both sides of an equation and close the goal if they are equal
+* `polynomial!`: run `polynomial` at default transparency
+* `polynomial_nf`: normalize all subexpression of the goal
+* `polynomial_nf at h₁ h₂ ⊢`: normalize all subexpressions at hypotheses `h₁` `h₁` and the goal
+* `polynomial_nf at *`: normalize all subexpressions of all local hypotheses and the goal
+* `polynomial_nf!`: run `polynomial_nf` at default transparency
+
+The `polynomial` tactic can be extended to work with algebras other than `Polynomial` using the
+attributes `polynomial_infer_base`, `polynomial_pre` and `polynomial_post`. This is only possible
+if the base ring can be inferred from the structure of the type.
+
+Examples:
+
+```
+example (a : ℚ) : (X + C a) * (X - C a) = X^2 + C (a^2) := by polynomial
+
+example {P : ℚ[X] → Prop} (h : P (X ^ 2 + X + C 4⁻¹)) : P ((X + C 2⁻¹) ^ 2) := by
+ polynomial_nf at h ⊢
+ exact h
+```
+
+-/
+elab (name := polynomial) "polynomial" tk:"!"? : tactic =>
+ withMainContext do
+ let g ← getMainGoal
+ let some (α, _, _) := (← whnfR <|← instantiateMVars <|← g.getType).eq?
+ | throwError "polynomial failed: not an equality"
+ let mut β : Expr := default
+ try
+ β ← Polynomial.inferBase α
+ catch _ =>
+ throwError "polynomial failed: not an equality of (mv)polynomials"
+ let some g ← transformAtTarget (fun e _ ↦ Polynomial.preprocess e) "polynomial" .silent g
+ default | done
+ let some g ← transformAtTarget (fun e _ ↦ Algebra.preprocess e) "polynomial" .silent g
+ default | done
+ AtomM.run (if tk.isSome then .default else .reducible)
+ (Algebra.proveEq (some (← getLevelQ' β)) g)
+
+@[tactic_alt polynomial]
+macro "polynomial!" : tactic => `(tactic| polynomial !)
+
+/-- A cleanup routine, which simplifies normalized expressions to a more human-friendly format.
+This is the `algebra_nf` cleanup routine with a little extra work to turn scalar multiplication
+into `(MV)Polynomial.C` -/
+def cleanup (cfg : RingNF.Config) (r : Simp.Result) : MetaM Simp.Result := do
+ match cfg.mode with
+ | .raw => pure r
+ | .SOP => do
+ let r ← cleanupSMul cfg r
+ let thms : SimpTheorems ← polynomialPostExt.getTheorems
+ let ctx ← Simp.mkContext { zetaDelta := cfg.zetaDelta }
+ (simpTheorems := #[thms])
+ (congrTheorems := ← getSimpCongrTheorems)
+ pure <| ←
+ r.mkEqTrans (← Simp.main r.expr ctx (methods := Lean.Meta.Simp.mkDefaultMethodsCore {})).1
+
+/-- Normalize a polynomial expression into standard form. Used by `polynomial_nf`. -/
+def evalExprPoly (e : Expr) : AtomM Simp.Result := do
+ let ⟨_, α, e⟩ ← inferTypeQ e
+ let mut R : Expr := default
+ try R ← inferBase α
+ catch _ => throwError "not a polynomial"
+ let r₁ ← Polynomial.preprocess e
+ let r₂ ← Algebra.preprocess r₁.expr
+ let ⟨_, R'⟩ ← getLevelQ' R
+ let r₃ ← evalExpr R' r₂.expr
+ (← r₁.mkEqTrans r₂).mkEqTrans r₃
+
+@[tactic_alt polynomial]
+elab (name := polynomialNF) "polynomial_nf" tk:"!"? loc:(location)? : tactic => withMainContext do
+ let mut cfg := {}
+ if tk.isSome then cfg := { cfg with red := .default, zetaDelta := true }
+ let loc := (loc.map expandLocation).getD (.targets #[] true)
+ let s ← IO.mkRef {}
+ let m := AtomM.recurse s cfg.toConfig (wellBehavedDischarge := true) (evalExprPoly) (cleanup cfg)
+ transformAtLocation (m ·) "polynomial_nf" loc cfg.ifUnchanged false
+
+@[tactic_alt polynomial]
+macro "polynomial_nf!" loc:(location)? : tactic =>
+ `(tactic| polynomial_nf ! $(loc)?)
+
+end Mathlib.Tactic.Polynomial
+
+open Polynomial
+
+end
diff --git a/Mathlib/Tactic/Polynomial/Core.lean b/Mathlib/Tactic/Polynomial/Core.lean
new file mode 100644
index 00000000000000..1c7ab0c3e182c1
--- /dev/null
+++ b/Mathlib/Tactic/Polynomial/Core.lean
@@ -0,0 +1,100 @@
+/-
+Copyright (c) 2025 Arend Mellendijk. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Arend Mellendijk
+-/
+module
+
+meta import Lean.Compiler.IR.CompilerM
+public meta import Lean.Meta.Tactic.Simp.Attr
+public import Mathlib.Init
+
+/-!
+# Setup for the `polynomial` tactic
+
+This file initializes the environment extensions and simp sets used by the `polynomial` tactic.
+
+These extensions let downstream users use their own polynomial-like types (such as `PowerSeries`)
+with the `polynomial` tactic suite.
+-/
+
+namespace Mathlib.Tactic.Polynomial
+
+open Lean Lean.Meta Lean.Elab Term
+
+public meta section
+
+/-- `polynomial_pre` marks a theorem to be used by the `polynomial` tactic as a preprocessing lemma.
+These serve the purpose of removing any definitions specific to polynomials that `algebra` can't
+handle. e.g. `Polynomial.C` and `Polynomial.map` -/
+initialize polynomialPreExt : SimpExtension ←
+ registerSimpAttr `polynomial_pre "\
+ The `polynomial_pre` simp attribute uses preprocessing lemmas \
+ to turn specialized functions into `algebraMap`s"
+
+/-- `polynomial_post` marks a theorem to be used by the `polynomial_nf` tactic as a postprocessing
+lemma. Used only by polynomial_nf. These serve the purpose of rewriting expressions in `algebra`
+normal form into a more readable form. e.g. `a • X` -> `algebraMap _ _ a * X` -> `C a * X`. -/
+initialize polynomialPostExt : SimpExtension ←
+ registerSimpAttr `polynomial_post "\
+ The `polynomial_post` simp attribute uses postprocessing lemmas \
+ to turn `algebraMap`s into more specialized functions."
+
+/-- `polynomial_infer_base` marks a procedure used by the `polynomial` tactic to infer
+the base ring of polynomial-like types. -/
+syntax (name := PolyInferBaseAttr) "polynomial_infer_base" : attr
+
+/-- An extension for `polynomial`. -/
+structure PolynomialExt where
+ /-- Attempts to infer the base `R` of an `Algebra R A` based only on `A`. e.g. returns `R` given
+ `Polynomial R`. -/
+ infer : Expr → MetaM Expr
+
+/-- Read a `polynomial` extension from a declaration of the right type. -/
+def mkPolynomialExt (n : Name) : ImportM PolynomialExt := do
+ let { env, opts, .. } ← read
+ IO.ofExcept <| unsafe env.evalConstCheck PolynomialExt opts ``PolynomialExt n
+
+/-- Environment extensions for `polynomial` declarations -/
+initialize polynomialExt : PersistentEnvExtension Name (Name × PolynomialExt)
+ (List Name × List (Name × PolynomialExt)) ←
+ registerPersistentEnvExtension {
+ mkInitial := pure ([], {})
+ addImportedFn := fun s => do
+ let dt ← s.foldlM (init := {}) fun dt s => s.foldlM (init := dt) fun dt n => do
+ return (n, ← mkPolynomialExt n) :: dt
+ pure ([], dt)
+ addEntryFn := fun (entries, s) (n, ext) => (n :: entries, (n, ext) :: s)
+ exportEntriesFn := fun s => s.1.reverse.toArray
+ }
+
+initialize registerBuiltinAttribute {
+ name := `PolyInferBaseAttr
+ descr := "adds a polynomial extension that infers the base ring of a polynomial-like type"
+ applicationTime := .afterCompilation
+ add := fun declName stx kind => match stx with
+ | `(attr| polynomial_infer_base) => do
+ unless kind == AttributeKind.global do
+ throwError "invalid attribute 'polynomial_infer_base', must be global"
+ let env ← getEnv
+ unless (env.getModuleIdxFor? declName).isNone do
+ throwError "invalid attribute 'polynomial_infer_base', declaration is in an imported module"
+ if (IR.getSorryDep env declName).isSome then return -- ignore in progress definitions
+ let ext ← mkPolynomialExt declName
+ setEnv <| polynomialExt.addEntry env (declName, ext)
+ | _ => throwUnsupportedSyntax
+}
+
+/-- Infer the base ring of `Polynomial`-like types that are registered using the `polynomial`
+environment extensions. Includes e.g. `Polynomial` and `MvPolynomial`. -/
+def inferBase (e : Expr) : MetaM Expr := do
+ for ⟨_, ext⟩ in (polynomialExt.getState (← getEnv)).2 do
+ try
+ return ← ext.infer e
+ catch _ =>
+ continue
+ failure
+
+end
+
+end Mathlib.Tactic.Polynomial
diff --git a/MathlibTest/Tactic/Polynomial.lean b/MathlibTest/Tactic/Polynomial.lean
new file mode 100644
index 00000000000000..72444858f74bb8
--- /dev/null
+++ b/MathlibTest/Tactic/Polynomial.lean
@@ -0,0 +1,82 @@
+module
+import Mathlib.Tactic.Polynomial.Basic
+import Mathlib.RingTheory.MvPolynomial
+
+
+/-! # The `polynomial` tactic -/
+
+axiom sorryPolynomialTest {P : Prop} : P
+
+section poly
+open Polynomial
+
+example (a : ℚ) : (X + C a)^2 = X^2 + C (2*a) * X + C (a^2) := by
+ polynomial
+
+example (a : ℚ) : (X + C a)^2 = X^2 + (2*a) • X + C (a^2) := by
+ polynomial
+
+example (a : ℚ) : (2*X + C a)^2 = 4 * monomial 2 1 + monomial 1 (4*a) + monomial 0 (a^2) := by
+ polynomial
+
+example (a : ℚ) : (X - C a)*(X + C a) = X^2 - C (a^2) := by
+ polynomial
+
+example (a : ℚ) : (C a * X + C 4)^2 = 0 := by
+ polynomial_nf
+ guard_target = C 16 + C (a * 8) * X + C (a ^ 2) * X ^ 2 = 0
+ apply sorryPolynomialTest
+
+example (a b c : ℚ) : (X + C a)^2 = X^2 + C c * X + C b := by
+ polynomial_nf
+ guard_target = C (a ^ 2) + C (a * 2) * X + X ^ 2 = C b + C c * X + X ^ 2
+ apply sorryPolynomialTest
+
+example (a : ℚ) (n : ℕ) : (X^n + C a)^2 = 0 := by
+ polynomial_nf
+ guard_target = C (a ^ 2) + C (a * 2) * X ^ n + X ^ (n * 2) = 0
+ apply sorryPolynomialTest
+
+variable {R A : Type*} [CommRing R] [CommRing A] [Algebra R A] {r₁ : R} {a₁ : A} in
+example : Polynomial.map (algebraMap R A) (C r₁ * X) = C a₁ * X := by
+ polynomial_nf
+ guard_target = C ((algebraMap R A) r₁) * X = C a₁ * X
+ apply sorryPolynomialTest
+
+example {P : ℚ[X] → Prop} (h : P (X ^ 2 + X + C 4⁻¹)) : P ((X + C 2⁻¹) ^ 2) := by
+ polynomial_nf at h ⊢
+ exact h
+
+end poly
+
+section mvpoly
+open MvPolynomial
+
+example (a : ℚ) : (X 0 + C a)^2 = X 0^2 + C (2*a) * X 0 + C (a^2) := by
+ polynomial
+
+example (a : ℚ) : (X 0 + C a)^2 = X 0^2 + (2*a) • X 0 + C (a^2) := by
+ polynomial
+
+example (a : ℚ) : (X 0 - C a)*(X 0 + C a) = (X 0)^2 - C (a^2) := by
+ polynomial
+
+example (a : ℚ) : (X 0 - X 1 * C a)*(X 0 + X 1 * C a) = (X 0)^2 - (X 1) ^ 2 * C (a^2) := by
+ polynomial
+
+example (a : ℚ) : ((X 0 + C a)^2).eval (fun _ ↦ -a) = 0 := by
+ polynomial_nf
+ guard_target = (eval fun i => -a) (C (a ^ 2) + C (a * 2) * X 0 + X 0 ^ 2) = 0
+ apply sorryPolynomialTest
+
+example (a b c : ℤ) : (X 0 * C a + X 1 * X 37 * C (b*(c-1)))^2 * (X 0 - 1) = 0 := by
+ polynomial_nf
+ guard_target = C (a * b * 2 - a * b * c * 2) * (X 0 * X 1 * X 37) +
+ C (b ^ 2 - b ^ 2 * c * 2 + b ^ 2 * c ^ 2) * (X 0 * X 1 ^ 2 * X 37 ^ 2) +
+ C (-a ^ 2) * X 0 ^ 2 +
+ C (-(a * b * 2) + a * b * c * 2) * (X 0 ^ 2 * X 1 * X 37) +
+ C (a ^ 2) * X 0 ^ 3 +
+ C (-b ^ 2 + (b ^ 2 * c * 2 - b ^ 2 * c ^ 2)) * (X 1 ^ 2 * X 37 ^ 2) = 0
+ apply sorryPolynomialTest
+
+end mvpoly
From 4364cadca3fd031315c292bf81a8e79cb2828a22 Mon Sep 17 00:00:00 2001
From: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com>
Date: Thu, 25 Jun 2026 15:32:54 +0000
Subject: [PATCH 0356/1300] feat(Order/Hom/Basic): equivalences of
`Order{Hom/Embedding/Iso}` (#38667)
- `OrderHom`s are equivalent to `RelHom`s of `LE` (unlike `OrderEmbedding`/`OrderIso` they aren't defined using it)
- Congruence equivs for `OrderEmbedding`/`OrderIso` when the two sides are order-isomorphic. This already exists for `OrderHom`.
---
Mathlib/Order/Hom/Basic.lean | 54 ++++++++++++++++++++++++++++++++++++
1 file changed, 54 insertions(+)
diff --git a/Mathlib/Order/Hom/Basic.lean b/Mathlib/Order/Hom/Basic.lean
index 5714f402520d64..22ef5cfb8f190a 100644
--- a/Mathlib/Order/Hom/Basic.lean
+++ b/Mathlib/Order/Hom/Basic.lean
@@ -285,6 +285,14 @@ def id : α →o α :=
instance : Inhabited (α →o α) :=
⟨id⟩
+variable (α β) in
+/-- Order homomorphisms are equivalent to relation homomorphisms between `LE` relations. -/
+def equivRelHom : (α →o β) ≃ @RelHom α β (· ≤ ·) (· ≤ ·) where
+ toFun f := ⟨f, @f.monotone⟩
+ invFun f := ⟨f, @f.map_rel⟩
+ left_inv _ := rfl
+ right_inv _ := rfl
+
/-- The preorder structure of `α →o β` is pointwise inequality: `f ≤ g ↔ ∀ a, f a ≤ g a`. -/
instance : Preorder (α →o β) :=
@Preorder.lift (α →o β) (α → β) _ DFunLike.coe
@@ -929,6 +937,52 @@ from `α` and `β` to themselves are order-isomorphic. -/
def conj {α β} [Preorder α] [Preorder β] (f : α ≃o β) : (α →o α) ≃ (β →o β) :=
arrowCongr f f
+/-- Transport an `OrderEmbedding` across a pair of `OrderIso`s, by pre- and post-composition.
+
+This is `Equiv.embeddingCongr`/`RelIso.relEmbeddingCongr` for `OrderEmbedding`. -/
+abbrev orderEmbeddingCongr (f : α ≃o γ) (g : β ≃o δ) : (α ↪o β) ≃ (γ ↪o δ) :=
+ RelIso.relEmbeddingCongr f g
+
+@[simp]
+theorem orderEmbeddingCongr_apply (f : α ≃o γ) (g : β ≃o δ) (h : α ↪o β) :
+ orderEmbeddingCongr f g h = .trans (.trans f.symm h) g :=
+ rfl
+
+@[simp]
+theorem orderEmbeddingCongr_symm_apply (f : α ≃o γ) (g : β ≃o δ) (h : γ ↪o δ) :
+ (orderEmbeddingCongr f g).symm h = .trans (.trans f h) g.symm :=
+ rfl
+
+/-- Transport an `OrderIso` across a pair of `OrderIso`s, by pre- and post-composition.
+
+This is `Equiv.equivCongr`/`RelIso.relIsoCongr` for `OrderIso`. -/
+abbrev orderIsoCongr (f : α ≃o γ) (g : β ≃o δ) : (α ≃o β) ≃ (γ ≃o δ) :=
+ RelIso.relIsoCongr f g
+
+@[simp]
+theorem orderIsoCongr_apply (f : α ≃o γ) (g : β ≃o δ) (h : α ≃o β) :
+ orderIsoCongr f g h = .trans (.trans f.symm h) g :=
+ rfl
+
+@[simp]
+theorem orderIsoCongr_symm_apply (f : α ≃o γ) (g : β ≃o δ) (h : γ ≃o δ) :
+ (orderIsoCongr f g).symm h = .trans (.trans f h) g.symm :=
+ rfl
+
+/-- A surjective order embedding is an order isomorphism. -/
+@[simps!]
+noncomputable def ofSurjective (f : α ↪o β) (hf : Function.Surjective f) : α ≃o β :=
+ RelIso.ofSurjective f hf
+
+/-- Surjective order embeddings are equivalent to order isomorphisms. -/
+@[simps apply symm_apply]
+noncomputable def equivEmbeddingSurjective :
+ α ≃o β ≃ { f : α ↪o β // Function.Surjective f } where
+ toFun f := ⟨f, f.surjective⟩
+ invFun f := ofSurjective f f.prop
+ left_inv _ := by ext; rfl
+ right_inv _ := rfl
+
/-- `Prod.swap` as an `OrderIso`. -/
def prodComm : α × β ≃o β × α where
toEquiv := Equiv.prodComm α β
From 5a07c5fc4c150dd4aa57cba67a56ebde2075bbf7 Mon Sep 17 00:00:00 2001
From: Riccardo Brasca
Date: Thu, 25 Jun 2026 16:37:52 +0000
Subject: [PATCH 0357/1300] feat: add nthRootsFinset_eq_of_prime and related
lemmas (#40662)
From flt-regular.
---
Mathlib/GroupTheory/OrderOfElement.lean | 11 ++++++
.../RootsOfUnity/PrimitiveRoots.lean | 39 +++++++++++++++++++
2 files changed, 50 insertions(+)
diff --git a/Mathlib/GroupTheory/OrderOfElement.lean b/Mathlib/GroupTheory/OrderOfElement.lean
index a3c866e905e6ff..b7ee3e5669e10e 100644
--- a/Mathlib/GroupTheory/OrderOfElement.lean
+++ b/Mathlib/GroupTheory/OrderOfElement.lean
@@ -275,6 +275,17 @@ theorem orderOf_dvd_iff_pow_eq_one {n : ℕ} : orderOf x ∣ n ↔ x ^ n = 1 :=
⟨fun h => by rw [← pow_mod_orderOf, Nat.mod_eq_zero_of_dvd h, _root_.pow_zero],
orderOf_dvd_of_pow_eq_one⟩
+/-- If `x ^ p = 1` for some odd `p`, then every power of `x` is an even power of `x`. -/
+@[to_additive /-- If `p • x = 0` for some odd `p`, then every multiple of `x` is an even
+multiple of `x`. -/]
+theorem exists_pow_eq_pow_two_mul {p : ℕ} (hx : x ^ p = 1) (hp : Odd p) (n : ℕ) :
+ ∃ m, x ^ n = x ^ (2 * m) := by
+ obtain ⟨r, rfl⟩ := hp
+ have key : x ^ (2 * (r + 1)) = x := by
+ have h2 : 2 * (r + 1) = 2 * r + 1 + 1 := by omega
+ rw [h2, pow_succ, hx, one_mul]
+ exact ⟨(r + 1) * n, by rw [← mul_assoc, pow_mul, key]⟩
+
@[to_additive addOrderOf_smul_dvd]
theorem orderOf_pow_dvd (n : ℕ) : orderOf (x ^ n) ∣ orderOf x := by
rw [orderOf_dvd_iff_pow_eq_one, pow_right_comm, pow_orderOf_eq_one, one_pow]
diff --git a/Mathlib/RingTheory/RootsOfUnity/PrimitiveRoots.lean b/Mathlib/RingTheory/RootsOfUnity/PrimitiveRoots.lean
index 783f533a4c74fe..b3e330e84338aa 100644
--- a/Mathlib/RingTheory/RootsOfUnity/PrimitiveRoots.lean
+++ b/Mathlib/RingTheory/RootsOfUnity/PrimitiveRoots.lean
@@ -33,6 +33,8 @@ monoids, expressing that an element is a primitive root of unity.
has a primitive `k`-th root of unity, then it has `φ k` of them.
* `primitiveRootsPowEquivOfCoprime`: An equivalence between `primitiveRoots k R` that takes each
root to a coprime power `a`.
+* `nthRootsFinset_eq_of_prime`: for `p` prime, the `p`-th roots of unity are the primitive `p`-th
+ roots of unity together with `1`.
## Implementation details
@@ -240,6 +242,12 @@ theorem pow_of_dvd (h : IsPrimitiveRoot ζ k) {p : ℕ} (hp : p ≠ 0) (hdiv : p
rw [← orderOf_pow_of_dvd hp hdiv]
exact IsPrimitiveRoot.orderOf _
+/-- If `ζ` is a primitive `k`-th root of unity with `k` odd, then every power of `ζ` is an even
+power of `ζ`. -/
+theorem exists_pow_eq_pow_two_mul (h : IsPrimitiveRoot ζ k) (hk : Odd k) (n : ℕ) :
+ ∃ m, ζ ^ n = ζ ^ (2 * m) :=
+ _root_.exists_pow_eq_pow_two_mul h.pow_eq_one hk n
+
protected theorem mem_rootsOfUnity {ζ : Mˣ} {n : ℕ} (h : IsPrimitiveRoot ζ n) :
ζ ∈ rootsOfUnity n M :=
h.pow_eq_one
@@ -830,6 +838,37 @@ end Automorphisms
end IsPrimitiveRoot
+section nthRootsFinsetPrime
+
+open IsPrimitiveRoot
+
+variable [CommRing R] [IsDomain R] {p : ℕ}
+
+/-- If `p` is prime, the `p`-th roots of unity in an integral domain are exactly the primitive
+`p`-th roots of unity together with `1`. -/
+theorem nthRootsFinset_eq_of_prime [DecidableEq R] (hp : p.Prime) :
+ nthRootsFinset p (1 : R) = primitiveRoots p R ∪ {1} := by
+ simp [nthRoots_one_eq_biUnion_primitiveRoots, hp.divisors]
+
+/-- For `p` prime, an element of `R` is a `p`-th root of unity if and only if it is either a
+primitive `p`-th root of unity or `1`. -/
+theorem mem_nthRootsFinset_iff_of_prime (hp : p.Prime) {η : R} :
+ η ∈ nthRootsFinset p (1 : R) ↔ IsPrimitiveRoot η p ∨ η = 1 := by
+ classical
+ simp [nthRootsFinset_eq_of_prime hp, mem_primitiveRoots hp.pos, or_comm]
+
+/-- A `p`-th root of unity that is not `1`, with `p` prime, satisfies `IsPrimitiveRoot η p`. -/
+theorem isPrimitiveRoot_of_mem_nthRootsFinset (hp : p.Prime) {η : R}
+ (hη : η ∈ nthRootsFinset p (1 : R)) (hne1 : η ≠ 1) : IsPrimitiveRoot η p :=
+ ((mem_nthRootsFinset_iff_of_prime hp).1 hη).resolve_right hne1
+
+/-- A `p`-th root of unity that is not `1`, with `p` prime, is a primitive `p`-th root of unity. -/
+theorem mem_primitiveRoots_of_mem_nthRootsFinset (hp : p.Prime) {η : R}
+ (hη : η ∈ nthRootsFinset p (1 : R)) (hne1 : η ≠ 1) : η ∈ primitiveRoots p R :=
+ (mem_primitiveRoots hp.pos).2 (isPrimitiveRoot_of_mem_nthRootsFinset hp hη hne1)
+
+end nthRootsFinsetPrime
+
section cyclic
/-- If `G` is cyclic of order `n` and `G'` contains a primitive `n`th root of unity,
From 609b527e6490d27b11731f8e4b49b7f5e0576909 Mon Sep 17 00:00:00 2001
From: Riccardo Brasca
Date: Thu, 25 Jun 2026 16:37:54 +0000
Subject: [PATCH 0358/1300] feat: add isCoprime_of_not_zeta_sub_one_dvd and
related lemmas (#40906)
From flt-regular.
---
.../NumberField/Cyclotomic/Basic.lean | 4 ++
.../NumberField/Cyclotomic/Ideal.lean | 50 +++++++++++++++++--
.../RootsOfUnity/CyclotomicUnits.lean | 14 +++---
3 files changed, 57 insertions(+), 11 deletions(-)
diff --git a/Mathlib/NumberTheory/NumberField/Cyclotomic/Basic.lean b/Mathlib/NumberTheory/NumberField/Cyclotomic/Basic.lean
index 35c65e0e82bdfa..c2a381225da3d1 100644
--- a/Mathlib/NumberTheory/NumberField/Cyclotomic/Basic.lean
+++ b/Mathlib/NumberTheory/NumberField/Cyclotomic/Basic.lean
@@ -187,6 +187,10 @@ end CharZero
lemma coe_toInteger {k : ℕ} [NeZero k] (hζ : IsPrimitiveRoot ζ k) : hζ.toInteger.1 = ζ := rfl
+@[simp]
+lemma toInteger_coe {k : ℕ} [NeZero k] {x : 𝓞 K} (hx : IsPrimitiveRoot (x : K) k) :
+ hx.toInteger = x := rfl
+
/-- `𝓞 K ⧸ Ideal.span {ζ - 1}` is finite. -/
lemma finite_quotient_toInteger_sub_one [NumberField K] {k : ℕ} (hk : 1 < k)
(hζ : IsPrimitiveRoot ζ k) :
diff --git a/Mathlib/NumberTheory/NumberField/Cyclotomic/Ideal.lean b/Mathlib/NumberTheory/NumberField/Cyclotomic/Ideal.lean
index e8061a821c8f55..59947f078ff144 100644
--- a/Mathlib/NumberTheory/NumberField/Cyclotomic/Ideal.lean
+++ b/Mathlib/NumberTheory/NumberField/Cyclotomic/Ideal.lean
@@ -47,9 +47,9 @@ namespace IsCyclotomicExtension.Rat
open Ideal NumberField RingOfIntegers
variable (n m p k : ℕ) [hp : Fact (Nat.Prime p)] (K : Type*) [Field K] [NumberField K]
- (P : Ideal (𝓞 K)) [hP₁ : P.IsPrime] [hP₂ : P.LiesOver (Ideal.span {(p : ℤ)})]
+ (P : Ideal (𝓞 K)) [hP₁ : P.IsPrime] [hP₂ : P.LiesOver (span {(p : ℤ)})]
-local notation3 "𝒑" => (Ideal.span {(p : ℤ)})
+local notation3 "𝒑" => (span {(p : ℤ)})
section PrimePow
@@ -57,7 +57,7 @@ variable {K} [hK : IsCyclotomicExtension {p ^ (k + 1)} ℚ K] {ζ : K}
(hζ : IsPrimitiveRoot ζ (p ^ (k + 1)))
instance isPrime_span_zeta_sub_one : IsPrime (span {hζ.toInteger - 1}) := by
- rw [Ideal.span_singleton_prime]
+ rw [span_singleton_prime]
· exact hζ.zeta_sub_one_prime
· exact Prime.ne_zero hζ.zeta_sub_one_prime
@@ -77,7 +77,7 @@ theorem absNorm_span_zeta_sub_one : absNorm (span {hζ.toInteger - 1}) = p := by
span_singleton_eq_span_singleton.mpr <| associated_norm_zeta_sub_one p k hζ
theorem p_mem_span_zeta_sub_one : (p : 𝓞 K) ∈ span {hζ.toInteger - 1} := by
- convert! Ideal.absNorm_mem _
+ convert! absNorm_mem _
exact (absNorm_span_zeta_sub_one ..).symm
theorem span_zeta_sub_one_ne_bot : span {hζ.toInteger - 1} ≠ ⊥ :=
@@ -85,7 +85,7 @@ theorem span_zeta_sub_one_ne_bot : span {hζ.toInteger - 1} ≠ ⊥ :=
instance liesOver_span_zeta_sub_one : (span {hζ.toInteger - 1}).LiesOver 𝒑 := by
rw [liesOver_iff]
- refine Ideal.IsMaximal.eq_of_le (Int.ideal_span_isMaximal_of_prime p) IsPrime.ne_top' ?_
+ refine IsMaximal.eq_of_le (Int.ideal_span_isMaximal_of_prime p) IsPrime.ne_top' ?_
rw [span_singleton_le_iff_mem, mem_comap, algebraMap_int_eq, map_natCast]
exact p_mem_span_zeta_sub_one p k hζ
@@ -182,6 +182,46 @@ instance isPrime_span_zeta_sub_one' : IsPrime (span {hζ.toInteger - 1}) := by
rw [← pow_one p] at hK hζ
exact isPrime_span_zeta_sub_one p 0 hζ
+/-- If `2 < p`, then `2` is not in the ideal `(ζ - 1)`, where `ζ` is a primitive `p`-th root of
+unity. -/
+theorem two_not_mem_span_zeta_sub_one' (h : 2 < p) : (2 : 𝓞 K) ∉ span {hζ.toInteger - 1} := by
+ rw [mem_span_singleton]
+ rw [← pow_one p] at hK hζ
+ exact hζ.toInteger_sub_one_not_dvd_two h.ne'
+
+omit hp hK [NumberField K] in
+lemma associated_sub_one_of_isPrimitiveRoot [NeZero p] {η : K} (hη : IsPrimitiveRoot η p) :
+ Associated (hζ.toInteger - 1) (hη.toInteger - 1) := by
+ obtain ⟨i, -, hi, hζη⟩ := hζ.isPrimitiveRoot_iff.mp hη
+ rw [show hη.toInteger = hζ.toInteger ^ i from RingOfIntegers.ext hζη.symm]
+ exact hζ.toInteger_isPrimitiveRoot.associated_sub_one_pow_sub_one_of_coprime hi
+
+omit [NumberField K] hK in
+open Polynomial in
+/-- `(ζ - 1) ^ (p - 1)` is associated to `p`, where `ζ` is a primitive `p`-th root of unity and
+`p` is prime. -/
+theorem associated_zeta_sub_one_pow_prime :
+ Associated ((hζ.toInteger - 1) ^ (p - 1)) (p : 𝓞 K) := by
+ rw [← eval_one_cyclotomic_prime (R := 𝓞 K) (p := p),
+ cyclotomic_eq_prod_X_sub_primitiveRoots hζ.toInteger_isPrimitiveRoot, eval_prod]
+ simp only [eval_sub, eval_X, eval_C]
+ rw [← Nat.totient_prime hp.out, ← hζ.toInteger_isPrimitiveRoot.card_primitiveRoots,
+ ← Finset.prod_const]
+ refine Associated.prod _ _ _ fun η hη ↦ ?_
+ have hη' : IsPrimitiveRoot (η : K) p :=
+ (isPrimitiveRoot_of_mem_primitiveRoots hη).map_of_injective RingOfIntegers.coe_injective
+ simpa using (associated_sub_one_of_isPrimitiveRoot p hζ hη').neg_right
+
+/-- If `ζ - 1` does not divide `x`, then `p` and `x` are coprime, where `ζ` is a primitive `p`-th
+root of unity and `p` is prime. -/
+theorem isCoprime_of_not_zeta_sub_one_dvd {x : 𝓞 K} (hx : ¬ hζ.toInteger - 1 ∣ x) :
+ IsCoprime (p : 𝓞 K) x := by
+ rwa [← isCoprime_span_singleton_iff, ← span_singleton_eq_span_singleton.mpr
+ (associated_zeta_sub_one_pow_prime p hζ), ← span_singleton_pow,
+ IsCoprime.pow_left_iff (by grind [hp.out.one_lt]), isCoprime_iff_gcd,
+ (prime_span_singleton_iff.mpr
+ hζ.zeta_sub_one_prime').irreducible.gcd_eq_one_iff, dvd_span_singleton, mem_span_singleton]
+
theorem inertiaDeg_span_zeta_sub_one' : inertiaDeg' (span {hζ.toInteger - 1}) ℤ = 1 := by
rw [← pow_one p] at hK hζ
exact inertiaDeg_span_zeta_sub_one p 0 hζ
diff --git a/Mathlib/RingTheory/RootsOfUnity/CyclotomicUnits.lean b/Mathlib/RingTheory/RootsOfUnity/CyclotomicUnits.lean
index 04235887b02e7e..3da9cd567185af 100644
--- a/Mathlib/RingTheory/RootsOfUnity/CyclotomicUnits.lean
+++ b/Mathlib/RingTheory/RootsOfUnity/CyclotomicUnits.lean
@@ -118,15 +118,13 @@ theorem associated_pow_add_sub_sub_one (hζ : IsPrimitiveRoot ζ n) (hn : 2 ≤
/-- If `p` is prime and `ζ` is a `p`-th primitive root of unity, then `ζ - 1` and `η₁ - η₂` are
associated for all distinct `p`-th roots of unity `η₁` and `η₂`. -/
-lemma ntRootsFinset_pairwise_associated_sub_one_sub_of_prime (hζ : IsPrimitiveRoot ζ p)
+lemma nthRootsFinset_pairwise_associated_sub_one_sub_of_prime (hζ : IsPrimitiveRoot ζ p)
(hp : p.Prime) :
- Set.Pairwise (nthRootsFinset p (1 : A)) (fun η₁ η₂ ↦ Associated (ζ - 1) (η₁ - η₂)) := by
+ Set.Pairwise (nthRootsFinset p (1 : A)) fun η₁ η₂ ↦ Associated (ζ - 1) (η₁ - η₂) := by
intro η₁ hη₁ η₂ hη₂ e
have : NeZero p := ⟨hp.ne_zero⟩
- obtain ⟨i, hi, rfl⟩ :=
- hζ.eq_pow_of_pow_eq_one ((Polynomial.mem_nthRootsFinset hp.pos 1).1 hη₁)
- obtain ⟨j, hj, rfl⟩ :=
- hζ.eq_pow_of_pow_eq_one ((Polynomial.mem_nthRootsFinset hp.pos 1).1 hη₂)
+ obtain ⟨i, hi, rfl⟩ := hζ.eq_pow_of_pow_eq_one ((Polynomial.mem_nthRootsFinset hp.pos 1).1 hη₁)
+ obtain ⟨j, hj, rfl⟩ := hζ.eq_pow_of_pow_eq_one ((Polynomial.mem_nthRootsFinset hp.pos 1).1 hη₂)
wlog hij : j ≤ i
· simpa using (this hζ ‹_› ‹_› _ hj ‹_› _ hi ‹_› e.symm (by lia)).neg_right
have H : (i - j).Coprime p := (coprime_of_lt_prime (by grind) (by grind) hp).symm
@@ -134,4 +132,8 @@ lemma ntRootsFinset_pairwise_associated_sub_one_sub_of_prime (hζ : IsPrimitiveR
simp only [hij, add_tsub_cancel_of_le] at h
rw [← h, associated_mul_unit_right_iff]
+@[deprecated (since := "2026-06-23")]
+alias ntRootsFinset_pairwise_associated_sub_one_sub_of_prime :=
+ nthRootsFinset_pairwise_associated_sub_one_sub_of_prime
+
end IsPrimitiveRoot
From 51801adf31b3f9b44f0a1072aac68344dd2f82b2 Mon Sep 17 00:00:00 2001
From: Christian Merten <136261474+chrisflav@users.noreply.github.com>
Date: Thu, 25 Jun 2026 16:37:57 +0000
Subject: [PATCH 0359/1300] chore(RingTheory/TensorProduct): remove left-over
`noncomputable`s (#41041)
These probably became non-`noncomputable` with the new compiler.
---
Mathlib/RingTheory/TensorProduct/Maps.lean | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Mathlib/RingTheory/TensorProduct/Maps.lean b/Mathlib/RingTheory/TensorProduct/Maps.lean
index 0e7c4b8e3b4116..10c1e8486bc3c8 100644
--- a/Mathlib/RingTheory/TensorProduct/Maps.lean
+++ b/Mathlib/RingTheory/TensorProduct/Maps.lean
@@ -412,7 +412,7 @@ omit [Algebra S A] [IsScalarTower R S A]
attribute [local instance] Algebra.TensorProduct.rightAlgebra in
/-- `S`-linear version of `Algebra.TensorProduct.comm` when `A ⊗[R] S`
is viewed as an `S`-algebra via the right component. -/
-noncomputable def commRight : S ⊗[R] A ≃ₐ[S] A ⊗[R] S where
+def commRight : S ⊗[R] A ≃ₐ[S] A ⊗[R] S where
__ := Algebra.TensorProduct.comm R S A
commutes' _ := rfl
@@ -554,11 +554,11 @@ lemma comm_comp_map_apply (f : A →ₐ[R] C) (g : B →ₐ[R] D) (x) :
variable (A) in
/-- `lTensor A g : A ⊗ B →ₐ A ⊗ D` is the natural algebra morphism induced by `g : B →ₐ D`. -/
-noncomputable abbrev lTensor (g : B →ₐ[R] D) : (A ⊗[R] B) →ₐ[S] (A ⊗[R] D) := map (.id S A) g
+abbrev lTensor (g : B →ₐ[R] D) : (A ⊗[R] B) →ₐ[S] (A ⊗[R] D) := map (.id S A) g
variable (B) in
/-- `rTensor B f : A ⊗ B →ₐ C ⊗ B` is the natural algebra morphism induced by `f : A →ₐ C`. -/
-noncomputable abbrev rTensor (f : A →ₐ[S] C) : A ⊗[R] B →ₐ[S] C ⊗[R] B := map f (.id R B)
+abbrev rTensor (f : A →ₐ[S] C) : A ⊗[R] B →ₐ[S] C ⊗[R] B := map f (.id R B)
/-- Construct an isomorphism between tensor products of an S-algebra with an R-algebra
from S- and R- isomorphisms between the tensor factors.
From 1f12a578d8573f0cc67f167cac9f0a1c96bde283 Mon Sep 17 00:00:00 2001
From: "mathlib-update-dependencies[bot]"
<258990618+mathlib-update-dependencies[bot]@users.noreply.github.com>
Date: Thu, 25 Jun 2026 16:37:59 +0000
Subject: [PATCH 0360/1300] chore: update Mathlib dependencies 2026-06-25
(#41044)
This PR updates the Mathlib dependencies.
---
lake-manifest.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lake-manifest.json b/lake-manifest.json
index 5ebacc94fd75e7..1dfeee55bef0ed 100644
--- a/lake-manifest.json
+++ b/lake-manifest.json
@@ -65,7 +65,7 @@
"type": "git",
"subDir": null,
"scope": "leanprover-community",
- "rev": "7d1b02eb63b526dff04cb990cf05b06b38ccbd3f",
+ "rev": "09c267c2706119a09606e6cde3f6cef5bb2ab72a",
"name": "batteries",
"manifestFile": "lake-manifest.json",
"inputRev": "main",
From 7af6b0b0f3e733d1c5a71471dcfd91bb35b2f913 Mon Sep 17 00:00:00 2001
From: Riccardo Brasca
Date: Thu, 25 Jun 2026 18:31:41 +0000
Subject: [PATCH 0361/1300] feat: add prime_units_mul and variants (#40220)
From flt-regular.
---
Mathlib/Algebra/Prime/Lemmas.lean | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/Mathlib/Algebra/Prime/Lemmas.lean b/Mathlib/Algebra/Prime/Lemmas.lean
index 704735cad894a0..0095606c2e6a93 100644
--- a/Mathlib/Algebra/Prime/Lemmas.lean
+++ b/Mathlib/Algebra/Prime/Lemmas.lean
@@ -62,6 +62,21 @@ theorem MulEquiv.prime_iff {E : Type*} [EquivLike E M N] [MulEquivClass E M N] (
end Map
+variable {x y : M}
+
+theorem prime_units_mul (u : Mˣ) : Prime (↑u * y) ↔ Prime y := by simp [Prime]
+
+theorem prime_isUnit_mul (h : IsUnit x) : Prime (x * y) ↔ Prime y :=
+ let ⟨u, hu⟩ := h
+ hu ▸ prime_units_mul u
+
+theorem prime_mul_units (u : Mˣ) : Prime (y * ↑u) ↔ Prime y := by
+ rw [mul_comm, prime_units_mul]
+
+theorem prime_mul_isUnit (h : IsUnit x) : Prime (y * x) ↔ Prime y :=
+ let ⟨u, hu⟩ := h
+ hu ▸ prime_mul_units u
+
end Prime
section IsCancelMulZero
From 12e0e35303caf39265f1afeed6f1d655aeefaef8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=ABl=20Riou?=
<37772949+joelriou@users.noreply.github.com>
Date: Thu, 25 Jun 2026 18:31:44 +0000
Subject: [PATCH 0362/1300] feat(Algebra/Homology): applying an additive
functor to the homotopy (co)fiber (#40924)
---
Mathlib/Algebra/Homology/HomotopyCofiber.lean | 206 +++++++++++++++++-
Mathlib/Algebra/Homology/HomotopyFiber.lean | 42 ++++
2 files changed, 246 insertions(+), 2 deletions(-)
diff --git a/Mathlib/Algebra/Homology/HomotopyCofiber.lean b/Mathlib/Algebra/Homology/HomotopyCofiber.lean
index aceb7b4f4d7f86..96920cb78c75bc 100644
--- a/Mathlib/Algebra/Homology/HomotopyCofiber.lean
+++ b/Mathlib/Algebra/Homology/HomotopyCofiber.lean
@@ -145,6 +145,33 @@ lemma inrX_fstX (i j : ι) (hij : c.Rel i j) :
obtain rfl := c.next_eq' hij
simp [inrX, fstX, dif_pos hij]
+@[reassoc (attr := simp)]
+lemma inlX_XIsoBiprod_hom (i j : ι) (hij : c.Rel j i) :
+ haveI := HasHomotopyCofiber.hasBinaryBiproduct φ _ _ hij
+ inlX φ i j hij ≫ (XIsoBiprod φ j i hij).hom = biprod.inl := by
+ haveI := HasHomotopyCofiber.hasBinaryBiproduct φ _ _ hij
+ simp [inlX]
+
+@[reassoc (attr := simp)]
+lemma inl_XIsoBiprod_inv (i j : ι) (hij : c.Rel j i) :
+ haveI := HasHomotopyCofiber.hasBinaryBiproduct φ _ _ hij
+ biprod.inl ≫ (XIsoBiprod φ j i hij).inv = inlX φ i j hij := by
+ simp [inlX]
+
+@[reassoc (attr := simp)]
+lemma inrX_XIsoBiprod_hom (i j : ι) (hij : c.Rel j i) :
+ haveI := HasHomotopyCofiber.hasBinaryBiproduct φ _ _ hij
+ inrX φ j ≫ (XIsoBiprod φ j i hij).hom = biprod.inr := by
+ obtain rfl := c.next_eq' hij
+ haveI := HasHomotopyCofiber.hasBinaryBiproduct φ _ _ hij
+ simp [inrX, XIsoBiprod, dif_pos hij]
+
+@[reassoc (attr := simp)]
+lemma inr_XIsoBiprod_inv (i j : ι) (hij : c.Rel j i) :
+ haveI := HasHomotopyCofiber.hasBinaryBiproduct φ _ _ hij
+ biprod.inr ≫ (XIsoBiprod φ j i hij).inv = inrX φ j := by
+ rw [← inrX_XIsoBiprod_hom φ i j hij, Category.assoc, Iso.hom_inv_id, Category.comp_id]
+
/-- The `d` field of the homological complex `homotopyCofiber φ`. -/
noncomputable def d (i j : ι) : X φ i ⟶ X φ j :=
if hij : c.Rel i j
@@ -175,9 +202,9 @@ lemma ext_from_X (i j : ι) (hij : c.Rel j i) {A : C} {f g : X φ j ⟶ A}
haveI := HasHomotopyCofiber.hasBinaryBiproduct φ _ _ hij
rw [← cancel_epi (XIsoBiprod φ j i hij).inv]
apply biprod.hom_ext'
- · simpa [inlX] using h₁
+ · simpa
· obtain rfl := c.next_eq' hij
- simpa [inrX, dif_pos hij] using h₂
+ simpa [-inr_XIsoBiprod_inv, -inr_XIsoBiprod_inv_assoc, inrX, dif_pos hij] using h₂
lemma ext_from_X' (i : ι) (hi : ¬ c.Rel i (c.next i)) {A : C} {f g : X φ i ⟶ A}
(h : inrX φ i ≫ f = inrX φ i ≫ g) : f = g := by
@@ -384,6 +411,131 @@ noncomputable def descEquiv (K : HomologicalComplex C c) (hc : ∀ j, ∃ i, c.R
rw [descSigma_ext_iff]
cat_disch
+section
+
+variable {F' F'' G' G'' : HomologicalComplex C c} (φ' : F' ⟶ G') (φ'' : F'' ⟶ G'')
+ [HasHomotopyCofiber φ'] [HasHomotopyCofiber φ'']
+ (H : ∀ (j : ι), ∃ i, c.Rel i j)
+
+set_option backward.defeqAttrib.useBackward true in
+/-- The morphism between homotopy cofibers that is induced by a
+morphism of arrows. -/
+noncomputable def mapArrowHom (α : Arrow.mk φ ⟶ Arrow.mk φ') :
+ homotopyCofiber φ ⟶ homotopyCofiber φ' :=
+ desc _ (α.right ≫ homotopyCofiber.inr φ')
+ ((Homotopy.ofEq (by
+ simp [reassoc_of% dsimp% α.w])).trans (((inrCompHomotopy φ' H).compLeft α.left).trans
+ (Homotopy.ofEq (by simp))))
+
+set_option backward.defeqAttrib.useBackward true in
+set_option backward.isDefEq.respectTransparency false in
+@[simp]
+lemma mapArrowHom_id : mapArrowHom φ φ H (𝟙 _) = 𝟙 _ := by
+ ext i
+ dsimp
+ by_cases hi : c.Rel i (c.next i)
+ · refine ext_to_X _ _ _ hi ?_ ?_
+ all_goals simp [mapArrowHom, desc_f _ _ _ _ _ hi, inrCompHomotopy_hom _ _ _ _ hi]
+ · exact ext_to_X' _ _ hi (by simp [mapArrowHom, desc_f' _ _ _ _ hi])
+
+set_option backward.defeqAttrib.useBackward true in
+set_option backward.isDefEq.respectTransparency false in
+@[reassoc]
+lemma mapArrowHom_comp
+ (α : Arrow.mk φ ⟶ Arrow.mk φ') (β : Arrow.mk φ' ⟶ Arrow.mk φ'') :
+ mapArrowHom φ φ'' H (α ≫ β) = mapArrowHom φ φ' H α ≫ mapArrowHom φ' φ'' H β := by
+ ext i
+ dsimp
+ by_cases hi : c.Rel i (c.next i)
+ · refine ext_to_X _ _ _ hi ?_ ?_
+ all_goals simp [mapArrowHom, desc_f _ _ _ _ _ hi, inrCompHomotopy_hom _ _ _ _ hi]
+ · exact ext_to_X' _ _ hi (by simp [mapArrowHom, desc_f' _ _ _ _ hi])
+
+/-- The isomorphism between homotopy cofibers that is induced by an
+isomorphism of arrows. -/
+@[simps]
+noncomputable def mapArrowIso (α : Arrow.mk φ ≅ Arrow.mk φ') :
+ homotopyCofiber φ ≅ homotopyCofiber φ' where
+ hom := mapArrowHom φ φ' H α.hom
+ inv := mapArrowHom φ' φ H α.inv
+ hom_inv_id := by rw [← mapArrowHom_comp, Iso.hom_inv_id, mapArrowHom_id]
+ inv_hom_id := by rw [← mapArrowHom_comp, Iso.inv_hom_id, mapArrowHom_id]
+
+end
+
+section
+
+variable {D : Type*} [Category* D] [Preadditive D] (H : C ⥤ D) [H.Additive]
+ [HasHomotopyCofiber ((H.mapHomologicalComplex c).map φ)]
+
+/-- Auxiliary definition for `mapHomologicalComplexObjIso`. -/
+noncomputable def mapHomologicalComplexObjXIso (i : ι) :
+ H.obj ((homotopyCofiber φ).X i) ≅
+ (homotopyCofiber ((H.mapHomologicalComplex c).map φ)).X i :=
+ if hi : c.Rel i (c.next i)
+ then by
+ haveI := preservesBinaryBiproducts_of_preservesBiproducts H
+ haveI := HasHomotopyCofiber.hasBinaryBiproduct φ _ _ hi
+ haveI := HasHomotopyCofiber.hasBinaryBiproduct ((H.mapHomologicalComplex c).map φ) _ _ hi
+ exact H.mapIso (homotopyCofiber.XIsoBiprod φ _ _ hi) ≪≫ H.mapBiprod _ _ ≪≫
+ (homotopyCofiber.XIsoBiprod ((H.mapHomologicalComplex c).map φ) _ _ hi).symm
+ else H.mapIso (homotopyCofiber.XIso φ i hi) ≪≫
+ (homotopyCofiber.XIso ((H.mapHomologicalComplex c).map φ) i hi).symm
+
+set_option backward.isDefEq.respectTransparency false in
+@[reassoc (attr := simp)]
+lemma inlX_mapHomologicalComplexObjXIso_inv
+ (i j : ι) (hij : c.Rel j i) :
+ inlX ((H.mapHomologicalComplex c).map φ) i j hij ≫
+ (mapHomologicalComplexObjXIso φ H j).inv = H.map (inlX φ i j hij) := by
+ obtain rfl := c.next_eq' hij
+ simp [mapHomologicalComplexObjXIso, dif_pos hij, ← Functor.map_comp]
+
+set_option backward.isDefEq.respectTransparency false in
+@[reassoc (attr := simp)]
+lemma inrX_mapHomologicalComplexObjXIso_inv (i : ι) :
+ inrX ((H.mapHomologicalComplex c).map φ) i ≫
+ (mapHomologicalComplexObjXIso φ H i).inv = H.map (inrX φ i) := by
+ by_cases hi : c.Rel i (c.next i)
+ · simp [mapHomologicalComplexObjXIso, dif_pos hi, ← Functor.map_comp]
+ · dsimp [mapHomologicalComplexObjXIso, XIso, inrX]
+ simp [dif_neg hi]
+
+set_option backward.isDefEq.respectTransparency false in
+@[reassoc (attr := simp)]
+lemma map_inrX_mapHomologicalComplexObjXIso_hom (i : ι) :
+ H.map (inrX φ i) ≫ (mapHomologicalComplexObjXIso φ H i).hom =
+ inrX ((H.mapHomologicalComplex c).map φ) i := by
+ rw [← inrX_mapHomologicalComplexObjXIso_inv_assoc, Iso.inv_hom_id, comp_id]
+
+set_option backward.defeqAttrib.useBackward true in
+set_option backward.isDefEq.respectTransparency false in
+/-- The isomorphism expressing the commutation between taking
+the homotopy cofiber of a morphism of homological complexes and
+applying an additive functor. -/
+noncomputable def mapHomologicalComplexObjIso :
+ (H.mapHomologicalComplex c).obj (homotopyCofiber φ) ≅
+ homotopyCofiber ((H.mapHomologicalComplex c).map φ) :=
+ Iso.symm (HomologicalComplex.Hom.isoOfComponents
+ (fun i ↦ (mapHomologicalComplexObjXIso φ H i).symm)
+ (fun i j hij ↦ by
+ dsimp
+ apply ext_from_X _ _ _ hij
+ · by_cases hj : c.Rel j (c.next j)
+ · simp [← Functor.map_comp, inlX_d _ _ _ _ _ hj, inlX_d_assoc _ _ _ _ _ hj]
+ · simp [← Functor.map_comp, inlX_d' _ _ _ _ hj, inlX_d'_assoc _ _ _ _ hj]
+ · simp [← Functor.map_comp]))
+
+set_option backward.isDefEq.respectTransparency false in
+@[reassoc (attr := simp)]
+lemma inr_mapHomologicalComplexObjIso_hom :
+ (H.mapHomologicalComplex c).map (inr φ) ≫
+ (mapHomologicalComplexObjIso φ H).hom = inr _ := by
+ ext
+ simp [mapHomologicalComplexObjIso]
+
+end
+
end homotopyCofiber
section
@@ -565,6 +717,56 @@ lemma map_ι₀_eq_map_ι₁ {D : Type*} [Category* D] (H : HomologicalComplex C
end
+
+section
+
+variable (F) {D : Type*} [Category* D] [Preadditive D] (H : C ⥤ D) [H.Additive]
+ [∀ i, HasBinaryBiproduct (F.X i) (F.X i)]
+ [HasHomotopyCofiber (biprod.lift (𝟙 F) (-𝟙 F))]
+ [∀ i, HasBinaryBiproduct (((H.mapHomologicalComplex c).obj F).X i)
+ (((H.mapHomologicalComplex c).obj F).X i)]
+ [HasHomotopyCofiber (biprod.lift (𝟙 ((H.mapHomologicalComplex c).obj F))
+ (-𝟙 ((H.mapHomologicalComplex c).obj F)))]
+ [HasHomotopyCofiber ((H.mapHomologicalComplex c).map (biprod.lift (𝟙 F) (-𝟙 F)))]
+ (hc : ∀ (j : ι), ∃ i, c.Rel i j)
+
+attribute [local instance] preservesBinaryBiproduct_of_preservesBiproduct
+
+set_option backward.defeqAttrib.useBackward true in
+/-- The isomorphism expressing the commutation between taking
+the cylinder of a homological complex and applying an additive functor. -/
+noncomputable def mapHomologicalComplexObjIso :
+ (H.mapHomologicalComplex c).obj (cylinder F) ≅
+ cylinder ((H.mapHomologicalComplex c).obj F) :=
+ homotopyCofiber.mapHomologicalComplexObjIso _ H ≪≫
+ homotopyCofiber.mapArrowIso _ _ hc
+ (Arrow.isoMk (Iso.refl _) ((H.mapHomologicalComplex c).mapBiprod F F) (by
+ apply biprod.hom_ext <;> simp [← Functor.map_comp]))
+
+set_option backward.defeqAttrib.useBackward true in
+@[reassoc (attr := simp)]
+lemma map_ι₀_mapHomologicalComplexObjIso_hom :
+ (H.mapHomologicalComplex c).map (cylinder.ι₀ F) ≫ (mapHomologicalComplexObjIso F H hc).hom =
+ cylinder.ι₀ _ := by
+ dsimp [mapHomologicalComplexObjIso, ι₀, homotopyCofiber.mapArrowHom]
+ rw [Functor.map_comp, assoc, homotopyCofiber.inr_mapHomologicalComplexObjIso_hom_assoc,
+ homotopyCofiber.inr_desc, ← Category.assoc]
+ congr 1
+ apply biprod.hom_ext <;> simp [← Functor.map_comp]
+
+set_option backward.defeqAttrib.useBackward true in
+@[reassoc (attr := simp)]
+lemma map_ι₁_mapHomologicalComplexObjIso_hom :
+ (H.mapHomologicalComplex c).map (cylinder.ι₁ F) ≫ (mapHomologicalComplexObjIso F H hc).hom =
+ cylinder.ι₁ _ := by
+ dsimp [mapHomologicalComplexObjIso, ι₁, homotopyCofiber.mapArrowHom]
+ rw [Functor.map_comp, assoc, homotopyCofiber.inr_mapHomologicalComplexObjIso_hom_assoc,
+ homotopyCofiber.inr_desc, ← Category.assoc]
+ congr 1
+ apply biprod.hom_ext <;> simp [← Functor.map_comp]
+
+end
+
end cylinder
omit [DecidableRel c.Rel] in
diff --git a/Mathlib/Algebra/Homology/HomotopyFiber.lean b/Mathlib/Algebra/Homology/HomotopyFiber.lean
index 1705df5e21c76b..24d5c09a3cec5c 100644
--- a/Mathlib/Algebra/Homology/HomotopyFiber.lean
+++ b/Mathlib/Algebra/Homology/HomotopyFiber.lean
@@ -161,6 +161,48 @@ lemma lift_π₁ : lift φ₀ φ₁ h ≫ π₁ K = φ₁ :=
end
+section
+
+variable (F) {D : Type*} [Category* D] [Preadditive D] (H : C ⥤ D) [H.Additive]
+ [∀ (i : α), HasBinaryBiproduct (((H.mapHomologicalComplex c).obj K).X i)
+ (((H.mapHomologicalComplex c).obj K).X i)]
+ [((H.mapHomologicalComplex c).obj K).HasPathObject]
+
+variable
+ [∀ (i : α),
+ HasBinaryBiproduct (((H.op.mapHomologicalComplex c.symm).obj K.op).X i)
+ (((H.op.mapHomologicalComplex c.symm).obj K.op).X i)]
+ [HasHomotopyCofiber (biprod.lift (𝟙 ((H.op.mapHomologicalComplex c.symm).obj K.op))
+ (-𝟙 ((H.op.mapHomologicalComplex c.symm).obj K.op)))]
+ [HasHomotopyCofiber ((H.op.mapHomologicalComplex c.symm).map (biprod.lift (𝟙 K.op) (-𝟙 K.op)))]
+ [∀ (i : α), HasBinaryBiproduct (K.op.X i) (K.op.X i)]
+
+variable (hc : ∀ (i : α), ∃ j, c.Rel i j)
+
+/-- The isomorphism expressing the commutation between taking
+the path object of a homological complex and applying an additive functor. -/
+@[no_expose]
+noncomputable def mapHomologicalComplexObjIso :
+ (H.mapHomologicalComplex c).obj (K.pathObject) ≅
+ pathObject ((H.mapHomologicalComplex c).obj K) :=
+ (unopFunctor _ _).mapIso (cylinder.mapHomologicalComplexObjIso K.op H.op hc).op.symm
+
+@[reassoc (attr := simp)]
+lemma mapHomologicalComplexObjIso_inv_map_π₀ :
+ (mapHomologicalComplexObjIso K H hc).inv ≫ (H.mapHomologicalComplex c).map (π₀ K) =
+ π₀ _ :=
+ Quiver.Hom.op_inj ((opFunctor _ _).map_injective
+ (cylinder.map_ι₀_mapHomologicalComplexObjIso_hom K.op H.op hc))
+
+@[reassoc (attr := simp)]
+lemma mapHomologicalComplexObjIso_inv_map_π₁ :
+ (mapHomologicalComplexObjIso K H hc).inv ≫ (H.mapHomologicalComplex c).map (π₁ K) =
+ π₁ _ :=
+ Quiver.Hom.op_inj ((opFunctor _ _).map_injective
+ (cylinder.map_ι₁_mapHomologicalComplexObjIso_hom K.op H.op hc))
+
+end
+
end pathObject
end HomologicalComplex
From ac06f381f4f7d5f7aa4617251e1c9f346dbad20a Mon Sep 17 00:00:00 2001
From: Eric Wieser <425260+eric-wieser@users.noreply.github.com>
Date: Thu, 25 Jun 2026 19:06:36 +0000
Subject: [PATCH 0363/1300] feat: add a `LawfulXor` typeclass (#37712)
I've put this in mathlib since it can use `Function.Involutive`; it can of course be upstreamed at a later date.
Having this generalization encourages downstream code in cslib to be expressed in terms of involutive functions, rather than just `^^^` on bitvectors.
---
Mathlib.lean | 1 +
Mathlib/Data/LawfulXor.lean | 129 ++++++++++++++++++++++++++++++++++++
2 files changed, 130 insertions(+)
create mode 100644 Mathlib/Data/LawfulXor.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 66d8dccb727666..f892e89c8d8f8e 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -4035,6 +4035,7 @@ public import Mathlib.Data.Int.Sqrt
public import Mathlib.Data.Int.Star
public import Mathlib.Data.Int.SuccPred
public import Mathlib.Data.Int.WithZero
+public import Mathlib.Data.LawfulXor
public import Mathlib.Data.List.AList
public import Mathlib.Data.List.Basic
public import Mathlib.Data.List.Chain
diff --git a/Mathlib/Data/LawfulXor.lean b/Mathlib/Data/LawfulXor.lean
new file mode 100644
index 00000000000000..bb486390d9a029
--- /dev/null
+++ b/Mathlib/Data/LawfulXor.lean
@@ -0,0 +1,129 @@
+/-
+Copyright (c) 2026 Eric Wieser. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Eric Wieser
+-/
+
+module
+public import Mathlib.Logic.Function.Basic
+
+/-!
+# The `LawfulXor` typeclass
+
+This file generalizes basic lemmas about the `^^^` operator across numeric types.
+-/
+
+@[expose] public section
+
+/-- A typeclass indicating that the xor operation, `^^^`, is lawful. -/
+class LawfulXor (α : Type*) [XorOp α] [Zero α] where
+ xor_assoc (a b c : α) : (a ^^^ b) ^^^ c = a ^^^ (b ^^^ c)
+ xor_self (a : α) : a ^^^ a = 0
+ xor_zero (a : α) : a ^^^ 0 = a
+ xor_comm (a b : α) : a ^^^ b = b ^^^ a
+
+export LawfulXor (xor_assoc xor_self xor_zero xor_comm)
+
+variable {α : Type*} [XorOp α] [Zero α] [LawfulXor α]
+
+attribute [simp] xor_zero LawfulXor.xor_self
+
+@[simp]
+theorem zero_xor (a : α) : 0 ^^^ a = a := by rw [LawfulXor.xor_comm, xor_zero]
+
+instance : Std.Commutative (α := α) XorOp.xor where comm := xor_comm
+instance : Std.Associative (α := α) XorOp.xor where assoc := xor_assoc
+
+instance : Std.LawfulCommIdentity (α := α) XorOp.xor 0 where
+ left_id := zero_xor
+ right_id := xor_zero
+
+@[simp]
+theorem xor_cancel_right (a b : α) : (a ^^^ b) ^^^ b = a := by
+ rw [xor_assoc, LawfulXor.xor_self, xor_zero]
+
+@[simp]
+theorem xor_cancel_left (a b : α) : a ^^^ (a ^^^ b) = b := by
+ rw [← xor_assoc, LawfulXor.xor_self, zero_xor]
+
+instance : LawfulXor Nat where
+ xor_assoc := Nat.xor_assoc
+ xor_comm := Nat.xor_comm
+ xor_self := Nat.xor_self
+ xor_zero := Nat.xor_zero
+
+instance {w : Nat} : LawfulXor (BitVec w) where
+ xor_assoc := BitVec.xor_assoc
+ xor_comm := BitVec.xor_comm
+ xor_self _ := BitVec.xor_self
+ xor_zero _ := BitVec.xor_zero
+
+instance : LawfulXor UInt8 where
+ xor_assoc := UInt8.xor_assoc
+ xor_comm := UInt8.xor_comm
+ xor_self _ := UInt8.xor_self
+ xor_zero _ := UInt8.xor_zero
+
+instance : LawfulXor UInt16 where
+ xor_assoc := UInt16.xor_assoc
+ xor_comm := UInt16.xor_comm
+ xor_self _ := UInt16.xor_self
+ xor_zero _ := UInt16.xor_zero
+
+instance : LawfulXor UInt32 where
+ xor_assoc := UInt32.xor_assoc
+ xor_comm := UInt32.xor_comm
+ xor_self _ := UInt32.xor_self
+ xor_zero _ := UInt32.xor_zero
+
+instance : LawfulXor UInt64 where
+ xor_assoc := UInt64.xor_assoc
+ xor_comm := UInt64.xor_comm
+ xor_self _ := UInt64.xor_self
+ xor_zero _ := UInt64.xor_zero
+
+instance : LawfulXor USize where
+ xor_assoc := USize.xor_assoc
+ xor_comm := USize.xor_comm
+ xor_self _ := USize.xor_self
+ xor_zero _ := USize.xor_zero
+
+instance : LawfulXor Int8 where
+ xor_assoc := Int8.xor_assoc
+ xor_comm := Int8.xor_comm
+ xor_self _ := Int8.xor_self
+ xor_zero _ := Int8.xor_zero
+
+instance : LawfulXor Int16 where
+ xor_assoc := Int16.xor_assoc
+ xor_comm := Int16.xor_comm
+ xor_self _ := Int16.xor_self
+ xor_zero _ := Int16.xor_zero
+
+instance : LawfulXor Int32 where
+ xor_assoc := Int32.xor_assoc
+ xor_comm := Int32.xor_comm
+ xor_self _ := Int32.xor_self
+ xor_zero _ := Int32.xor_zero
+
+instance : LawfulXor Int64 where
+ xor_assoc := Int64.xor_assoc
+ xor_comm := Int64.xor_comm
+ xor_self _ := Int64.xor_self
+ xor_zero _ := Int64.xor_zero
+
+instance : LawfulXor ISize where
+ xor_assoc := ISize.xor_assoc
+ xor_comm := ISize.xor_comm
+ xor_self _ := ISize.xor_self
+ xor_zero _ := ISize.xor_zero
+
+lemma xor_right_involutive (a : α) : Function.Involutive (a ^^^ ·) := xor_cancel_left a
+
+lemma xor_left_involutive (a : α) : Function.Involutive (· ^^^ a) := (xor_cancel_right · a)
+
+lemma xor_eq_iff_left_eq (a b c : α) :
+ a ^^^ b = c ↔ a = c ^^^ b := xor_left_involutive _ |>.eq_iff
+
+lemma xor_eq_iff_right_eq (a b c : α) :
+ a ^^^ b = c ↔ b = a ^^^ c := xor_right_involutive _ |>.eq_iff
From 68b30e537da83f057bd4f15dae11632af0a198eb Mon Sep 17 00:00:00 2001
From: Jovan Gerbscheid <56355248+JovanGerb@users.noreply.github.com>
Date: Thu, 25 Jun 2026 19:06:39 +0000
Subject: [PATCH 0364/1300] feat: `NSMul`/`NPow` type class (#38036)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This PR is adds `NSMul`, `NPow`, `ZSMul` and `ZPow` classes for the `nsmul`, `npow`, `zsmul`, `zpow` data fields.
This has a few advantages:
- If you first declare a `SMul` instance, then you don't need to manually write `nsmul := (· • ·)` and `zsmul := (· • ·)` . For `Pow`, the extra benefit is that inferring the instance is preferred over the default field `npowRecAuto`. So this helps avoid accidental diamonds.
- If you first declare a `SMul` instance on a type synonym, then `inferInstanceAs` will infer the `nsmul` field from the `SMul` instance. This makes it easier to avoid diamonds on type synonyms like `Matrix` and `MonoidAlgebra`.
- The not-yet-merged instance diamond linter will be able to detect cases where the `NSMul` and `SMul` classes do not agree.
In the process of making this PR, I have identified two existing `NPow` diamonds:
- In `Mathlib.Algebra.Order.Positive.Field`, there were two conflicting `NPow` instances.
- For `Fin`, there are two conflicting `NPow` instances. I have overwritten the one in core lean with the one from mathlib that is more computationally efficient.
TODO: the same for `QSMul` and `NNQSMul`.
---
Mathlib/Algebra/Colimit/DirectLimit.lean | 22 ++-
Mathlib/Algebra/FreeAlgebra.lean | 4 +-
Mathlib/Algebra/Group/Action/Opposite.lean | 2 +-
Mathlib/Algebra/Group/Defs.lean | 94 +++++++-----
Mathlib/Algebra/Group/Ext.lean | 4 +-
Mathlib/Algebra/Group/Int/Defs.lean | 4 +-
Mathlib/Algebra/Group/Opposite.lean | 1 +
Mathlib/Algebra/Group/Prod.lean | 4 +-
.../Algebra/Group/Submonoid/Membership.lean | 9 +-
Mathlib/Algebra/Group/TypeTags/Basic.lean | 46 +++---
Mathlib/Algebra/Group/Units/Defs.lean | 10 +-
.../GroupWithZero/Action/Opposite.lean | 2 +-
Mathlib/Algebra/Module/NatInt.lean | 23 +--
Mathlib/Algebra/MonoidAlgebra/Defs.lean | 13 +-
.../Order/Monoid/Unbundled/WithTop.lean | 6 +-
Mathlib/Algebra/Order/Positive/Field.lean | 8 +-
Mathlib/Algebra/Order/Ring/Archimedean.lean | 2 -
Mathlib/Algebra/Order/Ring/WithTop.lean | 4 +-
Mathlib/Algebra/Ring/MinimalAxioms.lean | 3 +-
Mathlib/Algebra/RingQuot.lean | 1 -
Mathlib/CategoryTheory/Preadditive/Comma.lean | 12 +-
.../CategoryTheory/Triangulated/Basic.lean | 2 -
Mathlib/Data/BitVec.lean | 3 +-
Mathlib/Data/Complex/Basic.lean | 2 -
Mathlib/Data/Rat/Defs.lean | 6 +-
Mathlib/Data/ZMod/Defs.lean | 47 +++---
Mathlib/Data/ZMod/IntUnitsPower.lean | 4 +-
Mathlib/FieldTheory/RatFunc/Basic.lean | 2 -
Mathlib/GroupTheory/GroupAction/Hom.lean | 2 -
Mathlib/LinearAlgebra/Matrix/Defs.lean | 16 +-
Mathlib/LinearAlgebra/Matrix/ZPow.lean | 3 +-
.../LinearAlgebra/TensorProduct/Basic.lean | 1 -
Mathlib/LinearAlgebra/TensorProduct/Defs.lean | 6 +-
.../NumberTheory/ArithmeticFunction/Defs.lean | 1 -
Mathlib/RingTheory/PolynomialLaw/Basic.lean | 18 ++-
.../Valuation/ValuativeRel/Basic.lean | 4 +-
Mathlib/SetTheory/Cardinal/Order.lean | 2 +-
Mathlib/Tactic/Abel.lean | 8 +-
Mathlib/Tactic/Translate/ToAdditive.lean | 4 +-
Mathlib/Topology/Algebra/GroupCompletion.lean | 144 ++++++++----------
.../Module/ContinuousLinearMap/Basic.lean | 2 -
MathlibTest/InstanceDiamonds.lean | 10 +-
42 files changed, 283 insertions(+), 278 deletions(-)
diff --git a/Mathlib/Algebra/Colimit/DirectLimit.lean b/Mathlib/Algebra/Colimit/DirectLimit.lean
index 7898ffcdedf41e..3a000c17f440e7 100644
--- a/Mathlib/Algebra/Colimit/DirectLimit.lean
+++ b/Mathlib/Algebra/Colimit/DirectLimit.lean
@@ -220,8 +220,12 @@ variable [∀ i j h, MonoidHomClass (T h) (G i) (G j)] [∀ i, MonoidHomClass (H
one_mul := one_mul
mul_one := mul_one
npow n := map _ _ (fun _ ↦ (· ^ n)) fun _ _ _ x ↦ map_pow _ x n
- npow_zero := DirectLimit.induction _ fun i _ ↦ by simp_rw [map_def, pow_zero, one_def i]
- npow_succ n := DirectLimit.induction _ fun i _ ↦ by simp_rw [map_def, pow_succ, mul_def]
+ npow_zero := DirectLimit.induction _ fun i _ ↦ by
+ simp_rw [HPow.hPow, Pow.pow]
+ simp_rw [map_def, pow_zero, one_def i]
+ npow_succ n := DirectLimit.induction _ fun i _ ↦ by
+ simp_rw [HPow.hPow, Pow.pow]
+ simp_rw [map_def, pow_succ, mul_def]
@[to_additive] theorem npow_def (i x) (n : ℕ) : ⟦⟨i, x⟩⟧ ^ n = (⟦⟨i, x ^ n⟩⟧ : DirectLimit G f) :=
rfl
@@ -256,11 +260,12 @@ variable [∀ i j h, MonoidHomClass (T h) (G i) (G j)] [∀ i, MonoidHomClass (H
zpow n := map _ _ (fun _ ↦ (· ^ n)) fun _ _ _ x ↦ map_zpow _ x n
div_eq_mul_inv := DirectLimit.induction₂ _ fun i _ _ ↦ show map₂ .. = _ * map .. by
simp_rw [map₂_def, map_def, div_eq_mul_inv, mul_def]
- zpow_zero' := DirectLimit.induction _ fun i _ ↦ by simp_rw [map_def, zpow_zero, one_def i]
+ zpow_zero' := DirectLimit.induction _ fun i _ ↦ by
+ simp_rw [HPow.hPow, Pow.pow, map_def, zpow_zero, one_def i]
zpow_succ' n := DirectLimit.induction _ fun i x ↦ by
- simp_rw [map_def, mul_def]; congr; apply DivInvMonoid.zpow_succ'
+ simp_rw [HPow.hPow, Pow.pow, map_def, mul_def]; congr; apply DivInvMonoid.zpow_succ'
zpow_neg' n := DirectLimit.induction _ fun i x ↦ by
- simp_rw +instances [map_def]; congr; apply DivInvMonoid.zpow_neg'
+ simp_rw [HPow.hPow, Pow.pow, map_def]; congr; apply DivInvMonoid.zpow_neg'
inv_mul_cancel := DirectLimit.induction _ fun i _ ↦ by
simp_rw [map_def, mul_def, inv_mul_cancel, one_def i]
@@ -341,11 +346,12 @@ instance : GroupWithZero (DirectLimit G f) where
zpow n := map _ _ (fun _ ↦ (· ^ n)) fun _ _ _ x ↦ map_zpow₀ _ x n
div_eq_mul_inv := DirectLimit.induction₂ _ fun i _ _ ↦ show map₂ .. = _ * map .. by
simp_rw [map₂_def, map_def, div_eq_mul_inv, mul_def]
- zpow_zero' := DirectLimit.induction _ fun i _ ↦ by simp_rw [map_def, zpow_zero, one_def i]
+ zpow_zero' := DirectLimit.induction _ fun i _ ↦ by
+ simp_rw [HPow.hPow, Pow.pow, map_def, zpow_zero, one_def i]
zpow_succ' n := DirectLimit.induction _ fun i x ↦ by
- simp_rw [map_def, mul_def]; congr; apply DivInvMonoid.zpow_succ'
+ simp_rw [HPow.hPow, Pow.pow, map_def, mul_def]; congr; apply DivInvMonoid.zpow_succ'
zpow_neg' n := DirectLimit.induction _ fun i x ↦ by
- simp_rw [map_def]; congr; apply DivInvMonoid.zpow_neg'
+ simp_rw [HPow.hPow, Pow.pow, map_def]; congr; apply DivInvMonoid.zpow_neg'
inv_zero := show ⟦_⟧ = ⟦_⟧ by simp_rw [inv_zero]
mul_inv_cancel := DirectLimit.induction _ fun i x ne ↦ by
have : x ≠ 0 := by rintro rfl; exact ne (zero_def i).symm
diff --git a/Mathlib/Algebra/FreeAlgebra.lean b/Mathlib/Algebra/FreeAlgebra.lean
index a3df9a637be7b2..2db44e9cca6e07 100644
--- a/Mathlib/Algebra/FreeAlgebra.lean
+++ b/Mathlib/Algebra/FreeAlgebra.lean
@@ -201,7 +201,6 @@ instance instMonoidWithZero : MonoidWithZero (FreeAlgebra R X) where
mul_assoc := by
rintro ⟨⟩ ⟨⟩ ⟨⟩
exact Quot.sound Rel.mul_assoc
- one := Quot.mk _ 1
one_mul := by
rintro ⟨⟩
exact Quot.sound Rel.one_mul
@@ -237,7 +236,6 @@ instance instAddCommMonoid : AddCommMonoid (FreeAlgebra R X) where
add_comm := by
rintro ⟨⟩ ⟨⟩
exact Quot.sound Rel.add_comm
- nsmul := (· • ·)
nsmul_zero := by
rintro ⟨⟩
change Quot.mk _ (_ * _) = _
@@ -245,7 +243,7 @@ instance instAddCommMonoid : AddCommMonoid (FreeAlgebra R X) where
exact Quot.sound Rel.zero_mul
nsmul_succ n := by
rintro ⟨a⟩
- dsimp +instances only [HSMul.hSMul, instSMul, Quot.map]
+ dsimp only [HSMul.hSMul, SMul.smul, NSMul.nsmul, Quot.map]
rw [map_add, map_one, mk_mul, mk_mul, ← add_one_mul (_ : FreeAlgebra R X)]
congr 1
exact Quot.sound Rel.add_scalar
diff --git a/Mathlib/Algebra/Group/Action/Opposite.lean b/Mathlib/Algebra/Group/Action/Opposite.lean
index ac0b3d9268b373..0725bd5184a37f 100644
--- a/Mathlib/Algebra/Group/Action/Opposite.lean
+++ b/Mathlib/Algebra/Group/Action/Opposite.lean
@@ -15,7 +15,7 @@ This file defines the actions on the opposite type `SMul R Mᵐᵒᵖ`, and acti
type, `SMul Rᵐᵒᵖ M`.
Note that `MulOpposite.smul` is provided in an earlier file as it is needed to
-provide the `AddMonoid.nsmul` and `AddCommGroup.zsmul` fields.
+provide the `NSMul.nsmul` and `ZSMul.zsmul` fields.
## Notation
diff --git a/Mathlib/Algebra/Group/Defs.lean b/Mathlib/Algebra/Group/Defs.lean
index 8da0e62370f626..fce530fbd241ac 100644
--- a/Mathlib/Algebra/Group/Defs.lean
+++ b/Mathlib/Algebra/Group/Defs.lean
@@ -637,38 +637,51 @@ theorem npowRec_eq_npowBinRec : @npowRecAuto = @npowBinRecAuto := by
iterate 2 rw [← npowBinRecAuto, ← npowRec_eq_npowBinRec]
rfl
-/-- An `AddMonoid` is an `AddSemigroup` with an element `0` such that `0 + a = a + 0 = a`. -/
-class AddMonoid (M : Type u) extends AddSemigroup M, AddZeroClass M where
+/-- `NSMul` is an implementation detail of `AddMonoid`. It is needed because it is
+impossible to extend `SMUl ℕ M` and `SMul ℤ M` at the same time. -/
+class NSMul (M : Type u) where
/-- Multiplication by a natural number.
Set this to `nsmulRec` unless `Module` diamonds are possible. -/
protected nsmul : ℕ → M → M
+
+/-- `NPow` is an implementation detail of `Monoid`. It is needed because it is
+impossible to extend `Pow M ℕ` and `Pow M ℤ` at the same time. -/
+@[to_additive]
+class NPow (M : Type u) where
+ /-- Raising to the power of a natural number. -/
+ protected npow : ℕ → M → M
+
+@[default_instance high, to_additive toSMul]
+instance NPow.toPow {M : Type*} [NPow M] : Pow M ℕ :=
+ ⟨fun x n ↦ NPow.npow n x⟩
+
+@[to_additive ofSMul]
+instance NPow.ofPow {M : Type*} [Pow M ℕ] : NPow M := ⟨fun n x ↦ Pow.pow x n⟩
+
+/-- An `AddMonoid` is an `AddSemigroup` with an element `0` such that `0 + a = a + 0 = a`. -/
+class AddMonoid (M : Type u) extends AddSemigroup M, AddZeroClass M, NSMul M where
/-- Multiplication by `(0 : ℕ)` gives `0`. -/
- protected nsmul_zero : ∀ x, nsmul 0 x = 0 := by intros; rfl
+ protected nsmul_zero (x : M) : 0 • x = 0 := by intros; rfl
/-- Multiplication by `(n + 1 : ℕ)` behaves as expected. -/
- protected nsmul_succ : ∀ (n : ℕ) (x), nsmul (n + 1) x = nsmul n x + x := by intros; rfl
+ protected nsmul_succ (n : ℕ) (x : M) : (n + 1) • x = n • x + x := by intros; rfl
attribute [instance 150] AddSemigroup.toAdd
attribute [instance 50] AddZero.toAdd
/-- A `Monoid` is a `Semigroup` with an element `1` such that `1 * a = a * 1 = a`. -/
@[to_additive]
-class Monoid (M : Type u) extends Semigroup M, MulOneClass M where
- /-- Raising to the power of a natural number. -/
- protected npow : ℕ → M → M := npowRecAuto
+class Monoid (M : Type u) extends Semigroup M, MulOneClass M, NPow M where
+ npow := npowRecAuto
/-- Raising to the power `(0 : ℕ)` gives `1`. -/
- protected npow_zero : ∀ x, npow 0 x = 1 := by intros; rfl
+ protected npow_zero (x : M) : x ^ 0 = 1 := by intros; rfl
/-- Raising to the power `(n + 1 : ℕ)` behaves as expected. -/
- protected npow_succ : ∀ (n : ℕ) (x), npow (n + 1) x = npow n x * x := by intros; rfl
-
-@[default_instance high, to_additive]
-instance Monoid.toPow {M : Type*} [Monoid M] : Pow M ℕ :=
- ⟨fun x n ↦ Monoid.npow n x⟩
+ protected npow_succ (n : ℕ) (x : M) : x ^ (n + 1) = x ^ n * x := by intros; rfl
section Monoid
variable {M : Type*} [Monoid M] {a b c : M}
@[to_additive (attr := simp) nsmul_eq_smul]
-theorem npow_eq_pow (n : ℕ) (x : M) : Monoid.npow n x = x ^ n :=
+theorem npow_eq_pow (n : ℕ) (x : M) : NPow.npow n x = x ^ n :=
rfl
@[to_additive] lemma left_inv_eq_right_inv (hba : b * a = 1) (hac : a * c = 1) : b = c := by
@@ -941,6 +954,27 @@ field of individual `DivInvMonoid`s constructed using that default value will no
`.instance` transparency. -/
def DivInvMonoid.div' {G : Type u} [Monoid G] [Inv G] (a b : G) : G := a * b⁻¹
+/-- `ZSMul` is an implementation detail of `SubNegMonoid`. It is needed because it is
+impossible to extend `SMUl ℕ M` and `SMul ℤ M` at the same time. -/
+class ZSMul (G : Type u) where
+ /-- Multiplication by an integer.
+ Set this to `zsmulRec` unless `Module` diamonds are possible. -/
+ protected zsmul : ℤ → G → G
+
+/-- `ZPow` is an implementation detail of `DivInvMonoid`. It is needed because it is
+impossible to extend `Pow M ℕ` and `Pow M ℤ` at the same time. -/
+@[to_additive]
+class ZPow (G : Type u) where
+ /-- The power operation: `a ^ n = a * ··· * a`; `a ^ (-n) = a⁻¹ * ··· a⁻¹` (`n` times) -/
+ protected zpow : ℤ → G → G
+
+@[to_additive toSMul]
+instance ZPow.toPow {M : Type*} [ZPow M] : Pow M ℤ :=
+ ⟨fun x n ↦ ZPow.zpow n x⟩
+
+@[to_additive ofSMul]
+instance ZPow.ofPow {M : Type*} [Pow M ℤ] : ZPow M := ⟨fun n x ↦ Pow.pow x n⟩
+
/-- A `DivInvMonoid` is a `Monoid` with operations `/` and `⁻¹` satisfying
`div_eq_mul_inv : ∀ a b, a / b = a * b⁻¹`.
@@ -959,19 +993,18 @@ In the same way, adding a `zpow` field makes it possible to avoid definitional f
in diamonds. See the definition of `Monoid` and Note [forgetful inheritance] for more
explanations on this.
-/
-class DivInvMonoid (G : Type u) extends Monoid G, Inv G, Div G where
+class DivInvMonoid (G : Type u) extends Monoid G, Inv G, Div G, ZPow G where
protected div := DivInvMonoid.div'
/-- `a / b := a * b⁻¹` -/
protected div_eq_mul_inv : ∀ a b : G, a / b = a * b⁻¹ := by intros; rfl
- /-- The power operation: `a ^ n = a * ··· * a`; `a ^ (-n) = a⁻¹ * ··· a⁻¹` (`n` times) -/
- protected zpow : ℤ → G → G := zpowRec npowRec
+ zpow := zpowRec npowRec
/-- `a ^ 0 = 1` -/
- protected zpow_zero' : ∀ a : G, zpow 0 a = 1 := by intros; rfl
+ protected zpow_zero' (a : G) : a ^ (0 : ℤ) = 1 := by intros; rfl
/-- `a ^ (n + 1) = a ^ n * a` -/
- protected zpow_succ' (n : ℕ) (a : G) : zpow n.succ a = zpow n a * a := by
+ protected zpow_succ' (n : ℕ) (a : G) : a ^ (n.succ : ℤ) = a ^ (n : ℤ) * a := by
intros; rfl
/-- `a ^ -(n + 1) = (a ^ (n + 1))⁻¹` -/
- protected zpow_neg' (n : ℕ) (a : G) : zpow (Int.negSucc n) a = (zpow n.succ a)⁻¹ := by intros; rfl
+ protected zpow_neg' (n : ℕ) (a : G) : a ^ Int.negSucc n = (a ^ (n.succ : ℤ))⁻¹ := by intros; rfl
/-- In a class equipped with instances of both `AddMonoid` and `Neg`, this definition records what
the default definition for `Sub` would be: `a + -b`. This is later provided as the default value
@@ -1001,29 +1034,18 @@ In the same way, adding a `zsmul` field makes it possible to avoid definitional
in diamonds. See the definition of `AddMonoid` and Note [forgetful inheritance] for more
explanations on this.
-/
-class SubNegMonoid (G : Type u) extends AddMonoid G, Neg G, Sub G where
+class SubNegMonoid (G : Type u) extends AddMonoid G, Neg G, Sub G, ZSMul G where
protected sub := SubNegMonoid.sub'
protected sub_eq_add_neg : ∀ a b : G, a - b = a + -b := by intros; rfl
- /-- Multiplication by an integer.
- Set this to `zsmulRec` unless `Module` diamonds are possible. -/
- protected zsmul : ℤ → G → G
- protected zsmul_zero' : ∀ a : G, zsmul 0 a = 0 := by intros; rfl
+ protected zsmul_zero' (a : G) : (0 : ℤ) • a = 0 := by intros; rfl
protected zsmul_succ' (n : ℕ) (a : G) :
- zsmul n.succ a = zsmul n a + a := by
+ (n.succ : ℤ) • a = (n : ℤ) • a + a := by
intros; rfl
- protected zsmul_neg' (n : ℕ) (a : G) : zsmul (Int.negSucc n) a = -zsmul n.succ a := by
+ protected zsmul_neg' (n : ℕ) (a : G) : (Int.negSucc n) • a = -((n.succ : ℤ) • a) := by
intros; rfl
attribute [to_additive SubNegMonoid] DivInvMonoid
-instance DivInvMonoid.toZPow {M} [DivInvMonoid M] : Pow M ℤ :=
- ⟨fun x n ↦ DivInvMonoid.zpow n x⟩
-
-instance SubNegMonoid.toZSMul {M} [SubNegMonoid M] : SMul ℤ M :=
- ⟨SubNegMonoid.zsmul⟩
-
-attribute [to_additive existing] DivInvMonoid.toZPow
-
/-- A group is called *cyclic* if it is generated by a single element. -/
class IsAddCyclic (G : Type u) [SMul ℤ G] : Prop where
protected exists_zsmul_surjective : ∃ g : G, Function.Surjective (· • g : ℤ → G)
@@ -1043,7 +1065,7 @@ section DivInvMonoid
variable [DivInvMonoid G]
@[to_additive (attr := simp) zsmul_eq_smul] theorem zpow_eq_pow (n : ℤ) (x : G) :
- DivInvMonoid.zpow n x = x ^ n :=
+ ZPow.zpow n x = x ^ n :=
rfl
@[to_additive (attr := simp) zero_zsmul] theorem zpow_zero (a : G) : a ^ (0 : ℤ) = 1 :=
diff --git a/Mathlib/Algebra/Group/Ext.lean b/Mathlib/Algebra/Group/Ext.lean
index 3874fa32780c75..279b4d658c69ac 100644
--- a/Mathlib/Algebra/Group/Ext.lean
+++ b/Mathlib/Algebra/Group/Ext.lean
@@ -47,7 +47,7 @@ theorem Monoid.ext {M : Type u} ⦃m₁ m₂ : Monoid M⦄
have : m₁.npow = m₂.npow := by
ext n x
exact @MonoidHom.map_pow M M m₁ m₂ f x n
- rcases m₁ with @⟨@⟨⟨_⟩⟩, ⟨_⟩⟩
+ rcases m₁ with @⟨@⟨⟨_⟩⟩, ⟨_⟩, _, _, ⟨_⟩⟩
congr
@[to_additive]
@@ -134,7 +134,7 @@ theorem DivInvMonoid.ext {M : Type*} ⦃m₁ m₂ : DivInvMonoid M⦄
exact (@div_eq_mul_inv _ m₁ a b).trans
(((congr_fun (congr_fun h_mul a) _).trans
(congr_arg _ (congr_fun h_inv b))).trans (@div_eq_mul_inv _ m₂ a b).symm)
- rcases m₁ with @⟨_, ⟨_⟩, ⟨_⟩⟩
+ rcases m₁ with @⟨_, ⟨_⟩, ⟨_⟩, ⟨_⟩⟩
congr
@[to_additive]
diff --git a/Mathlib/Algebra/Group/Int/Defs.lean b/Mathlib/Algebra/Group/Int/Defs.lean
index 09c73fe1a74bd5..9e06bbccfe336d 100644
--- a/Mathlib/Algebra/Group/Int/Defs.lean
+++ b/Mathlib/Algebra/Group/Int/Defs.lean
@@ -47,8 +47,8 @@ instance instAddCommGroup : AddCommGroup ℤ where
zsmul := (· * ·)
zsmul_zero' := Int.zero_mul
zsmul_succ' m n := by
- simp only [natCast_succ, Int.add_mul, Int.add_comm, Int.one_mul]
- zsmul_neg' m n := by simp only [negSucc_eq, natCast_succ, Int.neg_mul]
+ simp only [HSMul.hSMul, SMul.smul, natCast_succ, Int.add_mul, Int.add_comm, Int.one_mul]
+ zsmul_neg' m n := by simp only [HSMul.hSMul, SMul.smul, negSucc_eq, natCast_succ, Int.neg_mul]
sub_eq_add_neg _ _ := Int.sub_eq_add_neg
-- This instance can also be found from the `LinearOrderedCommMonoidWithZero ℤ` instance by
diff --git a/Mathlib/Algebra/Group/Opposite.lean b/Mathlib/Algebra/Group/Opposite.lean
index 68a9347be830d8..5668d2f7882e82 100644
--- a/Mathlib/Algebra/Group/Opposite.lean
+++ b/Mathlib/Algebra/Group/Opposite.lean
@@ -142,6 +142,7 @@ instance instDivInvMonoid [DivInvMonoid α] : DivInvMonoid αᵐᵒᵖ where
zpow n a := op <| a.unop ^ n
zpow_zero' _ := unop_injective <| zpow_zero _
zpow_succ' _ _ := unop_injective <| by
+ simp_rw [HPow.hPow, Pow.pow]
rw [unop_op, zpow_natCast, pow_succ', unop_mul, unop_op, zpow_natCast]
zpow_neg' _ _ := unop_injective <| DivInvMonoid.zpow_neg' _ _
diff --git a/Mathlib/Algebra/Group/Prod.lean b/Mathlib/Algebra/Group/Prod.lean
index 7c8bf82d187fa0..12b8deaed8d2d4 100644
--- a/Mathlib/Algebra/Group/Prod.lean
+++ b/Mathlib/Algebra/Group/Prod.lean
@@ -87,7 +87,7 @@ instance instMulOneClass [MulOneClass M] [MulOneClass N] : MulOneClass (M × N)
@[to_additive]
instance instMonoid [Monoid M] [Monoid N] : Monoid (M × N) :=
- { npow := fun z a => ⟨Monoid.npow z a.1, Monoid.npow z a.2⟩,
+ { npow := fun z a => ⟨NPow.npow z a.1, NPow.npow z a.2⟩,
npow_zero := fun _ => Prod.ext (Monoid.npow_zero _) (Monoid.npow_zero _),
npow_succ := fun _ _ => Prod.ext (Monoid.npow_succ _ _) (Monoid.npow_succ _ _),
one_mul := by simp,
@@ -102,7 +102,7 @@ instance instIsMulTorsionFree [Monoid M] [Monoid N] [IsMulTorsionFree M] [IsMulT
@[to_additive Prod.subNegMonoid]
instance [DivInvMonoid G] [DivInvMonoid H] : DivInvMonoid (G × H) where
div_eq_mul_inv _ _ := by ext <;> exact div_eq_mul_inv ..
- zpow z a := ⟨DivInvMonoid.zpow z a.1, DivInvMonoid.zpow z a.2⟩
+ zpow z a := ⟨ZPow.zpow z a.1, ZPow.zpow z a.2⟩
zpow_zero' _ := by ext <;> exact DivInvMonoid.zpow_zero' _
zpow_succ' _ _ := by ext <;> exact DivInvMonoid.zpow_succ' ..
zpow_neg' _ _ := by ext <;> exact DivInvMonoid.zpow_neg' ..
diff --git a/Mathlib/Algebra/Group/Submonoid/Membership.lean b/Mathlib/Algebra/Group/Submonoid/Membership.lean
index 9c81e885c1eef0..0e5c544e78d9ea 100644
--- a/Mathlib/Algebra/Group/Submonoid/Membership.lean
+++ b/Mathlib/Algebra/Group/Submonoid/Membership.lean
@@ -361,8 +361,12 @@ abbrev groupPowers {x : M} {n : ℕ} (hpos : 0 < n) (hx : x ^ n = 1) : Group (po
simp only [coe_one, coe_mul, SubmonoidClass.coe_pow]
rw [← pow_succ, Nat.sub_add_cancel hpos, ← pow_mul, mul_comm, pow_mul, hx, one_pow]
zpow z x := x ^ z.natMod n
- zpow_zero' z := by simp only [Int.natMod, Int.zero_emod, Int.toNat_zero, pow_zero]
- zpow_neg' m x := Subtype.ext <| by
+ zpow_zero' z := by
+ simp_rw [HPow.hPow, Pow.pow]
+ simp only [Int.natMod, Int.zero_emod, Int.toNat_zero, pow_zero]
+ zpow_neg' m x := by
+ change x ^ (Int.natMod _ n) = (x ^ (Int.natMod _ n)) ^ (n - 1)
+ ext
obtain ⟨_, k, rfl⟩ := x
simp only [← pow_mul, Int.natMod, SubmonoidClass.coe_pow]
rw [Int.negSucc_eq, ← Int.natCast_succ, ← Int.add_mul_emod_self_right (b := (m + 1 : ℕ))]
@@ -372,6 +376,7 @@ abbrev groupPowers {x : M} {n : ℕ} (hpos : 0 < n) (hx : x ^ n = 1) : Group (po
rw [mul_comm, pow_mul, ← pow_eq_pow_mod _ hx, mul_comm k, mul_assoc, pow_mul _ (_ % _),
← pow_eq_pow_mod _ hx, pow_mul, pow_mul]
zpow_succ' m x := Subtype.ext <| by
+ simp_rw [HPow.hPow, Pow.pow]
obtain ⟨_, k, rfl⟩ := x
simp only [← pow_mul, Int.natMod, SubmonoidClass.coe_pow, coe_mul]
norm_cast
diff --git a/Mathlib/Algebra/Group/TypeTags/Basic.lean b/Mathlib/Algebra/Group/TypeTags/Basic.lean
index 07c87018aaf344..a07b3ee9363195 100644
--- a/Mathlib/Algebra/Group/TypeTags/Basic.lean
+++ b/Mathlib/Algebra/Group/TypeTags/Basic.lean
@@ -261,17 +261,15 @@ instance Multiplicative.mulOneClass [AddZeroClass α] : MulOneClass (Multiplicat
one_mul := @zero_add α _
mul_one := @add_zero α _
-instance Additive.addMonoid [h : Monoid α] : AddMonoid (Additive α) :=
- { Additive.addZeroClass, Additive.addSemigroup with
- nsmul := @Monoid.npow α h
- nsmul_zero := @Monoid.npow_zero α h
- nsmul_succ := @Monoid.npow_succ α h }
+instance Additive.addMonoid [h : Monoid α] : AddMonoid (Additive α) where
+ nsmul := h.npow
+ nsmul_zero := h.npow_zero
+ nsmul_succ := h.npow_succ
-instance Multiplicative.monoid [h : AddMonoid α] : Monoid (Multiplicative α) :=
- { Multiplicative.mulOneClass, Multiplicative.semigroup with
- npow := @AddMonoid.nsmul α h
- npow_zero := @AddMonoid.nsmul_zero α h
- npow_succ := @AddMonoid.nsmul_succ α h }
+instance Multiplicative.monoid [h : AddMonoid α] : Monoid (Multiplicative α) where
+ npow := h.nsmul
+ npow_zero := h.nsmul_zero
+ npow_succ := h.nsmul_succ
@[simp]
theorem ofMul_pow [Monoid α] (n : ℕ) (a : α) : ofMul (a ^ n) = n • ofMul a :=
@@ -415,21 +413,19 @@ instance Additive.involutiveNeg [InvolutiveInv α] : InvolutiveNeg (Additive α)
instance Multiplicative.involutiveInv [InvolutiveNeg α] : InvolutiveInv (Multiplicative α) :=
{ Multiplicative.inv with inv_inv := @neg_neg α _ }
-instance Additive.subNegMonoid [DivInvMonoid α] : SubNegMonoid (Additive α) :=
- { Additive.neg, Additive.sub, Additive.addMonoid with
- sub_eq_add_neg := @div_eq_mul_inv α _
- zsmul := @DivInvMonoid.zpow α _
- zsmul_zero' := @DivInvMonoid.zpow_zero' α _
- zsmul_succ' := @DivInvMonoid.zpow_succ' α _
- zsmul_neg' := @DivInvMonoid.zpow_neg' α _ }
-
-instance Multiplicative.divInvMonoid [SubNegMonoid α] : DivInvMonoid (Multiplicative α) :=
- { Multiplicative.inv, Multiplicative.div, Multiplicative.monoid with
- div_eq_mul_inv := @sub_eq_add_neg α _
- zpow := @SubNegMonoid.zsmul α _
- zpow_zero' := @SubNegMonoid.zsmul_zero' α _
- zpow_succ' := @SubNegMonoid.zsmul_succ' α _
- zpow_neg' := @SubNegMonoid.zsmul_neg' α _ }
+instance Additive.subNegMonoid [h : DivInvMonoid α] : SubNegMonoid (Additive α) where
+ sub_eq_add_neg := h.div_eq_mul_inv
+ zsmul := h.zpow
+ zsmul_zero' := h.zpow_zero'
+ zsmul_succ' := h.zpow_succ'
+ zsmul_neg' := h.zpow_neg'
+
+instance Multiplicative.divInvMonoid [h : SubNegMonoid α] : DivInvMonoid (Multiplicative α) where
+ div_eq_mul_inv := h.sub_eq_add_neg
+ zpow := h.zsmul
+ zpow_zero' := h.zsmul_zero'
+ zpow_succ' := h.zsmul_succ'
+ zpow_neg' := h.zsmul_neg'
@[simp]
theorem ofMul_zpow [DivInvMonoid α] (z : ℤ) (a : α) : ofMul (a ^ z) = z • ofMul a :=
diff --git a/Mathlib/Algebra/Group/Units/Defs.lean b/Mathlib/Algebra/Group/Units/Defs.lean
index 96dd3018a9281f..3a7b7dd89014d1 100644
--- a/Mathlib/Algebra/Group/Units/Defs.lean
+++ b/Mathlib/Algebra/Group/Units/Defs.lean
@@ -233,8 +233,8 @@ instance instMonoid : Monoid αˣ :=
inv := a⁻¹ ^ n
val_inv := by rw [← a.commute_coe_inv.mul_pow]; simp
inv_val := by rw [← a.commute_inv_coe.mul_pow]; simp }
- npow_zero := fun a ↦ by ext; simp
- npow_succ := fun n a ↦ by ext; simp [pow_succ] }
+ npow_zero := fun a ↦ by simp only [HPow.hPow, Pow.pow]; ext; simp
+ npow_succ := fun n a ↦ by simp only [HPow.hPow, Pow.pow]; ext; simp [pow_succ] }
/-- Units of a monoid have division -/
@[to_additive /-- Additive units of an additive monoid have subtraction. -/]
@@ -251,9 +251,9 @@ instance instDivInvMonoid : DivInvMonoid αˣ where
zpow := fun n a ↦ match n, a with
| Int.ofNat n, a => a ^ n
| Int.negSucc n, a => (a ^ n.succ)⁻¹
- zpow_zero' := fun a ↦ by simp
- zpow_succ' := fun n a ↦ by simp [pow_succ]
- zpow_neg' := fun n a ↦ by simp
+ zpow_zero' := fun a ↦ by simp only [HPow.hPow, Pow.pow]; simp
+ zpow_succ' := fun n a ↦ by simp only [HPow.hPow, Pow.pow]; simp [pow_succ]
+ zpow_neg' := fun n a ↦ rfl
/-- Units of a monoid form a group. -/
@[to_additive /-- Additive units of an additive monoid form an additive group. -/]
diff --git a/Mathlib/Algebra/GroupWithZero/Action/Opposite.lean b/Mathlib/Algebra/GroupWithZero/Action/Opposite.lean
index 3bf4735533a0d3..90c30e1bd6160f 100644
--- a/Mathlib/Algebra/GroupWithZero/Action/Opposite.lean
+++ b/Mathlib/Algebra/GroupWithZero/Action/Opposite.lean
@@ -17,7 +17,7 @@ This file defines the actions on the opposite type `SMul R Mᵐᵒᵖ`, and acti
type, `SMul Rᵐᵒᵖ M`.
Note that `MulOpposite.smul` is provided in an earlier file as it is needed to
-provide the `AddMonoid.nsmul` and `AddCommGroup.zsmul` fields.
+provide the `NSMul.nsmul` and `ZSMul.zsmul` fields.
## Notation
diff --git a/Mathlib/Algebra/Module/NatInt.lean b/Mathlib/Algebra/Module/NatInt.lean
index 9daa84aa1c71eb..80968cbeb8c4a2 100644
--- a/Mathlib/Algebra/Module/NatInt.lean
+++ b/Mathlib/Algebra/Module/NatInt.lean
@@ -90,17 +90,18 @@ variable (R) in
structure.
See note [reducible non-instances]. -/
abbrev Module.addCommMonoidToAddCommGroup
- [Ring R] [AddCommMonoid M] [Module R M] : AddCommGroup M :=
- { (inferInstance : AddCommMonoid M) with
- neg := fun a => (-1 : R) • a
- neg_add_cancel := fun a =>
- show (-1 : R) • a + a = 0 by
- nth_rw 2 [← one_smul R a]
- rw [← add_smul, neg_add_cancel, zero_smul]
- zsmul := fun z a => (z : R) • a
- zsmul_zero' := fun a => by simpa only [Int.cast_zero] using zero_smul R a
- zsmul_succ' := fun z a => by simp [add_comm, add_smul]
- zsmul_neg' := fun z a => by simp [← smul_assoc] }
+ [Ring R] [AddCommMonoid M] [Module R M] : AddCommGroup M where
+ neg := fun a => (-1 : R) • a
+ neg_add_cancel := fun a =>
+ show (-1 : R) • a + a = 0 by
+ nth_rw 2 [← one_smul R a]
+ rw [← add_smul, neg_add_cancel, zero_smul]
+ zsmul z a := (z : R) • a
+ zsmul_zero' a := by simp_rw [HSMul.hSMul, SMul.smul, Int.cast_zero]; exact zero_smul R a
+ zsmul_succ' z a := by simp_rw [HSMul.hSMul, SMul.smul]; simp [add_comm, add_smul]
+ zsmul_neg' z a := by
+ change (Int.negSucc z : R) • a = -1 • ((z.succ : ℤ) : R) • a
+ simp [← smul_assoc]
section AddCommMonoid
diff --git a/Mathlib/Algebra/MonoidAlgebra/Defs.lean b/Mathlib/Algebra/MonoidAlgebra/Defs.lean
index f88a130a9fc3c9..8dbf75acb2780e 100644
--- a/Mathlib/Algebra/MonoidAlgebra/Defs.lean
+++ b/Mathlib/Algebra/MonoidAlgebra/Defs.lean
@@ -156,9 +156,12 @@ lemma ofCoeff_inj {x y : M →₀ R} : ofCoeff x = ofCoeff y ↔ x = y := ofCoef
inferInstanceAs <| DecidableEq <| M →₀ R
-- TODO: this instance abuses definitional equality with `Finsupp.mapRange`
+@[to_additive (dont_translate := A)]
+instance {A : Type*} [SMulZeroClass A R] : SMul A R[M] where
+ smul a x := x.mapRange (a • ·) (smul_zero _)
+
@[to_additive] instance addCommMonoid : AddCommMonoid R[M] :=
- fast_instance% { (inferInstance : AddCommMonoid <| M →₀ R) with
- nsmul n x := x.mapRange (n • ·) (smul_zero _) }
+ inferInstanceAs <| AddCommMonoid <| M →₀ R
@[to_additive] instance instIsCancelAdd [IsCancelAdd R] : IsCancelAdd R[M] :=
inferInstanceAs <| IsCancelAdd <| M →₀ R
@@ -260,15 +263,13 @@ Further results on scalar multiplication can be found in
variable {A : Type*} [SMulZeroClass A R]
--- TODO: this instance abuses definitional equality with `Finsupp.mapRange`
@[to_additive (dont_translate := A) smulZeroClass]
instance smulZeroClass : SMulZeroClass A R[M] :=
- fast_instance% { (inferInstance : SMulZeroClass A (M →₀ R)) with
- smul a x := x.mapRange (a • ·) (smul_zero _) }
+ inferInstanceAs <| SMulZeroClass A (M →₀ R)
section
-- Ensure that the different smul instances do not create a diamond.
-example : (smulZeroClass (A := ℕ) (R := R) (M := M)).toSMul = addCommMonoid.toNSMul := by
+example : (smulZeroClass (A := ℕ) (R := R) (M := M)).smul = addCommMonoid.nsmul := by
with_reducible_and_instances rfl
-- Ensure that smul has good defeq properties
diff --git a/Mathlib/Algebra/Order/Monoid/Unbundled/WithTop.lean b/Mathlib/Algebra/Order/Monoid/Unbundled/WithTop.lean
index b2576064241f69..54a80da3c58f50 100644
--- a/Mathlib/Algebra/Order/Monoid/Unbundled/WithTop.lean
+++ b/Mathlib/Algebra/Order/Monoid/Unbundled/WithTop.lean
@@ -264,8 +264,10 @@ instance addMonoid : AddMonoid (WithTop α) where
| (a : α), n => ↑(n • a)
| ⊤, 0 => 0
| ⊤, _n + 1 => ⊤
- nsmul_zero a := by cases a <;> simp [zero_nsmul]
- nsmul_succ n a := by cases a <;> cases n <;> simp [succ_nsmul, coe_add]
+ nsmul_zero a := by simp_rw [HSMul.hSMul, SMul.smul]; cases a <;> simp [zero_nsmul]
+ nsmul_succ n a := by
+ simp_rw [HSMul.hSMul, SMul.smul]
+ cases a <;> cases n <;> simp [succ_nsmul, coe_add]
@[simp, norm_cast] lemma coe_nsmul (a : α) (n : ℕ) : ↑(n • a) = n • (a : WithTop α) := rfl
diff --git a/Mathlib/Algebra/Order/Positive/Field.lean b/Mathlib/Algebra/Order/Positive/Field.lean
index 2c32175910675c..d2eda796258759 100644
--- a/Mathlib/Algebra/Order/Positive/Field.lean
+++ b/Mathlib/Algebra/Order/Positive/Field.lean
@@ -35,8 +35,10 @@ instance : Pow { x : K // 0 < x } ℤ :=
theorem coe_zpow (x : { x : K // 0 < x }) (n : ℤ) : ↑(x ^ n) = (x : K) ^ n :=
rfl
-instance : CommGroup { x : K // 0 < x } :=
- { Positive.commMonoid with
- inv_mul_cancel := fun a => Subtype.ext <| inv_mul_cancel₀ a.2.ne' }
+instance : CommGroup { x : K // 0 < x } where
+ inv_mul_cancel a := Subtype.ext <| inv_mul_cancel₀ a.2.ne'
+ zpow_zero' x := Subtype.ext <| zpow_zero _
+ zpow_succ' n x := Subtype.ext <| DivInvMonoid.zpow_succ' _ _
+ zpow_neg' n x := Subtype.ext <| DivInvMonoid.zpow_neg' _ _
end Positive
diff --git a/Mathlib/Algebra/Order/Ring/Archimedean.lean b/Mathlib/Algebra/Order/Ring/Archimedean.lean
index ad71ce19de201b..333b72c4ef4ae7 100644
--- a/Mathlib/Algebra/Order/Ring/Archimedean.lean
+++ b/Mathlib/Algebra/Order/Ring/Archimedean.lean
@@ -101,7 +101,6 @@ instance : AddCommMonoid (ArchimedeanClass R) where
add_assoc := private add_assoc'
zero_add := private zero_add'
add_zero x := private add_comm x _ ▸ zero_add' x
- nsmul n x := n • x
nsmul_zero x := by induction x with | mk x => rw [← mk_pow, pow_zero, mk_one]
nsmul_succ n x := by induction x with | mk x => rw [← mk_pow, pow_succ, mk_mul, mk_pow]
@@ -308,7 +307,6 @@ noncomputable instance : LinearOrderedAddCommGroupWithTop (ArchimedeanClass R) w
add_neg_cancel_of_ne_top x h := by
induction x with | mk x
simp [← mk_inv, ← mk_mul, mul_inv_cancel₀ (mk_eq_top_iff.not.1 h)]
- zsmul n x := n • x
zsmul_zero' x := by induction x with | mk x => rw [← mk_zpow, zpow_zero, mk_one]
zsmul_succ' := by exact zsmul_succ'
zsmul_neg' n x := by
diff --git a/Mathlib/Algebra/Order/Ring/WithTop.lean b/Mathlib/Algebra/Order/Ring/WithTop.lean
index 0ca11df2f6c5c6..a7a8bc4d82a7bd 100644
--- a/Mathlib/Algebra/Order/Ring/WithTop.lean
+++ b/Mathlib/Algebra/Order/Ring/WithTop.lean
@@ -173,8 +173,8 @@ instance instMonoidWithZero : MonoidWithZero (WithTop α) where
| (a : α), n => ↑(a ^ n)
| ⊤, 0 => 1
| ⊤, _n + 1 => ⊤
- npow_zero a := by cases a <;> simp
- npow_succ n a := by cases n <;> cases a <;> simp [pow_succ]
+ npow_zero a := by simp_rw [HPow.hPow, Pow.pow]; cases a <;> simp
+ npow_succ n a := by simp_rw [HPow.hPow, Pow.pow]; cases n <;> cases a <;> simp [pow_succ]
@[simp, norm_cast] lemma coe_pow (a : α) (n : ℕ) : (↑(a ^ n) : WithTop α) = a ^ n := rfl
diff --git a/Mathlib/Algebra/Ring/MinimalAxioms.lean b/Mathlib/Algebra/Ring/MinimalAxioms.lean
index 77dcc4cb9c8d32..984584c77b6929 100644
--- a/Mathlib/Algebra/Ring/MinimalAxioms.lean
+++ b/Mathlib/Algebra/Ring/MinimalAxioms.lean
@@ -69,8 +69,7 @@ abbrev Ring.ofMinimalAxioms {R : Type u}
mul_assoc := mul_assoc
one_mul := one_mul
mul_one := mul_one
- neg_add_cancel := neg_add_cancel
- zsmul := (· • ·) }
+ neg_add_cancel := neg_add_cancel }
/-- Define a `CommRing` structure on a Type by proving a minimized set of axioms.
Note that this uses the default definitions for `npow`, `nsmul`, `zsmul` and `sub`
diff --git a/Mathlib/Algebra/RingQuot.lean b/Mathlib/Algebra/RingQuot.lean
index b5a17e7e40604c..86352ce1c777b7 100644
--- a/Mathlib/Algebra/RingQuot.lean
+++ b/Mathlib/Algebra/RingQuot.lean
@@ -278,7 +278,6 @@ instance instRing {R : Type uR} [Ring R] (r : R → R → Prop) : Ring (RingQuot
sub_eq_add_neg := by
rintro ⟨⟨⟩⟩ ⟨⟨⟩⟩
simp [neg_quot, sub_quot, add_quot, sub_eq_add_neg]
- zsmul := (· • ·)
zsmul_zero' := by
rintro ⟨⟨⟩⟩
simp [smul_quot, ← zero_quot]
diff --git a/Mathlib/CategoryTheory/Preadditive/Comma.lean b/Mathlib/CategoryTheory/Preadditive/Comma.lean
index 0037e50fefdb7a..0b7ab15aba7870 100644
--- a/Mathlib/CategoryTheory/Preadditive/Comma.lean
+++ b/Mathlib/CategoryTheory/Preadditive/Comma.lean
@@ -68,11 +68,13 @@ instance : AddCommGroup (u ⟶ v) where
(by simp [Functor.map_nsmul, Preadditive.comp_nsmul, Preadditive.nsmul_comp])
zsmul n α := CommaMorphism.mk (n • α.left) (n • α.right)
(by simp [Functor.map_zsmul, Preadditive.comp_zsmul, Preadditive.zsmul_comp])
- nsmul_zero := by cat_disch
- nsmul_succ _ _ := by ext <;> dsimp <;> simp [add_nsmul]
- zsmul_zero' := by cat_disch
- zsmul_succ' _ _ := by ext <;> dsimp <;> simp [add_zsmul]
- zsmul_neg' _ _ := by ext <;> dsimp <;> simp [add_nsmul, add_zsmul]
+ nsmul_zero := by simp_rw [HSMul.hSMul, SMul.smul]; cat_disch
+ nsmul_succ _ _ := by simp_rw [HSMul.hSMul, SMul.smul]; ext <;> dsimp <;> simp [add_nsmul]
+ zsmul_zero' := by simp_rw [HSMul.hSMul, SMul.smul]; cat_disch
+ zsmul_succ' _ _ := by simp_rw [HSMul.hSMul, SMul.smul]; ext <;> dsimp <;> simp [add_zsmul]
+ zsmul_neg' _ _ := by
+ simp_rw [HSMul.hSMul, SMul.smul]
+ ext <;> dsimp <;> simp [add_nsmul, add_zsmul]
/-- If we have additive functors `L : A ⥤ T` and `R : B ⥤ T` between preadditive categories,
then the category `Comma L R` is preadditive.
diff --git a/Mathlib/CategoryTheory/Triangulated/Basic.lean b/Mathlib/CategoryTheory/Triangulated/Basic.lean
index 59f0b77bf32158..f19199686ad02f 100644
--- a/Mathlib/CategoryTheory/Triangulated/Basic.lean
+++ b/Mathlib/CategoryTheory/Triangulated/Basic.lean
@@ -289,10 +289,8 @@ instance : AddCommGroup (T₁ ⟶ T₂) where
add_comm f g := by ext <;> apply add_comm
neg_add_cancel f := by ext <;> apply neg_add_cancel
sub_eq_add_neg f g := by ext <;> apply sub_eq_add_neg
- nsmul n f := n • f
nsmul_zero f := by cat_disch
nsmul_succ n f := by ext <;> apply AddMonoid.nsmul_succ
- zsmul n f := n • f
zsmul_zero' := by cat_disch
zsmul_succ' n f := by ext <;> apply SubNegMonoid.zsmul_succ'
zsmul_neg' n f := by ext <;> apply SubNegMonoid.zsmul_neg'
diff --git a/Mathlib/Data/BitVec.lean b/Mathlib/Data/BitVec.lean
index c76df5a6a66593..7e2da761f6c3bf 100644
--- a/Mathlib/Data/BitVec.lean
+++ b/Mathlib/Data/BitVec.lean
@@ -71,6 +71,7 @@ lemma toFin_zsmul (z : ℤ) (x : BitVec w) : toFin (z • x) = z • x.toFin :=
open scoped Fin.CommRing in
simp only [zsmul_eq_mul, toFin_intCast]
+set_option backward.isDefEq.respectTransparency false in
lemma toFin_pow (x : BitVec w) (n : ℕ) : toFin (x ^ n) = x.toFin ^ n := by
induction n with
| zero => simp
@@ -81,7 +82,7 @@ lemma toFin_pow (x : BitVec w) (n : ℕ) : toFin (x ^ n) = x.toFin ^ n := by
-/
-- Verify that the `HPow` instance from Lean agrees definitionally with the instance via `Monoid`.
-example : @instHPow (Fin (2 ^ w)) ℕ Monoid.toPow = Lean.Grind.Fin.instHPowFinNatOfNeZero := rfl
+example : @instHPow (Fin (2 ^ w)) ℕ NPow.toPow = Lean.Grind.Fin.instHPowFinNatOfNeZero := rfl
instance : CommSemiring (BitVec w) :=
open Fin.CommRing in
diff --git a/Mathlib/Data/Complex/Basic.lean b/Mathlib/Data/Complex/Basic.lean
index feb4b396fd8848..12f58340718e32 100644
--- a/Mathlib/Data/Complex/Basic.lean
+++ b/Mathlib/Data/Complex/Basic.lean
@@ -323,8 +323,6 @@ theorem real_smul {x : ℝ} {z : ℂ} : x • z = x * z :=
end SMul
instance addCommGroup : AddCommGroup ℂ where
- nsmul := (· • ·)
- zsmul := (· • ·)
zsmul_zero' := by intros; ext <;> simp [smul_re, smul_im]
nsmul_zero := by intros; ext <;> simp [smul_re, smul_im]
nsmul_succ := by intros; ext <;> simp [smul_re, smul_im] <;> ring
diff --git a/Mathlib/Data/Rat/Defs.lean b/Mathlib/Data/Rat/Defs.lean
index 1b3f964e3a1f79..c252257fc8dd95 100644
--- a/Mathlib/Data/Rat/Defs.lean
+++ b/Mathlib/Data/Rat/Defs.lean
@@ -160,8 +160,10 @@ instance addCommGroup : AddCommGroup ℚ where
rw [Rat.intCast_add, Rat.add_mul, Rat.intCast_one, Rat.one_mul]
rfl
zsmul_zero' := Rat.zero_mul
- zsmul_succ' _ _ := by simp [Rat.add_mul]
- zsmul_neg' _ _ := by rw [Int.negSucc_eq, Rat.intCast_neg, Rat.neg_mul]; rfl
+ zsmul_succ' _ _ := by simp_rw [HSMul.hSMul, SMul.smul]; simp [Rat.add_mul]
+ zsmul_neg' _ _ := by
+ simp_rw [HSMul.hSMul, SMul.smul]
+ rw [Int.negSucc_eq, Rat.intCast_neg, Rat.neg_mul]; rfl
instance addGroup : AddGroup ℚ := by infer_instance
diff --git a/Mathlib/Data/ZMod/Defs.lean b/Mathlib/Data/ZMod/Defs.lean
index e1b9832a88d149..e6b35cc6f970f0 100644
--- a/Mathlib/Data/ZMod/Defs.lean
+++ b/Mathlib/Data/ZMod/Defs.lean
@@ -65,17 +65,22 @@ open scoped Fin.IntCast Fin.NatCast
lia
/-- Multiplicative commutative semigroup structure on `Fin n`. -/
-instance instCommSemigroup (n : ℕ) : CommSemigroup (Fin n) :=
- { (inferInstance : Mul (Fin n)) with
- mul_assoc := fun ⟨a, _⟩ ⟨b, _⟩ ⟨c, _⟩ =>
- Fin.eq_of_val_eq <|
- calc
- a * b % n * c ≡ a * b * c [MOD n] := (Nat.mod_modEq _ _).mul_right _
- _ ≡ a * (b * c) [MOD n] := by rw [mul_assoc]
- _ ≡ a * (b * c % n) [MOD n] := (Nat.mod_modEq _ _).symm.mul_left _
- mul_comm := Fin.mul_comm }
-
-set_option backward.privateInPublic true in
+instance instCommSemigroup (n : ℕ) : CommSemigroup (Fin n) where
+ mul_assoc := fun ⟨a, _⟩ ⟨b, _⟩ ⟨c, _⟩ =>
+ Fin.eq_of_val_eq <|
+ calc
+ a * b % n * c ≡ a * b * c [MOD n] := (Nat.mod_modEq _ _).mul_right _
+ _ ≡ a * (b * c) [MOD n] := by rw [mul_assoc]
+ _ ≡ a * (b * c % n) [MOD n] := (Nat.mod_modEq _ _).symm.mul_left _
+ mul_comm := Fin.mul_comm
+
+-- Shortcut instances to replace the power operation on `Fin` with a more efficient one
+instance (n : ℕ) [NeZero n] : HPow (Fin n) ℕ (Fin n) where
+ hPow a m := npowRecAuto m a
+
+instance (n : ℕ) [NeZero n] : Pow (Fin n) ℕ where
+ pow a m := npowRecAuto m a
+
private theorem left_distrib_aux (n : ℕ) : ∀ a b c : Fin n, a * (b + c) = a * b + a * c :=
fun ⟨a, _⟩ ⟨b, _⟩ ⟨c, _⟩ =>
Fin.eq_of_val_eq <|
@@ -84,19 +89,13 @@ private theorem left_distrib_aux (n : ℕ) : ∀ a b c : Fin n, a * (b + c) = a
_ ≡ a * b + a * c [MOD n] := by rw [mul_add]
_ ≡ a * b % n + a * c % n [MOD n] := (Nat.mod_modEq _ _).symm.add (Nat.mod_modEq _ _).symm
-set_option backward.privateInPublic true in
-set_option backward.privateInPublic.warn false in
/-- Distributive structure on `Fin n`. -/
-instance instDistrib (n : ℕ) : Distrib (Fin n) :=
- { Fin.addCommSemigroup n, Fin.instCommSemigroup n with
- left_distrib := left_distrib_aux n
- right_distrib := fun a b c => by
- rw [mul_comm, left_distrib_aux, mul_comm _ b, mul_comm] }
+instance instDistrib (n : ℕ) : Distrib (Fin n) where
+ left_distrib := private left_distrib_aux n
+ right_distrib := fun a b c => by
+ rw [mul_comm, left_distrib_aux, mul_comm _ b, mul_comm]
instance instNonUnitalCommRing (n : ℕ) [NeZero n] : NonUnitalCommRing (Fin n) where
- __ := Fin.addCommGroup n
- __ := Fin.instCommSemigroup n
- __ := Fin.instDistrib n
zero_mul := Fin.zero_mul
mul_zero := Fin.mul_zero
@@ -106,7 +105,6 @@ instance instCommMonoid (n : ℕ) [NeZero n] : CommMonoid (Fin n) where
/-- Note this is more general than `Fin.instCommRing` as it applies (vacuously) to `Fin 0` too. -/
instance instHasDistribNeg (n : ℕ) : HasDistribNeg (Fin n) where
- toInvolutiveNeg := Fin.instInvolutiveNeg n
mul_neg := Nat.casesOn n finZeroElim fun _i => mul_neg
neg_mul := Nat.casesOn n finZeroElim fun _i => neg_mul
@@ -126,11 +124,6 @@ silently introducing wraparound arithmetic.
-/
@[instance_reducible]
def instCommRing (n : ℕ) [NeZero n] : CommRing (Fin n) where
- __ := Fin.instAddMonoidWithOne n
- __ := Fin.addCommGroup n
- __ := Fin.instCommSemigroup n
- __ := Fin.instNonUnitalCommRing n
- __ := Fin.instCommMonoid n
intCast n := Fin.intCast n
namespace CommRing
diff --git a/Mathlib/Data/ZMod/IntUnitsPower.lean b/Mathlib/Data/ZMod/IntUnitsPower.lean
index 687842a20a1f6a..e341f184756521 100644
--- a/Mathlib/Data/ZMod/IntUnitsPower.lean
+++ b/Mathlib/Data/ZMod/IntUnitsPower.lean
@@ -65,8 +65,8 @@ instance Int.instUnitsPow : Pow ℤˣ R where
-- The above instances form no typeclass diamonds with the standard power operators
-- but we will need `reducible_and_instances` which currently fails https://github.com/leanprover-community/mathlib4/issues/10906
-example : Int.instUnitsPow = Monoid.toPow := rfl
-example : Int.instUnitsPow = DivInvMonoid.toZPow := rfl
+example : Int.instUnitsPow = NPow.toPow := rfl
+example : Int.instUnitsPow = ZPow.toPow := rfl
@[simp] lemma ofMul_uzpow (u : ℤˣ) (r : R) : Additive.ofMul (u ^ r) = r • Additive.ofMul u := rfl
diff --git a/Mathlib/FieldTheory/RatFunc/Basic.lean b/Mathlib/FieldTheory/RatFunc/Basic.lean
index fb732adde49cc6..d99cb6ca38a10a 100644
--- a/Mathlib/FieldTheory/RatFunc/Basic.lean
+++ b/Mathlib/FieldTheory/RatFunc/Basic.lean
@@ -290,10 +290,8 @@ def instAddCommGroup : AddCommGroup K⟮X⟯ where
add_zero := by frac_tac
neg_add_cancel := by frac_tac
sub_eq_add_neg := by frac_tac
- nsmul := (· • ·)
nsmul_zero := by smul_tac
nsmul_succ _ := by smul_tac
- zsmul := (· • ·)
zsmul_zero' := by smul_tac
zsmul_succ' _ := by smul_tac
zsmul_neg' _ := by smul_tac
diff --git a/Mathlib/GroupTheory/GroupAction/Hom.lean b/Mathlib/GroupTheory/GroupAction/Hom.lean
index c1906b4166e9c1..4ba57e5936c038 100644
--- a/Mathlib/GroupTheory/GroupAction/Hom.lean
+++ b/Mathlib/GroupTheory/GroupAction/Hom.lean
@@ -448,7 +448,6 @@ lemma coe_add [SMul M X] [AddZeroClass Y] [DistribSMul N Y] (f g : X →ₑ[σ]
instance [SMul M X] [AddMonoid Y] [DistribSMul N Y] :
AddMonoid (X →ₑ[σ] Y) where
add_assoc _ _ _ := ext fun _ ↦ add_assoc _ _ _
- nsmul n f := n • f
nsmul_zero f := ext fun x ↦ AddMonoid.nsmul_zero (f x)
nsmul_succ n f := ext fun x ↦ AddMonoid.nsmul_succ n (f x)
@@ -484,7 +483,6 @@ instance [SMul M X] [AddGroup Y] [DistribSMul N Y] : AddGroup (X →ₑ[σ] Y) w
neg f := ⟨-f, by simp⟩
neg_add_cancel f := ext fun _ ↦ neg_add_cancel _
sub_eq_add_neg _ _ := ext fun _ ↦ sub_eq_add_neg _ _
- zsmul z f := z • f
zsmul_zero' f := ext fun x ↦ SubNegMonoid.zsmul_zero' _
zsmul_neg' _ _ := ext fun x ↦ SubNegMonoid.zsmul_neg' _ _
zsmul_succ' _ _ := ext fun x ↦ SubNegMonoid.zsmul_succ' _ _
diff --git a/Mathlib/LinearAlgebra/Matrix/Defs.lean b/Mathlib/LinearAlgebra/Matrix/Defs.lean
index 73ad6a744b33d9..12896de854d302 100644
--- a/Mathlib/LinearAlgebra/Matrix/Defs.lean
+++ b/Mathlib/LinearAlgebra/Matrix/Defs.lean
@@ -153,6 +153,9 @@ instance inhabited [Inhabited α] : Inhabited (Matrix m n α) :=
instance add [Add α] : Add (Matrix m n α) :=
inferInstanceAs <| Add (m → n → α)
+instance smul [SMul R α] : SMul R (Matrix m n α) where
+ smul a b := fun i ↦ a • b i
+
instance addSemigroup [AddSemigroup α] : AddSemigroup (Matrix m n α) :=
inferInstanceAs <| AddSemigroup (m → n → α)
@@ -165,9 +168,8 @@ instance zero [Zero α] : Zero (Matrix m n α) :=
instance addZeroClass [AddZeroClass α] : AddZeroClass (Matrix m n α) :=
inferInstanceAs <| AddZeroClass (m → n → α)
-instance addMonoid [AddMonoid α] : AddMonoid (Matrix m n α) where
- __ : AddMonoid (Matrix m n α) := inferInstanceAs <| AddMonoid (m → n → α)
- nsmul a b := fun i ↦ a • b i
+instance addMonoid [AddMonoid α] : AddMonoid (Matrix m n α) :=
+ inferInstanceAs <| AddMonoid (m → n → α)
instance addCommMonoid [AddCommMonoid α] : AddCommMonoid (Matrix m n α) :=
inferInstanceAs <| AddCommMonoid (m → n → α)
@@ -181,9 +183,8 @@ instance involutiveNeg [InvolutiveNeg α] : InvolutiveNeg (Matrix m n α) :=
instance sub [Sub α] : Sub (Matrix m n α) :=
inferInstanceAs <| Sub (m → n → α)
-instance addGroup [AddGroup α] : AddGroup (Matrix m n α) where
- __ : AddGroup (Matrix m n α) := inferInstanceAs <| AddGroup (m → n → α)
- zsmul a b := fun i ↦ a • b i
+instance addGroup [AddGroup α] : AddGroup (Matrix m n α) :=
+ inferInstanceAs <| AddGroup (m → n → α)
instance addCommGroup [AddCommGroup α] : AddCommGroup (Matrix m n α) :=
inferInstanceAs <| AddCommGroup (m → n → α)
@@ -197,9 +198,6 @@ instance subsingleton [Subsingleton α] : Subsingleton (Matrix m n α) :=
instance nonempty [Nonempty m] [Nonempty n] [Nontrivial α] : Nontrivial (Matrix m n α) :=
Function.nontrivial
-instance smul [SMul R α] : SMul R (Matrix m n α) where
- smul a b := fun i ↦ a • b i
-
instance smulCommClass [SMul R α] [SMul S α] [SMulCommClass R S α] :
SMulCommClass R S (Matrix m n α) :=
Pi.smulCommClass
diff --git a/Mathlib/LinearAlgebra/Matrix/ZPow.lean b/Mathlib/LinearAlgebra/Matrix/ZPow.lean
index b32635593790f5..0ba75ca5ab6ff3 100644
--- a/Mathlib/LinearAlgebra/Matrix/ZPow.lean
+++ b/Mathlib/LinearAlgebra/Matrix/ZPow.lean
@@ -97,8 +97,7 @@ theorem inv_zpow (A : M) : ∀ n : ℤ, A⁻¹ ^ n = (A ^ n)⁻¹
@[simp]
theorem zpow_neg_one (A : M) : A ^ (-1 : ℤ) = A⁻¹ := by
- convert! DivInvMonoid.zpow_neg' 0 A
- simp only [zpow_one, Int.ofNat_zero, Int.natCast_succ, zpow_eq_pow, zero_add]
+ simpa using DivInvMonoid.zpow_neg' 0 A
@[simp]
theorem zpow_neg_natCast (A : M) (n : ℕ) : A ^ (-n : ℤ) = (A ^ n)⁻¹ := by
diff --git a/Mathlib/LinearAlgebra/TensorProduct/Basic.lean b/Mathlib/LinearAlgebra/TensorProduct/Basic.lean
index e7526ce6d6d11e..4784cba63d32aa 100644
--- a/Mathlib/LinearAlgebra/TensorProduct/Basic.lean
+++ b/Mathlib/LinearAlgebra/TensorProduct/Basic.lean
@@ -393,7 +393,6 @@ protected theorem neg_add_cancel (x : M ⊗[R] N) : -x + x = 0 :=
instance addCommGroup : AddCommGroup (M ⊗[R] N) where
neg_add_cancel := fun x => TensorProduct.neg_add_cancel x
- zsmul := (· • ·)
zsmul_zero' := by simp
zsmul_succ' := by simp [add_comm, TensorProduct.add_smul]
zsmul_neg' := fun n x => by
diff --git a/Mathlib/LinearAlgebra/TensorProduct/Defs.lean b/Mathlib/LinearAlgebra/TensorProduct/Defs.lean
index 528e297c60fed3..0a0180c5d21455 100644
--- a/Mathlib/LinearAlgebra/TensorProduct/Defs.lean
+++ b/Mathlib/LinearAlgebra/TensorProduct/Defs.lean
@@ -224,7 +224,7 @@ Note that in the special case that `R = R'`, since `R` is commutative, we just g
action on a tensor product of two modules. This special case is important enough that, for
performance reasons, we define it explicitly below. -/
instance leftHasSMul : SMul R' (M ⊗[R] N) :=
- ⟨fun r =>
+ id ⟨fun r =>
(addConGen (TensorProduct.Eqv R M N)).lift (SMul.aux r : _ →+ M ⊗[R] N) <|
AddCon.addConGen_le.2 fun x y hxy =>
match x, y, hxy with
@@ -270,10 +270,8 @@ protected theorem add_smul (r s : R'') (x : M ⊗[R] N) : (r + s) • x = r •
rw [ihx, ihy, add_add_add_comm]
instance addMonoid : AddMonoid (M ⊗[R] N) where
- nsmul := fun n v => n • v
nsmul_zero := by simp [TensorProduct.zero_smul]
- nsmul_succ := by simp only [TensorProduct.one_smul, TensorProduct.add_smul, add_comm,
- forall_const]
+ nsmul_succ := by simp only [TensorProduct.one_smul, TensorProduct.add_smul, forall_const]
instance addCommMonoid : AddCommMonoid (M ⊗[R] N) where
diff --git a/Mathlib/NumberTheory/ArithmeticFunction/Defs.lean b/Mathlib/NumberTheory/ArithmeticFunction/Defs.lean
index 4c83802fcc713f..60325c75a83656 100644
--- a/Mathlib/NumberTheory/ArithmeticFunction/Defs.lean
+++ b/Mathlib/NumberTheory/ArithmeticFunction/Defs.lean
@@ -294,7 +294,6 @@ instance [CommSemiring R] : CommSemiring (ArithmeticFunction R) where
instance [CommRing R] : CommRing (ArithmeticFunction R) where
neg_add_cancel := neg_add_cancel
mul_comm := mul_comm
- zsmul n f := n • f
instance {S : Type*} [Semiring R] [AddCommMonoid S] [Module R S] :
Module R (ArithmeticFunction S) where
diff --git a/Mathlib/RingTheory/PolynomialLaw/Basic.lean b/Mathlib/RingTheory/PolynomialLaw/Basic.lean
index 586d31ab7c3f83..1a1843e362c313 100644
--- a/Mathlib/RingTheory/PolynomialLaw/Basic.lean
+++ b/Mathlib/RingTheory/PolynomialLaw/Basic.lean
@@ -165,8 +165,10 @@ instance : AddCommMonoid (M →ₚₗ[R] N) where
zero_add f := by ext; simp only [add_def, zero_add, zero_def]
add_zero f := by ext; simp only [add_def, add_zero, zero_def]
nsmul n f := (n : R) • f
- nsmul_zero f := by simp only [Nat.cast_zero, zero_smul f]
- nsmul_succ n f := by simp only [Nat.cast_add, Nat.cast_one, add_smul, one_smul]
+ nsmul_zero f := by simp_rw [HSMul.hSMul, SMul.smul]; simp only [Nat.cast_zero, zero_smul f]
+ nsmul_succ n f := by
+ simp_rw [HSMul.hSMul, SMul.smul]
+ simp only [Nat.cast_add, Nat.cast_one, add_smul, one_smul]
add_comm f g := by ext; simp only [add_def, add_comm]
instance : Module R (M →ₚₗ[R] N) where
@@ -195,15 +197,17 @@ theorem neg_def (S : Type u) [CommSemiring S] [Algebra R S] :
instance : AddCommGroup (M →ₚₗ[R] N) where
zsmul n f := (n : R) • f
- zsmul_zero' f := by simp only [Int.cast_zero, zero_smul]
- zsmul_succ' n f := by simp only [Nat.cast_succ, Int.cast_add, Int.cast_natCast,
- Int.cast_one, add_smul, _root_.one_smul]
+ zsmul_zero' f := by simp_rw [HSMul.hSMul, SMul.smul]; simp only [Int.cast_zero, zero_smul]
+ zsmul_succ' n f := by
+ simp_rw [HSMul.hSMul, SMul.smul]
+ simp only [Nat.cast_succ, Int.cast_add, Int.cast_natCast, Int.cast_one, add_smul, one_smul]
zsmul_neg' n f := by
+ simp_rw [HSMul.hSMul, SMul.smul]
ext S _ _ m
rw [neg_def]
- simp only [Int.cast_negSucc, Nat.cast_add, Nat.cast_one, neg_add_rev, _root_.add_smul,
+ simp only [Int.cast_negSucc, Nat.cast_add, Nat.cast_one, neg_add_rev, add_smul,
add_def_apply, smul_def_apply, Nat.succ_eq_add_one, Int.cast_add, Int.cast_natCast,
- Int.cast_one, _root_.one_smul, add_def, smul_def, Pi.smul_apply, Pi.add_apply, smul_add,
+ Int.cast_one, one_smul, add_def, smul_def, Pi.smul_apply, Pi.add_apply, smul_add,
smul_smul, neg_mul, one_mul]
rw [add_comm]
neg_add_cancel f := by
diff --git a/Mathlib/RingTheory/Valuation/ValuativeRel/Basic.lean b/Mathlib/RingTheory/Valuation/ValuativeRel/Basic.lean
index 26f6b4663daee7..0cb1d9d4957031 100644
--- a/Mathlib/RingTheory/Valuation/ValuativeRel/Basic.lean
+++ b/Mathlib/RingTheory/Valuation/ValuativeRel/Basic.lean
@@ -568,8 +568,8 @@ instance : CommMonoidWithZero (ValueGroupWithZero R) where
simp only [pow_succ, ← ValueGroupWithZero.mk_mul_mk, ih]
apply congrArg (_ * ·)
exact ValueGroupWithZero.sound h₁ h₂
- npow_zero := ValueGroupWithZero.ind (by simp)
- npow_succ n := ValueGroupWithZero.ind (by simp [pow_succ])
+ npow_zero := ValueGroupWithZero.ind (by simp_rw [HPow.hPow, Pow.pow]; simp)
+ npow_succ n := ValueGroupWithZero.ind (by simp_rw [HPow.hPow, Pow.pow]; simp [pow_succ])
instance : LE (ValueGroupWithZero R) where
le := ValueGroupWithZero.lift₂ (fun a s b t => a * t ≤ᵥ b * s) <| by
diff --git a/Mathlib/SetTheory/Cardinal/Order.lean b/Mathlib/SetTheory/Cardinal/Order.lean
index eabd0ad6e5b4c1..600df08519e708 100644
--- a/Mathlib/SetTheory/Cardinal/Order.lean
+++ b/Mathlib/SetTheory/Cardinal/Order.lean
@@ -231,7 +231,7 @@ instance commSemiring : CommSemiring Cardinal.{u} where
nsmul := nsmulRec
npow n c := c ^ (n : Cardinal)
npow_zero := power_zero
- npow_succ n c := by rw [cast_succ, power_add, power_one]
+ npow_succ n c := by simp_rw [HPow.hPow, Pow.pow]; rw [cast_succ, power_add, power_one]
natCast n := lift #(Fin n)
natCast_zero := rfl
natCast_succ n := cast_succ n
diff --git a/Mathlib/Tactic/Abel.lean b/Mathlib/Tactic/Abel.lean
index 757090587b50a7..32543310433274 100644
--- a/Mathlib/Tactic/Abel.lean
+++ b/Mathlib/Tactic/Abel.lean
@@ -377,11 +377,11 @@ partial def eval (e : Expr) : M (NormalExpr × Expr) := do
let (e₁, p₁) ← eval e
let (e₂, p₂) ← evalNeg e₁
return (e₂, ← iapp `Mathlib.Tactic.Abel.subst_into_neg #[e, e₁, e₂, p₁, p₂])
- | (``AddMonoid.nsmul, #[_, _, e₁, e₂]) => do
+ | (``NSMul.nsmul, #[_, _, e₁, e₂]) => do
let n ← if (← read).isGroup then mkAppM ``Int.ofNat #[e₁] else pure e₁
let (e', p) ← eval <| ← iapp ``smul #[n, e₂]
return (e', ← iapp ``unfold_smul #[e₁, e₂, e', p])
- | (``SubNegMonoid.zsmul, #[_, _, e₁, e₂]) => do
+ | (``ZSMul.zsmul, #[_, _, e₁, e₂]) => do
if ¬ (← read).isGroup then failure
let (e', p) ← eval <| ← iapp ``smul #[e₁, e₂]
return (e', (← read).app ``unfold_zsmul (← read).inst #[e₁, e₂, e', p])
@@ -411,8 +411,8 @@ def isAtom (e : Expr) : Bool :=
| (``HAdd.hAdd, #[_, _, _, _, _, _])
| (``HSub.hSub, #[_, _, _, _, _, _])
| (``Neg.neg, #[_, _, _])
- | (``AddMonoid.nsmul, #[_, _, _, _])
- | (``SubNegMonoid.zsmul, #[_, _, _, _])
+ | (``NSMul.nsmul, #[_, _, _, _])
+ | (``ZSMul.zsmul, #[_, _, _, _])
| (``SMul.smul, #[.const ``Int _, _, _, _, _])
| (``SMul.smul, #[.const ``Nat _, _, _, _, _])
| (``HSMul.hSMul, #[.const ``Int _, _, _, _, _, _])
diff --git a/Mathlib/Tactic/Translate/ToAdditive.lean b/Mathlib/Tactic/Translate/ToAdditive.lean
index 0ce40eceb1fac7..bb007e638676af 100644
--- a/Mathlib/Tactic/Translate/ToAdditive.lean
+++ b/Mathlib/Tactic/Translate/ToAdditive.lean
@@ -190,8 +190,8 @@ mismatch error.
This likely only happens when the multiplicative declaration involves `pow`/`^`. Solutions:
* Ensure that the order of arguments of all relevant declarations are the same for the
multiplicative and additive version. This might mean that arguments have an "unnatural" order
- (e.g. `Monoid.npow n x` corresponds to `x ^ n`, but it is convenient that `Monoid.npow` has this
- argument order, since it matches `AddMonoid.nsmul n x`.
+ (e.g. `NPow.npow n x` corresponds to `x ^ n`, but it is convenient that `NPow.npow` has this
+ argument order, since it matches `NSMul.nsmul n x`.
* If this is not possible, add `(reorder := ...)` argument to `to_additive`.
If neither of these solutions work, and `to_additive` is unable to automatically generate the
diff --git a/Mathlib/Topology/Algebra/GroupCompletion.lean b/Mathlib/Topology/Algebra/GroupCompletion.lean
index 9c96a32278eae8..82cc3cda791079 100644
--- a/Mathlib/Topology/Algebra/GroupCompletion.lean
+++ b/Mathlib/Topology/Algebra/GroupCompletion.lean
@@ -70,12 +70,11 @@ open UniformSpace
section Zero
instance [UniformSpace α] [MonoidWithZero M] [Zero α] [MulActionWithZero M α]
- [UniformContinuousConstSMul M α] : MulActionWithZero M (Completion α) :=
- { (inferInstance : MulAction M <| Completion α) with
- smul_zero := fun r ↦ by rw [← coe_zero, ← coe_smul, MulActionWithZero.smul_zero r]
- zero_smul :=
- ext' (continuous_const_smul _) continuous_const fun a ↦ by
- rw [← coe_smul, zero_smul, coe_zero] }
+ [UniformContinuousConstSMul M α] : MulActionWithZero M (Completion α) where
+ smul_zero := fun r ↦ by rw [← coe_zero, ← coe_smul, MulActionWithZero.smul_zero r]
+ zero_smul :=
+ ext' (continuous_const_smul _) continuous_const fun a ↦ by
+ rw [← coe_smul, zero_smul, coe_zero]
end Zero
@@ -95,82 +94,73 @@ theorem coe_sub (a b : α) : ((a - b : α) : Completion α) = a - b :=
theorem coe_add (a b : α) : ((a + b : α) : Completion α) = a + b :=
(map₂_coe_coe a b (· + ·) uniformContinuous_add).symm
-instance : AddMonoid (Completion α) :=
- { (inferInstance : Zero <| Completion α),
- (inferInstance : Add <| Completion α) with
- zero_add := fun a ↦
- Completion.induction_on a
- (isClosed_eq (continuous_map₂ continuous_const continuous_id) continuous_id) fun a ↦
- show 0 + (a : Completion α) = a by rw [← coe_zero, ← coe_add, zero_add]
- add_zero := fun a ↦
- Completion.induction_on a
- (isClosed_eq (continuous_map₂ continuous_id continuous_const) continuous_id) fun a ↦
- show (a : Completion α) + 0 = a by rw [← coe_zero, ← coe_add, add_zero]
- add_assoc := fun a b c ↦
- Completion.induction_on₃ a b c
- (isClosed_eq
- (continuous_map₂ (continuous_map₂ continuous_fst (by fun_prop)) (by fun_prop))
- (continuous_map₂ continuous_fst (continuous_map₂ (by fun_prop) (by fun_prop))))
- fun a b c ↦
- show (a : Completion α) + b + c = a + (b + c) by repeat' rw_mod_cast [add_assoc]
- nsmul := (· • ·)
- nsmul_zero := fun a ↦
- Completion.induction_on a (isClosed_eq continuous_map continuous_const) fun a ↦
- show 0 • (a : Completion α) = 0 by rw [← coe_smul, ← coe_zero, zero_smul]
- nsmul_succ := fun n a ↦
- Completion.induction_on a
- (isClosed_eq continuous_map <| continuous_map₂ continuous_map continuous_id) fun a ↦
- show (n + 1) • (a : Completion α) = n • (a : Completion α) + (a : Completion α) by
- rw [← coe_smul, succ_nsmul, coe_add, coe_smul] }
-
-instance : SubNegMonoid (Completion α) :=
- { (inferInstance : AddMonoid <| Completion α),
- (inferInstance : Neg <| Completion α),
- (inferInstance : Sub <| Completion α) with
- sub_eq_add_neg := fun a b ↦
- Completion.induction_on₂ a b
- (isClosed_eq (continuous_map₂ continuous_fst continuous_snd)
- (continuous_map₂ continuous_fst (Completion.continuous_map.comp continuous_snd)))
- fun a b ↦ mod_cast congr_arg ((↑) : α → Completion α) (sub_eq_add_neg a b)
- zsmul := (· • ·)
- zsmul_zero' := fun a ↦
- Completion.induction_on a (isClosed_eq continuous_map continuous_const) fun a ↦
- show (0 : ℤ) • (a : Completion α) = 0 by rw [← coe_smul, ← coe_zero, zero_smul]
- zsmul_succ' := fun n a ↦
- Completion.induction_on a
- (isClosed_eq continuous_map <| continuous_map₂ continuous_map continuous_id) fun a ↦
- show (n.succ : ℤ) • (a : Completion α) = _ by
- rw [← coe_smul, show (n.succ : ℤ) • a = (n : ℤ) • a + a from
- SubNegMonoid.zsmul_succ' n a, coe_add, coe_smul]
- zsmul_neg' := fun n a ↦
- Completion.induction_on a
- (isClosed_eq continuous_map <| Completion.continuous_map.comp continuous_map) fun a ↦
- show (Int.negSucc n) • (a : Completion α) = _ by
- rw [← coe_smul, show (Int.negSucc n) • a = -((n.succ : ℤ) • a) from
- SubNegMonoid.zsmul_neg' n a, coe_neg, coe_smul] }
-
-instance addGroup : AddGroup (Completion α) :=
- { (inferInstance : SubNegMonoid <| Completion α) with
- neg_add_cancel := fun a ↦
- Completion.induction_on a
- (isClosed_eq (continuous_map₂ Completion.continuous_map continuous_id) continuous_const)
- fun a ↦
- show -(a : Completion α) + a = 0 by
- rw_mod_cast [neg_add_cancel]
- rfl }
+instance : AddMonoid (Completion α) where
+ zero_add a :=
+ Completion.induction_on a
+ (isClosed_eq (continuous_map₂ continuous_const continuous_id) continuous_id) fun a ↦
+ show 0 + (a : Completion α) = a by rw [← coe_zero, ← coe_add, zero_add]
+ add_zero a :=
+ Completion.induction_on a
+ (isClosed_eq (continuous_map₂ continuous_id continuous_const) continuous_id) fun a ↦
+ show (a : Completion α) + 0 = a by rw [← coe_zero, ← coe_add, add_zero]
+ add_assoc := fun a b c ↦
+ Completion.induction_on₃ a b c
+ (isClosed_eq
+ (continuous_map₂ (continuous_map₂ continuous_fst (by fun_prop)) (by fun_prop))
+ (continuous_map₂ continuous_fst (continuous_map₂ (by fun_prop) (by fun_prop))))
+ fun a b c ↦
+ show (a : Completion α) + b + c = a + (b + c) by repeat' rw_mod_cast [add_assoc]
+ nsmul_zero a :=
+ Completion.induction_on a (isClosed_eq continuous_map continuous_const) fun a ↦
+ show 0 • (a : Completion α) = 0 by rw [← coe_smul, ← coe_zero, zero_smul]
+ nsmul_succ n a :=
+ Completion.induction_on a
+ (isClosed_eq continuous_map <| continuous_map₂ continuous_map continuous_id) fun a ↦
+ show (n + 1) • (a : Completion α) = n • (a : Completion α) + (a : Completion α) by
+ rw [← coe_smul, succ_nsmul, coe_add, coe_smul]
+
+instance : SubNegMonoid (Completion α) where
+ sub_eq_add_neg a b :=
+ Completion.induction_on₂ a b
+ (isClosed_eq (continuous_map₂ continuous_fst continuous_snd)
+ (continuous_map₂ continuous_fst (Completion.continuous_map.comp continuous_snd)))
+ fun a b ↦ mod_cast congr_arg ((↑) : α → Completion α) (sub_eq_add_neg a b)
+ zsmul_zero' a :=
+ Completion.induction_on a (isClosed_eq continuous_map continuous_const) fun a ↦
+ show (0 : ℤ) • (a : Completion α) = 0 by rw [← coe_smul, ← coe_zero, zero_smul]
+ zsmul_succ' n a :=
+ Completion.induction_on a
+ (isClosed_eq continuous_map <| continuous_map₂ continuous_map continuous_id) fun a ↦
+ show (n.succ : ℤ) • (a : Completion α) = _ by
+ rw [← coe_smul, show (n.succ : ℤ) • a = (n : ℤ) • a + a from
+ SubNegMonoid.zsmul_succ' n a, coe_add, coe_smul]
+ zsmul_neg' n a :=
+ Completion.induction_on a
+ (isClosed_eq continuous_map <| Completion.continuous_map.comp continuous_map) fun a ↦
+ show (Int.negSucc n) • (a : Completion α) = _ by
+ rw [← coe_smul, show (Int.negSucc n) • a = -((n.succ : ℤ) • a) from
+ SubNegMonoid.zsmul_neg' n a, coe_neg, coe_smul]
+
+instance addGroup : AddGroup (Completion α) where
+ neg_add_cancel a :=
+ Completion.induction_on a
+ (isClosed_eq (continuous_map₂ Completion.continuous_map continuous_id) continuous_const)
+ fun a ↦
+ show -(a : Completion α) + a = 0 by
+ rw_mod_cast [neg_add_cancel]
+ rfl
instance isUniformAddGroup : IsUniformAddGroup (Completion α) :=
⟨uniformContinuous_map₂ Sub.sub⟩
instance {M} [Monoid M] [DistribMulAction M α] [UniformContinuousConstSMul M α] :
- DistribMulAction M (Completion α) :=
- { (inferInstance : MulAction M <| Completion α) with
- smul_add := fun r x y ↦
- induction_on₂ x y
- (isClosed_eq ((continuous_fst.fun_add continuous_snd).fun_const_smul _)
- ((continuous_fst.fun_const_smul _).fun_add (continuous_snd.fun_const_smul _)))
- fun a b ↦ by simp only [← coe_add, ← coe_smul, smul_add]
- smul_zero := fun r ↦ by rw [← coe_zero, ← coe_smul, smul_zero r] }
+ DistribMulAction M (Completion α) where
+ smul_add r x y :=
+ induction_on₂ x y
+ (isClosed_eq ((continuous_fst.fun_add continuous_snd).fun_const_smul _)
+ ((continuous_fst.fun_const_smul _).fun_add (continuous_snd.fun_const_smul _)))
+ fun a b ↦ by simp only [← coe_add, ← coe_smul, smul_add]
+ smul_zero := fun r ↦ by rw [← coe_zero, ← coe_smul, smul_zero r]
/-- The map from a group to its completion as a group hom. -/
@[simps]
diff --git a/Mathlib/Topology/Algebra/Module/ContinuousLinearMap/Basic.lean b/Mathlib/Topology/Algebra/Module/ContinuousLinearMap/Basic.lean
index f0f7d0b1425042..b05b66b66b0ea6 100644
--- a/Mathlib/Topology/Algebra/Module/ContinuousLinearMap/Basic.lean
+++ b/Mathlib/Topology/Algebra/Module/ContinuousLinearMap/Basic.lean
@@ -450,7 +450,6 @@ instance : AddMonoid (M₁ →SL[σ₁₂] M₂) where
intros
ext
apply_rules [zero_add, add_assoc, add_zero, neg_add_cancel, add_comm]
- nsmul := (· • ·)
nsmul_zero f := by
ext
simp
@@ -883,7 +882,6 @@ instance : IsSubApply (M →SL[σ₁₂] M₂) M M₂ where
-- Todo: figure out how to use `FunLike.addCommGroup` here
instance addCommGroup : AddCommGroup (M →SL[σ₁₂] M₂) where
sub_eq_add_neg _ _ := by ext; apply sub_eq_add_neg
- zsmul := (· • ·)
zsmul_zero' f := by ext; simp
zsmul_succ' n f := by ext; simp [add_smul, add_comm]
zsmul_neg' n f := by ext; simp [add_smul]
diff --git a/MathlibTest/InstanceDiamonds.lean b/MathlibTest/InstanceDiamonds.lean
index 291be1c646dcb5..17ead3a7c8b831 100644
--- a/MathlibTest/InstanceDiamonds.lean
+++ b/MathlibTest/InstanceDiamonds.lean
@@ -24,7 +24,7 @@ section SMul
open scoped Polynomial
-example : (SubNegMonoid.toZSMul : SMul ℤ ℂ) = (Complex.SMul.instSMulRealComplex : SMul ℤ ℂ) := by
+example : (ZSMul.toSMul : SMul ℤ ℂ) = (Complex.SMul.instSMulRealComplex : SMul ℤ ℂ) := by
with_reducible_and_instances rfl
example : Module.restrictScalars ℝ ℂ ℂ = Complex.instModule := by
@@ -35,19 +35,19 @@ example : Algebra.restrictScalars ℝ ℂ ℂ = Complex.instAlgebraOfReal := by
rfl
example (α β : Type _) [AddMonoid α] [AddMonoid β] :
- (Prod.instSMul : SMul ℕ (α × β)) = AddMonoid.toNSMul := by
+ (Prod.instSMul : SMul ℕ (α × β)) = NSMul.toSMul := by
with_reducible_and_instances rfl
example (α β : Type _) [SubNegMonoid α] [SubNegMonoid β] :
- (Prod.instSMul : SMul ℤ (α × β)) = SubNegMonoid.toZSMul := by
+ (Prod.instSMul : SMul ℤ (α × β)) = ZSMul.toSMul := by
with_reducible_and_instances rfl
example (α : Type _) (β : α → Type _) [∀ a, AddMonoid (β a)] :
- (Pi.instSMul : SMul ℕ (∀ a, β a)) = AddMonoid.toNSMul := by
+ (Pi.instSMul : SMul ℕ (∀ a, β a)) = NSMul.toSMul := by
with_reducible_and_instances rfl
example (α : Type _) (β : α → Type _) [∀ a, SubNegMonoid (β a)] :
- (Pi.instSMul : SMul ℤ (∀ a, β a)) = SubNegMonoid.toZSMul := by
+ (Pi.instSMul : SMul ℤ (∀ a, β a)) = ZSMul.toSMul := by
with_reducible_and_instances rfl
namespace TensorProduct
From 5c985547b4e1ed70f8bc6e4d3a341912b221ff9c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mar=C3=ADa=20In=C3=A9s=20de=20Frutos-Fern=C3=A1ndez?=
<88536493+mariainesdff@users.noreply.github.com>
Date: Thu, 25 Jun 2026 19:06:42 +0000
Subject: [PATCH 0365/1300] feat(Algebra/Order/Antidiag/Prod): add
HasMulAntidiagonal (#38623)
We add a multiplicative version of `Finset.HasAntidiagonal`, called `Finset.HasMulAntidiagonal`. This requires some naming changes (since otherwise, for instance, there would be a clash between [Finset.swap_mem_mulAntidiagonal](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Data/Finset/MulAntidiagonal.html#Finset.swap_mem_mulAntidiagonal) and the corresponding lemma in Mathlib.Algebra.Order.Antidiag.Prod).
We made the following naming choices:
- Since the additive version will be much more commonly used, we still keep `Finset.HasAntidiagonal` for the additive version, and translate `mulAntidiagonal` to `antidiagonal` rather than to `addAntidiagonal`. Note that in particular this affects the API in Mathlib.Data.Finset.MulAntidiagonal, which used `addAntidiagonal`.
- We move most of the results in `Mathlib.Algebra.Order.Antidiag.Prod` into the `Has(Mul)Antidiagonal` namespace, so that e.g. `Finset.swap_mem_antidiagonal` and `Finset.HasAntidiagonal.mem_antidiagonal` can co-exist.
Note that some deprecation tags (e.g., for Finset.antidiagonal, Finset.antidiagonal.fst_le, ...) are intentionally missing. The reason is that `antidiagonal` and related lemmas are normally used inside an open `Finset` namespace, and since these declarations have moved into `Finset.HasAntidiagonal`, the deprecation causes errors which would require to explicitly add `HasAntidiagonal` in many places. An easier solution is just to open `Finset.HasAntidiagonal`, after which the preexisting code works.
Co-authored-by : @xgenereux
Co-authored-by: mariainesdff
---
.../Algebra/BigOperators/NatAntidiagonal.lean | 2 +
Mathlib/Algebra/Order/Antidiag/Pi.lean | 4 +-
Mathlib/Algebra/Order/Antidiag/Prod.lean | 205 ++++++++++++------
.../Enumerative/Catalan/Tree.lean | 2 +-
Mathlib/Data/Fin/Tuple/NatAntidiagonal.lean | 2 +-
Mathlib/Data/Finset/MulAntidiagonal.lean | 13 +-
Mathlib/Data/Finset/NatAntidiagonal.lean | 2 +
Mathlib/NumberTheory/Bernoulli.lean | 3 +-
Mathlib/RingTheory/Binomial.lean | 2 +-
Mathlib/RingTheory/HahnSeries/HEval.lean | 6 +-
.../RingTheory/HahnSeries/Multiplication.lean | 34 +--
.../RingTheory/HahnSeries/PowerSeries.lean | 8 +-
Mathlib/RingTheory/HahnSeries/Summable.lean | 10 +-
Mathlib/RingTheory/MvPowerSeries/Basic.lean | 8 +-
.../MvPowerSeries/LinearTopology.lean | 6 +-
.../RingTheory/PowerSeries/CoeffMulMem.lean | 4 +-
.../PowerSeries/WeierstrassPreparation.lean | 4 +-
Mathlib/Tactic/Translate/ToAdditive.lean | 1 +
.../Topology/Algebra/InfiniteSum/Ring.lean | 6 +-
19 files changed, 211 insertions(+), 111 deletions(-)
diff --git a/Mathlib/Algebra/BigOperators/NatAntidiagonal.lean b/Mathlib/Algebra/BigOperators/NatAntidiagonal.lean
index 3a1586e333a224..c3e776db575321 100644
--- a/Mathlib/Algebra/BigOperators/NatAntidiagonal.lean
+++ b/Mathlib/Algebra/BigOperators/NatAntidiagonal.lean
@@ -20,6 +20,8 @@ variable {M N : Type*} [CommMonoid M] [AddCommMonoid N]
namespace Finset
+open HasAntidiagonal
+
namespace Nat
theorem prod_antidiagonal_succ {n : ℕ} {f : ℕ × ℕ → M} :
diff --git a/Mathlib/Algebra/Order/Antidiag/Pi.lean b/Mathlib/Algebra/Order/Antidiag/Pi.lean
index b9f0282abd1dda..9202ca123bbffc 100644
--- a/Mathlib/Algebra/Order/Antidiag/Pi.lean
+++ b/Mathlib/Algebra/Order/Antidiag/Pi.lean
@@ -162,8 +162,8 @@ lemma pairwiseDisjoint_piAntidiag_map_addRightEmbedding (hi : i ∉ s) (n : μ)
(antidiagonal n : Set (μ × μ)).PairwiseDisjoint fun p ↦
map (addRightEmbedding fun j ↦ if j = i then p.1 else 0) (s.piAntidiag p.2) := by
rintro ⟨a, b⟩ hab ⟨c, d⟩ hcd
- simp only [ne_eq, antidiagonal_congr' hab hcd, disjoint_left, mem_map, mem_piAntidiag,
- addRightEmbedding_apply, not_exists, not_and, and_imp, forall_exists_index]
+ simp only [ne_eq, HasAntidiagonal.antidiagonal_congr' hab hcd, disjoint_left, mem_map,
+ mem_piAntidiag, addRightEmbedding_apply, not_exists, not_and, and_imp, forall_exists_index]
rintro hfg _ f rfl - rfl g rfl - hgf
exact hfg <| by simpa [sum_add_distrib, hi] using congr_arg (∑ j ∈ s, · j) hgf.symm
diff --git a/Mathlib/Algebra/Order/Antidiag/Prod.lean b/Mathlib/Algebra/Order/Antidiag/Prod.lean
index e53c0810966ba6..58db0a53be1126 100644
--- a/Mathlib/Algebra/Order/Antidiag/Prod.lean
+++ b/Mathlib/Algebra/Order/Antidiag/Prod.lean
@@ -5,6 +5,7 @@ Authors: Antoine Chambert-Loir, María Inés de Frutos-Fernández, Bhavik Mehta,
-/
module
+public import Mathlib.Algebra.Group.TypeTags.Basic
public import Mathlib.Algebra.Order.Monoid.Canonical.Defs
public import Mathlib.Algebra.Order.Sub.Defs
public import Mathlib.Data.Finset.Basic
@@ -16,6 +17,10 @@ We define a type class `Finset.HasAntidiagonal A` which contains a function
`antidiagonal : A → Finset (A × A)` such that `antidiagonal n`
is the finset of all pairs adding to `n`, as witnessed by `mem_antidiagonal`.
+Analogously, the type class `Finset.HasMulAntidiagonal A` contains a function
+`mulAntidiagonal : A → Finset (A × A)` such that `mulAntidiagonal n`
+is the finset of all pairs multiplying to `n`, as witnessed by `mem_mulAntidiagonal`.
+
When `A` is a canonically ordered additive monoid with locally finite order
this typeclass can be instantiated with `Finset.antidiagonalOfLocallyFinite`.
This applies in particular when `A` is `ℕ`, more generally or `σ →₀ ℕ`,
@@ -27,8 +32,9 @@ and any finiteness condition would be OK.)
For computational reasons it is better to manually provide instances for `ℕ`
and `σ →₀ ℕ`, to avoid quadratic runtime performance.
-These instances are provided as `Finset.Nat.instHasAntidiagonal` and `Finsupp.instHasAntidiagonal`.
-This is why `Finset.antidiagonalOfLocallyFinite` is an `abbrev` and not an `instance`.
+These instances are provided as `Finset.Nat.instHasAntidiagonal` and
+`Finsupp.instHasAntidiagonal`.
+This is why `Finset.mulAntidiagonalOfLocallyFinite` is an `abbrev` and not an `instance`.
This definition does not exactly match with that of `Multiset.antidiagonal`
defined in `Mathlib/Data/Multiset/Antidiagonal.lean`, because of the multiplicities.
@@ -45,8 +51,7 @@ def s : Multiset ℕ := {0, 0, 0}
## TODO
-* Define `HasMulAntidiagonal` (for monoids).
- For `PNat`, we will recover the set of divisors of a strictly positive integer.
+* For `PNat`, `HasMulAntidiagonal` will recover the set of divisors of a strictly positive integer.
-/
@[expose] public section
@@ -55,9 +60,10 @@ open Function
namespace Finset
-/-- The class of additive monoids with an antidiagonal -/
+/-- The class of additive monoids with an antidiagonal. -/
class HasAntidiagonal (A : Type*) [AddMonoid A] where
- /-- The antidiagonal of an element `n` is the finset of pairs `(i, j)` such that `i + j = n`. -/
+ /-- The antidiagonal of an element `n` is the finset of pairs `(i, j)` such that
+ `i + j = n`. -/
antidiagonal : A → Finset (A × A)
/-- A pair belongs to `antidiagonal n` iff the sum of its components is equal to `n`. -/
mem_antidiagonal {n} {a} : a ∈ antidiagonal n ↔ a.fst + a.snd = n
@@ -66,88 +72,127 @@ export HasAntidiagonal (antidiagonal mem_antidiagonal)
attribute [simp] mem_antidiagonal
+/-- The class of (multiplicative) monoids with a mulAntidiagonal. -/
+class HasMulAntidiagonal (A : Type*) [Monoid A] where
+ /-- The mulAntidiagonal of an element `n` is the finset of pairs `(i, j)` such that
+ `i * j = n`. -/
+ mulAntidiagonal : A → Finset (A × A)
+ /-- A pair belongs to `mulAntidiagonal n` iff the product of its components is equal to `n`. -/
+ mem_mulAntidiagonal {n} {a} : a ∈ mulAntidiagonal n ↔ a.fst * a.snd = n
+
+attribute [to_additive] HasMulAntidiagonal
+
+export HasMulAntidiagonal (mulAntidiagonal mem_mulAntidiagonal)
+
+attribute [simp] HasMulAntidiagonal.mem_mulAntidiagonal
+
variable {A : Type*}
-/-- All `HasAntidiagonal` instances are equal -/
-instance [AddMonoid A] : Subsingleton (HasAntidiagonal A) where
+namespace HasMulAntidiagonal
+
+/-- All `HasMulAntidiagonal` instances are equal -/
+@[to_additive /-- All `HasAntidiagonal` instances are equal -/]
+instance [Monoid A] : Subsingleton (HasMulAntidiagonal A) where
allEq := by
rintro ⟨a, ha⟩ ⟨b, hb⟩
congr with n xy
rw [ha, hb]
--- The goal of this lemma is to allow to rewrite antidiagonal
+-- The goal of this lemma is to allow to rewrite mulAntidiagonal/antidiagonal
-- when the decidability instances obfuscate Lean
set_option linter.overlappingInstances false in
-lemma hasAntidiagonal_congr (A : Type*) [AddMonoid A]
- [H1 : HasAntidiagonal A] [H2 : HasAntidiagonal A] :
- H1.antidiagonal = H2.antidiagonal := by congr!; subsingleton
+@[to_additive]
+lemma congr (A : Type*) [Monoid A]
+ [H1 : HasMulAntidiagonal A] [H2 : HasMulAntidiagonal A] :
+ H1.mulAntidiagonal = H2.mulAntidiagonal := by congr!; subsingleton
-theorem swap_mem_antidiagonal [AddCommMonoid A] [HasAntidiagonal A] {n : A} {xy : A × A} :
- xy.swap ∈ antidiagonal n ↔ xy ∈ antidiagonal n := by
- simp [add_comm]
+@[to_additive]
+theorem swap_mem_mulAntidiagonal [CommMonoid A] [HasMulAntidiagonal A] {n : A} {xy : A × A} :
+ xy.swap ∈ mulAntidiagonal n ↔ xy ∈ mulAntidiagonal n := by
+ simp [mul_comm]
-@[simp] theorem map_prodComm_antidiagonal [AddCommMonoid A] [HasAntidiagonal A] {n : A} :
- (antidiagonal n).map (Equiv.prodComm A A) = antidiagonal n :=
- Finset.ext fun ⟨a, b⟩ => by simp [add_comm]
+@[to_additive (attr := simp) map_prodComm_antidiagonal]
+theorem map_prodComm_mulAntidiagonal [CommMonoid A] [HasMulAntidiagonal A] {n : A} :
+ (mulAntidiagonal n).map (Equiv.prodComm A A) = mulAntidiagonal n :=
+ Finset.ext fun ⟨a, b⟩ => by simp [mul_comm]
-/-- See also `Finset.map_prodComm_antidiagonal`. -/
-@[simp] theorem map_swap_antidiagonal [AddCommMonoid A] [HasAntidiagonal A] {n : A} :
- (antidiagonal n).map ⟨Prod.swap, Prod.swap_injective⟩ = antidiagonal n :=
- map_prodComm_antidiagonal
+/-- See also `Finset.map_prodComm_mulAntidiagonal`. -/
+@[to_additive (attr := simp)]
+theorem map_swap_mulAntidiagonal [CommMonoid A] [HasMulAntidiagonal A] {n : A} :
+ (mulAntidiagonal n).map ⟨Prod.swap, Prod.swap_injective⟩ = mulAntidiagonal n :=
+ map_prodComm_mulAntidiagonal
-section AddCancelMonoid
-variable [AddCancelMonoid A] [HasAntidiagonal A] {p q : A × A} {n : A}
+section CancelMonoid
+variable [CancelMonoid A] [HasMulAntidiagonal A] {p q : A × A} {n : A}
+
+/-- A point in the mulAntidiagonal is determined by its first coordinate.
+
+See also `Finset.mulAntidiagonal_congr'`. -/
+@[to_additive
/-- A point in the antidiagonal is determined by its first coordinate.
-See also `Finset.antidiagonal_congr'`. -/
-theorem antidiagonal_congr (hp : p ∈ antidiagonal n) (hq : q ∈ antidiagonal n) :
+See also `Finset.antidiagonal_congr'`. -/]
+theorem mulAntidiagonal_congr (hp : p ∈ mulAntidiagonal n) (hq : q ∈ mulAntidiagonal n) :
p = q ↔ p.1 = q.1 := by
- refine ⟨congr_arg Prod.fst, fun h ↦ Prod.ext h ((add_right_inj q.fst).mp ?_)⟩
- rw [mem_antidiagonal] at hp hq
+ refine ⟨congr_arg Prod.fst, fun h ↦ Prod.ext h ((mul_right_inj q.fst).mp ?_)⟩
+ rw [mem_mulAntidiagonal] at hp hq
rw [hq, ← h, hp]
+/-- A point in the mulAntidiagonal is determined by its first co-ordinate (subtype version of
+`Finset.mulAntidiagonal_congr`). This lemma is used by the `ext` tactic. -/
+@[to_additive (attr := ext)
/-- A point in the antidiagonal is determined by its first co-ordinate (subtype version of
-`Finset.antidiagonal_congr`). This lemma is used by the `ext` tactic. -/
-@[ext] theorem antidiagonal_subtype_ext {p q : antidiagonal n} (h : p.val.1 = q.val.1) : p = q :=
- Subtype.ext ((antidiagonal_congr p.prop q.prop).mpr h)
+`Finset.antidiagonal_congr`). This lemma is used by the `ext` tactic. -/]
+theorem mulAntidiagonal_subtype_ext {p q : mulAntidiagonal n} (h : p.val.1 = q.val.1) : p = q :=
+ Subtype.ext ((mulAntidiagonal_congr p.prop q.prop).mpr h)
+
+end CancelMonoid
-end AddCancelMonoid
+section CancelCommMonoid
+variable [CancelCommMonoid A] [HasMulAntidiagonal A] {p q : A × A} {n : A}
-section AddCancelCommMonoid
-variable [AddCancelCommMonoid A] [HasAntidiagonal A] {p q : A × A} {n : A}
+/-- A point in the mulAntidiagonal is determined by its second coordinate.
-/-- A point in the antidiagonal is determined by its second coordinate.
+See also `Finset.mulAntidiagonal_congr`. -/
+@[to_additive /-- A point in the antidiagonal is determined by its second coordinate.
-See also `Finset.antidiagonal_congr`. -/
-lemma antidiagonal_congr' (hp : p ∈ antidiagonal n) (hq : q ∈ antidiagonal n) :
+See also `Finset.antidiagonal_congr`. -/]
+lemma mulAntidiagonal_congr' (hp : p ∈ mulAntidiagonal n) (hq : q ∈ mulAntidiagonal n) :
p = q ↔ p.2 = q.2 := by
rw [← Prod.swap_inj]
- exact antidiagonal_congr (swap_mem_antidiagonal.2 hp) (swap_mem_antidiagonal.2 hq)
+ exact mulAntidiagonal_congr (swap_mem_mulAntidiagonal.2 hp) (swap_mem_mulAntidiagonal.2 hq)
-end AddCancelCommMonoid
+end CancelCommMonoid
-section CanonicallyOrderedAdd
-variable [AddCommMonoid A] [PartialOrder A] [CanonicallyOrderedAdd A] [HasAntidiagonal A]
+section CanonicallyOrderedMul
-@[simp]
-theorem antidiagonal_zero : antidiagonal (0 : A) = {(0, 0)} := by
+variable [CommMonoid A] [PartialOrder A] [CanonicallyOrderedMul A] [HasMulAntidiagonal A]
+
+@[to_additive (attr := simp)]
+theorem mulAntidiagonal_one : mulAntidiagonal (1 : A) = {(1, 1)} := by
ext ⟨x, y⟩
simp
-theorem antidiagonal.fst_le {n : A} {kl : A × A} (hlk : kl ∈ antidiagonal n) : kl.1 ≤ n := by
- rw [le_iff_exists_add]
+@[to_additive]
+theorem mulAntidiagonal.fst_le {n : A} {kl : A × A} (hlk : kl ∈ mulAntidiagonal n) : kl.1 ≤ n := by
+ rw [le_iff_exists_mul]
use kl.2
- rwa [mem_antidiagonal, eq_comm] at hlk
+ rwa [mem_mulAntidiagonal, eq_comm] at hlk
-theorem antidiagonal.snd_le {n : A} {kl : A × A} (hlk : kl ∈ antidiagonal n) : kl.2 ≤ n := by
- rw [le_iff_exists_add]
+@[to_additive]
+theorem mulAntidiagonal.snd_le {n : A} {kl : A × A} (hlk : kl ∈ mulAntidiagonal n) : kl.2 ≤ n := by
+ rw [le_iff_exists_mul]
use kl.1
- rwa [mem_antidiagonal, eq_comm, add_comm] at hlk
+ rwa [mem_mulAntidiagonal, eq_comm, mul_comm] at hlk
+
+end CanonicallyOrderedMul
-end CanonicallyOrderedAdd
+end HasMulAntidiagonal
+namespace HasAntidiagonal
section OrderedSub
+
variable [AddCommMonoid A] [PartialOrder A] [CanonicallyOrderedAdd A] [Sub A] [OrderedSub A]
variable [AddLeftReflectLE A]
variable [HasAntidiagonal A]
@@ -173,30 +218,66 @@ theorem filter_snd_eq_antidiagonal (n m : A) [DecidablePred (· = m)] [Decidable
end OrderedSub
-/-- The disjoint union of antidiagonals `Σ (n : A), antidiagonal n` is equivalent to the product
- `A × A`. This is such an equivalence, obtained by mapping `(n, (k, l))` to `(k, l)`. -/
-@[simps]
-def sigmaAntidiagonalEquivProd [AddMonoid A] [HasAntidiagonal A] :
- (Σ n : A, antidiagonal n) ≃ A × A where
+end HasAntidiagonal
+
+namespace HasMulAntidiagonal
+
+/-- The disjoint union of mulAntidiagonals `Σ (n : A), mulAntidiagonal n` is equivalent to the
+ product `A × A`. This is such an equivalence, obtained by mapping `(n, (k, l))` to `(k, l)`. -/
+@[to_additive (attr := simps) sigmaAntidiagonalEquivProd
+/-- The disjoint union of antidiagonals `Σ (n : A), antidiagonal n` is equivalent to the
+ product `A × A`. This is such an equivalence, obtained by mapping `(n, (k, l))` to `(k, l)`. -/]
+def sigmaMulAntidiagonalEquivProd [Monoid A] [HasMulAntidiagonal A] :
+ (Σ n : A, mulAntidiagonal n) ≃ A × A where
toFun x := x.2
- invFun x := ⟨x.1 + x.2, x, mem_antidiagonal.mpr rfl⟩
+ invFun x := ⟨x.1 * x.2, x, mem_mulAntidiagonal.mpr rfl⟩
left_inv := by
rintro ⟨n, ⟨k, l⟩, h⟩
- rw [mem_antidiagonal] at h
+ rw [mem_mulAntidiagonal] at h
exact Sigma.subtype_ext h rfl
+section
+
variable {A : Type*}
- [AddCommMonoid A] [PartialOrder A] [CanonicallyOrderedAdd A]
+ [CommMonoid A] [PartialOrder A] [CanonicallyOrderedMul A]
[LocallyFiniteOrderBot A] [DecidableEq A]
+/-- In a canonically ordered multiplicative monoid, the mulAntidiagonal can be constructed by
+filtering.
+
+Note that this is not an instance, as for sometimes a more efficient algorithm is available. -/
+@[to_additive
/-- In a canonically ordered additive monoid, the antidiagonal can be construct by filtering.
-Note that this is not an instance, as for some times a more efficient algorithm is available. -/
-abbrev antidiagonalOfLocallyFinite : HasAntidiagonal A where
- antidiagonal n := {uv ∈ Iic n ×ˢ Iic n | uv.fst + uv.snd = n}
- mem_antidiagonal {n} {a} := by
+Note that this is not an instance, as for some times a more efficient algorithm is available. -/]
+abbrev mulAntidiagonalOfLocallyFinite : HasMulAntidiagonal A where
+ mulAntidiagonal n := {uv ∈ Iic n ×ˢ Iic n | uv.fst * uv.snd = n}
+ mem_mulAntidiagonal {n} {a} := by
simp only [mem_filter, and_iff_right_iff_imp]
intro h
simp [← h]
+end
+
+section Multiplicative
+
+open Multiplicative
+
+variable {A : Type*} [AddMonoid A] [HasAntidiagonal A]
+
+instance : HasMulAntidiagonal (Multiplicative A) where
+ mulAntidiagonal a :=
+ (antidiagonal (toAdd a)).map ⟨fun p ↦ (ofAdd p.1 , ofAdd p.2), fun _ _ h ↦ by aesop⟩
+ mem_mulAntidiagonal {a p} := by aesop
+
+lemma mem_mulAntidiagonal_ofAdd_iff_toAdd_mem_antidiagonal {a : A}
+ {p : Multiplicative A × Multiplicative A} :
+ p ∈ mulAntidiagonal (ofAdd a) ↔ (toAdd p.1, toAdd p.2) ∈ antidiagonal a := by
+ simp only [mem_mulAntidiagonal, mem_antidiagonal]
+ rw [Multiplicative.ext_iff, toAdd_mul, toAdd_ofAdd]
+
+end Multiplicative
+
+end HasMulAntidiagonal
+
end Finset
diff --git a/Mathlib/Combinatorics/Enumerative/Catalan/Tree.lean b/Mathlib/Combinatorics/Enumerative/Catalan/Tree.lean
index c29da1292184a8..007aa07a4cbc53 100644
--- a/Mathlib/Combinatorics/Enumerative/Catalan/Tree.lean
+++ b/Mathlib/Combinatorics/Enumerative/Catalan/Tree.lean
@@ -26,7 +26,7 @@ import Mathlib.Tactic.Field
open Finset
-open Finset.antidiagonal (fst_le snd_le)
+open Finset.HasAntidiagonal.antidiagonal (fst_le snd_le)
namespace BinaryTree
diff --git a/Mathlib/Data/Fin/Tuple/NatAntidiagonal.lean b/Mathlib/Data/Fin/Tuple/NatAntidiagonal.lean
index 980e2e2030d76d..9a7d516485437b 100644
--- a/Mathlib/Data/Fin/Tuple/NatAntidiagonal.lean
+++ b/Mathlib/Data/Fin/Tuple/NatAntidiagonal.lean
@@ -234,7 +234,7 @@ section EquivProd
/-- The disjoint union of antidiagonal tuples `Σ n, antidiagonalTuple k n` is equivalent to the
`k`-tuple `Fin k → ℕ`. This is such an equivalence, obtained by mapping `(n, x)` to `x`.
-This is the tuple version of `Finset.sigmaAntidiagonalEquivProd`. -/
+This is the tuple version of `Finset..HasAntidiagonal.sigmaAntidiagonalEquivProd`. -/
@[simps]
def sigmaAntidiagonalTupleEquivTuple (k : ℕ) : (Σ n, antidiagonalTuple k n) ≃ (Fin k → ℕ) where
toFun x := x.2
diff --git a/Mathlib/Data/Finset/MulAntidiagonal.lean b/Mathlib/Data/Finset/MulAntidiagonal.lean
index 0390ccad26f580..f3da3e9b6a94e3 100644
--- a/Mathlib/Data/Finset/MulAntidiagonal.lean
+++ b/Mathlib/Data/Finset/MulAntidiagonal.lean
@@ -57,7 +57,7 @@ variable [CommMonoid α] [PartialOrder α] [IsOrderedCancelMonoid α]
/-- `Finset.mulAntidiagonal hs ht a` is the set of all pairs of an element in `s` and an
element in `t` that multiply to `a`, but its construction requires proofs that `s` and `t` are
well-ordered. -/
-@[to_additive /-- `Finset.addAntidiagonal hs ht a` is the set of all pairs of an element in
+@[to_additive /-- `Finset.antidiagonal hs ht a` is the set of all pairs of an element in
`s` and an element in `t` that add to `a`, but its construction requires proofs that `s` and `t` are
well-ordered. -/]
noncomputable def mulAntidiagonal : Finset (α × α) :=
@@ -108,4 +108,15 @@ theorem mulAntidiagonal_min_mul_min {α} [CommMonoid α] [LinearOrder α] [IsOrd
· rintro ⟨rfl, rfl⟩
exact ⟨hs.min_mem _, ht.min_mem _, rfl⟩
+@[deprecated (since := "2026-06-08")] alias addAntidiagonal := antidiagonal
+@[deprecated (since := "2026-06-08")] alias mem_addAntidiagonal := mem_antidiagonal
+@[deprecated (since := "2026-06-08")] alias addAntidiagonal_mono_left := antidiagonal_mono_left
+@[deprecated (since := "2026-06-08")] alias addAntidiagonal_mono_right := antidiagonal_mono_right
+@[deprecated (since := "2026-06-08")] alias swap_mem_addAntidiagonal := swap_mem_antidiagonal
+@[deprecated (since := "2026-06-08")]
+alias support_addAntidiagonal_subset_add := support_antidiagonal_subset_add
+@[deprecated (since := "2026-06-08")]
+alias isPWO_support_addAntidiagonal := isPWO_support_antidiagonal
+@[deprecated (since := "2026-06-08")] alias addAntidiagonal_min_mul_min := antidiagonal_min_add_min
+
end Finset
diff --git a/Mathlib/Data/Finset/NatAntidiagonal.lean b/Mathlib/Data/Finset/NatAntidiagonal.lean
index a9bc329d17e3ee..9bfb92c051c2aa 100644
--- a/Mathlib/Data/Finset/NatAntidiagonal.lean
+++ b/Mathlib/Data/Finset/NatAntidiagonal.lean
@@ -30,6 +30,8 @@ open Function
namespace Finset
+open Finset.HasAntidiagonal
+
namespace Nat
/-- The antidiagonal of a natural number `n` is
diff --git a/Mathlib/NumberTheory/Bernoulli.lean b/Mathlib/NumberTheory/Bernoulli.lean
index 1e723409e73cf4..9a289e32988008 100644
--- a/Mathlib/NumberTheory/Bernoulli.lean
+++ b/Mathlib/NumberTheory/Bernoulli.lean
@@ -262,7 +262,8 @@ theorem bernoulli_spec' (n : ℕ) :
convert! eq_sub_of_add_eq' H using 1
· refine sum_congr rfl fun p h => ?_
obtain ⟨h', h''⟩ : p ∈ _ ∧ p ≠ _ := by rwa [mem_sdiff, mem_singleton] at h
- simp [bernoulli_eq_bernoulli'_of_ne_one ((not_congr (antidiagonal_congr h' h₁)).mp h'')]
+ simp [bernoulli_eq_bernoulli'_of_ne_one
+ ((not_congr (HasAntidiagonal.antidiagonal_congr h' h₁)).mp h'')]
· simp [field, h₃]
norm_num
diff --git a/Mathlib/RingTheory/Binomial.lean b/Mathlib/RingTheory/Binomial.lean
index 48cf0d55e89325..ede4089fd6784e 100644
--- a/Mathlib/RingTheory/Binomial.lean
+++ b/Mathlib/RingTheory/Binomial.lean
@@ -523,7 +523,7 @@ theorem add_choose_eq [Ring R] [BinomialRing R] {r s : R} (k : ℕ) (h : Commute
← descPochhammer_eq_factorial_smul_choose, smul_sum, descPochhammer_smeval_add _ h]
refine sum_congr rfl ?_
intro x hx
- rw [← Nat.choose_mul_factorial_mul_factorial (antidiagonal.fst_le hx),
+ rw [← Nat.choose_mul_factorial_mul_factorial (HasAntidiagonal.antidiagonal.fst_le hx),
tsub_eq_of_eq_add_rev (List.Nat.mem_antidiagonal.mp hx).symm, mul_assoc, nsmul_eq_mul,
Nat.cast_mul, Nat.cast_mul, ← mul_assoc _ (x.1.factorial : R), mul_assoc _ (x.2.factorial : R),
← mul_assoc (x.2.factorial : R), Nat.cast_commute x.2.factorial,
diff --git a/Mathlib/RingTheory/HahnSeries/HEval.lean b/Mathlib/RingTheory/HahnSeries/HEval.lean
index c5d26e685b07bb..1fd494bca65b66 100644
--- a/Mathlib/RingTheory/HahnSeries/HEval.lean
+++ b/Mathlib/RingTheory/HahnSeries/HEval.lean
@@ -94,7 +94,7 @@ theorem support_powerSeriesFamily_subset {x : V⟦Γ⟧} (a b : PowerSeries R) (
((powers x) n).coeff g ≠ 0 := by
refine exists_ne_zero_of_sum_ne_zero ?_
simpa [PowerSeries.coeff_mul, sum_smul, mul_smul, h] using hn
- simp only [powers_of_orderTop_pos h, mem_antidiagonal] at he
+ simp only [powers_of_orderTop_pos h, HasAntidiagonal.mem_antidiagonal] at he
obtain ⟨c, hcn, hc⟩ := he
simp only [coe_image, Set.Finite.coe_toFinset, Set.mem_image]
use c
@@ -129,7 +129,7 @@ theorem hsum_powerSeriesFamily_mul {x : V⟦Γ⟧} (a b : PowerSeries R) :
(fun _ _ => by simp [smul_smul, mul_comm, pow_add])).symm
· intro ij hij
simp only [coe_sigma, coe_image, Set.mem_sigma_iff, Set.mem_image, Prod.exists, mem_coe,
- mem_antidiagonal, and_true]
+ HasAntidiagonal.mem_antidiagonal, and_true]
use ij.1, ij.2
simp_all
· intro i hi his
@@ -140,7 +140,7 @@ theorem hsum_powerSeriesFamily_mul {x : V⟦Γ⟧} (a b : PowerSeries R) :
simp only [powers_of_orderTop_pos h, Set.Finite.coe_toFinset, Set.mem_image,
Function.mem_support, ne_eq, Prod.exists, not_exists, not_and] at his
exact his m n
- simp only [mem_sigma, mem_antidiagonal] at hi
+ simp only [mem_sigma, HasAntidiagonal.mem_antidiagonal] at hi
rw [mul_comm ((PowerSeries.coeff i.snd.1) a), ← hi.2, mul_smul, pow_add]
exact hisc i.snd.1 i.snd.2 <| Sigma.eq hi.2 (by simp)
· simp only [h, not_false_eq_true, powerSeriesFamily_of_not_orderTop_pos,
diff --git a/Mathlib/RingTheory/HahnSeries/Multiplication.lean b/Mathlib/RingTheory/HahnSeries/Multiplication.lean
index b79bc4fe3c4980..f5c2ba2c1e6c99 100644
--- a/Mathlib/RingTheory/HahnSeries/Multiplication.lean
+++ b/Mathlib/RingTheory/HahnSeries/Multiplication.lean
@@ -400,7 +400,7 @@ theorem of_symm_smul_of_eq_mul [NonUnitalNonAssocSemiring R] {x y : R⟦Γ⟧} :
theorem coeff_mul [NonUnitalNonAssocSemiring R] {x y : R⟦Γ⟧} {a : Γ} :
(x * y).coeff a =
- ∑ ij ∈ addAntidiagonal x.isPWO_support y.isPWO_support a, x.coeff ij.fst * y.coeff ij.snd :=
+ ∑ ij ∈ antidiagonal x.isPWO_support y.isPWO_support a, x.coeff ij.fst * y.coeff ij.snd :=
rfl
protected lemma map_mul [NonUnitalNonAssocSemiring R] [NonUnitalNonAssocSemiring S] (f : R →ₙ+* S)
@@ -408,9 +408,9 @@ protected lemma map_mul [NonUnitalNonAssocSemiring R] [NonUnitalNonAssocSemiring
ext
simp only [map_coeff, coeff_mul, map_sum, map_mul]
refine Eq.symm (sum_subset (fun gh hgh => ?_) (fun gh hgh hz => ?_))
- · simp_all only [mem_addAntidiagonal, mem_support, map_coeff, ne_eq, and_true]
+ · simp_all only [mem_antidiagonal, mem_support, map_coeff, ne_eq, and_true]
exact ⟨fun h => hgh.1 (map_zero f ▸ congrArg f h), fun h => hgh.2.1 (map_zero f ▸ congrArg f h)⟩
- · simp_all only [mem_addAntidiagonal, mem_support, ne_eq, map_coeff, and_true,
+ · simp_all only [mem_antidiagonal, mem_support, ne_eq, map_coeff, and_true,
not_and, not_not]
by_cases h : f (x.coeff gh.1) = 0
· exact mul_eq_zero_of_left h (f (y.coeff gh.2))
@@ -419,13 +419,13 @@ protected lemma map_mul [NonUnitalNonAssocSemiring R] [NonUnitalNonAssocSemiring
theorem coeff_mul_left' [NonUnitalNonAssocSemiring R] {x y : R⟦Γ⟧} {a : Γ} {s : Set Γ}
(hs : s.IsPWO) (hxs : x.support ⊆ s) :
(x * y).coeff a =
- ∑ ij ∈ addAntidiagonal hs y.isPWO_support a, x.coeff ij.fst * y.coeff ij.snd :=
+ ∑ ij ∈ antidiagonal hs y.isPWO_support a, x.coeff ij.fst * y.coeff ij.snd :=
HahnModule.coeff_smul_left hs hxs
theorem coeff_mul_right' [NonUnitalNonAssocSemiring R] {x y : R⟦Γ⟧} {a : Γ} {s : Set Γ}
(hs : s.IsPWO) (hys : y.support ⊆ s) :
(x * y).coeff a =
- ∑ ij ∈ addAntidiagonal x.isPWO_support hs a, x.coeff ij.fst * y.coeff ij.snd :=
+ ∑ ij ∈ antidiagonal x.isPWO_support hs a, x.coeff ij.fst * y.coeff ij.snd :=
HahnModule.coeff_smul_right hs hys
instance [NonUnitalNonAssocSemiring R] : Distrib R⟦Γ⟧ where
@@ -453,14 +453,14 @@ theorem coeff_mul_single_add [NonUnitalNonAssocSemiring R] {r : R} {x : R⟦Γ
rw [sum_congr _ fun _ _ => rfl, sum_empty]
ext ⟨a1, a2⟩
simp only [notMem_empty, not_and, Set.mem_singleton_iff,
- mem_addAntidiagonal, iff_false]
+ mem_antidiagonal, iff_false]
rintro h2 rfl h1
rw [← add_right_cancel h1] at hx
exact h2 hx
trans ∑ ij ∈ {(a, b)}, x.coeff ij.fst * (single b r).coeff ij.snd
· apply sum_congr _ fun _ _ => rfl
ext ⟨a1, a2⟩
- simp only [Set.mem_singleton_iff, Prod.mk_inj, mem_addAntidiagonal, mem_singleton]
+ simp only [Set.mem_singleton_iff, Prod.mk_inj, mem_antidiagonal, mem_singleton]
constructor
· rintro ⟨_, rfl, h1⟩
exact ⟨add_right_cancel h1, rfl⟩
@@ -621,7 +621,7 @@ instance [NonUnitalCommSemiring R] : NonUnitalCommSemiring R⟦Γ⟧ where
mul_comm x y := by
ext
simp_rw [coeff_mul, mul_comm]
- exact Finset.sum_equiv (Equiv.prodComm _ _) (fun _ ↦ swap_mem_addAntidiagonal.symm) <| by simp
+ exact Finset.sum_equiv (Equiv.prodComm _ _) (fun _ ↦ swap_mem_antidiagonal.symm) <| by simp
instance [CommSemiring R] : CommSemiring R⟦Γ⟧ where
instance [NonUnitalNonAssocRing R] : NonUnitalNonAssocRing R⟦Γ⟧ where
@@ -780,7 +780,7 @@ theorem single_mul_single {a b : Γ} {r s : R} :
· rw [h, coeff_mul_single_add]
simp
· rw [coeff_single_of_ne h, coeff_mul, sum_eq_zero]
- simp_rw [mem_addAntidiagonal]
+ simp_rw [mem_antidiagonal]
rintro ⟨y, z⟩ ⟨hy, hz, rfl⟩
rw [eq_of_mem_support_single hy, eq_of_mem_support_single hz] at h
exact (h rfl).elim
@@ -863,13 +863,13 @@ theorem embDomain_mul [NonUnitalNonAssocSemiring R] (f : Γ ↪o Γ')
simp only [coeff_mul, embDomain_coeff]
trans
∑ ij ∈
- (addAntidiagonal x.isPWO_support y.isPWO_support g).map
+ (antidiagonal x.isPWO_support y.isPWO_support g).map
(f.toEmbedding.prodMap f.toEmbedding),
(embDomain f x).coeff ij.1 * (embDomain f y).coeff ij.2
· simp
apply sum_subset
· rintro ⟨i, j⟩ hij
- simp only [mem_map, mem_addAntidiagonal,
+ simp only [mem_map, mem_antidiagonal,
Function.Embedding.coe_prodMap, mem_support, Prod.exists] at hij
obtain ⟨i, j, ⟨hx, hy, rfl⟩, rfl, rfl⟩ := hij
simp [hx, hy, hf]
@@ -877,9 +877,9 @@ theorem embDomain_mul [NonUnitalNonAssocSemiring R] (f : Γ ↪o Γ')
contrapose! h2
obtain ⟨i, _, rfl⟩ := support_embDomain_subset (ne_zero_and_ne_zero_of_mul h2).1
obtain ⟨j, _, rfl⟩ := support_embDomain_subset (ne_zero_and_ne_zero_of_mul h2).2
- simp only [mem_map, mem_addAntidiagonal,
+ simp only [mem_map, mem_antidiagonal,
Function.Embedding.coe_prodMap, mem_support, Prod.exists]
- simp only [mem_addAntidiagonal, embDomain_coeff, mem_support, ← hf,
+ simp only [mem_antidiagonal, embDomain_coeff, mem_support, ← hf,
OrderEmbedding.eq_iff_eq] at h1
exact ⟨i, j, h1, rfl⟩
· rw [embDomain_notin_range hg, eq_comm]
@@ -975,11 +975,11 @@ instance [IsCancelAdd R] [IsCancelMulZero R] : IsCancelMulZero R⟦Γ⟧ where
have ha : y.coeff a ≠ z.coeff a := this.min_mem hyz
refine ⟨x.order + a, ?_⟩
rwa [coeff_mul, coeff_mul, sum_subset subset_union_left,
- sum_subset (s₁ := addAntidiagonal _ _ _) subset_union_right,
+ sum_subset (s₁ := antidiagonal _ _ _) subset_union_right,
sum_eq_sum_iff_single (i := (x.order, a)), mul_right_inj' (coeff_order_eq_zero.not.2 hx)]
· simp [hx]
grind
- · simp +contextual only [mem_union, mem_addAntidiagonal, mul_eq_mul_left_iff, Prod.mk.injEq,
+ · simp +contextual only [mem_union, mem_antidiagonal, mul_eq_mul_left_iff, Prod.mk.injEq,
ne_eq, ← and_or_left, ← or_and_right, or_false, and_imp, Prod.forall, mem_support, not_and]
rintro b c hxb - hbc hbc'
contrapose! hbc'
@@ -997,11 +997,11 @@ instance [IsCancelAdd R] [IsCancelMulZero R] : IsCancelMulZero R⟦Γ⟧ where
have ha : y.coeff a ≠ z.coeff a := this.min_mem hyz
refine ⟨a + x.order, ?_⟩
rwa [coeff_mul, coeff_mul, sum_subset subset_union_left,
- sum_subset (s₁ := addAntidiagonal _ _ _) subset_union_right,
+ sum_subset (s₁ := antidiagonal _ _ _) subset_union_right,
sum_eq_sum_iff_single (i := (a, x.order)), mul_left_inj' (coeff_order_eq_zero.not.2 hx)]
· simp [hx]
grind
- · simp +contextual only [mem_union, mem_addAntidiagonal, mul_eq_mul_right_iff, Prod.mk.injEq,
+ · simp +contextual only [mem_union, mem_antidiagonal, mul_eq_mul_right_iff, Prod.mk.injEq,
ne_eq, ← or_and_right, or_false, and_imp, Prod.forall, mem_support, not_and]
rintro b c - hxb hbc hbc'
contrapose! hbc'
diff --git a/Mathlib/RingTheory/HahnSeries/PowerSeries.lean b/Mathlib/RingTheory/HahnSeries/PowerSeries.lean
index 0dec034a06063a..38e71a494b1d20 100644
--- a/Mathlib/RingTheory/HahnSeries/PowerSeries.lean
+++ b/Mathlib/RingTheory/HahnSeries/PowerSeries.lean
@@ -70,8 +70,8 @@ def toPowerSeries : R⟦ℕ⟧ ≃+* PowerSeries R where
refine (sum_filter_ne_zero _).symm.trans <| (sum_congr ?_ fun _ _ ↦ rfl).trans <|
sum_filter_ne_zero _
ext m
- simp only [mem_antidiagonal, mem_addAntidiagonal, and_congr_left_iff, mem_filter,
- mem_support]
+ simp only [HasAntidiagonal.mem_antidiagonal, Finset.mem_antidiagonal, and_congr_left_iff,
+ mem_filter, mem_support]
rintro h
rw [and_iff_right (left_ne_zero_of_mul h), and_iff_right (right_ne_zero_of_mul h)]
@@ -169,8 +169,8 @@ def toMvPowerSeries {σ : Type*} [Finite σ] : R⟦σ →₀ ℕ⟧ ≃+* MvPowe
refine (sum_filter_ne_zero _).symm.trans <| (sum_congr ?_ fun _ _ ↦ rfl).trans <|
sum_filter_ne_zero _
ext m
- simp only [and_congr_left_iff, mem_addAntidiagonal, mem_filter, mem_support,
- Finset.mem_antidiagonal]
+ simp only [and_congr_left_iff, Finset.mem_antidiagonal, mem_filter, mem_support,
+ HasAntidiagonal.mem_antidiagonal]
rintro h
rw [and_iff_right (left_ne_zero_of_mul h), and_iff_right (right_ne_zero_of_mul h)]
diff --git a/Mathlib/RingTheory/HahnSeries/Summable.lean b/Mathlib/RingTheory/HahnSeries/Summable.lean
index 022cf4cf2e1e47..7894e2b75bb025 100644
--- a/Mathlib/RingTheory/HahnSeries/Summable.lean
+++ b/Mathlib/RingTheory/HahnSeries/Summable.lean
@@ -555,7 +555,7 @@ theorem mul_eq_smul (s : SummableFamily Γ R α) (t : SummableFamily Γ R β) :
rfl
theorem coeff_hsum_mul (s : SummableFamily Γ R α) (t : SummableFamily Γ R β) (g : Γ) :
- (mul s t).hsum.coeff g = ∑ gh ∈ addAntidiagonal s.isPWO_iUnion_support
+ (mul s t).hsum.coeff g = ∑ gh ∈ antidiagonal s.isPWO_iUnion_support
t.isPWO_iUnion_support g, (s.hsum.coeff gh.1) * (t.hsum.coeff gh.2) := by
simp_rw [← smul_eq_mul, mul_eq_smul]
exact coeff_smul s t g
@@ -694,17 +694,17 @@ theorem pow_finite_co_support {x : R⟦Γ⟧} (hx : 0 < x.orderTop) (g : Γ) :
swap; · exact Set.finite_empty.subset fun n hn => hg (Set.mem_iUnion.2 ⟨n, hn⟩)
apply hpwo.isWF.induction hg
intro y ys hy
- refine ((((addAntidiagonal x.isPWO_support hpwo y).finite_toSet.biUnion
- fun ij hij => hy ij.snd (mem_addAntidiagonal.1 (mem_coe.1 hij)).2.1 ?_).image Nat.succ).union
+ refine ((((antidiagonal x.isPWO_support hpwo y).finite_toSet.biUnion
+ fun ij hij => hy ij.snd (mem_antidiagonal.1 (mem_coe.1 hij)).2.1 ?_).image Nat.succ).union
(Set.finite_singleton 0)).subset ?_
- · obtain ⟨hi, _, rfl⟩ := mem_addAntidiagonal.1 (mem_coe.1 hij)
+ · obtain ⟨hi, _, rfl⟩ := mem_antidiagonal.1 (mem_coe.1 hij)
exact lt_add_of_pos_left ij.2 <| lt_of_lt_of_le ((zero_lt_orderTop_iff h0).mp hx) <|
order_le_of_coeff_ne_zero <| Function.mem_support.mp hi
· rintro (_ | n) hn
· exact Set.mem_union_right _ (Set.mem_singleton 0)
· obtain ⟨i, hi, j, hj, rfl⟩ := support_mul_subset hn
refine Set.mem_union_left _ ⟨n, Set.mem_iUnion.2 ⟨⟨j, i⟩, Set.mem_iUnion.2 ⟨?_, hi⟩⟩, rfl⟩
- simp only [mem_coe, mem_addAntidiagonal, mem_support, ne_eq, Set.mem_iUnion]
+ simp only [mem_coe, mem_antidiagonal, mem_support, ne_eq, Set.mem_iUnion]
exact ⟨hj, ⟨n, hi⟩, add_comm j i⟩
/-- A summable family of powers of a Hahn series `x`. If `x` has non-positive `orderTop`, then
diff --git a/Mathlib/RingTheory/MvPowerSeries/Basic.lean b/Mathlib/RingTheory/MvPowerSeries/Basic.lean
index 9fc6b1497de12c..08e0614e76f1b0 100644
--- a/Mathlib/RingTheory/MvPowerSeries/Basic.lean
+++ b/Mathlib/RingTheory/MvPowerSeries/Basic.lean
@@ -234,8 +234,8 @@ theorem coeff_monomial_mul (a : R) :
∀ p ∈ antidiagonal m,
coeff (p : (σ →₀ ℕ) × (σ →₀ ℕ)).1 (monomial n a) * coeff p.2 φ ≠ 0 → p.1 = n :=
fun p _ hp => eq_of_coeff_monomial_ne_zero (left_ne_zero_of_mul hp)
- rw [coeff_mul, ← Finset.sum_filter_of_ne this, Finset.filter_fst_eq_antidiagonal _ n,
- Finset.sum_ite_index]
+ rw [coeff_mul, ← Finset.sum_filter_of_ne this, Finset.HasAntidiagonal.filter_fst_eq_antidiagonal
+ _ n, Finset.sum_ite_index]
simp only [Finset.sum_singleton, coeff_monomial_same, Finset.sum_empty]
theorem coeff_mul_monomial (a : R) :
@@ -245,8 +245,8 @@ theorem coeff_mul_monomial (a : R) :
∀ p ∈ antidiagonal m,
coeff (p : (σ →₀ ℕ) × (σ →₀ ℕ)).1 φ * coeff p.2 (monomial n a) ≠ 0 → p.2 = n :=
fun p _ hp => eq_of_coeff_monomial_ne_zero (right_ne_zero_of_mul hp)
- rw [coeff_mul, ← Finset.sum_filter_of_ne this, Finset.filter_snd_eq_antidiagonal _ n,
- Finset.sum_ite_index]
+ rw [coeff_mul, ← Finset.sum_filter_of_ne this, Finset.HasAntidiagonal.filter_snd_eq_antidiagonal
+ _ n, Finset.sum_ite_index]
simp only [Finset.sum_singleton, coeff_monomial_same, Finset.sum_empty]
theorem coeff_add_monomial_mul (a : R) :
diff --git a/Mathlib/RingTheory/MvPowerSeries/LinearTopology.lean b/Mathlib/RingTheory/MvPowerSeries/LinearTopology.lean
index b74e68083b7fd5..c47f513096cff1 100644
--- a/Mathlib/RingTheory/MvPowerSeries/LinearTopology.lean
+++ b/Mathlib/RingTheory/MvPowerSeries/LinearTopology.lean
@@ -61,13 +61,15 @@ noncomputable def basis (σ : Type*) (R : Type*) [Ring R] (Jd : TwoSidedIdeal R
rw [coeff_mul]
apply sum_mem
rintro uv huv
- exact TwoSidedIdeal.mul_mem_left _ _ _ (hg _ (le_trans (Finset.antidiagonal.snd_le huv) he)))
+ exact TwoSidedIdeal.mul_mem_left _ _ _
+ (hg _ (le_trans (Finset.HasAntidiagonal.antidiagonal.snd_le huv) he)))
(fun {f g} hf e he ↦ by
classical
rw [coeff_mul]
apply sum_mem
rintro uv huv
- exact TwoSidedIdeal.mul_mem_right _ _ _ (hf _ (le_trans (Finset.antidiagonal.fst_le huv) he)))
+ exact TwoSidedIdeal.mul_mem_right _ _ _
+ (hf _ (le_trans (Finset.HasAntidiagonal.antidiagonal.fst_le huv) he)))
variable {σ : Type*} {R : Type*} [Ring R]
diff --git a/Mathlib/RingTheory/PowerSeries/CoeffMulMem.lean b/Mathlib/RingTheory/PowerSeries/CoeffMulMem.lean
index aa70f8948a49ca..cff3f91d0f39c4 100644
--- a/Mathlib/RingTheory/PowerSeries/CoeffMulMem.lean
+++ b/Mathlib/RingTheory/PowerSeries/CoeffMulMem.lean
@@ -45,8 +45,8 @@ theorem coeff_mul_mem_ideal_mul_ideal_of_coeff_mem_ideal (hf : ∀ i ≤ n, coef
(hg : ∀ i ≤ n, coeff i g ∈ J) : ∀ i ≤ n, coeff i (f * g) ∈ I * J := fun i hi ↦ by
rw [coeff_mul]
exact Ideal.sum_mem _ fun p hp ↦ Ideal.mul_mem_mul
- (hf _ ((Finset.antidiagonal.fst_le hp).trans hi))
- (hg _ ((Finset.antidiagonal.snd_le hp).trans hi))
+ (hf _ ((Finset.HasAntidiagonal.antidiagonal.fst_le hp).trans hi))
+ (hg _ ((Finset.HasAntidiagonal.antidiagonal.snd_le hp).trans hi))
theorem coeff_mul_mem_ideal_mul_ideal_of_coeff_mem_ideal' (hf : ∀ i, coeff i f ∈ I)
(hg : ∀ i, coeff i g ∈ J) : ∀ i, coeff i (f * g) ∈ I * J :=
diff --git a/Mathlib/RingTheory/PowerSeries/WeierstrassPreparation.lean b/Mathlib/RingTheory/PowerSeries/WeierstrassPreparation.lean
index a0dc12fa468ee4..ac4b409fb6d2a3 100644
--- a/Mathlib/RingTheory/PowerSeries/WeierstrassPreparation.lean
+++ b/Mathlib/RingTheory/PowerSeries/WeierstrassPreparation.lean
@@ -746,9 +746,9 @@ theorem IsWeierstrassDivision.isUnit_of_map_ne_zero
· rw [coeff_of_lt_order p.1 ?_]
· rw [zero_mul]
· rw [← ENat.lt_lift_iff (h := order_finite_iff_ne_zero.2 hg), ENat.lift_eq_toNat_of_lt_top]
- refine (Finset.antidiagonal.fst_le hp).lt_of_ne ?_
+ refine (Finset.HasAntidiagonal.antidiagonal.fst_le hp).lt_of_ne ?_
contrapose hnotMem
- rwa [Finset.mem_singleton, Finset.antidiagonal_congr hp (by simp)]
+ rwa [Finset.mem_singleton, Finset.HasAntidiagonal.antidiagonal_congr hp (by simp)]
theorem IsWeierstrassDivision.isWeierstrassFactorization
{g q : A⟦X⟧} {r : A[X]} (hg : g.map (IsLocalRing.residue A) ≠ 0)
diff --git a/Mathlib/Tactic/Translate/ToAdditive.lean b/Mathlib/Tactic/Translate/ToAdditive.lean
index bb007e638676af..0b1719bb2b5cd6 100644
--- a/Mathlib/Tactic/Translate/ToAdditive.lean
+++ b/Mathlib/Tactic/Translate/ToAdditive.lean
@@ -351,6 +351,7 @@ def abbreviationDict : Std.HashMap String String := .ofList [
("le_zero", "Nonpos"),
("ltzero", "Neg"),
("lt_zero", "Neg"),
+ ("addAntidiagonal", "Antidiagonal"),
("addSingle", "Single"),
("addSupport", "Support"),
("addTSupport", "TSupport"),
diff --git a/Mathlib/Topology/Algebra/InfiniteSum/Ring.lean b/Mathlib/Topology/Algebra/InfiniteSum/Ring.lean
index d806ef213421b3..e925b80f9c5ff0 100644
--- a/Mathlib/Topology/Algebra/InfiniteSum/Ring.lean
+++ b/Mathlib/Topology/Algebra/InfiniteSum/Ring.lean
@@ -214,7 +214,7 @@ variable [TopologicalSpace α] [NonUnitalNonAssocSemiring α] {f g : A → α}
theorem summable_mul_prod_iff_summable_mul_sigma_antidiagonal :
(Summable fun x : A × A ↦ f x.1 * g x.2) ↔
Summable fun x : Σ n : A, antidiagonal n ↦ f (x.2 : A × A).1 * g (x.2 : A × A).2 :=
- Finset.sigmaAntidiagonalEquivProd.summable_iff.symm
+ Finset.HasAntidiagonal.sigmaAntidiagonalEquivProd.summable_iff.symm
variable [T3Space α] [IsTopologicalSemiring α]
@@ -226,7 +226,7 @@ theorem summable_sum_mul_antidiagonal_of_summable_mul
exact h.sigma' fun n ↦ (hasSum_fintype _).summable
/-- The **Cauchy product formula** for the product of two infinite sums indexed by `ℕ`, expressed
-by summing on `Finset.antidiagonal`.
+by summing on `Finset.HasAntidiagonal.antidiagonal`.
See also `tsum_mul_tsum_eq_tsum_sum_antidiagonal_of_summable_norm` if `f` and `g` are absolutely
summable. -/
@@ -234,7 +234,7 @@ protected theorem Summable.tsum_mul_tsum_eq_tsum_sum_antidiagonal (hf : Summable
(hg : Summable g) (hfg : Summable fun x : A × A ↦ f x.1 * g x.2) :
((∑' n, f n) * ∑' n, g n) = ∑' n, ∑ kl ∈ antidiagonal n, f kl.1 * g kl.2 := by
conv_rhs => congr; ext; rw [← Finset.sum_finset_coe, ← tsum_fintype (L := .unconditional _)]
- rw [hf.tsum_mul_tsum hg hfg, ← sigmaAntidiagonalEquivProd.tsum_eq (_ : A × A → α)]
+ rw [hf.tsum_mul_tsum hg hfg, ← HasAntidiagonal.sigmaAntidiagonalEquivProd.tsum_eq (_ : A × A → α)]
exact (summable_mul_prod_iff_summable_mul_sigma_antidiagonal.mp hfg).tsum_sigma'
(fun n ↦ (hasSum_fintype _).summable)
From e7c715d39466d958a1b956dad3072a8dce6b37a3 Mon Sep 17 00:00:00 2001
From: Sebastien Gouezel <10818434+sgouezel@users.noreply.github.com>
Date: Thu, 25 Jun 2026 19:06:44 +0000
Subject: [PATCH 0366/1300] chore: fix non-reducible diamond in `StarRingEnd`
(#41047)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The following fails before the PR, works after it
```
example : starRingEnd ℝ = RingHom.id ℝ := by
with_reducible_and_instances rfl
```
Co-authored-by: sgouezel
---
Mathlib/Algebra/Star/Basic.lean | 16 +++++++++++-----
Mathlib/Analysis/CStarAlgebra/Basic.lean | 3 +--
Mathlib/NumberTheory/MulChar/Lemmas.lean | 5 ++---
3 files changed, 14 insertions(+), 10 deletions(-)
diff --git a/Mathlib/Algebra/Star/Basic.lean b/Mathlib/Algebra/Star/Basic.lean
index f37f1dc5d07931..c109f8adb81226 100644
--- a/Mathlib/Algebra/Star/Basic.lean
+++ b/Mathlib/Algebra/Star/Basic.lean
@@ -94,8 +94,10 @@ theorem star_inj [InvolutiveStar R] {x y : R} : star x = star y ↔ x = y :=
/-- `star` as an equivalence when it is involutive. -/
@[simps! apply]
-protected def Equiv.Perm.star [InvolutiveStar R] : Equiv.Perm R :=
- star_involutive.toPerm _
+protected def Equiv.Perm.star [InvolutiveStar R] : Equiv.Perm R where
+ toFun := star
+ invFun := star
+ __ : Equiv.Perm R := star_involutive.toPerm _
@[simp]
theorem Equiv.Perm.symm_star [InvolutiveStar R] :
@@ -213,7 +215,7 @@ theorem star_div [CommGroup R] [StarMul R] (x y : R) : star (x / y) = star x / s
See note [reducible non-instances].
-/
abbrev starMulOfComm {R : Type*} [CommMonoid R] : StarMul R where
- star := id
+ star x := x
star_involutive _ := rfl
star_mul := mul_comm
@@ -324,7 +326,8 @@ variable [CommSemiring R] [StarRing R]
/-- `star` as a ring automorphism, for commutative `R`. -/
@[simps apply]
-def starRingAut : RingAut R := { starAddEquiv, starMulAut (R := R) with toFun := star }
+def starRingAut : RingAut R :=
+ { starAddEquiv, starMulAut (R := R) with toFun := star, invFun := star }
variable (R) in
/-- `star` as a ring endomorphism, for commutative `R`. This is used to denote complex
@@ -334,7 +337,10 @@ Note that this is the preferred form (over `starRingAut`, available under the sa
because the notation `E →ₗ⋆[R] F` for an `R`-conjugate-linear map (short for
`E →ₛₗ[starRingEnd R] F`) does not pretty-print if there is a coercion involved, as would be the
case for `(↑starRingAut : R →* R)`. -/
-def starRingEnd : R →+* R := @starRingAut R _ _
+@[implicit_reducible]
+def starRingEnd : R →+* R where
+ toFun := star
+ __ := (@starRingAut R _ _).toRingHom
@[inherit_doc]
scoped[ComplexConjugate] notation "conj" => starRingEnd _
diff --git a/Mathlib/Analysis/CStarAlgebra/Basic.lean b/Mathlib/Analysis/CStarAlgebra/Basic.lean
index d8a77466ce59d5..59c88f4ea228c1 100644
--- a/Mathlib/Analysis/CStarAlgebra/Basic.lean
+++ b/Mathlib/Analysis/CStarAlgebra/Basic.lean
@@ -90,8 +90,7 @@ class CStarRing (E : Type*) [NonUnitalNormedRing E] [StarRing E] : Prop where
norm_mul_self_le : ∀ x : E, ‖x‖ * ‖x‖ ≤ ‖x⋆ * x‖
instance : CStarRing ℝ where
- norm_mul_self_le x := by
- simp only [Real.norm_eq_abs, abs_mul_abs_self, star, id, norm_mul, le_refl]
+ norm_mul_self_le x := by simp
namespace CStarRing
diff --git a/Mathlib/NumberTheory/MulChar/Lemmas.lean b/Mathlib/NumberTheory/MulChar/Lemmas.lean
index 1f2af16ecf0116..a12ad1c2c010eb 100644
--- a/Mathlib/NumberTheory/MulChar/Lemmas.lean
+++ b/Mathlib/NumberTheory/MulChar/Lemmas.lean
@@ -51,11 +51,10 @@ instance instStarMul [StarRing R'] : StarMul (MulChar R R') where
star := starComp
star_involutive χ := by
ext1
- simp only [starComp_apply, RingHomCompTriple.comp_apply, RingHom.id_apply]
+ simp [starComp_apply]
star_mul χ χ' := by
ext1
- simp only [starComp_apply, starRingEnd, coeToFun_mul, Pi.mul_apply, map_mul, RingHom.coe_coe,
- starRingAut_apply, mul_comm]
+ simp [starComp_apply, mul_comm]
@[simp]
lemma star_apply [StarRing R'] (χ : MulChar R R') (a : R) : (star χ) a = star (χ a) :=
From f50308756c78c29d5a859b850967def7b94b7c4b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=ABl=20Riou?=
<37772949+joelriou@users.noreply.github.com>
Date: Thu, 25 Jun 2026 20:10:56 +0000
Subject: [PATCH 0367/1300] feat(CategoryTheory/Localization): small additions
(#40884)
This PR adds a few properties about localization with respect to isomorphisms.
The lemma `LocalizerMorphism.Derives.isIso` is renamed `isIso_of_isRightDerivedFunctor`, because we shall need the dual lemma in the future.
---
.../Localization/DerivabilityStructure/Derives.lean | 8 +++++---
.../CategoryTheory/Localization/LocalizerMorphism.lean | 3 +++
Mathlib/CategoryTheory/Localization/Predicate.lean | 3 +++
Mathlib/CategoryTheory/MorphismProperty/IsInvertedBy.lean | 5 +++++
4 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/Mathlib/CategoryTheory/Localization/DerivabilityStructure/Derives.lean b/Mathlib/CategoryTheory/Localization/DerivabilityStructure/Derives.lean
index 1944773a64883d..a9539a1d84d6dc 100644
--- a/Mathlib/CategoryTheory/Localization/DerivabilityStructure/Derives.lean
+++ b/Mathlib/CategoryTheory/Localization/DerivabilityStructure/Derives.lean
@@ -65,7 +65,7 @@ section
variable {L₂ : C₂ ⥤ D₂} [L₂.IsLocalization W₂] {RF : D₂ ⥤ H} (α : F ⟶ L₂ ⋙ RF)
-lemma isIso (X₁ : C₁) [RF.IsRightDerivedFunctor α W₂] :
+lemma isIso_of_isRightDerivedFunctor (X₁ : C₁) [RF.IsRightDerivedFunctor α W₂] :
IsIso (α.app (Φ.functor.obj X₁)) := by
let G : W₁.Localization ⥤ H := Localization.lift (Φ.functor ⋙ F) h W₁.Q
let eG := Localization.Lifting.iso W₁.Q W₁ (Φ.functor ⋙ F) G
@@ -74,12 +74,14 @@ lemma isIso (X₁ : C₁) [RF.IsRightDerivedFunctor α W₂] :
rw [← Φ.isIso_iff_of_isRightDerivabilityStructure W₁.Q L₂ F G eG.inv RF α]
infer_instance
+@[deprecated (since := "2026-06-22")] alias isIso := isIso_of_isRightDerivedFunctor
+
set_option backward.defeqAttrib.useBackward true in
set_option backward.isDefEq.respectTransparency false in
lemma isRightDerivedFunctor_of_isIso (hα : ∀ (X₁ : C₁), IsIso (α.app (Φ.functor.obj X₁))) :
RF.IsRightDerivedFunctor α W₂ := by
have := h.hasPointwiseRightDerivedFunctor
- have := h.isIso (F.totalRightDerivedUnit L₂ W₂)
+ have := h.isIso_of_isRightDerivedFunctor (F.totalRightDerivedUnit L₂ W₂)
have := Φ.essSurj_of_hasRightResolutions L₂
let φ := (F.totalRightDerived L₂ W₂).rightDerivedDesc (F.totalRightDerivedUnit L₂ W₂) W₂ RF α
have hφ : F.totalRightDerivedUnit L₂ W₂ ≫ Functor.whiskerLeft L₂ φ = α :=
@@ -97,7 +99,7 @@ lemma isRightDerivedFunctor_of_isIso (hα : ∀ (X₁ : C₁), IsIso (α.app (Φ
lemma isRightDerivedFunctor_iff_isIso :
RF.IsRightDerivedFunctor α W₂ ↔ ∀ (X₁ : C₁), IsIso (α.app (Φ.functor.obj X₁)) :=
- ⟨fun _ _ ↦ h.isIso α _, h.isRightDerivedFunctor_of_isIso α⟩
+ ⟨fun _ _ ↦ h.isIso_of_isRightDerivedFunctor α _, h.isRightDerivedFunctor_of_isIso α⟩
end
diff --git a/Mathlib/CategoryTheory/Localization/LocalizerMorphism.lean b/Mathlib/CategoryTheory/Localization/LocalizerMorphism.lean
index d69534f721a440..2be304d60b9ec4 100644
--- a/Mathlib/CategoryTheory/Localization/LocalizerMorphism.lean
+++ b/Mathlib/CategoryTheory/Localization/LocalizerMorphism.lean
@@ -64,6 +64,9 @@ def id : LocalizerMorphism W₁ W₁ where
functor := 𝟭 C₁
map _ _ _ hf := hf
+instance : (id W₁).functor.IsEquivalence :=
+ inferInstanceAs (𝟭 C₁).IsEquivalence
+
variable {W₁ W₂ W₃}
/-- The composition of two localizers morphisms. -/
diff --git a/Mathlib/CategoryTheory/Localization/Predicate.lean b/Mathlib/CategoryTheory/Localization/Predicate.lean
index 28242d1d57c21d..c882cb52d75bcb 100644
--- a/Mathlib/CategoryTheory/Localization/Predicate.lean
+++ b/Mathlib/CategoryTheory/Localization/Predicate.lean
@@ -127,6 +127,9 @@ theorem IsLocalization.for_id (hW : W ≤ MorphismProperty.isomorphisms C) : (
IsLocalization.mk' _ _ (Localization.strictUniversalPropertyFixedTargetId W _ hW)
(Localization.strictUniversalPropertyFixedTargetId W _ hW)
+instance : (𝟭 C).IsLocalization (MorphismProperty.isomorphisms C) :=
+ IsLocalization.for_id _ (by rfl)
+
end Functor
namespace Localization
diff --git a/Mathlib/CategoryTheory/MorphismProperty/IsInvertedBy.lean b/Mathlib/CategoryTheory/MorphismProperty/IsInvertedBy.lean
index 8c5678c5a04da0..2f3a35a8d1f94f 100644
--- a/Mathlib/CategoryTheory/MorphismProperty/IsInvertedBy.lean
+++ b/Mathlib/CategoryTheory/MorphismProperty/IsInvertedBy.lean
@@ -173,6 +173,11 @@ lemma IsInvertedBy.map_iff {C₁ C₂ C₃ : Type*} [Category* C₁] [Category*
(W.map F).IsInvertedBy G ↔ W.IsInvertedBy (F ⋙ G) := by
simp only [IsInvertedBy.iff_map_le_isomorphisms, map_map]
+lemma isInvertedBy_isomorphisms (F : C ⥤ D) : (isomorphisms C).IsInvertedBy F := by
+ intro _ _ _ hf
+ simp only [isomorphisms.iff] at hf
+ infer_instance
+
end MorphismProperty
end CategoryTheory
From 81bc6062d7bebe880f7a60d8f6ccaf474e7c29c0 Mon Sep 17 00:00:00 2001
From: Wrenna Robson
Date: Thu, 25 Jun 2026 20:24:28 +0000
Subject: [PATCH 0368/1300] feat(Logic/Function/Defs): Add dcomp lemmas
(#39195)
This PR adds API lemmas about `dcomp` that were previously missing.
---
Mathlib/Logic/Function/Defs.lean | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/Mathlib/Logic/Function/Defs.lean b/Mathlib/Logic/Function/Defs.lean
index 04b9b63554da8d..3208b2496fc569 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
+section DComp
+
+variable {ι} {β : ι → Sort*} {φ : ∀ {i : ι}, β i → Sort*} (f : ∀ {i : ι} (y : β i), φ y)
+ (g : ∀ i, β i) (i : ι)
+
+theorem dcomp_def : @f ∘' g = fun i => f (g i) := rfl
+
+theorem dcomp_apply : dcomp @f g i = f (g i) := rfl
+
+@[simp] theorem dcomp_eq_comp {α β γ} (f : β → γ) (g : α → β) : f ∘' g = f ∘ g := rfl
+
+end 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 : ι) :
From 737969ff4c952612b17c953921eeecfb8d6df26c Mon Sep 17 00:00:00 2001
From: Michael Rothgang <10105016+grunweg@users.noreply.github.com>
Date: Thu, 25 Jun 2026 20:24:31 +0000
Subject: [PATCH 0369/1300] feat: PiLp.equivOfUnique (#40634)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add the isomorphism between `PiLp` over any `Unique` index type and the corresponding individual type.
Generalise `PiLp.linearEquiv` to topological vector spaces, then this is straightforward.
This will be used in #29077, to yield an equivalence `EuclideanSpace 𝕜 (Fin 1) ≃L[𝕜] 𝕜`.
---
Mathlib/Analysis/Normed/Lp/PiLp.lean | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/Mathlib/Analysis/Normed/Lp/PiLp.lean b/Mathlib/Analysis/Normed/Lp/PiLp.lean
index 5bc4fbdc2b7368..7da73c0fdbdcec 100644
--- a/Mathlib/Analysis/Normed/Lp/PiLp.lean
+++ b/Mathlib/Analysis/Normed/Lp/PiLp.lean
@@ -1129,13 +1129,13 @@ end Fintype
section
-variable [Semiring 𝕜] [∀ i, SeminormedAddCommGroup (β i)] [∀ i, Module 𝕜 (β i)]
+variable [Semiring 𝕜] [∀ i, AddCommGroup (β i)] [∀ i, Module 𝕜 (β i)] [∀ i, TopologicalSpace (β i)]
-set_option backward.defeqAttrib.useBackward true in
/-- `WithLp.linearEquiv` as a continuous linear equivalence. -/
@[simps! apply symm_apply]
def continuousLinearEquiv : PiLp p β ≃L[𝕜] ∀ i, β i where
toLinearEquiv := WithLp.linearEquiv _ _ _
+ continuous_invFun := (by fun_prop : Continuous fun (a : Π i, β i) ↦ toLp p a)
lemma coe_continuousLinearEquiv :
⇑(PiLp.continuousLinearEquiv p 𝕜 β) = ofLp := rfl
@@ -1143,6 +1143,18 @@ lemma coe_continuousLinearEquiv :
lemma coe_symm_continuousLinearEquiv :
⇑(PiLp.continuousLinearEquiv p 𝕜 β).symm = toLp p := rfl
+/-- The natural equivalence between `PiLp p β` and `β default`,
+for any index type `ι` with a unique element. -/
+@[simps! apply symm_apply]
+def equivOfUnique [Unique ι] : PiLp p β ≃L[𝕜] β default :=
+ (continuousLinearEquiv p 𝕜 β).trans <| .piUnique 𝕜 β
+
+end
+
+section
+
+variable [Semiring 𝕜] [∀ i, NormedAddCommGroup (β i)] [∀ i, Module 𝕜 (β i)]
+
variable {𝕜} in
/-- The projection on the `i`-th coordinate of `PiLp p β`, as a continuous linear map. -/
@[simps!]
From 99d6cda6e33df5cb5ef77971cd7997a4e15e0a25 Mon Sep 17 00:00:00 2001
From: Michael Rothgang <10105016+grunweg@users.noreply.github.com>
Date: Thu, 25 Jun 2026 20:24:33 +0000
Subject: [PATCH 0370/1300] fix: correct deprecation for
`orthogonalProjection_mem_subspace_orthogonalComplement_eq_zero` (#40722)
#38970 tried to create one, but made a slip (and created a deprecated alias for a declaration that never existed). Correct this oversight. Noticed during leanprover-community/sphere-eversion#143.
---
Mathlib/Analysis/InnerProductSpace/Projection/Basic.lean | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/Mathlib/Analysis/InnerProductSpace/Projection/Basic.lean b/Mathlib/Analysis/InnerProductSpace/Projection/Basic.lean
index 95e52bd3f93f08..09797f0b3d9bc8 100644
--- a/Mathlib/Analysis/InnerProductSpace/Projection/Basic.lean
+++ b/Mathlib/Analysis/InnerProductSpace/Projection/Basic.lean
@@ -434,7 +434,8 @@ theorem orthogonalProjectionOnto_apply_of_mem_orthogonal
[K.HasOrthogonalProjection] {v : E} (hv : v ∈ Kᗮ) : K.orthogonalProjectionOnto v = 0 :=
orthogonalProjectionOnto_eq_zero_iff.mpr hv
-@[deprecated (since := "2026-05-06")] alias orthogonalProjection_apply_of_mem_orthogonal :=
+@[deprecated (since := "2026-05-06")] alias
+orthogonalProjection_mem_subspace_orthogonalComplement_eq_zero :=
orthogonalProjectionOnto_apply_of_mem_orthogonal
/-- The projection into `U` from an orthogonal submodule `V` is the zero map. -/
From 3afead49a87418666bb06b2814383a1eaa2f878d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=ABl=20Riou?=
<37772949+joelriou@users.noreply.github.com>
Date: Thu, 25 Jun 2026 21:17:52 +0000
Subject: [PATCH 0371/1300] feat(Algebra/Homology): the injective derivability
structure (#41000)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Let `C` be an abelian category with enough injectives. In this file, we define a localizer morphism `CochainComplex.Plus.localizerMorphism` (relatively to quasi-isomorphisms) which is given by the (fully faithful) functor `CochainComplex.Plus (InjectiveObject C) ⥤ CochainComplex.Plus C`, and we show that it is a right derivability structure in the sense of Kahn and Maltsiniotis. (The proof proceeds by showing that up to equivalences of categories, this functor is the inclusion of the full subcategory of fibrant objects in the model category `CochainComplex.Plus C`.)
---
Mathlib.lean | 2 +
.../DerivabilityStructureInjectives.lean | 121 ++++++++++++++++++
.../CategoryWithCofibrations.lean | 4 +
.../Injective/InjectiveObject.lean | 60 +++++++++
4 files changed, 187 insertions(+)
create mode 100644 Mathlib/Algebra/Homology/DerivedCategory/DerivabilityStructureInjectives.lean
create mode 100644 Mathlib/CategoryTheory/Preadditive/Injective/InjectiveObject.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index f892e89c8d8f8e..32975106c2b8df 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -573,6 +573,7 @@ public import Mathlib.Algebra.Homology.ComplexShape
public import Mathlib.Algebra.Homology.ComplexShapeSigns
public import Mathlib.Algebra.Homology.ConcreteCategory
public import Mathlib.Algebra.Homology.DerivedCategory.Basic
+public import Mathlib.Algebra.Homology.DerivedCategory.DerivabilityStructureInjectives
public import Mathlib.Algebra.Homology.DerivedCategory.ExactFunctor
public import Mathlib.Algebra.Homology.DerivedCategory.Ext.Basic
public import Mathlib.Algebra.Homology.DerivedCategory.Ext.EnoughInjectives
@@ -3238,6 +3239,7 @@ public import Mathlib.CategoryTheory.Preadditive.FunctorCategory
public import Mathlib.CategoryTheory.Preadditive.HomOrthogonal
public import Mathlib.CategoryTheory.Preadditive.Indization
public import Mathlib.CategoryTheory.Preadditive.Injective.Basic
+public import Mathlib.CategoryTheory.Preadditive.Injective.InjectiveObject
public import Mathlib.CategoryTheory.Preadditive.Injective.LiftingProperties
public import Mathlib.CategoryTheory.Preadditive.Injective.Preserves
public import Mathlib.CategoryTheory.Preadditive.Injective.Resolution
diff --git a/Mathlib/Algebra/Homology/DerivedCategory/DerivabilityStructureInjectives.lean b/Mathlib/Algebra/Homology/DerivedCategory/DerivabilityStructureInjectives.lean
new file mode 100644
index 00000000000000..4d93315312b95a
--- /dev/null
+++ b/Mathlib/Algebra/Homology/DerivedCategory/DerivabilityStructureInjectives.lean
@@ -0,0 +1,121 @@
+/-
+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.Homology.FullSubcategory
+public import Mathlib.Algebra.Homology.ModelCategory.Injective
+public import Mathlib.AlgebraicTopology.ModelCategory.DerivabilityStructureFibrant
+public import Mathlib.CategoryTheory.Localization.DerivabilityStructure.OfLocalizedEquivalences
+public import Mathlib.CategoryTheory.Preadditive.Injective.InjectiveObject
+
+/-!
+# The injective derivability structure
+
+Let `C` be an abelian category with enough injectives.
+In this file, we define a localizer morphism `CochainComplex.Plus.localizerMorphism`
+(relative to quasi-isomorphisms) which is given by the (fully faithful) functor
+`CochainComplex.Plus (InjectiveObject C) ⥤ CochainComplex.Plus C`, and we show
+that it is a right derivability structure. (The proof proceeds by showing that
+up to equivalences of categories, this functor is the inclusion of the full
+subcategory of fibrant objects in the model category `CochainComplex.Plus C`.)
+
+TODO(@joelriou): obtain similar results for the bounded below homotopy category.
+
+-/
+
+@[expose] public section
+
+open HomotopicalAlgebra CategoryTheory Limits ZeroObject Category
+
+variable (C : Type*) [Category* C] [Abelian C]
+
+namespace CochainComplex.Plus
+
+/-- The localizer morphism (relative to quasi-isomorphisms) that is
+given by the "inclusion functor"
+`CochainComplex.Plus (InjectiveObject C) ⥤ CochainComplex.Plus C`. -/
+@[simps]
+def localizerMorphism :
+ LocalizerMorphism ((quasiIso C).inverseImage (InjectiveObject.ι C).mapCochainComplexPlus)
+ (quasiIso C) where
+ functor := (InjectiveObject.ι C).mapCochainComplexPlus
+ map := by rfl
+
+instance : (localizerMorphism C).IsInduced where
+ inverseImage_eq := rfl
+
+instance (K : Plus (InjectiveObject C)) (n : ℤ) :
+ Injective (K.obj.X n).obj :=
+ (K.obj.X n).property
+
+variable [EnoughInjectives C]
+
+open modelCategoryQuillen
+
+instance (K : FibrantObject (Plus C)) (n : ℤ) :
+ Injective (K.obj.obj.X n) := by
+ obtain ⟨K, hK⟩ := K
+ rw [fibrantObjects, modelCategoryQuillen.isFibrant_iff] at hK
+ dsimp
+ infer_instance
+
+set_option backward.defeqAttrib.useBackward true in
+/-- The equivalence between `CochainComplex.Plus (InjectiveObject C)`
+and the category of fibrant object in `CochainComplex.Plus C` for the
+Quillen model category structure. -/
+def fibrantObjectEquivalence :
+ Plus (InjectiveObject C) ≌ FibrantObject (Plus C) where
+ functor := ObjectProperty.lift _ (InjectiveObject.ι C).mapCochainComplexPlus (fun K ↦ by
+ dsimp [fibrantObjects]
+ rw [modelCategoryQuillen.isFibrant_iff]
+ intro n
+ dsimp
+ infer_instance)
+ inverse := ObjectProperty.lift _
+ (HomologicalComplex.liftFunctorObjectProperty _ (FibrantObject.ι ⋙ Plus.ι C)
+ (fun K n ↦ by dsimp; infer_instance)) (by
+ rintro ⟨⟨K, n, hn⟩, _⟩
+ refine ⟨n, ?_⟩
+ rw [isStrictlyGE_iff]
+ intro i hi
+ rw [IsZero.iff_id_eq_zero]
+ ext
+ apply (K.isZero_of_isStrictlyGE n i hi).eq_of_tgt)
+ unitIso := Iso.refl _
+ counitIso := Iso.refl _
+
+/-- The localizer morphism (relative to quasi-isomorphisms) that is
+given by the equivalence of categories
+`CochainComplex.Plus (InjectiveObject C) ≌ FibrantObject (CochainComplex.Plus C)`. -/
+@[simps]
+def fibrantObjectLocalizerMorphism :
+ LocalizerMorphism ((quasiIso C).inverseImage (InjectiveObject.ι C).mapCochainComplexPlus)
+ (weakEquivalences (FibrantObject (Plus C))) where
+ functor := (fibrantObjectEquivalence C).functor
+ map := by rfl
+
+instance : (fibrantObjectLocalizerMorphism C).IsInduced where
+ inverseImage_eq := rfl
+
+set_option backward.defeqAttrib.useBackward true in
+instance : (fibrantObjectLocalizerMorphism C).functor.IsEquivalence := by
+ dsimp; infer_instance
+
+set_option backward.isDefEq.respectTransparency false in
+instance : (localizerMorphism C).IsRightDerivabilityStructure := by
+ rw [LocalizerMorphism.isRightDerivabilityStructure_iff_of_equivalences
+ (T := localizerMorphism C) (B := FibrantObject.localizerMorphism (Plus C))
+ (R := .id _) (L := fibrantObjectLocalizerMorphism C) (Iso.refl _)]
+ infer_instance
+
+set_option backward.isDefEq.respectTransparency false in
+instance : (localizerMorphism C).arrow.HasRightResolutions := by
+ rw [LocalizerMorphism.hasRightResolutions_arrow_iff_of_equivalences
+ (T := localizerMorphism C) (B := FibrantObject.localizerMorphism (Plus C))
+ (R := .id _) (L := fibrantObjectLocalizerMorphism C) (Iso.refl _)]
+ infer_instance
+
+end CochainComplex.Plus
diff --git a/Mathlib/AlgebraicTopology/ModelCategory/CategoryWithCofibrations.lean b/Mathlib/AlgebraicTopology/ModelCategory/CategoryWithCofibrations.lean
index b088b3d15ccb4b..bd654074d1fbd5 100644
--- a/Mathlib/AlgebraicTopology/ModelCategory/CategoryWithCofibrations.lean
+++ b/Mathlib/AlgebraicTopology/ModelCategory/CategoryWithCofibrations.lean
@@ -267,6 +267,10 @@ instance [(weakEquivalences C).IsMultiplicative] :
(weakEquivalences P.FullSubcategory).IsMultiplicative :=
inferInstanceAs ((weakEquivalences C).inverseImage P.ι).IsMultiplicative
+instance [(weakEquivalences C).RespectsIso] :
+ (weakEquivalences P.FullSubcategory).RespectsIso :=
+ inferInstanceAs ((weakEquivalences C).inverseImage P.ι).RespectsIso
+
lemma weakEquivalence_iff_of_objectProperty
{X Y : P.FullSubcategory} (f : X ⟶ Y) :
WeakEquivalence f ↔ WeakEquivalence f.hom := by
diff --git a/Mathlib/CategoryTheory/Preadditive/Injective/InjectiveObject.lean b/Mathlib/CategoryTheory/Preadditive/Injective/InjectiveObject.lean
new file mode 100644
index 00000000000000..ddbe6ccb6f79dd
--- /dev/null
+++ b/Mathlib/CategoryTheory/Preadditive/Injective/InjectiveObject.lean
@@ -0,0 +1,60 @@
+/-
+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.Limits.FullSubcategory
+public import Mathlib.CategoryTheory.Preadditive.Biproducts
+public import Mathlib.CategoryTheory.Preadditive.Injective.Basic
+
+/-!
+# The full subcategory of injective objects
+
+-/
+
+@[expose] public section
+
+universe v u
+
+namespace CategoryTheory
+
+open Limits ZeroObject
+
+variable (C : Type u) [Category.{v} C]
+
+/-- The full subcategory of injective objects in a category `C`. -/
+abbrev InjectiveObject : Type u := ObjectProperty.FullSubcategory (isInjective C)
+
+namespace InjectiveObject
+
+instance (J : Type*) :
+ ObjectProperty.IsClosedUnderLimitsOfShape (isInjective C) (Discrete J) where
+ limitsOfShape_le := by
+ rintro Y ⟨p⟩
+ have (j : J) : Injective (p.diag.obj ⟨j⟩) := p.prop_diag_obj _
+ exact ⟨fun q i _ ↦ ⟨p.isLimit.lift (Cone.mk _
+ (Discrete.natTrans (fun ⟨j⟩ ↦ (Injective.factorThru (q ≫ p.π.app ⟨j⟩) i :)))),
+ p.isLimit.hom_ext (fun ⟨j⟩ ↦ by simp [p.isLimit.fac])⟩⟩
+
+instance [HasFiniteProducts C] : HasFiniteProducts (InjectiveObject C) where
+ out _ := inferInstance
+
+instance [Preadditive C] [HasFiniteProducts C] : HasFiniteBiproducts (InjectiveObject C) :=
+ HasFiniteBiproducts.of_hasFiniteProducts
+
+instance [HasZeroMorphisms C] [HasZeroObject C] : (isInjective C).ContainsZero where
+ exists_zero := ⟨0, by simp [IsZero.iff_id_eq_zero], Injective.zero_injective⟩
+
+/-- The inclusion `InjectiveObject C ⥤ C` of the full subcategory of
+injective objects in `C`. -/
+abbrev ι : InjectiveObject C ⥤ C := ObjectProperty.ι _
+
+instance (X : InjectiveObject C) : Injective ((ι C).obj X) := X.2
+
+instance (X : InjectiveObject C) : Injective X.obj := X.2
+
+end InjectiveObject
+
+end CategoryTheory
From 11b908e5cdd941b2d54b1b2ab55d069f5d8281d4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Iv=C3=A1n=20Renison?=
<85908989+IvanRenison@users.noreply.github.com>
Date: Thu, 25 Jun 2026 22:37:49 +0000
Subject: [PATCH 0372/1300] feat(Combinatorics/SimpleGraph/Girth): add lemma
`Walk.IsCircuit.egirth_le_length` (#37578)
Co-authored-by: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com>
---
Mathlib/Combinatorics/SimpleGraph/Girth.lean | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/Mathlib/Combinatorics/SimpleGraph/Girth.lean b/Mathlib/Combinatorics/SimpleGraph/Girth.lean
index 13b8b3f6dfd40e..409d1ea7b00153 100644
--- a/Mathlib/Combinatorics/SimpleGraph/Girth.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/Girth.lean
@@ -43,6 +43,17 @@ lemma le_egirth {n : ℕ∞} : n ≤ G.egirth ↔ ∀ a (w : G.Walk a a), w.IsCy
lemma egirth_le_length {a} {w : G.Walk a a} (h : w.IsCycle) : G.egirth ≤ w.length :=
le_egirth.mp le_rfl a w h
+lemma Walk.IsCircuit.egirth_le_length {a} {w : G.Walk a a} (hwc : w.IsCircuit) :
+ G.egirth ≤ w.length := by
+ classical
+ by_contra! hlg
+ let w' : G.Walk a a := w.cycleBypass
+ have hwc' : w'.IsCycle := hwc.isCycle_cycleBypass
+ have hwlg' : w'.length < G.egirth := by
+ grw [w.length_cycleBypass_le_length]
+ exact hlg
+ exact not_le_of_gt hwlg' (SimpleGraph.egirth_le_length hwc')
+
@[simp]
lemma egirth_eq_top : G.egirth = ⊤ ↔ G.IsAcyclic := by simp [egirth, IsAcyclic]
@@ -104,6 +115,10 @@ protected alias ⟨_, IsAcyclic.girth_eq_zero⟩ := girth_eq_zero
lemma girth_anti {G' : SimpleGraph α} (hab : G ≤ G') (h : ¬ G.IsAcyclic) : G'.girth ≤ G.girth :=
ENat.toNat_le_toNat (egirth_anti hab) <| egirth_eq_top.not.mpr h
+lemma Walk.IsCircuit.girth_le_length {a} {w : G.Walk a a} (hwc : w.IsCircuit) :
+ G.girth ≤ w.length :=
+ ENat.coe_le_coe.mp <| G.egirth.coe_toNat_le_self.trans <| hwc.egirth_le_length
+
lemma exists_girth_eq_length :
(∃ (a : α) (w : G.Walk a a), w.IsCycle ∧ G.girth = w.length) ↔ ¬ G.IsAcyclic := by
refine ⟨by tauto, fun h ↦ ?_⟩
From 490fac787fde80783e0ce38d133a20cf0e039ec9 Mon Sep 17 00:00:00 2001
From: Whysoserioushah <109107491+Whysoserioushah@users.noreply.github.com>
Date: Fri, 26 Jun 2026 07:58:21 +0000
Subject: [PATCH 0373/1300] feat(RepresentationTheory/Homological): add more
APIs to groupCohomology and groupHomology (#40714)
This is another upstreaming PR originated from the [CFT](https://github.com/kbuzzard/ClassFieldTheory) repo, it's a collaborative work from 2025 Clay Summer School on Formalizing Class Field Theory.
---
.../GroupCohomology/Functoriality.lean | 27 +++++++++++++++++++
.../GroupHomology/Functoriality.lean | 27 +++++++++++++++++++
Mathlib/RepresentationTheory/Rep/Res.lean | 2 ++
3 files changed, 56 insertions(+)
diff --git a/Mathlib/RepresentationTheory/Homological/GroupCohomology/Functoriality.lean b/Mathlib/RepresentationTheory/Homological/GroupCohomology/Functoriality.lean
index fb27295e645d1f..a4845a06718d8c 100644
--- a/Mathlib/RepresentationTheory/Homological/GroupCohomology/Functoriality.lean
+++ b/Mathlib/RepresentationTheory/Homological/GroupCohomology/Functoriality.lean
@@ -112,6 +112,11 @@ noncomputable abbrev cocyclesMap (n : ℕ) :
groupCohomology.cocycles A n ⟶ groupCohomology.cocycles B n :=
HomologicalComplex.cyclesMap (cochainsMap f φ) n
+lemma cochainsMap_congr {f g : G →* H} {φ : res f A ⟶ B} {ψ : res g A ⟶ B} (hfg : f = g)
+ (hφψ : φ.hom.toLinearMap = ψ.hom.toLinearMap) :
+ cochainsMap f φ = cochainsMap g ψ := by
+ subst hfg; congr; ext; simp [hφψ]
+
@[simp]
lemma cocyclesMap_id : cocyclesMap (MonoidHom.id G) (𝟙 B) n = 𝟙 _ :=
HomologicalComplex.cyclesMap_id _ _
@@ -137,6 +142,11 @@ noncomputable abbrev map (n : ℕ) :
groupCohomology A n ⟶ groupCohomology B n :=
HomologicalComplex.homologyMap (cochainsMap f φ) n
+lemma map_congr {f g : G →* H} {φ : res f A ⟶ B} {ψ : res g A ⟶ B} (hfg : f = g)
+ (hφψ : φ.hom.toLinearMap = ψ.hom.toLinearMap) (n : ℕ) :
+ map f φ n = map g ψ n := by
+ subst hfg; congr; ext; simp [hφψ]
+
set_option backward.isDefEq.respectTransparency false in
@[reassoc, elementwise]
theorem π_map (n : ℕ) :
@@ -161,6 +171,23 @@ theorem map_id_comp {A B C : Rep k G} (φ : A ⟶ B) (ψ : B ⟶ C) (n : ℕ) :
map (MonoidHom.id G) φ n ≫ map (MonoidHom.id G) ψ n := by
rw [map, cochainsMap_id_comp, HomologicalComplex.homologyMap_comp]
+/-- The isomorphism between cohomology groups induced by a group isomorphism `e : G ≃* H` and a
+isomorphism between representations (restricted by `e`). -/
+@[simps]
+noncomputable def mapIso (e : G ≃* H) (e' : B.V ≃ₗ[k] A.V)
+ (he : ∀ g, e' ∘ₗ B.ρ g = A.ρ (e g) ∘ₗ e') (n : ℕ) :
+ groupCohomology B n ≅ groupCohomology A n where
+ hom := groupCohomology.map e.symm (ofHom ⟨e', fun h ↦ by simp [he]⟩) n
+ inv := groupCohomology.map e (ofHom ⟨e'.symm, fun g ↦ by
+ rw [e'.toLinearMap_symm_comp_eq, ← LinearMap.comp_assoc]
+ simp [he, LinearMap.comp_assoc]⟩) n
+ hom_inv_id := by
+ rw [← groupCohomology.map_comp, ← groupCohomology.map_id]
+ exact map_congr (by simp) (by simp [res_id]) n
+ inv_hom_id := by
+ rw [← groupCohomology.map_comp, ← groupCohomology.map_id]
+ exact groupCohomology.map_congr (by simp) e'.comp_symm n
+
/-- Given a group homomorphism `f : G →* H` and a representation morphism `φ : Res(f)(A) ⟶ B`,
this is the induced map sending `x : H → A` to `(g : G) ↦ φ (x (f g))`. -/
noncomputable abbrev cochainsMap₁ :
diff --git a/Mathlib/RepresentationTheory/Homological/GroupHomology/Functoriality.lean b/Mathlib/RepresentationTheory/Homological/GroupHomology/Functoriality.lean
index 12df1c5618ad2b..f63037ce882e3d 100644
--- a/Mathlib/RepresentationTheory/Homological/GroupHomology/Functoriality.lean
+++ b/Mathlib/RepresentationTheory/Homological/GroupHomology/Functoriality.lean
@@ -64,6 +64,11 @@ noncomputable def chainsMap :
simp [Fin.comp_contractNth, map_add, inhomogeneousChains.d, Rep.hom_comm_apply φ]
rfl
+lemma chainsMap_congr {f g : G →* H} {φ : A ⟶ res f B} {ψ : A ⟶ res g B} (hfg : f = g)
+ (hφψ : φ.hom.toLinearMap = ψ.hom.toLinearMap) :
+ chainsMap f φ = chainsMap g ψ := by
+ subst hfg; congr; ext; simp [hφψ]
+
@[reassoc (attr := simp)]
lemma lsingle_comp_chainsMap_f (n : ℕ) (x : Fin n → G) :
ModuleCat.ofHom (lsingle x) ≫ (chainsMap f φ).f n =
@@ -153,6 +158,11 @@ noncomputable abbrev map (n : ℕ) :
groupHomology A n ⟶ groupHomology B n :=
HomologicalComplex.homologyMap (chainsMap f φ) n
+lemma map_congr {f g : G →* H} {φ : A ⟶ res f B} {ψ : A ⟶ res g B} (hfg : f = g)
+ (hφψ : φ.hom.toLinearMap = ψ.hom.toLinearMap) (n : ℕ) :
+ map f φ n = map g ψ n := by
+ subst hfg; congr; ext; simp [hφψ]
+
set_option backward.isDefEq.respectTransparency false in
@[reassoc, elementwise]
theorem π_map (n : ℕ) :
@@ -177,6 +187,23 @@ theorem map_id_comp {A B C : Rep k G} (φ : A ⟶ B) (ψ : B ⟶ C) (n : ℕ) :
map (MonoidHom.id G) φ n ≫ map (MonoidHom.id G) ψ n := by
rw [map, chainsMap_id_comp, HomologicalComplex.homologyMap_comp]
+/-- The isomorphism between homology groups induced by a group isomorphism `e : G ≃* H` and a
+isomorphism between representations (restricted by `e`). -/
+@[simps]
+noncomputable def mapIso (e : G ≃* H) (e' : A.V ≃ₗ[k] B.V)
+ (he : ∀ g, e' ∘ₗ A.ρ g = B.ρ (e g) ∘ₗ e') (n : ℕ) :
+ groupHomology A n ≅ groupHomology B n where
+ hom := groupHomology.map (A := A) e (ofHom ⟨e', by simp [he]⟩) n
+ inv := groupHomology.map (A := B) e.symm (ofHom ⟨e'.symm, fun h ↦ by
+ rw [LinearEquiv.toLinearMap_symm_comp_eq, ← LinearMap.comp_assoc]
+ simp [he, LinearMap.comp_assoc]⟩) n
+ hom_inv_id := by
+ rw [← groupHomology.map_comp, ← groupHomology.map_id]
+ exact groupHomology.map_congr e.coe_monoidHom_symm_comp_coe_monoidHom e'.symm_comp n
+ inv_hom_id := by
+ rw [← groupHomology.map_comp, ← groupHomology.map_id]
+ exact groupHomology.map_congr e.coe_monoidHom_comp_coe_monoidHom_symm e'.comp_symm n
+
/-- Given a group homomorphism `f : G →* H` and a representation morphism `φ : A ⟶ Res(f)(B)`,
this is the induced map sending `∑ aᵢ·gᵢ : G →₀ A` to `∑ φ(aᵢ)·f(gᵢ) : H →₀ B`. -/
noncomputable abbrev chainsMap₁ : ModuleCat.of k (G →₀ A) ⟶ ModuleCat.of k (H →₀ B) :=
diff --git a/Mathlib/RepresentationTheory/Rep/Res.lean b/Mathlib/RepresentationTheory/Rep/Res.lean
index 55467f78b051bd..5ccc77a7f66c4e 100644
--- a/Mathlib/RepresentationTheory/Rep/Res.lean
+++ b/Mathlib/RepresentationTheory/Rep/Res.lean
@@ -35,6 +35,8 @@ abbrev res (f : H →* G) (M : Rep k G) := (resFunctor f).obj M
variable (f : H →* G) (M : Rep k G)
+lemma res_id : res (MonoidHom.id G) M = M := rfl
+
@[simp] lemma res_obj_ρ : (res f M).ρ = (M.ρ.comp f) := rfl
lemma coe_res_obj_ρ' (h : H) : (res f M).ρ h = M.ρ (f h) := rfl
From 88f283c91564068ad6925562300f93ec6ec033f1 Mon Sep 17 00:00:00 2001
From: Thomas Browning <13339017+tb65536@users.noreply.github.com>
Date: Fri, 26 Jun 2026 07:58:23 +0000
Subject: [PATCH 0374/1300] chore(Algebra/Algebra/Basic): delete redundant
`Algebra.ofSubring` instance (#40914)
The instance `Algebra.ofSubring` is a specialization of `Algebra.ofSubsemiring` and is causing diamonds in #40912.
Co-authored-by: tb65536
Co-authored-by: Monica Omar <23701951+themathqueen@users.noreply.github.com>
---
Mathlib/Algebra/Algebra/Basic.lean | 4 ----
1 file changed, 4 deletions(-)
diff --git a/Mathlib/Algebra/Algebra/Basic.lean b/Mathlib/Algebra/Algebra/Basic.lean
index 9cbe3d32bdbb75..7888df545f1144 100644
--- a/Mathlib/Algebra/Algebra/Basic.lean
+++ b/Mathlib/Algebra/Algebra/Basic.lean
@@ -113,10 +113,6 @@ theorem coe_algebraMap_ofSubsemiring (S : C) : (algebraMap S R : S → R) = Subt
theorem algebraMap_ofSubsemiring_apply (S : C) (x : S) : algebraMap S R x = x :=
rfl
-/-- Algebra over a subring. This builds upon `Subring.module`. -/
-instance ofSubring {R A : Type*} [CommRing R] [Ring A] [Algebra R A] (S : Subring R) :
- Algebra S A := inferInstance
-
theorem algebraMap_ofSubring {R : Type*} [CommRing R] (S : Subring R) :
(algebraMap S R : S →+* R) = S.subtype :=
rfl
From 087d8a23b1152e4cac684614729d4690a24345bd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=ABl=20Riou?=
<37772949+joelriou@users.noreply.github.com>
Date: Fri, 26 Jun 2026 08:33:19 +0000
Subject: [PATCH 0375/1300] feat(CategoryTheory/Presentable): the category of
types is locally presentable (#41048)
---
Mathlib/CategoryTheory/Presentable/Type.lean | 25 ++++++++++++++++++--
1 file changed, 23 insertions(+), 2 deletions(-)
diff --git a/Mathlib/CategoryTheory/Presentable/Type.lean b/Mathlib/CategoryTheory/Presentable/Type.lean
index 81f42fa1a9c37d..e7b611774ca7f3 100644
--- a/Mathlib/CategoryTheory/Presentable/Type.lean
+++ b/Mathlib/CategoryTheory/Presentable/Type.lean
@@ -5,8 +5,8 @@ Authors: Joël Riou
-/
module
-public import Mathlib.CategoryTheory.Presentable.Basic
-public import Mathlib.CategoryTheory.Limits.Types.Filtered
+public import Mathlib.CategoryTheory.Generator.Type
+public import Mathlib.CategoryTheory.Presentable.StrongGenerator
public import Mathlib.CategoryTheory.Types.Set
/-!
@@ -145,6 +145,27 @@ instance (X : Type u) : IsPresentable.{u} X := by
have := hX.isCardinalPresentable
exact isPresentable_of_isCardinalPresentable X κ
+lemma isStrongGenerator_punit :
+ (ObjectProperty.singleton (PUnit.{u + 1})).IsStrongGenerator := by
+ rw [ObjectProperty.isStrongGenerator_iff]
+ refine ⟨isSeparator_punit, fun _ _ i hi₁ hi₂ ↦ ?_⟩
+ · rw [mono_iff_injective] at hi₁
+ rw [isIso_iff_bijective]
+ refine ⟨hi₁, fun y ↦ ?_⟩
+ obtain ⟨f, hf⟩ := hi₂ PUnit ⟨.unit⟩ (↾fun _ ↦ y)
+ exact ⟨f .unit, ConcreteCategory.congr_hom hf .unit⟩
+
+instance (κ : Cardinal.{u}) [Fact κ.IsRegular] :
+ IsCardinalLocallyPresentable (Type u) κ := by
+ rw [IsCardinalLocallyPresentable.iff_exists_isStrongGenerator]
+ exact ⟨.singleton PUnit, inferInstance, isStrongGenerator_punit, by
+ simp only [ObjectProperty.singleton_le_iff,
+ CategoryTheory.isCardinalPresentable_iff, isCardinalPresentable_iff]
+ exact hasCardinalLT_of_finite _ _ (Cardinal.IsRegular.aleph0_le Fact.out)⟩
+
+instance : IsLocallyPresentable.{u} (Type u) where
+ exists_cardinal := ⟨_, Cardinal.fact_isRegular_aleph0, inferInstance⟩
+
end Types
end CategoryTheory
From 165cce0299b3eaa398bf56cbee85e86ff948dc5f Mon Sep 17 00:00:00 2001
From: Riccardo Brasca
Date: Fri, 26 Jun 2026 09:08:36 +0000
Subject: [PATCH 0376/1300] feat: add lemmas about primitive roots (#40555)
---
.../NumberTheory/NumberField/Units/Basic.lean | 5 +++++
.../RootsOfUnity/CyclotomicUnits.lean | 20 +++++++++++++++++++
.../RootsOfUnity/PrimitiveRoots.lean | 4 ++++
3 files changed, 29 insertions(+)
diff --git a/Mathlib/NumberTheory/NumberField/Units/Basic.lean b/Mathlib/NumberTheory/NumberField/Units/Basic.lean
index 6a5ddcb0ad9a36..6a8326e2412435 100644
--- a/Mathlib/NumberTheory/NumberField/Units/Basic.lean
+++ b/Mathlib/NumberTheory/NumberField/Units/Basic.lean
@@ -77,6 +77,11 @@ variable {K}
theorem coe_coe (u : (𝓞 K)ˣ) : ((u : 𝓞 K) : K) = (u : K) := rfl
+theorem _root_.IsPrimitiveRoot.coe_coe_iff {ν : (𝓞 K)ˣ} {n : ℕ} :
+ IsPrimitiveRoot (ν : K) n ↔ IsPrimitiveRoot ν n :=
+ IsPrimitiveRoot.map_iff_of_injective
+ (f := (algebraMap (𝓞 K) K).toMonoidHom.comp (Units.coeHom (𝓞 K))) (coe_injective K)
+
theorem coe_mul (x y : (𝓞 K)ˣ) : ((x * y : (𝓞 K)ˣ) : K) = (x : K) * (y : K) := rfl
theorem coe_pow (x : (𝓞 K)ˣ) (n : ℕ) : ((x ^ n : (𝓞 K)ˣ) : K) = (x : K) ^ n := by
diff --git a/Mathlib/RingTheory/RootsOfUnity/CyclotomicUnits.lean b/Mathlib/RingTheory/RootsOfUnity/CyclotomicUnits.lean
index 3da9cd567185af..fc7e3eb66e9157 100644
--- a/Mathlib/RingTheory/RootsOfUnity/CyclotomicUnits.lean
+++ b/Mathlib/RingTheory/RootsOfUnity/CyclotomicUnits.lean
@@ -39,6 +39,20 @@ variable {n i j p : ℕ} {A K : Type*} {ζ : A}
variable [CommRing A] [IsDomain A] {R : Type*} [CommRing R] [Algebra R A]
+/-- If `ζ ^ n = 1` and `ζ ≠ 1`, then `ζ - 1` divides `n`. This does not require `ζ` to be a
+ primitive root of unity, only a root of unity different from `1`. -/
+theorem sub_one_dvd_natCast_of_pow_eq_one (hζ : ζ ^ n = 1) (hζ1 : ζ ≠ 1) : ζ - 1 ∣ (n : A) := by
+ have key : (n : A) = ∑ i ∈ range n, (1 - ζ ^ i) := by
+ have hgs : ∑ i ∈ range n, ζ ^ i = 0 := by
+ have := geom_sum_mul ζ n
+ rw [hζ, sub_self] at this
+ exact (mul_eq_zero.1 this).resolve_right fun h ↦ hζ1 (sub_eq_zero.1 h)
+ rw [Finset.sum_sub_distrib, hgs, sub_zero, Finset.sum_const, card_range, nsmul_eq_mul, mul_one]
+ rw [key]
+ refine Finset.dvd_sum fun i _ ↦ ?_
+ have h : ζ - 1 ∣ ζ ^ i - 1 := by simpa using sub_dvd_pow_sub_pow ζ 1 i
+ rwa [← dvd_neg, neg_sub] at h
+
namespace IsPrimitiveRoot
/-- Given an `n`-th primitive root of unity `ζ,` we have that `ζ - 1` and `ζ ^ j - 1` are associated
@@ -136,4 +150,10 @@ lemma nthRootsFinset_pairwise_associated_sub_one_sub_of_prime (hζ : IsPrimitive
alias ntRootsFinset_pairwise_associated_sub_one_sub_of_prime :=
nthRootsFinset_pairwise_associated_sub_one_sub_of_prime
+/-- Given an `n`-th primitive root of unity `ζ`, where `1 < n`, we have that `ζ - 1` divides `n`.
+ In particular, if `ζ` is a `p`-th primitive root of unity with `p` prime, then `ζ - 1` divides
+ `p`. -/
+theorem sub_one_dvd_natCast (hζ : IsPrimitiveRoot ζ n) (hn : 1 < n) : ζ - 1 ∣ (n : A) :=
+ sub_one_dvd_natCast_of_pow_eq_one hζ.pow_eq_one (hζ.ne_one hn)
+
end IsPrimitiveRoot
diff --git a/Mathlib/RingTheory/RootsOfUnity/PrimitiveRoots.lean b/Mathlib/RingTheory/RootsOfUnity/PrimitiveRoots.lean
index b3e330e84338aa..be1a3f7a9616f5 100644
--- a/Mathlib/RingTheory/RootsOfUnity/PrimitiveRoots.lean
+++ b/Mathlib/RingTheory/RootsOfUnity/PrimitiveRoots.lean
@@ -156,6 +156,10 @@ theorem one_right_iff : IsPrimitiveRoot ζ 1 ↔ ζ = 1 := by
· intro h; rw [← pow_one ζ, h.pow_eq_one]
· rintro rfl; exact one
+@[simp]
+theorem one_left_iff : IsPrimitiveRoot (1 : M) k ↔ k = 1 :=
+ ⟨fun h ↦ Nat.dvd_one.mp (h.dvd_of_pow_eq_one 1 (one_pow _)), fun e ↦ e ▸ one⟩
+
@[simp]
theorem coe_submonoidClass_iff {M B : Type*} [CommMonoid M] [SetLike B M] [SubmonoidClass B M]
{N : B} {ζ : N} : IsPrimitiveRoot (ζ : M) k ↔ IsPrimitiveRoot ζ k := by
From 179c414389904eae3185dd468ff162b0384966ea Mon Sep 17 00:00:00 2001
From: Andrew Yang <36414270+erdOne@users.noreply.github.com>
Date: Fri, 26 Jun 2026 09:39:03 +0000
Subject: [PATCH 0377/1300] feat(RingTheory): etale local decomposition of
finite extensions (#41034)
---
Mathlib.lean | 1 +
Mathlib/Logic/Equiv/Fin/Basic.lean | 3 +
Mathlib/RingTheory/Etale/QuasiFinite.lean | 196 ++++++++++++++++++
Mathlib/RingTheory/Ideal/Quotient/Over.lean | 38 ++++
Mathlib/RingTheory/Idempotents.lean | 41 ++++
.../LocalRing/ResidueField/Ideal.lean | 3 +
.../RingTheory/TensorProduct/Quotient.lean | 41 ++++
7 files changed, 323 insertions(+)
create mode 100644 Mathlib/RingTheory/Ideal/Quotient/Over.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 32975106c2b8df..1e5e2a2ac83c42 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -6664,6 +6664,7 @@ public import Mathlib.RingTheory.Ideal.Quotient.Index
public import Mathlib.RingTheory.Ideal.Quotient.Nilpotent
public import Mathlib.RingTheory.Ideal.Quotient.Noetherian
public import Mathlib.RingTheory.Ideal.Quotient.Operations
+public import Mathlib.RingTheory.Ideal.Quotient.Over
public import Mathlib.RingTheory.Ideal.Quotient.PowTransition
public import Mathlib.RingTheory.Ideal.Span
public import Mathlib.RingTheory.IdealFilter.Basic
diff --git a/Mathlib/Logic/Equiv/Fin/Basic.lean b/Mathlib/Logic/Equiv/Fin/Basic.lean
index 341c95b6f54503..5bea2e2b9b043e 100644
--- a/Mathlib/Logic/Equiv/Fin/Basic.lean
+++ b/Mathlib/Logic/Equiv/Fin/Basic.lean
@@ -126,6 +126,9 @@ theorem finSuccEquiv_zero : (finSuccEquiv n) 0 = none :=
theorem finSuccEquiv_succ (m : Fin n) : (finSuccEquiv n) m.succ = some m :=
finSuccEquiv'_above (Fin.zero_le _)
+@[simp]
+theorem finSuccEquiv_last (n : ℕ) : finSuccEquiv (n + 1) (Fin.last (n + 1)) = Fin.last n := rfl
+
@[simp]
theorem finSuccEquiv_symm_none : (finSuccEquiv n).symm none = 0 :=
finSuccEquiv'_symm_none _
diff --git a/Mathlib/RingTheory/Etale/QuasiFinite.lean b/Mathlib/RingTheory/Etale/QuasiFinite.lean
index 41233a5afde995..b9b01203bd9345 100644
--- a/Mathlib/RingTheory/Etale/QuasiFinite.lean
+++ b/Mathlib/RingTheory/Etale/QuasiFinite.lean
@@ -7,6 +7,7 @@ module
public import Mathlib.RingTheory.Polynomial.UniversalFactorizationRing
public import Mathlib.RingTheory.ZariskisMainTheorem
+public import Mathlib.RingTheory.Ideal.Quotient.Over
/-!
# Etale local structure of finite maps
@@ -435,3 +436,198 @@ lemma Algebra.exists_etale_isIdempotentElem_forall_liesOver_eq
(Localization.Away f ⊗[R] S) (.powers (f ⊗ₜ 1)) (a₁ := ⟨P'', ‹_›⟩)
(a₂ := ⟨P'f, ‹_›⟩) (PrimeSpectrum.ext ?_)).1)
exact (H (P''.under _) inferInstance inferInstance hP'').trans (P'f.over_def P')
+
+open TensorProduct
+
+attribute [local instance] RingHom.ker_isPrime
+
+open scoped nonZeroDivisors
+
+attribute [local instance] Localization.AtPrime.algebraOfLiesOver
+
+/-- A key induction step of `exists_etale_completeOrthogonalIdempotents_forall_liesOver_eq`. -/
+private theorem Algebra.exists_etale_completeOrthogonalIdempotents_forall_liesOver_eq_aux
+ {R : Type u} {S : Type (max u v)} [CommRing R] [CommRing S] [Algebra R S] [Module.Finite R S]
+ (p : Ideal R) [p.IsPrime] (q : Ideal S) [q.IsPrime]
+ [q.LiesOver p] (R' : Type u) [CommRing R'] [Algebra R R'] [Algebra.Etale R R'] (P : Ideal R')
+ [P.IsPrime] [P.LiesOver p] (e : R' ⊗[R] S) (P' : Ideal (R' ⊗[R] S))
+ [P'.IsPrime] [P'.LiesOver P]
+ (hP'q : Ideal.comap Algebra.TensorProduct.includeRight.toRingHom P' = q)
+ (heP' : e ∉ P') (hpP : Function.Bijective
+ (Ideal.ResidueField.mapₐ p P (Algebra.ofId _ _) (P.over_def p)))
+ (H : ∀ (P'' : Ideal (R' ⊗[R] S)), P''.IsPrime → P''.LiesOver P → e ∉ P'' → P'' = P')
+ (R'' : Type u) [CommRing R''] [Algebra R' R''] [Algebra R R''] [IsScalarTower R R' R'']
+ [Algebra.Etale R' R''] (Q : Ideal R'')
+ [Q.IsPrime] [Q.LiesOver P] (n : ℕ)
+ (e' : Fin ((n + 1) + 1) → R'' ⊗[R] S)
+ (he' : CompleteOrthogonalIdempotents e')
+ (he'0 : e' 0 = Algebra.TensorProduct.map (Algebra.ofId R' R'') (AlgHom.id R S) e)
+ (Q' : Fin n → Ideal (R'' ⊗[R] S)) [∀ i, (Q' i).IsPrime] [∀ i, (Q' i).LiesOver Q]
+ (hPQ : Function.Bijective (Ideal.ResidueField.mapₐ P Q (Algebra.ofId _ _) (Q.over_def P)))
+ (hQ' : ∀ (i : Fin n), e' i.succ.castSucc ∉ Q' i)
+ (H' : ∀ (P'' : Ideal (R'' ⊗[R] S)), e' 0 ∈ P'' → P''.IsPrime → P''.LiesOver Q →
+ e' (.last _) ∈ P'' ∧ ∀ (i : Fin n), e' i.succ.castSucc ∉ P'' → P'' = Q' i) :
+ ∃ (R' : Type u) (_ : CommRing R') (_ : Algebra R R') (_ : Algebra.Etale R R') (P : Ideal R')
+ (_ : P.IsPrime) (_ : P.LiesOver p) (n : ℕ) (e : Fin (n + 1) → R' ⊗[R] S)
+ (_ : CompleteOrthogonalIdempotents e) (P' : Fin n → Ideal (R' ⊗[R] S))
+ (_ : ∀ i, (P' i).IsPrime) (_ : ∀ i, (P' i).LiesOver P),
+ Function.Bijective (Ideal.ResidueField.mapₐ p P (Algebra.ofId _ _) (P.over_def p)) ∧
+ (∀ i, e i.castSucc ∉ P' i) ∧
+ ∀ (P'' : Ideal (R' ⊗[R] S)), P''.IsPrime → P''.LiesOver P →
+ e (.last n) ∈ P'' ∧ ∀ i, e i.castSucc ∉ P'' → P'' = P' i := by
+ let φ := Algebra.TensorProduct.map (Algebra.ofId R' R'') (AlgHom.id R S)
+ have : Q.LiesOver p := .trans _ P _
+ have hpQ :
+ Function.Bijective (Ideal.ResidueField.mapₐ p Q (Algebra.ofId _ _) (Q.over_def p)) := by
+ convert hPQ.comp hpP
+ rw [← @AlgHom.coe_restrictScalars' R R', ← AlgHom.coe_comp]; congr 1; ext
+ let P'φ := (Ideal.fiberIsoOfBijectiveResidueField hpQ).symm
+ (Ideal.fiberIsoOfBijectiveResidueField hpP ⟨P', ‹_›, ‹_›⟩)
+ have : P'φ.1.LiesOver P := .trans _ Q _
+ have : (P'φ.1.comap φ.toRingHom).LiesOver P := inferInstanceAs ((P'φ.1.comap φ).LiesOver P)
+ have hP'φ : P'φ.1.comap φ.toRingHom = P' := by
+ apply Ideal.eq_of_comap_eq_comap_of_bijective_residueFieldMap hpP
+ rw [Ideal.comap_comap]
+ convert Ideal.comap_fiberIsoOfBijectiveResidueField_symm hpQ _
+ · ext; simp [φ]
+ · simp; rfl
+ refine ⟨R'', inferInstance, _, .comp R R' R'', Q, ‹_›, .trans _ P _, _, _, he', Fin.cons P'φ
+ Q', Fin.cases P'φ.2.1 ?_, Fin.cases P'φ.2.2 ?_, hpQ, Fin.cases ?_ ?_, ?_⟩
+ · intro P'' _ _
+ by_cases heP'' : e ∈ P''.comap φ
+ · obtain ⟨h₁, h₂⟩ := H' P'' (by simpa [he'0]) inferInstance inferInstance
+ exact ⟨h₁, Fin.cases (fun h ↦ (h (by simpa [he'0])).elim) (by simpa)⟩
+ · have : P''.LiesOver P := .trans _ Q _
+ obtain rfl := H _ inferInstance inferInstance heP''
+ have : ∀ i ≠ 0, e' i ∈ P'' := by
+ intro j hj
+ rw [← Ideal.IsPrime.mul_mem_left_iff (I := P'') heP'']
+ simp [φ, ← he'0, he'.ortho hj.symm]
+ refine ⟨by simp [this], Fin.cases (fun _ ↦ ?_) (by simp [this])⟩
+ simp only [Fin.cons_zero]
+ apply Ideal.eq_of_comap_eq_comap_of_bijective_residueFieldMap hpQ
+ have : (φ.restrictScalars _).comp Algebra.TensorProduct.includeRight =
+ Algebra.TensorProduct.includeRight := by ext; simp [φ]
+ rw [← this]
+ exact congr(($hP'φ).comap Algebra.TensorProduct.includeRight).symm
+ · simp only [Fin.cons_succ]; infer_instance
+ · simp only [Fin.cons_succ]; infer_instance
+ · rw [← hP'φ] at heP'; simpa [he'0]
+ · simpa
+
+set_option backward.isDefEq.respectTransparency false in
+/-- A less universe polymorphic version of
+`exists_etale_completeOrthogonalIdempotents_forall_liesOver_eq`. Use that instead. -/
+private lemma Algebra.exists_etale_completeOrthogonalIdempotents_forall_liesOver_eq'
+ {R : Type u} {S : Type max u v} [CommRing R] [CommRing S] [Algebra R S] [Module.Finite R S]
+ (p : Ideal R) [p.IsPrime] :
+ ∃ (R' : Type u) (_ : CommRing R') (_ : Algebra R R') (_ : Algebra.Etale R R') (P : Ideal R')
+ (_ : P.IsPrime) (_ : P.LiesOver p) (n : ℕ) (e : Fin (n + 1) → R' ⊗[R] S)
+ (_ : CompleteOrthogonalIdempotents e) (P' : Fin n → Ideal (R' ⊗[R] S))
+ (_ : ∀ i, (P' i).IsPrime) (_ : ∀ i, (P' i).LiesOver P),
+ Function.Bijective (Ideal.ResidueField.mapₐ p P (Algebra.ofId _ _) (P.over_def p)) ∧
+ (∀ i, e i.castSucc ∉ P' i) ∧
+ ∀ (P'' : Ideal (R' ⊗[R] S)), P''.IsPrime → P''.LiesOver P →
+ e (.last n) ∈ P'' ∧ ∀ i, e i.castSucc ∉ P'' → P'' = P' i := by
+ induction h : (p.primesOver S).ncard using Nat.strong_induction_on generalizing R S with
+ | h n IH =>
+ have : IsArtinianRing (p.ResidueField ⊗[R] S) := IsArtinianRing.of_finite p.ResidueField _
+ have hpSfin : (p.primesOver S).Finite :=
+ (PrimeSpectrum.primesOverOrderIsoFiber R S p).finite_iff.mpr inferInstance
+ cases n with
+ | zero =>
+ have := (Set.ncard_eq_zero hpSfin).mp h
+ refine ⟨R, inferInstance, inferInstance, inferInstance, p, inferInstance, ⟨rfl⟩, 0, 1,
+ ⟨⟨by simp [IsIdempotentElem],
+ by simp only [Nat.reduceAdd, Pi.one_apply, mul_one, Subsingleton.pairwise]⟩,
+ by simp⟩, nofun, nofun, nofun, ?_, nofun, ?_⟩
+ · rw! [ofId_self, Ideal.ResidueField.mapₐ_id]; exact Function.bijective_id
+ · exact fun P h₁ h₂ ↦ (this.le
+ ⟨show (P.comap Algebra.TensorProduct.includeRight.toRingHom).IsPrime from inferInstance,
+ ⟨by simp [P.over_def p, Ideal.under, Ideal.comap_comap]⟩⟩).elim
+ | succ n =>
+ obtain ⟨q, hq, hq'⟩ := Set.nonempty_of_ncard_ne_zero (h.trans_ne (by simp))
+ obtain ⟨R', _, _, _, P, _, _, e, he, P', _, _, hP'q, heP', hpP, _, H⟩ :=
+ Algebra.exists_etale_isIdempotentElem_forall_liesOver_eq p q
+ have : (P.primesOver (R' ⊗[R] S ⧸ Ideal.span {e})).ncard < n + 1 := by
+ let F := Ideal.fiberIsoOfBijectiveResidueField hpP (S := S)
+ refine (Ideal.ncard_primesOver_quotient_singleton_lt_of_notMem _ _
+ P' heP' (F.finite_iff.mpr hpSfin)).trans_le ?_
+ rw [← h, ← Nat.card_coe_set_eq, ← Nat.card_coe_set_eq, Nat.card_congr F.toEquiv]
+ obtain ⟨R'', _, _, _, Q, _, _, n, e' : _ → R'' ⊗[R'] (R' ⊗[R] S ⧸ Ideal.span {e}),
+ he', Q' : _ → Ideal (R'' ⊗[R'] (R' ⊗[R] S ⧸ Ideal.span {e})), _, _, hPQ, hQ', H'⟩ :=
+ IH _ this (R := R') (S := R' ⊗[R] S ⧸ Ideal.span {e}) P rfl
+ let : Algebra R R'' := .compHom _ (algebraMap R R')
+ have : IsScalarTower R R' R'' := .of_algebraMap_eq' rfl
+ let φ := Algebra.TensorProduct.map (Algebra.ofId R' R'') (AlgHom.id R S)
+ let e₁ : R'' ⊗[R'] (R' ⊗[R] S ⧸ Ideal.span {e}) ≃ₐ[R''] (R'' ⊗[R] S ⧸ Ideal.span {φ e}) :=
+ tensorQuotientTensorEquiv (R'' := R'') e
+ obtain ⟨e'', he'', he''e'⟩ := CompleteOrthogonalIdempotents.exists_eq_comp_of_ker_eq_span
+ (Ideal.Quotient.mk (Ideal.span {φ e})) (I := Fin (n + 1)) (φ e) (he.map φ) (by simp)
+ (e₁ ∘ e') (he'.map e₁.toRingHom) (fun _ ↦ Ideal.Quotient.mk_surjective _)
+ have he''e'' (i : _) : e₁ (e' i) = e'' i := congr_fun he''e' i
+ have hψe'' (i : _) : (e' i) = e₁.symm (e'' i) := e₁.eq_symm_apply.mpr (he''e'' i)
+ refine exists_etale_completeOrthogonalIdempotents_forall_liesOver_eq_aux p q R' P e P'
+ hP'q heP' hpP (fun P'' h₁ h₂ heP'' ↦ H P'' h₁ h₂ heP'') R'' Q n _
+ ((CompleteOrthogonalIdempotents.equiv (finSuccEquiv _)).mpr he'') rfl
+ (Q' · |>.comap (e₁.symm.toAlgHom.comp (Ideal.Quotient.mkₐ _ _))) hPQ
+ (fun i ↦ by rw [Function.comp_def]; simpa [← hψe''] using hQ' i) ?_
+ simp only [Function.comp_apply, finSuccEquiv_zero, finSuccEquiv_last, Fin.castSucc_succ,
+ finSuccEquiv_succ]
+ intro P'' heP'' _ _
+ have : (P''.map (Ideal.Quotient.mk (.span {φ e}))).IsPrime :=
+ Ideal.map_isPrime_of_surjective Ideal.Quotient.mk_surjective (by simpa [Ideal.span_le])
+ have : (P''.map (Ideal.Quotient.mk (.span {φ e}))).LiesOver Q := ⟨by
+ have : P'' ⊔ Ideal.span {φ e} = P'' := by simpa [Ideal.span_le]
+ rw [← Ideal.under_under (B := R'' ⊗[R] S)]
+ simpa [Ideal.under, Ideal.comap_map_of_surjective _ Ideal.Quotient.mk_surjective,
+ ← RingHom.ker_eq_comap_bot, this] using P''.over_def Q⟩
+ have := H' ((P''.map (Ideal.Quotient.mk (.span {φ e}))).comap e₁) inferInstance
+ (inferInstanceAs <| ((P''.map (Ideal.Quotient.mk (.span {φ e}))).comap
+ e₁.toAlgHom).LiesOver Q)
+ have hP'' : (1 - φ e) ∉ P'' :=
+ fun h ↦ ‹P''.IsPrime›.one_notMem (by convert add_mem heP'' h; ring)
+ simp only [Ideal.mem_comap, he''e'',
+ Ideal.mem_map_span_singleton_iff_of_isIdempotentElem (he.map φ),
+ Ideal.IsPrime.mul_mem_left_iff hP''] at this
+ refine ⟨this.1, fun i hi ↦ (this.2 i hi).symm ▸ ?_⟩
+ -- TODO: clean-up when `Ideal.comap` is refactored to take a `RingHom`
+ change _ = Ideal.comap (Ideal.Quotient.mk _) (Ideal.comap (e₁.symm.trans e₁).toRingHom _)
+ simp only [AlgEquiv.symm_trans_self, RingEquiv.toRingHom_eq_coe,
+ AlgEquiv.toRingEquiv_toRingHom, AlgEquiv.refl_toRingHom, Ideal.comap_id]
+ rw [Ideal.comap_map_of_surjective _ Ideal.Quotient.mk_surjective]
+ simpa [left_eq_sup, ← RingHom.ker_eq_comap_bot, Ideal.span_le] using heP''
+
+/--
+If `S` is finite over `R`, and `p` is a prime of `R`, then there exists an étale neighborhood
+`(R', P)` of `p` with `κ(p) = κ(P)` such that `R' ⊗[R] S ≃ₐ[R'] R₁ × ... × Rₙ × A`,
+each `Rᵢ` has a unique prime `Pᵢ` lying over `P`, and no other prime in `R' ⊗[R] S` lies over `P`.
+
+This is merely an iterated application of `exists_etale_isIdempotentElem_forall_liesOver_eq`.
+This is weaker than the corresponding statement of stacks project (in particular we asked for
+`Module.Finite` instead of quasi finite when localized at `p`, so that we don't need to keep
+track of this when passing to quotients and tensor products), and the only reason is that
+the corresponding stronger statement is even harder to state and even more annoying to prove.
+-/
+@[stacks 00UL]
+lemma Algebra.exists_etale_completeOrthogonalIdempotents_forall_liesOver_eq
+ {R : Type u} {S : Type v} [CommRing R] [CommRing S] [Algebra R S] [Module.Finite R S]
+ (p : Ideal R) [p.IsPrime] :
+ ∃ (R' : Type u) (_ : CommRing R') (_ : Algebra R R') (_ : Algebra.Etale R R') (P : Ideal R')
+ (_ : P.IsPrime) (_ : P.LiesOver p) (n : ℕ) (e : Fin (n + 1) → R' ⊗[R] S)
+ (_ : CompleteOrthogonalIdempotents e) (P' : Fin n → Ideal (R' ⊗[R] S))
+ (_ : ∀ i, (P' i).IsPrime) (_ : ∀ i, (P' i).LiesOver P),
+ Function.Bijective (Ideal.ResidueField.mapₐ p P (Algebra.ofId _ _) (P.over_def p)) ∧
+ (∀ i, e i.castSucc ∉ P' i) ∧
+ ∀ (P'' : Ideal (R' ⊗[R] S)), P''.IsPrime → P''.LiesOver P →
+ e (.last n) ∈ P'' ∧ ∀ i, e i.castSucc ∉ P'' → P'' = P' i := by
+ have ⟨R', _, _, _, P, _, _, n, e, he, P', _, _, hP, hP', H⟩ :=
+ exists_etale_completeOrthogonalIdempotents_forall_liesOver_eq' (S := ULift.{u} S) p
+ let e₁ : R' ⊗[R] S ≃ₐ[R'] R' ⊗[R] ULift.{u} S :=
+ Algebra.TensorProduct.congr .refl ULift.algEquiv.symm
+ refine ⟨R', _, _, ‹_›, P, ‹_›, ‹_›, n, e₁.symm ∘ e, he.map _,
+ fun i ↦ (P' i).comap e₁.toAlgHom, inferInstance, inferInstance, hP, by simpa,
+ fun P'' _ _ ↦ ?_⟩
+ have := H (P''.comap e₁.symm.toAlgHom) inferInstance inferInstance
+ refine ⟨by simpa using this.1, fun i hi ↦ ?_⟩
+ simp [← this.2 i (by simpa), Ideal.comap_comapₐ]
diff --git a/Mathlib/RingTheory/Ideal/Quotient/Over.lean b/Mathlib/RingTheory/Ideal/Quotient/Over.lean
new file mode 100644
index 00000000000000..8758ad62465ed9
--- /dev/null
+++ b/Mathlib/RingTheory/Ideal/Quotient/Over.lean
@@ -0,0 +1,38 @@
+/-
+Copyright (c) 2026 Andrew Yang. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Andrew Yang
+-/
+module
+
+public import Mathlib.Data.Set.Card
+public import Mathlib.RingTheory.Ideal.Over
+
+/-! # Lemmas about `primesOver` in quotient rings. -/
+
+@[expose] public section
+
+variable {R S T : Type*} [CommRing R] [CommRing S] [CommRing T] [Algebra R S] [Algebra R T]
+
+/-- Given a prime `P` of `R` and an ideal `I` in an `R`-algebra `S`.
+Suppose we can find a prime `P'` over `P`, not containing `I`,
+then the number of primes of `S / I` over `P`
+is strictly less than primes of `S` over `P` (provided they are finite).
+
+The lemma is stated in terms of surjections for syntactic generality. -/
+lemma Ideal.ncard_primesOver_lt_of_not_le
+ (f : S →ₐ[R] T) (Hf : Function.Surjective f)
+ (P : Ideal R) (P' : Ideal S) [P'.IsPrime] [P'.LiesOver P]
+ (hkP' : ¬ RingHom.ker f.toRingHom ≤ P') (H : (P.primesOver S).Finite) :
+ (P.primesOver T).ncard < (P.primesOver S).ncard := by
+ rw [← Set.ncard_image_of_injective _ (Ideal.comap_injective_of_surjective _ Hf)]
+ refine Set.ncard_lt_ncard (Set.ssubset_iff_exists.mpr ⟨?_, P', ⟨‹_›, ‹_›⟩, ?_⟩) H
+ · rintro _ ⟨q, ⟨_, _⟩, rfl⟩
+ exact ⟨inferInstance, inferInstanceAs ((q.comap f).LiesOver _)⟩
+ · rintro ⟨q, ⟨_, _⟩, rfl⟩; exact hkP' (Ideal.ker_le_comap _)
+
+lemma Ideal.ncard_primesOver_quotient_singleton_lt_of_notMem
+ (P : Ideal R) (e : S) (P' : Ideal S) [P'.IsPrime] [P'.LiesOver P]
+ (heP' : e ∉ P') (H : (P.primesOver S).Finite) :
+ (P.primesOver (S ⧸ Ideal.span {e})).ncard < (P.primesOver S).ncard :=
+ Ideal.ncard_primesOver_lt_of_not_le _ (Ideal.Quotient.mkₐ_surjective R _) _ P' (by simpa) H
diff --git a/Mathlib/RingTheory/Idempotents.lean b/Mathlib/RingTheory/Idempotents.lean
index 32e9eb9f8daab6..815c057d4267b7 100644
--- a/Mathlib/RingTheory/Idempotents.lean
+++ b/Mathlib/RingTheory/Idempotents.lean
@@ -8,6 +8,7 @@ module
public import Mathlib.Algebra.BigOperators.Fin
public import Mathlib.Algebra.Ring.GeomSum
public import Mathlib.RingTheory.Ideal.Quotient.Operations
+public import Mathlib.Tactic.LinearCombination
/-!
@@ -478,6 +479,36 @@ noncomputable def AlgEquiv.prodQuotientOfIsIdempotentElem
AlgEquiv.ofBijective ((Ideal.Quotient.mkₐ _ _).prod (Ideal.Quotient.mkₐ _ _)) <|
RingHom.prod_bijective_of_isIdempotentElem he hf hef₁ hef₂
+/-- One can lift a family of complete orthogonal idempotents of `R/e₀` to get one on `R`.
+
+Note that the lemma itself is stated in terms of surjections (where `T = S / I`)
+instead for syntactic generality. -/
+lemma CompleteOrthogonalIdempotents.exists_eq_comp_of_ker_eq_span
+ (f : R →+* S) (e₀ : R) (he₀ : IsIdempotentElem e₀) (hfe₀ : RingHom.ker f = .span {e₀})
+ (e : I → S) (he : CompleteOrthogonalIdempotents e) (hef : ∀ i, e i ∈ f.range) :
+ ∃ e', CompleteOrthogonalIdempotents (Option.rec e₀ e') ∧ e = f ∘ e' := by
+ choose e' he' using hef
+ choose k hk using fun i ↦ Ideal.mem_span_singleton.mp
+ (hfe₀.le (show f (e' i * e' i - e' i) = 0 by simp [he', (he.1.1 i).eq]))
+ refine ⟨(1 - e₀) • e', ⟨⟨Option.rec he₀ fun i ↦ ?_, ?_⟩, ?_⟩, ?_⟩
+ · rintro (_|i) (_|j) h
+ · simp at h
+ · dsimp; linear_combination - he₀.eq * e' j
+ · dsimp; linear_combination - he₀.eq * e' i
+ · obtain ⟨k, hk⟩ := Ideal.mem_span_singleton.mp
+ (hfe₀.le (show f (e' i * e' j) = 0 by simp [he', he.1.2 (by simpa using h)]))
+ dsimp
+ rw [mul_mul_mul_comm, hk, he₀.one_sub.eq, ← mul_assoc, he₀.one_sub_mul_self, zero_mul]
+ · obtain ⟨k, hk⟩ := Ideal.mem_span_singleton.mp
+ (hfe₀.le (show f (∑ i, e' i - 1) = 0 by simpa [he', sub_eq_zero] using he.2))
+ simp only [Fintype.sum_option, Pi.smul_apply, smul_eq_mul, ← Finset.mul_sum,
+ sub_eq_iff_eq_add.mp hk]
+ linear_combination - he₀.eq * k
+ · have : f e₀ = 0 := by simpa using hfe₀.ge (Ideal.mem_span_singleton_self _)
+ aesop
+ · dsimp [IsIdempotentElem]
+ linear_combination congr($(he₀.eq) * ((e' i) ^ 2 - k i) + (1 - e₀) * $(hk i))
+
end CommRing
section corner
@@ -589,4 +620,14 @@ def CompleteOrthogonalIdempotents.ringEquivOfComm [CommSemiring R]
(he : CompleteOrthogonalIdempotents e) : R ≃+* Π i, (he.idem i).Corner :=
he.ringEquivOfIsMulCentral fun _ ↦ Semigroup.mem_center_iff.mpr fun _ ↦ mul_comm ..
+lemma Ideal.mem_map_span_singleton_iff_of_isIdempotentElem
+ [CommRing R] {e r : R} (he : IsIdempotentElem e) {I : Ideal R} :
+ Ideal.Quotient.mk _ r ∈ I.map (Ideal.Quotient.mk (Ideal.span {e})) ↔ (1 - e) * r ∈ I := by
+ simp only [Ideal.mem_map_iff_of_surjective _ Ideal.Quotient.mk_surjective,
+ Ideal.Quotient.mk_eq_mk_iff_sub_mem, Ideal.mem_span_singleton]
+ refine ⟨?_, fun H ↦ ⟨_, H, by simp [sub_mul]⟩⟩
+ intro ⟨s, hs, t, hrst⟩
+ convert I.mul_mem_left (1 - e) hs using 1
+ linear_combination he.eq * t - (1 - e) * hrst
+
end corner
diff --git a/Mathlib/RingTheory/LocalRing/ResidueField/Ideal.lean b/Mathlib/RingTheory/LocalRing/ResidueField/Ideal.lean
index f6d7e2c2ee8dfd..e76b5c7af8ce7c 100644
--- a/Mathlib/RingTheory/LocalRing/ResidueField/Ideal.lean
+++ b/Mathlib/RingTheory/LocalRing/ResidueField/Ideal.lean
@@ -246,3 +246,6 @@ lemma Ideal.ResidueField.ringHom_ext {I : Ideal R} [I.IsPrime]
lemma Ideal.ResidueField.algHom_ext {I : Ideal A} [I.IsPrime] {f g : I.ResidueField →ₐ[R] B}
(H : f.comp (IsScalarTower.toAlgHom R A _) = g.comp (IsScalarTower.toAlgHom R A _)) : f = g :=
AlgHom.coe_ringHom_injective (ringHom_ext congr($H))
+
+@[simp] lemma Ideal.ResidueField.mapₐ_id (I : Ideal A) [I.IsPrime] :
+ Ideal.ResidueField.mapₐ I I (.id R A) rfl = .id _ _ := by ext; simp
diff --git a/Mathlib/RingTheory/TensorProduct/Quotient.lean b/Mathlib/RingTheory/TensorProduct/Quotient.lean
index f12cad92295d8e..14602fe0fed508 100644
--- a/Mathlib/RingTheory/TensorProduct/Quotient.lean
+++ b/Mathlib/RingTheory/TensorProduct/Quotient.lean
@@ -150,3 +150,44 @@ lemma Ideal.subtype_rTensor_range {R : Type*} [CommRing R] (M : Type*) [AddCommG
← Submodule.map_symm_eq_iff, ← Submodule.comap_equiv_eq_map_symm, ← LinearMap.ker_comp,
← TensorProduct.quotTensorEquivQuotSMul_comp_mkQ_rTensor, LinearEquiv.ker_comp]
exact LinearMap.exact_iff.mp (rTensor_exact M (LinearMap.exact_subtype_mkQ I) I.mkQ_surjective)
+
+section
+
+variable {R R' R'' S : Type*} [CommRing R] [CommRing R'] [CommRing R''] [CommRing S]
+ [Algebra R R'] [Algebra R R''] [Algebra R' R''] [IsScalarTower R R' R''] [Algebra R S]
+
+variable (R'') in
+set_option backward.isDefEq.respectTransparency false in
+attribute [local ext high] Ideal.Quotient.algHom_ext in
+/-- Let `e` be an element of `R' ⊗[R] S`. Then `R'' ⊗[R'] ((R' ⊗[R] S) / e)` is isomorphic to
+`(R'' ⊗[R] S) / e` as `R''`-algebras. -/
+noncomputable
+def Algebra.tensorQuotientTensorEquiv (e : R' ⊗[R] S) :
+ R'' ⊗[R'] (R' ⊗[R] S ⧸ Ideal.span {e}) ≃ₐ[R'']
+ (R'' ⊗[R] S ⧸ Ideal.span {Algebra.TensorProduct.rTensor S (Algebra.ofId R' R'') e}) :=
+ letI φ := Algebra.TensorProduct.rTensor S (Algebra.ofId R' R'')
+ letI ψ : R'' ⊗[R] S →ₐ[R''] R'' ⊗[R'] (R' ⊗[R] S ⧸ Ideal.span {e}) :=
+ Algebra.TensorProduct.lift (Algebra.ofId _ _)
+ ((Algebra.TensorProduct.includeRight.restrictScalars R).comp
+ ((Ideal.Quotient.mkₐ _ _).comp Algebra.TensorProduct.includeRight)) fun _ _ ↦ .all _ _
+ haveI hψφ : (ψ.restrictScalars R').comp φ =
+ (Algebra.TensorProduct.includeRight.restrictScalars R').comp (Ideal.Quotient.mkₐ _ _) := by
+ ext; simp [ψ, φ]
+ haveI heψ : Ideal.span {φ e} ≤ RingHom.ker ψ := by simpa [Ideal.span_le] using congr($hψφ e)
+ AlgEquiv.ofAlgHom (Algebra.TensorProduct.lift (Algebra.ofId _ _) (Ideal.quotientMapₐ _ φ
+ (Ideal.map_le_iff_le_comap.mp (by simp [Ideal.map_span, φ]))) fun _ _ ↦ .all _ _)
+ (Ideal.Quotient.liftₐ _ ψ heψ) (by ext; simp [ψ, φ]) (by ext; simp [φ, ψ])
+
+@[simp]
+lemma Algebra.tensorQuotientTensorEquiv_tmul (e : R' ⊗[R] S) (a : R'') (b : R') (c : S) :
+ Algebra.tensorQuotientTensorEquiv R'' e (a ⊗ₜ Ideal.Quotient.mk _ (b ⊗ₜ c)) =
+ Ideal.Quotient.mk _ ((a * algebraMap R' R'' b) ⊗ₜ c) := by
+ simp [Algebra.tensorQuotientTensorEquiv, ← Ideal.Quotient.mk_algebraMap, ← map_mul]
+
+@[simp]
+lemma Algebra.tensorQuotientTensorEquiv_symm_tmul (e : R' ⊗[R] S) (a : R'') (b : S) :
+ (Algebra.tensorQuotientTensorEquiv R'' e).symm (Ideal.Quotient.mk _ (a ⊗ₜ b)) =
+ a ⊗ₜ Ideal.Quotient.mk _ (1 ⊗ₜ b) := by
+ simp [Algebra.tensorQuotientTensorEquiv]
+
+end
From 31bbfafb600ea1426f9a651da991b724feb0bb3d Mon Sep 17 00:00:00 2001
From: Jovan Gerbscheid <56355248+JovanGerb@users.noreply.github.com>
Date: Fri, 26 Jun 2026 10:13:09 +0000
Subject: [PATCH 0378/1300] fix(ClickSuggestions): instantiate all
metavariables in the local context (#41001)
This PR fixes the panic in `#click_suggestions` that was reported at https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/.23click_suggestions.20doesn.27t.20find.20lemma/near/606231763
There are two separate problems that this PR fixes:
1. The implementation of `#click_suggestion` assumes all metavariables have already been instantiated. This is valid because they are all instantiated at the very beginning. However, this was not done properly, causing the types of free variables to still be able to contain metavariables.
2. The function `viewKAbstractSubExpr` was able to reach its `unreachable!` block. The reason is that `kabstractPositions` calls `instantiateMVars` on the expression `e` but not on the pattern `p`, causing a discrepancy. This is fixed by removing the `instantiateMVars` call. This is fine, because in both of the use cases (`rw??` and `#click_suggestions`), the expressions already have instantiated metavariables at this point.
---
Mathlib/Lean/Meta/KAbstractPositions.lean | 1 -
Mathlib/Tactic/ClickSuggestions.lean | 5 +++--
MathlibTest/ClickSuggestions/Test.lean | 7 +++++++
3 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/Mathlib/Lean/Meta/KAbstractPositions.lean b/Mathlib/Lean/Meta/KAbstractPositions.lean
index 836b353cdcef16..55291bb6c9111e 100644
--- a/Mathlib/Lean/Meta/KAbstractPositions.lean
+++ b/Mathlib/Lean/Meta/KAbstractPositions.lean
@@ -33,7 +33,6 @@ namespace Lean.Meta
/-- Return the positions that `kabstract` would abstract for pattern `p` in expression `e`.
i.e. the positions that unify with `p`. -/
def kabstractPositions (p e : Expr) : MetaM (Array SubExpr.Pos) := do
- let e ← instantiateMVars e
let mctx ← getMCtx
let pHeadIdx := p.toHeadIndex
let pNumArgs := p.headNumArgs
diff --git a/Mathlib/Tactic/ClickSuggestions.lean b/Mathlib/Tactic/ClickSuggestions.lean
index 0164cde3a2f665..9ef3d8ebeedff3 100644
--- a/Mathlib/Tactic/ClickSuggestions.lean
+++ b/Mathlib/Tactic/ClickSuggestions.lean
@@ -72,12 +72,13 @@ def viewKAbstractSubExpr' {m α}
/-- Compute the suggestions. Use `token` for the output. -/
public def generateSuggestions (loc : SubExpr.GoalsLocation) (parentDecl? : Option Name)
(token : RefreshToken) : ClickSuggestionsM Unit := withReducible do
+ -- Instantiate all metavariables, so that we won't need to worry about this later on.
+ instantiateMVarDeclMVars loc.mvarId
+ loc.mvarId.withContext do
-- TODO: instead of just putting `✝` after inaccessible names,
-- we should figure out how to use `rename_i` to actually refer to shadowed local variables.
let lctx := (← getLCtx).sanitizeNames.run' { options := (← getOptions) }
Meta.withLCtx' lctx do
- -- Instantiate all metavariables, so that we will not need to do this later on.
- instantiateMVarDeclMVars loc.mvarId
trackingComputation "click_suggestions" do
let (fvarId?, pos) ← match loc.loc with
| .hypType fvarId pos => pure (some fvarId, pos)
diff --git a/MathlibTest/ClickSuggestions/Test.lean b/MathlibTest/ClickSuggestions/Test.lean
index 4bac764b6ddc0d..884700e3f7b4ae 100644
--- a/MathlibTest/ClickSuggestions/Test.lean
+++ b/MathlibTest/ClickSuggestions/Test.lean
@@ -184,3 +184,10 @@ example (a b c : Nat) : a + b + c = a + b := by
click_test "/1" => "nth_rw 2 [Nat.add_comm a b]"
click_test "/0/1/0/1" => "nth_rw 1 [Nat.add_comm a b]"
exact test_sorry
+
+-- This example used to panic
+example : True := by
+ by_cases h : False
+ · click_test h "" => "rw [← true_eq_false_of_false h] at h"
+ trivial
+ · trivial
From d7d52d2a2ff057635f3eada6f0bc7ec113fa62ac Mon Sep 17 00:00:00 2001
From: Justus Springer <50165510+justus-springer@users.noreply.github.com>
Date: Fri, 26 Jun 2026 11:02:03 +0000
Subject: [PATCH 0379/1300] feat(Algebra/MvPolynomial/PDeriv): a coefficient
formula for `pderiv` (#39624)
This formula is useful for defining partial derivatives of multivariate power series, see PR #39626.
---
Mathlib/Algebra/MvPolynomial/PDeriv.lean | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/Mathlib/Algebra/MvPolynomial/PDeriv.lean b/Mathlib/Algebra/MvPolynomial/PDeriv.lean
index c70e5c5bb4f250..929abd0a853464 100644
--- a/Mathlib/Algebra/MvPolynomial/PDeriv.lean
+++ b/Mathlib/Algebra/MvPolynomial/PDeriv.lean
@@ -118,6 +118,21 @@ theorem pderiv_pow {i : σ} {f : MvPolynomial σ R} {n : ℕ} :
theorem pderiv_C_mul {f : MvPolynomial σ R} {i : σ} : pderiv i (C a * f) = C a * pderiv i f := by
rw [C_mul', Derivation.map_smul, C_mul']
+theorem coeff_pderiv {i : σ} (p : MvPolynomial σ R) (m : σ →₀ ℕ) :
+ coeff m (pderiv i p) = coeff (m + single i 1) p * (m i + 1) := by
+ classical
+ induction p using MvPolynomial.induction_on' with
+ | add p q hp hq => simp [hp, hq, add_mul]
+ | monomial n a =>
+ rw [pderiv_monomial, coeff_monomial, coeff_monomial]
+ by_cases h : n = m + single i 1
+ · simp [h]
+ simp only [h, ↓reduceIte, zero_mul]
+ by_cases hn : n i = 0
+ · simp [hn]
+ apply if_neg
+ rwa [tsub_eq_iff_eq_add_of_le (fun _ ↦ by grind)]
+
theorem pderiv_map {S} [CommSemiring S] {φ : R →+* S} {f : MvPolynomial σ R} {i : σ} :
pderiv i (map φ f) = map φ (pderiv i f) := by
apply induction_on f (fun r ↦ by simp) (fun p q hp hq ↦ by simp [hp, hq]) fun p j eq ↦ ?_
From cd86c80f970f42ac97c5494ed7d3bfa61d05c27d Mon Sep 17 00:00:00 2001
From: Suzuka Yu <109365723+Yu-Misaka@users.noreply.github.com>
Date: Fri, 26 Jun 2026 11:02:06 +0000
Subject: [PATCH 0380/1300] feat(LinearAlgebra): preliminary APIs for
symplectic matrices (#40449)
Greetings! This PR attempts to provide some basic APIs for #40352. Involving `Matrix.mulVec_apply`, `Matrix.map_J` and `SymplecticGroup.map_mem`.
Honestly I'm not sure if, for example, `Matrix.mulVec_apply` exists somewhere else in the library (I tried my best but didn't find it)
---
Mathlib/Data/Matrix/Mul.lean | 12 ++++++++++++
Mathlib/LinearAlgebra/SymplecticGroup.lean | 11 +++++++++++
2 files changed, 23 insertions(+)
diff --git a/Mathlib/Data/Matrix/Mul.lean b/Mathlib/Data/Matrix/Mul.lean
index 6eaadb3b0e2fa1..e9b9cc920a2be9 100644
--- a/Mathlib/Data/Matrix/Mul.lean
+++ b/Mathlib/Data/Matrix/Mul.lean
@@ -701,6 +701,12 @@ def mulVec [Fintype n] (M : Matrix m n α) (v : n → α) : m → α
@[inherit_doc]
scoped infixr:73 " *ᵥ " => Matrix.mulVec
+lemma mulVec_apply [Fintype n] (M : Matrix m n α) (v : n → α) (i : m) :
+ (M *ᵥ v) i = M.row i ⬝ᵥ v := rfl
+
+lemma mulVec_apply_eq_sum [Fintype n] (M : Matrix m n α) (v : n → α) (i : m) :
+ (M *ᵥ v) i = ∑ j : n, M i j * v j := rfl
+
/--
`v ᵥ* M` (notation for `vecMul v M`) is the vector-matrix product of vector `v` and matrix `M`,
where `v` is seen as a row vector.
@@ -714,6 +720,12 @@ def vecMul [Fintype m] (v : m → α) (M : Matrix m n α) : n → α
@[inherit_doc]
scoped infixl:73 " ᵥ* " => Matrix.vecMul
+lemma vecMul_apply [Fintype m] (v : m → α) (M : Matrix m n α) (i : n) :
+ (v ᵥ* M) i = v ⬝ᵥ M.col i := rfl
+
+lemma vecMul_apply_eq_sum [Fintype m] (v : m → α) (M : Matrix m n α) (i : n) :
+ (v ᵥ* M) i = ∑ j : m, v j * M j i := rfl
+
/-- Left multiplication by a matrix, as an `AddMonoidHom` from vectors to vectors. -/
@[simps]
def mulVec.addMonoidHomLeft [Fintype n] (v : n → α) : Matrix m n α →+ m → α where
diff --git a/Mathlib/LinearAlgebra/SymplecticGroup.lean b/Mathlib/LinearAlgebra/SymplecticGroup.lean
index f45df9b46ce4d0..c5ae74d50bf355 100644
--- a/Mathlib/LinearAlgebra/SymplecticGroup.lean
+++ b/Mathlib/LinearAlgebra/SymplecticGroup.lean
@@ -39,6 +39,13 @@ section JMatrixLemmas
def J : Matrix (l ⊕ l) (l ⊕ l) R :=
Matrix.fromBlocks 0 (-1) 1 0
+variable {R} in
+@[simp]
+theorem map_J {F S : Type*} [CommRing S] [FunLike F R S]
+ [AddMonoidHomClass F R S] [OneHomClass F R S] (f : F) :
+ (J l R).map f = J l S := by
+ simp [J, fromBlocks_map, Matrix.map_neg]
+
@[simp]
theorem J_transpose : (J l R)ᵀ = -J l R := by
rw [J, fromBlocks_transpose, ← neg_one_smul R (fromBlocks _ _ _ _ : Matrix (l ⊕ l) (l ⊕ l) R),
@@ -129,6 +136,10 @@ theorem symplectic_det (hA : A ∈ symplecticGroup l R) : IsUnit <| det A := by
rw [mul_comm A.det, mul_assoc] at hA
exact hA
+theorem map_mem {F S : Type*} [CommRing S] [FunLike F R S] [RingHomClass F R S]
+ (hA : A ∈ symplecticGroup l R) (f : F) : A.map f ∈ symplecticGroup l S := by
+ simp_rw [mem_iff, ← transpose_map, ← map_J _ f, ← Matrix.map_mul, mem_iff.mp hA]
+
theorem transpose_mem (hA : A ∈ symplecticGroup l R) : Aᵀ ∈ symplecticGroup l R := by
rw [mem_iff] at hA ⊢
rw [transpose_transpose]
From e2102acfd2d7fcf51dec47cbeaad829f419c1dd0 Mon Sep 17 00:00:00 2001
From: "mathlib-splicebot[bot]"
<261196803+mathlib-splicebot[bot]@users.noreply.github.com>
Date: Fri, 26 Jun 2026 11:02:08 +0000
Subject: [PATCH 0381/1300] chore(Algebra/Ring/Periodic): automated extraction
from #38483 (#40669)
Co-authored-by: jessealama <56691+jessealama@users.noreply.github.com>
---
Mathlib/Algebra/Ring/Periodic.lean | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/Mathlib/Algebra/Ring/Periodic.lean b/Mathlib/Algebra/Ring/Periodic.lean
index bb0a228b59f6d7..71fcf017b9bc3e 100644
--- a/Mathlib/Algebra/Ring/Periodic.lean
+++ b/Mathlib/Algebra/Ring/Periodic.lean
@@ -415,4 +415,11 @@ theorem Antiperiodic.mul [Add α] [Mul β] [HasDistribNeg β] (hf : Antiperiodic
theorem Antiperiodic.div [Add α] [DivisionMonoid β] [HasDistribNeg β] (hf : Antiperiodic f c)
(hg : Antiperiodic g c) : Periodic (f / g) c := by simp_all [neg_div_neg_eq]
+/-- For an antiperiodic function `f` with antiperiod `c`, summing `f` over a `Finset` shifted by
+`c` (via `addRightEmbedding c`) negates the sum over the original `Finset`. -/
+theorem Antiperiodic.sum_map_addRightEmbedding [Add α] [IsRightCancelAdd α]
+ [SubtractionCommMonoid β] (hf : Antiperiodic f c) (s : Finset α) :
+ ∑ k ∈ s.map (addRightEmbedding c), f k = -∑ k ∈ s, f k := by
+ simp [hf _]
+
end Function
From 8f49886e72d0b26ec9bc6366ee218afd0b6d1e93 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=ABl=20Riou?=
<37772949+joelriou@users.noreply.github.com>
Date: Fri, 26 Jun 2026 11:02:10 +0000
Subject: [PATCH 0382/1300] feat(Algebra/Homology): the injective derivability
structure on the homotopy category (#41063)
---
.../DerivabilityStructureInjectives.lean | 311 +++++++++++++++++-
.../Homology/Embedding/CochainComplex.lean | 18 +
.../Homology/Embedding/IsSupported.lean | 7 +
.../Injective/InjectiveObject.lean | 3 +
4 files changed, 323 insertions(+), 16 deletions(-)
diff --git a/Mathlib/Algebra/Homology/DerivedCategory/DerivabilityStructureInjectives.lean b/Mathlib/Algebra/Homology/DerivedCategory/DerivabilityStructureInjectives.lean
index 4d93315312b95a..d5764ce6531e99 100644
--- a/Mathlib/Algebra/Homology/DerivedCategory/DerivabilityStructureInjectives.lean
+++ b/Mathlib/Algebra/Homology/DerivedCategory/DerivabilityStructureInjectives.lean
@@ -5,9 +5,12 @@ Authors: Joël Riou
-/
module
+public import Mathlib.Algebra.Homology.DerivedCategory.Plus
public import Mathlib.Algebra.Homology.FullSubcategory
public import Mathlib.Algebra.Homology.ModelCategory.Injective
public import Mathlib.AlgebraicTopology.ModelCategory.DerivabilityStructureFibrant
+public import Mathlib.CategoryTheory.GuitartExact.Quotient
+public import Mathlib.CategoryTheory.Localization.DerivabilityStructure.Derives
public import Mathlib.CategoryTheory.Localization.DerivabilityStructure.OfLocalizedEquivalences
public import Mathlib.CategoryTheory.Preadditive.Injective.InjectiveObject
@@ -22,18 +25,80 @@ that it is a right derivability structure. (The proof proceeds by showing that
up to equivalences of categories, this functor is the inclusion of the full
subcategory of fibrant objects in the model category `CochainComplex.Plus C`.)
-TODO(@joelriou): obtain similar results for the bounded below homotopy category.
+We also obtain a similar right derivability structure `HomotopyCategory.Plus.localizerMorphism`
+for the functor `HomotopyCategory.Plus (InjectiveObject C) ⥤ HomotopyCategory.Plus C`, where
+the target category is equipped with the class of quasi-isomorphisms while
+the source category `HomotopyCategory.Plus (InjectiveObject C)` is equipped
+with the class of isomorphisms (which is exactly the same as quasi-isomorphisms).
+The consequence is that any functor from the category `HomotopyCategory.Plus C`
+has a right derived functor, and we show that the unit natural transformation for
+such a derived functor is an isomorphism on objects coming from
+`HomotopyCategory.Plus (InjectiveObject C)`.
-/
@[expose] public section
-open HomotopicalAlgebra CategoryTheory Limits ZeroObject Category
+open HomotopicalAlgebra CategoryTheory Limits
-variable (C : Type*) [Category* C] [Abelian C]
+variable {C H : Type*} [Category* C] [Abelian C] [Category* H]
namespace CochainComplex.Plus
+instance (X : HomotopyCategory.Plus (InjectiveObject C)) (n : ℤ) :
+ Injective (((InjectiveObject.ι C).mapHomotopyCategoryPlus.obj X).obj.as.X n) :=
+ inferInstanceAs (Injective ((InjectiveObject.ι C).obj (X.obj.as.X n)))
+
+set_option backward.defeqAttrib.useBackward true in
+instance (K : CochainComplex.Plus (InjectiveObject C)) :
+ CochainComplex.IsKInjective
+ (((InjectiveObject.ι C).mapHomologicalComplex (.up ℤ)).obj K.obj) := by
+ obtain ⟨K, n, hn⟩ := K
+ let L := ((InjectiveObject.ι C).mapHomologicalComplex (.up ℤ)).obj K
+ have (n : ℤ) : Injective (L.X n) := by dsimp [L]; infer_instance
+ exact CochainComplex.isKInjective_of_injective L n
+
+lemma exists_quasiIso_injective [EnoughInjectives C]
+ (K : CochainComplex.Plus C) (n : ℤ) [K.obj.IsStrictlyGE n] :
+ ∃ (L : CochainComplex.Plus (InjectiveObject C)) (_ : L.obj.IsStrictlyGE n)
+ (i : K ⟶ (InjectiveObject.ι C).mapCochainComplexPlus.obj L),
+ quasiIso C i := by
+ obtain ⟨L, i, _, _, _⟩ := modelCategoryQuillen.exists_quasiIso_injective K.obj n
+ let L' : CochainComplex (InjectiveObject C) ℤ :=
+ HomologicalComplex.liftObjectProperty _ L inferInstance
+ have hL' : L'.IsStrictlyGE n := by
+ rwa [← isStrictlyGE_mapHomologicalComplex_obj_iff _ (InjectiveObject.ι _)]
+ exact ⟨⟨L', n, hL'⟩, hL', ObjectProperty.homMk i, by assumption⟩
+
+end CochainComplex.Plus
+
+namespace DerivedCategory.Plus
+
+variable [HasDerivedCategory C]
+
+/-- Let `K` be an object in the bounded below derived category of an abelian category `C`
+with enough injectives. Assume that `K` is cohomologically `≥ n`. Then, `K`
+admits an "injective resolution", in the sense that there exists a cochain
+complex `L` consisting of injective object and lying in degrees `≥ n`, such that `K`
+is isomorphic to the image of `L`. -/
+lemma exists_injective_nonempty_iso [EnoughInjectives C] (K : DerivedCategory.Plus C)
+ (n : ℤ) [K.IsGE n] :
+ ∃ (L : CochainComplex.Plus (InjectiveObject C)) (_ : L.obj.IsStrictlyGE n),
+ Nonempty (DerivedCategory.Plus.Q.obj
+ ((InjectiveObject.ι C).mapCochainComplexPlus.obj L) ≅ K) := by
+ have : K.obj.IsGE n := (K.isGE_ι_obj_iff n).2 (by assumption)
+ obtain ⟨L, _, ⟨e⟩⟩ := DerivedCategory.exists_iso_Q_obj_of_isGE K.obj n
+ obtain ⟨M, _, i, hi⟩ :=
+ CochainComplex.Plus.exists_quasiIso_injective ⟨L, ⟨n, inferInstance⟩⟩ n
+ have : QuasiIso i.hom := by assumption
+ exact ⟨M, inferInstance,
+ ⟨DerivedCategory.Plus.ι.preimageIso ((asIso (DerivedCategory.Q.map i.hom)).symm ≪≫ e.symm)⟩⟩
+
+end DerivedCategory.Plus
+
+namespace CochainComplex.Plus
+
+variable (C) in
/-- The localizer morphism (relative to quasi-isomorphisms) that is
given by the "inclusion functor"
`CochainComplex.Plus (InjectiveObject C) ⥤ CochainComplex.Plus C`. -/
@@ -59,9 +124,9 @@ instance (K : FibrantObject (Plus C)) (n : ℤ) :
Injective (K.obj.obj.X n) := by
obtain ⟨K, hK⟩ := K
rw [fibrantObjects, modelCategoryQuillen.isFibrant_iff] at hK
- dsimp
infer_instance
+variable (C) in
set_option backward.defeqAttrib.useBackward true in
/-- The equivalence between `CochainComplex.Plus (InjectiveObject C)`
and the category of fibrant object in `CochainComplex.Plus C` for the
@@ -77,21 +142,17 @@ def fibrantObjectEquivalence :
inverse := ObjectProperty.lift _
(HomologicalComplex.liftFunctorObjectProperty _ (FibrantObject.ι ⋙ Plus.ι C)
(fun K n ↦ by dsimp; infer_instance)) (by
- rintro ⟨⟨K, n, hn⟩, _⟩
+ rintro ⟨⟨_, n, _⟩, _⟩
refine ⟨n, ?_⟩
- rw [isStrictlyGE_iff]
- intro i hi
- rw [IsZero.iff_id_eq_zero]
- ext
- apply (K.isZero_of_isStrictlyGE n i hi).eq_of_tgt)
+ rwa [← isStrictlyGE_mapHomologicalComplex_obj_iff _ (InjectiveObject.ι _)])
unitIso := Iso.refl _
counitIso := Iso.refl _
+variable (C) in
/-- The localizer morphism (relative to quasi-isomorphisms) that is
given by the equivalence of categories
`CochainComplex.Plus (InjectiveObject C) ≌ FibrantObject (CochainComplex.Plus C)`. -/
-@[simps]
-def fibrantObjectLocalizerMorphism :
+abbrev fibrantObjectLocalizerMorphism :
LocalizerMorphism ((quasiIso C).inverseImage (InjectiveObject.ι C).mapCochainComplexPlus)
(weakEquivalences (FibrantObject (Plus C))) where
functor := (fibrantObjectEquivalence C).functor
@@ -100,10 +161,6 @@ def fibrantObjectLocalizerMorphism :
instance : (fibrantObjectLocalizerMorphism C).IsInduced where
inverseImage_eq := rfl
-set_option backward.defeqAttrib.useBackward true in
-instance : (fibrantObjectLocalizerMorphism C).functor.IsEquivalence := by
- dsimp; infer_instance
-
set_option backward.isDefEq.respectTransparency false in
instance : (localizerMorphism C).IsRightDerivabilityStructure := by
rw [LocalizerMorphism.isRightDerivabilityStructure_iff_of_equivalences
@@ -119,3 +176,225 @@ instance : (localizerMorphism C).arrow.HasRightResolutions := by
infer_instance
end CochainComplex.Plus
+
+namespace HomotopyCategory.Plus
+
+variable (C) in
+/-- The localizer morphism that is given by the "inclusion functor"
+`HomotopyCategory.Plus (InjectiveObject C) ⥤ HomotopyCategory.Plus C`.
+The target category is equipped with the class of quasi-isomorphisms while
+the source category `HomotopyCategory.Plus (InjectiveObject C)` is equipped
+with the class of isomorphisms (which is exactly the same as quasi-isomorphisms). -/
+abbrev localizerMorphism : LocalizerMorphism
+ (MorphismProperty.isomorphisms (HomotopyCategory.Plus (InjectiveObject C)))
+ (HomotopyCategory.Plus.quasiIso C) where
+ functor := (InjectiveObject.ι C).mapHomotopyCategoryPlus
+ map K L f (hf : IsIso f) := by
+ dsimp only [MorphismProperty.inverseImage, HomotopyCategory.Plus.quasiIso]
+ rw [HomotopyCategory.mem_quasiIso_iff]
+ intro n
+ infer_instance
+
+set_option backward.defeqAttrib.useBackward true in
+set_option backward.isDefEq.respectTransparency false in
+lemma isIso_quotient_map_iff
+ {K L : CochainComplex.Plus (InjectiveObject C)} (f : K ⟶ L) :
+ IsIso ((quotient _).map f) ↔
+ CochainComplex.Plus.quasiIso C ((InjectiveObject.ι C).mapCochainComplexPlus.map f) := by
+ rw [← isIso_iff_of_reflects_iso _ (HomotopyCategory.Plus.ι (InjectiveObject C)),
+ ← isIso_iff_of_reflects_iso _ (Functor.mapHomotopyCategory (InjectiveObject.ι C) (.up ℤ))]
+ dsimp
+ rw [HomologicalComplex.isIso_quotient_map_iff_homotopyEquivalences,
+ ← CochainComplex.IsKInjective.quasiIso_iff]
+ rfl
+
+set_option backward.defeqAttrib.useBackward true in
+set_option backward.isDefEq.respectTransparency false in
+open HomologicalComplex in
+lemma inverseImage_quasiIso_mapCochainComplexPlus_injectiveObjectι :
+ (CochainComplex.Plus.quasiIso C).inverseImage (InjectiveObject.ι C).mapCochainComplexPlus =
+ (homotopyEquivalences (InjectiveObject C) (.up ℤ)).inverseImage
+ (CochainComplex.Plus.ι (InjectiveObject C)) := by
+ ext K L f
+ simp [CochainComplex.Plus.quasiIso, Functor.mapCochainComplexPlus,
+ ← HomologicalComplex.isIso_quotient_map_iff_homotopyEquivalences,
+ CochainComplex.IsKInjective.quasiIso_iff,
+ ← isIso_iff_of_reflects_iso _ ((InjectiveObject.ι C).mapHomotopyCategory (.up ℤ))]
+
+instance :
+ (HomotopyCategory.Plus.quotient (InjectiveObject C)).IsLocalization
+ ((CochainComplex.Plus.quasiIso C).inverseImage
+ (InjectiveObject.ι C).mapCochainComplexPlus) := by
+ rw [inverseImage_quasiIso_mapCochainComplexPlus_injectiveObjectι]
+ infer_instance
+
+set_option backward.isDefEq.respectTransparency false in
+open HomologicalComplex in
+instance (L : Plus C ⥤ H) [L.IsLocalization (quasiIso C)] :
+ (quotient C ⋙ L).IsLocalization (CochainComplex.Plus.quasiIso C) := by
+ refine Functor.IsLocalization.comp _ _
+ ((homotopyEquivalences C (.up ℤ)).inverseImage (CochainComplex.Plus.ι C))
+ (quasiIso C) _ ?_ ?_ ?_
+ · intro _ _ f hf
+ refine Localization.inverts L (quasiIso C) _ ?_
+ simpa [quasiIso, quotient_map_mem_quasiIso_iff]
+ · intro K L f hf
+ exact homotopyEquivalences_le_quasiIso _ _ _ hf
+ · rintro K L f hf
+ obtain ⟨K, rfl⟩ := Plus.quotient_obj_surjective K
+ obtain ⟨L, rfl⟩ := Plus.quotient_obj_surjective L
+ obtain ⟨f, rfl⟩ := (Plus.quotient C).map_surjective f
+ apply MorphismProperty.map_mem_map
+ simpa [quasiIso, quotient_map_mem_quasiIso_iff] using! hf
+
+namespace isRightDerivabilityStructure
+
+/-! The following private definitions are used to deduce that
+`HomotopyCategory.Plus.localizerMorphism` is a right derivability structure
+from the fact that `CochainComplex.Plus.localizerMorphism` is.
+
+The strategy is to observe that the following commutative square
+of localizer morphisms gives a Guitart exact square:
+```
+ CochainComplex.Plus.localizerMorphism C
+CochainComplex.Plus (InjectiveObject C) ----------> CochainComplex.Plus C
+ | |
+ L C | | R C
+ v v
+HomotopyCategory.Plus (InjectiveObject C) --------> HomotopyCategory.Plus C
+ HomotopyCategory.Plus.localizerMorphism C
+```
+That the square is Guitart exact will follow from the lemma
+`TwoSquare.GuitartExact.quotient_of_nonempty_rightHomotopy`
+from the file `Mathlib/CategoryTheory/GuitartExact/Quotient.lean`.
+
+-/
+
+open MorphismProperty
+
+variable (C) in
+/-- The left localizer morphism in the Guitart exact square `iso`. -/
+private abbrev L : LocalizerMorphism
+ ((CochainComplex.Plus.quasiIso C).inverseImage (InjectiveObject.ι C).mapCochainComplexPlus)
+ (isomorphisms (Plus (InjectiveObject C))) where
+ functor := HomotopyCategory.Plus.quotient (InjectiveObject C)
+ map _ _ f hf := (isIso_quotient_map_iff f).2 hf
+
+private instance : (L C).IsInduced where
+ inverseImage_eq := by ext; apply isIso_quotient_map_iff
+
+variable (C) in
+set_option backward.isDefEq.respectTransparency false in
+/-- The right localizer morphism in the Guitart exact square `iso`. -/
+private abbrev R : LocalizerMorphism (CochainComplex.Plus.quasiIso C) (quasiIso C) where
+ functor := HomotopyCategory.Plus.quotient C
+ map _ _ _ _ := by simpa [quasiIso, quotient_map_mem_quasiIso_iff]
+
+private instance : (R C).IsInduced where
+ inverseImage_eq := by ext; apply quotient_map_mem_quasiIso_iff
+
+private instance : (L C).IsLocalizedEquivalence := by
+ have :
+ ((L C).functor ⋙ 𝟭 (Plus (InjectiveObject C))).IsLocalization
+ ((CochainComplex.Plus.quasiIso C).inverseImage
+ (InjectiveObject.ι C).mapCochainComplexPlus) :=
+ inferInstanceAs ((HomotopyCategory.Plus.quotient (InjectiveObject C)).IsLocalization _)
+ exact LocalizerMorphism.IsLocalizedEquivalence.of_isLocalization_of_isLocalization (L C) (𝟭 _)
+
+private instance : (R C).IsLocalizedEquivalence :=
+ LocalizerMorphism.IsLocalizedEquivalence.of_isLocalization_of_isLocalization
+ (R C) ((quasiIso C).Q)
+
+variable (C) in
+/-- The "commutative" square of functors involving the underlying functors
+of the localizer morphisms `CochainComplex.Plus.localizerMorphism C`
+and `HomotopyCategory.Plus.localizerMorphism C`. -/
+private def iso :
+ (CochainComplex.Plus.localizerMorphism C).functor ⋙ (R C).functor ≅
+ (L C).functor ⋙ (localizerMorphism C).functor := Iso.refl _
+
+set_option backward.defeqAttrib.useBackward true in
+open HomologicalComplex CochainComplex in
+private instance : TwoSquare.GuitartExact (iso C).hom :=
+ TwoSquare.GuitartExact.quotient_of_nonempty_rightHomotopy (iso C).symm (by
+ rintro ⟨K₁, n₁, hn₁⟩ ⟨K₂, n₂, hn₂⟩ f₀ f₁ hf
+ obtain ⟨f₀, rfl⟩ := ObjectProperty.homMk_surjective f₀
+ obtain ⟨f₁, rfl⟩ := ObjectProperty.homMk_surjective f₁
+ dsimp [Functor.mapCochainComplexPlus] at f₀ f₁
+ refine ⟨Plus.prepathObject _, ?_, ⟨?_⟩⟩
+ · ext : 1
+ exact eq_of_homotopy _ _ (pathObject.homotopy₀₁ _ (fun n ↦ ⟨n + 1, by simp⟩))
+ · refine PrepathObject.RightHomotopy.fullSubcategoryEquiv.symm
+ { h := pathObject.lift f₀ f₁ (homotopyOfEq _ _
+ ((HomotopyCategory.Plus.ι C).congr_map hf)) ≫
+ (pathObject.mapHomologicalComplexObjIso K₂ (InjectiveObject.ι C)
+ (fun n ↦ ⟨n + 1, by simp⟩)).inv
+ h₀ := ?_
+ h₁ := ?_ }
+ all_goals
+ dsimp [Functor.mapCochainComplexPlus]
+ cat_disch)
+
+end isRightDerivabilityStructure
+
+variable [EnoughInjectives C]
+
+instance isRightDerivabilityStructure : (localizerMorphism C).IsRightDerivabilityStructure :=
+ LocalizerMorphism.isRightDerivabilityStructure_of_isLocalizedEquivalence
+ (isRightDerivabilityStructure.iso C)
+
+instance : (HomotopyCategory.Plus.localizerMorphism C).arrow.HasRightResolutions :=
+ LocalizerMorphism.hasRightResolutions_arrow_of_essSurj_of_full
+ (isRightDerivabilityStructure.iso C)
+
+instance [HasDerivedCategory C] :
+ ((InjectiveObject.ι C).mapHomotopyCategoryPlus ⋙ DerivedCategory.Plus.Qh).EssSurj where
+ mem_essImage K := by
+ let r : (HomotopyCategory.Plus.localizerMorphism C).RightResolution
+ (DerivedCategory.Plus.Qh.objPreimage K) := Classical.arbitrary _
+ have := Localization.inverts DerivedCategory.Plus.Qh _ _ r.hw
+ exact ⟨r.X₁, ⟨(asIso (DerivedCategory.Plus.Qh.map r.w)).symm ≪≫
+ DerivedCategory.Plus.Qh.objObjPreimageIso K⟩⟩
+
+section
+
+variable (F : HomotopyCategory.Plus C ⥤ H)
+
+omit [EnoughInjectives C] in
+lemma localizerMorphism_derives : (localizerMorphism C).Derives F :=
+ MorphismProperty.isInvertedBy_isomorphisms _
+
+/-- Any functor from the bounded below homotopy category has a right derived functor
+with respect to quasi-isomorphisms. -/
+instance : F.HasPointwiseRightDerivedFunctor (HomotopyCategory.Plus.quasiIso C) :=
+ (localizerMorphism_derives F).hasPointwiseRightDerivedFunctor
+
+variable [HasDerivedCategory C] (F' : DerivedCategory.Plus C ⥤ H)
+ (α : F ⟶ DerivedCategory.Plus.Qh ⋙ F')
+ [F'.IsRightDerivedFunctor α (HomotopyCategory.Plus.quasiIso C)]
+
+instance (K : HomotopyCategory.Plus C) [(∀ (n : ℤ), Injective (K.obj.as.X n))] :
+ IsIso (α.app K) := by
+ have (Y : HomotopyCategory.Plus (InjectiveObject C)) :
+ IsIso (α.app ((InjectiveObject.ι C).mapHomotopyCategoryPlus.obj Y)) :=
+ (localizerMorphism_derives F).isIso_of_isRightDerivedFunctor _ _
+ obtain ⟨Y, ⟨e⟩⟩ : (InjectiveObject.ι C).mapHomotopyCategoryPlus.essImage K := by
+ obtain ⟨X, hX⟩ := K
+ obtain ⟨K, rfl⟩ := HomotopyCategory.quotient_obj_surjective X
+ refine ⟨(quotient _).obj
+ ((CochainComplex.Plus.fibrantObjectEquivalence C).inverse.obj
+ ⟨⟨K, by simpa using hX⟩, ?_⟩), ⟨Iso.refl _⟩⟩
+ dsimp [fibrantObjects]
+ rwa [CochainComplex.Plus.modelCategoryQuillen.isFibrant_iff]
+ rw [← NatTrans.isIso_app_iff_of_iso α e]
+ infer_instance
+
+example (X : HomotopyCategory.Plus (InjectiveObject C)) :
+ IsIso ((F.totalRightDerivedUnit DerivedCategory.Plus.Qh
+ (HomotopyCategory.Plus.quasiIso C)).app
+ ((InjectiveObject.ι C).mapHomotopyCategoryPlus.obj X)) := by
+ infer_instance
+
+end
+
+end HomotopyCategory.Plus
diff --git a/Mathlib/Algebra/Homology/Embedding/CochainComplex.lean b/Mathlib/Algebra/Homology/Embedding/CochainComplex.lean
index ae06df80b4d34b..ad35d9c62b186f 100644
--- a/Mathlib/Algebra/Homology/Embedding/CochainComplex.lean
+++ b/Mathlib/Algebra/Homology/Embedding/CochainComplex.lean
@@ -313,6 +313,24 @@ lemma quasiIso_truncLEMap_iff :
end
+section
+
+variable {D : Type*} [Category* D] [HasZeroMorphisms D]
+
+lemma isStrictlyGE_mapHomologicalComplex_obj_iff
+ (F : C ⥤ D) [F.Faithful] [F.PreservesZeroMorphisms] (n : ℤ) :
+ CochainComplex.IsStrictlyGE ((F.mapHomologicalComplex (.up ℤ)).obj K) n ↔
+ K.IsStrictlyGE n :=
+ isStrictlySupported_mapHomologicalComplex_obj_iff ..
+
+lemma isStrictlyLE_mapHomologicalComplex_obj_iff
+ (F : C ⥤ D) [F.Faithful] [F.PreservesZeroMorphisms] (n : ℤ) :
+ CochainComplex.IsStrictlyLE ((F.mapHomologicalComplex (.up ℤ)).obj K) n ↔
+ K.IsStrictlyLE n :=
+ isStrictlySupported_mapHomologicalComplex_obj_iff ..
+
+end
+
end HasZeroMorphisms
section Preadditive
diff --git a/Mathlib/Algebra/Homology/Embedding/IsSupported.lean b/Mathlib/Algebra/Homology/Embedding/IsSupported.lean
index 9a1987a6598c4e..9f7fbefa73e179 100644
--- a/Mathlib/Algebra/Homology/Embedding/IsSupported.lean
+++ b/Mathlib/Algebra/Homology/Embedding/IsSupported.lean
@@ -161,6 +161,13 @@ instance map_isStrictlySupported [K.IsStrictlySupported e] :
dsimp
rw [← F.map_id, (K.isZero_X_of_isStrictlySupported e i' hi').eq_of_src (𝟙 _) 0, F.map_zero]
+lemma isStrictlySupported_mapHomologicalComplex_obj_iff [F.Faithful] :
+ ((F.mapHomologicalComplex c').obj K).IsStrictlySupported e ↔ K.IsStrictlySupported e := by
+ refine ⟨fun _ ↦ ⟨fun i' hi' ↦ ?_⟩, fun _ ↦ inferInstance⟩
+ rw [IsZero.iff_id_eq_zero]
+ exact F.map_injective ((isZero_X_of_isStrictlySupported
+ ((F.mapHomologicalComplex c').obj K) e i' hi').eq_of_src _ _)
+
end
end HomologicalComplex
diff --git a/Mathlib/CategoryTheory/Preadditive/Injective/InjectiveObject.lean b/Mathlib/CategoryTheory/Preadditive/Injective/InjectiveObject.lean
index ddbe6ccb6f79dd..d8955a1baf21ae 100644
--- a/Mathlib/CategoryTheory/Preadditive/Injective/InjectiveObject.lean
+++ b/Mathlib/CategoryTheory/Preadditive/Injective/InjectiveObject.lean
@@ -44,6 +44,9 @@ instance [HasFiniteProducts C] : HasFiniteProducts (InjectiveObject C) where
instance [Preadditive C] [HasFiniteProducts C] : HasFiniteBiproducts (InjectiveObject C) :=
HasFiniteBiproducts.of_hasFiniteProducts
+instance [Preadditive C] [HasBinaryBiproducts C] : HasBinaryBiproducts (InjectiveObject C) :=
+ HasBinaryBiproducts.of_hasBinaryProducts
+
instance [HasZeroMorphisms C] [HasZeroObject C] : (isInjective C).ContainsZero where
exists_zero := ⟨0, by simp [IsZero.iff_id_eq_zero], Injective.zero_injective⟩
From cce5343a273fb368713331e6b357cdf9d79cd2cb Mon Sep 17 00:00:00 2001
From: Michael Stoll <99838730+MichaelStollBayreuth@users.noreply.github.com>
Date: Fri, 26 Jun 2026 12:09:41 +0000
Subject: [PATCH 0383/1300] feat(NumberTheory/Height/NumberField): Northcott
property (#39744)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This PR finally adds a proof of the *Northcott property* for heights on number fields:
```lean
theorem NumberField.finite_setOf_mulHeight₁_le (B : ℝ) : {x : K | mulHeight₁ x ≤ B}.Finite
```
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
---
Mathlib/NumberTheory/Height/NumberField.lean | 224 ++++++++++++++++++-
1 file changed, 222 insertions(+), 2 deletions(-)
diff --git a/Mathlib/NumberTheory/Height/NumberField.lean b/Mathlib/NumberTheory/Height/NumberField.lean
index 3c154daa059b4d..852c8132ed30e9 100644
--- a/Mathlib/NumberTheory/Height/NumberField.lean
+++ b/Mathlib/NumberTheory/Height/NumberField.lean
@@ -5,8 +5,9 @@ Authors: Michael Stoll, Ralf Stephan
-/
module
-public import Mathlib.NumberTheory.NumberField.ProductFormula
public import Mathlib.NumberTheory.Height.Basic
+public import Mathlib.NumberTheory.Height.Northcott
+public import Mathlib.NumberTheory.NumberField.ProductFormula
import Mathlib.Algebra.FiniteSupport.Basic
import Mathlib.Algebra.Order.Hom.Lattice
@@ -19,6 +20,14 @@ import Mathlib.NumberTheory.NumberField.InfinitePlace.TotallyRealComplex
We provide an instance of `Height.AdmissibleAbsValues` for algebraic number fields
and set up some API.
+## Main results
+
+* Heights on number fields satisfy the **Northcott property**: If `K` is a number field,
+ then the set of elements of `K` of bounded (multiplicative or logarithmic) height is finite;
+ see `NumberField.finite_setOf_mulHeight₁_le` and `NumberField.finite_setOf_logHeight₁_le`.
+ We also provide instances for `Northcott (mulHeight₁ (K := K))` (which automatically leads
+ also to `Northcott (logHeight₁ (K := K))`).
+
## TODO
When this file gets long, split the material on heights over `ℚ` off into a file `Rat.lean`.
@@ -96,7 +105,6 @@ lemma sum_nonarchAbsVal_eq {M : Type*} [AddCommMonoid M] (f : AbsoluteValue K
(∑ᶠ v : nonarchAbsVal, f v.val) = ∑ᶠ v : FinitePlace K, f v.val :=
rfl
-
/-- This is the familiar definition of the multiplicative height on a number field. -/
lemma mulHeight₁_eq (x : K) :
mulHeight₁ x =
@@ -184,6 +192,218 @@ lemma absNorm_mul_finprod_finitePlace_eq_one (hx : x ≠ 0) :
end NumberField
+/-!
+### The Northcott property for heights on number fields
+
+We show that a number field `K` has the **Northcott property** with respect to the multiplicative
+and with respect to the logarithmic height, i.e., for any `B : ℝ` the set of elements `x : K`
+such that `mulHeight₁ x ≤ B` (resp., `logHeight₁ x ≤ B`) is finite.
+See `NumberField.finite_setOf_mulHeight₁_le` and `NumberField.finite_setOf_logHeight₁_le`.
+
+The main idea of the proof is as follows. We show that for every `x : K` there is `n : ℕ` such that
+`n * x` is an algebraic integer and `n ≤ mulHeight₁ x`; see `NumberField.exists_nat_le_mulHeight₁`.
+We also show that the set of `a : 𝓞 K` such that `mulHeight₁ (a / n)` is bounded is finite;
+see `NumberField.finite_setOf_prod_infinitePlace_iSup_le`. The result for the multiplicative height
+follows by combining these two ingredients, and the result for the logarithmic height follows
+from that for any field with a family of admissible absolute values
+(see `Mathlib.NumberTheory.Height.Northcott`).
+-/
+
+section Northcott
+
+namespace NumberField
+
+variable {K : Type*} [Field K] [NumberField K]
+
+section withIdeal
+
+open Ideal
+
+private lemma relIndex_span_span_nat_mul (m : ℕ) {n : ℕ} (hn : n ≠ 0) (a : 𝓞 K) :
+ (span {(m : 𝓞 K)}).toAddSubgroup.relIndex (span {↑m, a}).toAddSubgroup =
+ (span {(n * m : 𝓞 K)}).toAddSubgroup.relIndex (span {↑(n * m), n * a}).toAddSubgroup := by
+ let f : 𝓞 K →ₗ[𝓞 K] 𝓞 K := .mulLeft _ n
+ have hf : Function.Injective (f : 𝓞 K →+ 𝓞 K) :=
+ (injective_iff_map_eq_zero f).mpr fun _ _ ↦ by simp_all [f]
+ have H₁ : span {(n * m : 𝓞 K)} = Submodule.map f (span {↑m}) := by
+ simp [LinearMap.map_span, f]
+ have H₂ : span {↑(n * m), n * a} = Submodule.map f (span {↑m, a}) := by
+ simp [LinearMap.map_span, f, Set.image_pair]
+ rw [H₁, H₂]
+ exact AddSubgroup.relIndex_map_map_of_injective _ _ hf |>.symm
+
+private lemma relIndex_span_span_eq_relIndex_span_span {m n : ℕ} (hm : m ≠ 0) (hn : n ≠ 0)
+ {a b : 𝓞 K} (h : n * a = m * b) :
+ (span {(m : 𝓞 K)}).toAddSubgroup.relIndex (span {↑m, a}).toAddSubgroup =
+ (span {(n : 𝓞 K)}).toAddSubgroup.relIndex (span {↑n, b}).toAddSubgroup := by
+ refine (relIndex_span_span_nat_mul m hn a).trans ?_
+ rw [mul_comm, mul_comm n, h]
+ exact (relIndex_span_span_nat_mul n hm b).symm
+
+open Module AddSubgroup LinearMap in
+lemma exists_nat_ne_zero_exists_integer_mul_eq_and_absNorm_span_eq_pow (x : K) :
+ ∃ n : ℕ, n ≠ 0 ∧ ∃ a : 𝓞 K, n * x = a ∧
+ (span {(n : 𝓞 K), a}).absNorm = n ^ (Module.finrank ℚ K - 1) := by
+ have hx : IsAlgebraic ℤ x := IsFractionRing.isAlgebraic_iff ℤ _ _ |>.mpr (.of_finite ℚ x)
+ obtain ⟨m, r, hm, hmr⟩ := hx.exists_nsmul_eq (𝓞 K)
+ rw [← RingOfIntegers.coe_eq_algebraMap r] at hmr
+ set n := (span {(m : 𝓞 K)}).toAddSubgroup.relIndex (span {(m : 𝓞 K), r}).toAddSubgroup with hndef
+ have hn : n ≠ 0 := isFiniteRelIndex (by simp [hm]) _ |>.relIndex_ne_zero
+ obtain ⟨a, ha'⟩ : ∃ a, m * a = n * r := by
+ have : n • r ∈ span {(m : 𝓞 K)} :=
+ (span {(m : 𝓞 K)}).toAddSubgroup.nsmul_relIndex_mem <| Submodule.mem_span_of_mem <| by grind
+ simpa [mem_span_singleton', mul_comm] using this
+ have ha : n * x = a := by
+ refine mul_left_cancel₀ (mod_cast hm : (m : K) ≠ 0) ?_
+ rw [mul_left_comm, ← nsmul_eq_mul m, hmr]
+ exact_mod_cast ha'.symm
+ refine ⟨n, hn, a, ha, mul_left_cancel₀ hn ?_⟩
+ nth_rewrite 1 [hndef]
+ rw [absNorm_eq_index, mul_pow_sub_one finrank_pos.ne', ← RingOfIntegers.rank,
+ ← absNorm_span_natCast, absNorm_eq_index, ← relIndex_span_span_eq_relIndex_span_span hn hm ha']
+ exact relIndex_mul_index <| Submodule.toAddSubgroup_mono <| span_mono <| by grind
+
+open Height in
+private lemma one_le_pow_totalWeight_mul_finprod {n : ℕ} (hn : n ≠ 0) (a : 𝓞 K) :
+ 1 ≤ (n ^ totalWeight K : ℝ) * ∏ᶠ (v : FinitePlace K), ⨆ i, v (![↑a, ↑n] i) := by
+ have Hw : (0 : ℝ) < n ^ totalWeight K := by positivity
+ rw_mod_cast [totalWeight_eq_finrank, ← RingOfIntegers.rank, ← absNorm_span_natCast] at Hw ⊢
+ rw [← absNorm_mul_finprod_finitePlace_eq_one (show ![a, n] ≠ 0 by simp [hn])]
+ gcongr
+ · exact finprod_nonneg fun _ ↦ Real.iSup_nonneg_of_nonnegHomClass ..
+ · exact Nat.le_of_dvd Hw <| absNorm_dvd_absNorm_of_le <| span_mono <| by simp
+ · apply le_of_eq; congr; ext; congr; ext i; fin_cases i <;> simp
+
+end withIdeal
+
+open Height
+
+section withFinset
+
+open Finset
+
+/-- If `x : K` (for a number field `K`), then we can find a nonzero `n : ℕ` such that
+`n ≤ mulHeight₁ x` and `n * x` is integral. I.e., the denominator of `x` can be bounded by
+its multplicative height. -/
+-- TODO: Use this to show `natDenominator x ≤ mulHeight₁ x` once #39872 is merged.
+lemma exists_nat_le_mulHeight₁ (x : K) :
+ ∃ n : ℕ, n ≠ 0 ∧ n ≤ mulHeight₁ x ∧ IsIntegral ℤ (n * x) := by
+ obtain ⟨n, hn, a, ha₁, ha₂⟩ := exists_nat_ne_zero_exists_integer_mul_eq_and_absNorm_span_eq_pow x
+ refine ⟨n, hn, ?_, ha₁ ▸ a.isIntegral_coe⟩
+ rw [← totalWeight_eq_finrank] at ha₂
+ have hv (i : Fin 2) : (![a, n] i : K) = ![(a : K), n] i := by fin_cases i <;> rfl
+ rw [← mul_div_cancel_left₀ x (mod_cast hn : (n : K) ≠ 0), ha₁, mulHeight₁_div_eq_mulHeight,
+ mulHeight_eq (by simp [hn])]
+ refine le_of_mul_le_mul_left ?_ (show (0 : ℝ) < n ^ (totalWeight K - 1) by positivity)
+ have : n ^ (totalWeight K - 1) * ∏ᶠ (v : FinitePlace K), ⨆ i, v (![(a : K), n] i) = 1 := by
+ simpa [ha₂, hv] using absNorm_mul_finprod_finitePlace_eq_one (show ![a, n] ≠ 0 by simp [hn])
+ rw [pow_sub_one_mul (totalWeight_pos K).ne', mul_left_comm, this, mul_one,
+ totalWeight_eq_sum_mult, ← prod_pow_eq_pow_sum univ]
+ gcongr
+ exact Finite.le_ciSup_of_le 1 <| by simp
+
+private lemma pow_totalWeight_sub_one_eq [DecidableEq (InfinitePlace K)] {n : ℕ} (hn : n ≠ 0)
+ (v : InfinitePlace K) :
+ (n ^ (totalWeight K - 1) : ℝ) = (∏ w ∈ univ.erase v, (n ^ w.mult : ℝ)) * n ^ (v.mult - 1) := by
+ refine mul_right_cancel₀ (b := (n : ℝ)) (mod_cast hn) ?_
+ rw [pow_sub_one_mul (totalWeight_pos K).ne', totalWeight_eq_sum_mult, ← prod_pow_eq_pow_sum,
+ ← prod_erase_mul _ _ (mem_univ v), ← pow_sub_one_mul v.mult_ne_zero, ← mul_assoc]
+
+private lemma infinitePlace_apply_le_of_prod_le {n : ℕ} (hn : n ≠ 0) (B : ℝ) {x : 𝓞 K}
+ (h : ∏ v : InfinitePlace K, (⨆ i, v (![(x : K), n] i)) ^ v.mult ≤ B) (v : InfinitePlace K) :
+ v x ≤ B / n ^ (totalWeight K - 1) := by
+ classical
+ rw [le_div_iff₀' (by positivity)]
+ calc
+ _ ≤ n ^ (totalWeight K - 1) * ⨆ i, v (![(x : K), n] i) := by
+ gcongr; exact Finite.le_ciSup_of_le 0 le_rfl
+ _ ≤ (∏ v' ∈ univ.erase v, (⨆ i, v' (![↑x, ↑n] i)) ^ v'.mult) *
+ (⨆ i, v (![↑x, ↑n] i)) ^ (v.mult - 1) * ⨆ i, v (![(x : K), n] i) := by
+ rw [pow_totalWeight_sub_one_eq hn]
+ gcongr
+ · exact Real.iSup_nonneg_of_nonnegHomClass ..
+ · exact prod_nonneg fun _ _ ↦ pow_nonneg (Real.iSup_nonneg_of_nonnegHomClass ..) _
+ all_goals exact Finite.le_ciSup_of_le 1 <| by simp
+ _ ≤ B := by
+ rwa [mul_assoc, pow_sub_one_mul v.mult_ne_zero, prod_erase_mul _ _ (mem_univ v)]
+
+end withFinset
+
+lemma finite_setOf_prod_infinitePlace_iSup_le {n : ℕ} (hn : n ≠ 0) (B : ℝ) :
+ {x : 𝓞 K | ∏ v : InfinitePlace K, (⨆ i, v (![(x : K), n] i)) ^ v.mult ≤ B}.Finite := by
+ set B' := B / n ^ (totalWeight K - 1)
+ suffices Set.BijOn ((↑) : 𝓞 K → K) {x | ∀ (v : InfinitePlace K), v x ≤ B'}
+ {x | IsIntegral ℤ x ∧ ∀ (φ : K →+* ℂ), ‖φ x‖ ≤ B'} from
+ this.finite_iff_finite.mpr (Embeddings.finite_of_norm_le K ℂ B') |>.subset
+ fun _ _ ↦ by grind [infinitePlace_apply_le_of_prod_le hn B]
+ refine .mk (fun x hx ↦ ?_) (fun _ _ _ _ ↦ RingOfIntegers.ext) fun a ha ↦ ?_ <;>
+ simp only [Set.mem_image, Set.mem_setOf_eq] at *
+ · exact ⟨x.isIntegral_coe, fun φ ↦ hx <| .mk φ⟩
+ · rw [← mem_integralClosure_iff ℤ K] at ha
+ exact ⟨⟨a, ha.1⟩, fun v ↦ v.norm_embedding_eq a ▸ ha.2 v.embedding, rfl⟩
+
+/-- The set of `a : 𝓞 K` such that `mulHeight₁ (a / n) = mulHeight ![a, n]` is bounded
+(for some given nonzero `n : ℕ`) is finite. -/
+lemma finite_setOf_mulHeight_nat_le {n : ℕ} (hn : n ≠ 0) (B : ℝ) :
+ {a : 𝓞 K | mulHeight ![(a : K), n] ≤ B}.Finite := by
+ suffices {a : 𝓞 K | mulHeight ![(a : K), n] ≤ B} ⊆
+ {a | ∏ v : InfinitePlace K, (⨆ i, v (![(a : K), n] i)) ^ v.mult ≤ n ^ totalWeight K * B} from
+ (finite_setOf_prod_infinitePlace_iSup_le hn _).subset this
+ refine Set.setOf_subset_setOf_of_imp fun a ha ↦ ?_
+ rw [mulHeight_eq <| by simp [hn], mul_comm] at ha
+ grw [← ha, ← mul_assoc, ← one_le_pow_totalWeight_mul_finprod hn, one_mul]
+ -- nonnegativity side goal
+ exact Finset.prod_nonneg fun _ _ ↦ pow_nonneg (Real.iSup_nonneg_of_nonnegHomClass ..) _
+
+variable (K) in
+/- The set of `x : K` such that `mulHeight₁ x` is bounded and `n * x` is integral
+(for some given nonzero `n : ℕ`) is finite.
+This is a stepping stone for the proof of the next result, which is strictly stronger. -/
+private lemma finite_setOf_isIntegral_nat_mul_and_mulHeight₁_le {n : ℕ} (hn : n ≠ 0) (B : ℝ) :
+ {x : K | IsIntegral ℤ (n * x) ∧ mulHeight₁ x ≤ B}.Finite := by
+ have hn' : (n : K) ≠ 0 := mod_cast hn
+ suffices Set.BijOn (fun a : 𝓞 K ↦ (a / n : K)) {a | mulHeight ![(a : K), n] ≤ B}
+ {x | IsIntegral ℤ (n * x) ∧ mulHeight₁ x ≤ B} from
+ this.finite_iff_finite.mp <| finite_setOf_mulHeight_nat_le hn B
+ refine .mk (fun a ha ↦ ?_) (fun a _ b _ h ↦ ?_) fun x ⟨hx₁, hx₂⟩ ↦ ?_
+ · simp only [Set.mem_setOf_eq] at ha ⊢
+ rw [mul_div_cancel₀ (a : K) hn', mulHeight₁_div_eq_mulHeight]
+ exact ⟨a.isIntegral_coe, ha⟩
+ · rwa [div_left_inj' hn', RingOfIntegers.eq_iff] at h
+ · simp only [Set.mem_setOf_eq, Set.mem_image]
+ obtain ⟨a, ha⟩ : ∃ a : 𝓞 K, n * x = a := ⟨⟨_, hx₁⟩, rfl⟩
+ refine ⟨a, ?_, (EuclideanDomain.eq_div_of_mul_eq_right hn' ha).symm⟩
+ rwa [← ha, ← mulHeight₁_div_eq_mulHeight, mul_div_cancel_left₀ x hn']
+
+variable (K) in
+/-- A number field `K` satisfies the **Northcott property**:
+The set of elements of bounded multiplicative height is finite. -/
+theorem finite_setOf_mulHeight₁_le (B : ℝ) : {x : K | mulHeight₁ x ≤ B}.Finite := by
+ have H : {x : K | mulHeight₁ x ≤ B} =
+ ⋃ n : Fin ⌊B⌋₊, {x : K | IsIntegral ℤ ((n + 1) * x) ∧ mulHeight₁ x ≤ B} := by
+ ext x : 1
+ obtain ⟨n, hn₀, hn₁, hn⟩ := exists_nat_le_mulHeight₁ x
+ simp only [Set.mem_setOf_eq, Set.mem_iUnion, exists_and_right, iff_and_self]
+ refine fun h ↦ ⟨⟨n - 1, by grind [Nat.le_floor <| hn₁.trans h]⟩, ?_⟩
+ rwa [← Nat.cast_add_one, Nat.sub_one_add_one hn₀]
+ rw [H]
+ exact Set.finite_iUnion fun n ↦
+ mod_cast finite_setOf_isIntegral_nat_mul_and_mulHeight₁_le K (Nat.zero_ne_add_one n).symm B
+
+instance : Northcott (mulHeight₁ (K := K)) where
+ finite_le := finite_setOf_mulHeight₁_le K
+
+variable (K) in
+/-- A number field `K` satisfies the **Northcott property**:
+The set of elements of bounded logarithmic height is finite. -/
+theorem finite_setOf_logHeight₁_le (B : ℝ) :
+ {x : K | logHeight₁ x ≤ B}.Finite :=
+ Northcott.finite_le B
+
+end NumberField
+
+end Northcott
+
/-!
### Positivity extension for totalWeight on number fields
-/
From 96b5d752df4a2a4e43850cecf0b21cb0598605b4 Mon Sep 17 00:00:00 2001
From: Riccardo Brasca
Date: Fri, 26 Jun 2026 12:17:48 +0000
Subject: [PATCH 0384/1300] feat: add zeta_sub_one_dvd_intCast_iff and related
declarations (#40232)
---
.../NumberField/Cyclotomic/Ideal.lean | 15 +++++++++++++++
.../RingTheory/RootsOfUnity/CyclotomicUnits.lean | 10 ++++++++++
2 files changed, 25 insertions(+)
diff --git a/Mathlib/NumberTheory/NumberField/Cyclotomic/Ideal.lean b/Mathlib/NumberTheory/NumberField/Cyclotomic/Ideal.lean
index 59947f078ff144..af04ef34127885 100644
--- a/Mathlib/NumberTheory/NumberField/Cyclotomic/Ideal.lean
+++ b/Mathlib/NumberTheory/NumberField/Cyclotomic/Ideal.lean
@@ -72,6 +72,14 @@ theorem associated_norm_zeta_sub_one : Associated (Algebra.norm ℤ (hζ.toInteg
rw [hζ.norm_toInteger_sub_one_of_eq_two_pow, h, Int.ofNat_two]
· rw [hζ.norm_toInteger_sub_one_of_prime_ne_two h]
+/-- An integer `n` is divisible by `ζ - 1` in `𝓞 K` if and only if it is divisible by `p`,
+where `ζ` is a primitive `p ^ (k + 1)`-th root of unity. -/
+theorem zeta_sub_one_dvd_intCast_iff {n : ℤ} :
+ hζ.toInteger - 1 ∣ (n : 𝓞 K) ↔ (p : ℤ) ∣ n := by
+ have h := associated_norm_zeta_sub_one p k hζ
+ rw [← Ideal.norm_dvd_iff (h.symm.prime (Nat.prime_iff_prime_int.mp hp.out))]
+ exact h.dvd_iff_dvd_left
+
theorem absNorm_span_zeta_sub_one : absNorm (span {hζ.toInteger - 1}) = p := by
simpa using congr_arg absNorm <|
span_singleton_eq_span_singleton.mpr <| associated_norm_zeta_sub_one p k hζ
@@ -231,6 +239,13 @@ theorem ramificationIdx_span_zeta_sub_one' :
rw [← pow_one p] at hK hζ
rw [ramificationIdx_span_zeta_sub_one p 0 hζ, pow_zero, one_mul]
+/-- An integer `n` is divisible by `ζ - 1` in `𝓞 K` if and only if it is divisible by `p`,
+where `ζ` is a primitive `p`-th root of unity. -/
+theorem zeta_sub_one_dvd_intCast_iff' {n : ℤ} :
+ hζ.toInteger - 1 ∣ (n : 𝓞 K) ↔ (p : ℤ) ∣ n := by
+ rw [← pow_one p] at hK hζ
+ exact zeta_sub_one_dvd_intCast_iff p 0 hζ
+
variable (K)
include hK in
diff --git a/Mathlib/RingTheory/RootsOfUnity/CyclotomicUnits.lean b/Mathlib/RingTheory/RootsOfUnity/CyclotomicUnits.lean
index fc7e3eb66e9157..2b2c98494c996d 100644
--- a/Mathlib/RingTheory/RootsOfUnity/CyclotomicUnits.lean
+++ b/Mathlib/RingTheory/RootsOfUnity/CyclotomicUnits.lean
@@ -150,6 +150,16 @@ lemma nthRootsFinset_pairwise_associated_sub_one_sub_of_prime (hζ : IsPrimitive
alias ntRootsFinset_pairwise_associated_sub_one_sub_of_prime :=
nthRootsFinset_pairwise_associated_sub_one_sub_of_prime
+/-- If `p` is prime and `ζ` is a `p`-th primitive root of unity, then `ζ - 1` divides `η₁ - η₂`
+for all `p`-th roots of unity `η₁` and `η₂`. -/
+lemma sub_one_dvd_sub (hζ : IsPrimitiveRoot ζ p) (hp : p.Prime)
+ {η₁ : A} (hη₁ : η₁ ∈ nthRootsFinset p (1 : A))
+ {η₂ : A} (hη₂ : η₂ ∈ nthRootsFinset p (1 : A)) :
+ ζ - 1 ∣ η₁ - η₂ := by
+ rcases eq_or_ne η₁ η₂ with rfl | h
+ · simp
+ · exact (hζ.nthRootsFinset_pairwise_associated_sub_one_sub_of_prime hp hη₁ hη₂ h).dvd
+
/-- Given an `n`-th primitive root of unity `ζ`, where `1 < n`, we have that `ζ - 1` divides `n`.
In particular, if `ζ` is a `p`-th primitive root of unity with `p` prime, then `ζ - 1` divides
`p`. -/
From 7e24eee7495b5b71447e07ce99c7d2dfedd09702 Mon Sep 17 00:00:00 2001
From: Luigi Massacci <48868075+luigi-massacci@users.noreply.github.com>
Date: Fri, 26 Jun 2026 12:17:50 +0000
Subject: [PATCH 0385/1300] feat: the space of Schwartz maps is T3 (#40931)
Proves that the space of Schwartz functions is T2. The proof is basically a copy-paste of the same proof for `TestFunction`.
Thanks to @BenKBreen for pointing out this was missing.
Co-Authored by: Luigi Massacci @luigimassacci-ax
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
---
Mathlib/Analysis/Distribution/SchwartzSpace/Basic.lean | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/Mathlib/Analysis/Distribution/SchwartzSpace/Basic.lean b/Mathlib/Analysis/Distribution/SchwartzSpace/Basic.lean
index 8216465d129539..6246742ddac1b8 100644
--- a/Mathlib/Analysis/Distribution/SchwartzSpace/Basic.lean
+++ b/Mathlib/Analysis/Distribution/SchwartzSpace/Basic.lean
@@ -1202,6 +1202,15 @@ theorem toBoundedContinuousFunctionCLM_apply (f : 𝓢(E, F)) (x : E) :
toBoundedContinuousFunctionCLM 𝕜 E F f x = f x :=
rfl
+theorem toBoundedContinuousFunctionCLM_injective :
+ Function.Injective (toBoundedContinuousFunctionCLM .. : 𝓢(E, F) →L[𝕜] E →ᵇ F) :=
+ fun _ _ h ↦ DFunLike.ext _ _ fun x ↦ DFunLike.congr_fun h x
+
+instance : T3Space 𝓢(E, F) :=
+ suffices T2Space 𝓢(E, F) from inferInstance
+ .of_injective_continuous (toBoundedContinuousFunctionCLM_injective ℝ ..)
+ (ContinuousLinearMap.continuous _)
+
end BoundedContinuousFunction
section ZeroAtInfty
From 8afd8c533914c66035fbf86679b10d75536bc294 Mon Sep 17 00:00:00 2001
From: smorel394 <67864981+smorel394@users.noreply.github.com>
Date: Fri, 26 Jun 2026 12:17:52 +0000
Subject: [PATCH 0386/1300] feat(CategoryTheory/Preadditive/AdditiveFunctor):
finite products in a quotient preadditive category (#41067)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Prove that, if `F : C ⥤ D` is an additive essentially surjective functor between preadditive categories and `C` has finite products, then `D` also has finite products. Use this to shorten the proof that the localization of an abelian category by a Serre class has finite products (in `CategoryTheory/Abelian/SerreClass/Localization`).
Co-authored-by: morel
---
.../Abelian/SerreClass/Localization.lean | 24 +++++++------------
.../Preadditive/AdditiveFunctor.lean | 6 +++++
2 files changed, 14 insertions(+), 16 deletions(-)
diff --git a/Mathlib/CategoryTheory/Abelian/SerreClass/Localization.lean b/Mathlib/CategoryTheory/Abelian/SerreClass/Localization.lean
index f92126f9c75b86..6c7b58e5c62803 100644
--- a/Mathlib/CategoryTheory/Abelian/SerreClass/Localization.lean
+++ b/Mathlib/CategoryTheory/Abelian/SerreClass/Localization.lean
@@ -5,10 +5,9 @@ Authors: Joël Riou
-/
module
+public import Mathlib.Algebra.Homology.ShortComplex.ExactFunctor
public import Mathlib.CategoryTheory.Abelian.SerreClass.MorphismProperty
public import Mathlib.CategoryTheory.Localization.CalculusOfFractions.Preadditive
-public import Mathlib.Algebra.Homology.ShortComplex.ExactFunctor
-public import Mathlib.CategoryTheory.Limits.ExactFunctor
/-!
# Localization with respect to a Serre class
@@ -29,7 +28,7 @@ universe v'' v' v u'' u' u
namespace CategoryTheory
-open Limits ZeroObject
+open Limits
namespace ObjectProperty
@@ -139,9 +138,6 @@ lemma isZero_obj_iff (X : C) :
rintro ⟨Y, h⟩
simpa using h.2
-lemma hasZeroObject : HasZeroObject D :=
- ⟨L.obj 0, by simpa [isZero_obj_iff L P] using P.prop_zero⟩
-
lemma map_eq_zero_iff {X Y : C} (f : X ⟶ Y) :
L.map f = 0 ↔ P (Abelian.image f) := by
rw [← L.map_zero, MorphismProperty.map_eq_iff_precomp L P.isoModSerre]
@@ -381,17 +377,9 @@ lemma hasCoequalizers : HasCoequalizers D :=
Preadditive.hasCoequalizer_of_hasCokernel _ _
hasCoequalizers_of_hasColimit_parallelPair _
-lemma hasBinaryProducts : HasBinaryProducts D :=
- have := Localization.essSurj L P.isoModSerre
- have (X Y : D) : HasBinaryProduct X Y :=
- hasLimit_of_iso (show Limits.pair _ _ ≅ _ from
- mapPairIso (L.objObjPreimageIso X) (L.objObjPreimageIso Y))
- hasBinaryProducts_of_hasLimit_pair D
-
lemma hasFiniteProducts : HasFiniteProducts D :=
- have := hasZeroObject L P
- have := hasBinaryProducts L P
- hasFiniteProducts_of_has_binary_and_terminal
+ have := Localization.essSurj L P.isoModSerre
+ L.hasFiniteProducts_of_additive_of_essSurj
lemma isNormalMonoCategory : IsNormalMonoCategory D where
normalMonoOfMono f hf := by
@@ -438,6 +426,10 @@ def abelian : Abelian D := by
have := isNormalEpiCategory L P
constructor
+lemma hasZeroObject : HasZeroObject D :=
+ have := abelian L P
+ Abelian.hasZeroObject
+
lemma preservesFiniteLimits : PreservesFiniteLimits L := by
letI := abelian L P
rw [((Functor.preservesFiniteLimits_tfae L).out 3 2 :)]
diff --git a/Mathlib/CategoryTheory/Preadditive/AdditiveFunctor.lean b/Mathlib/CategoryTheory/Preadditive/AdditiveFunctor.lean
index 364ab457c95d6e..c8554346bf6ea6 100644
--- a/Mathlib/CategoryTheory/Preadditive/AdditiveFunctor.lean
+++ b/Mathlib/CategoryTheory/Preadditive/AdditiveFunctor.lean
@@ -218,6 +218,12 @@ instance (priority := 100) preservesFiniteProductsOfAdditive [Additive F] :
PreservesFiniteProducts F where
preserves _ := preservesProductsOfShape_of_preservesBiproductsOfShape F
+lemma hasFiniteProducts_of_additive_of_essSurj [HasFiniteProducts C] [Additive F]
+ [EssSurj F] : HasFiniteProducts D :=
+ ⟨fun _ ↦ ⟨fun K ↦ hasLimit_of_iso
+ (F := Discrete.functor (fun i ↦ F.objPreimage (K.obj ⟨i⟩)) ⋙ F)
+ (Discrete.natIso (fun _ ↦ F.objObjPreimageIso _))⟩⟩
+
theorem additive_of_preservesBinaryBiproducts [HasBinaryBiproducts C] [PreservesZeroMorphisms F]
[PreservesBinaryBiproducts F] : Additive F where
map_add {X Y f g} := by
From 44ae7119e3a75cd737b4063c240db888c57fb7fd Mon Sep 17 00:00:00 2001
From: Justus Springer <50165510+justus-springer@users.noreply.github.com>
Date: Fri, 26 Jun 2026 12:32:05 +0000
Subject: [PATCH 0387/1300] feat(RingTheory/MvPowerSeries/Trunc): generalize
truncation lemmas (#39625)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Generalize `coeff_trunc_mul_trunc_eq_coeff_mul` (and its analogs for `truncFinset` and `trunc'`) to allow for different truncation levels for the two arguments. This matches the API for univariate power series, where we already have `PowerSeries.coeff_mul_eq_coeff_trunc_mul_trunc₂`.
This is useful for defining partial derivatives of multivariate power series, see PR #39626.
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
---
Mathlib/RingTheory/MvPowerSeries/Trunc.lean | 46 +++++++++++++++++----
1 file changed, 39 insertions(+), 7 deletions(-)
diff --git a/Mathlib/RingTheory/MvPowerSeries/Trunc.lean b/Mathlib/RingTheory/MvPowerSeries/Trunc.lean
index 2816b558c8d744..0f01f106605e17 100644
--- a/Mathlib/RingTheory/MvPowerSeries/Trunc.lean
+++ b/Mathlib/RingTheory/MvPowerSeries/Trunc.lean
@@ -126,16 +126,24 @@ theorem truncFinset_map [CommSemiring S] (f : R →+* S) (p : MvPowerSeries σ R
ext x
by_cases x ∈ s <;> grind [coeff_map, MvPolynomial.coeff_map]
-theorem coeff_truncFinset_mul_truncFinset_eq_coeff_mul (hs : IsLowerSet (s : Set (σ →₀ ℕ)))
- {x : σ →₀ ℕ} (f g : MvPowerSeries σ R) (hx : x ∈ s) :
- (truncFinset R s f * truncFinset R s g).coeff x = coeff x (f * g) := by
+/-- A coefficient of a product of finset-truncated power series equals the coefficient of the
+untruncated product, with the two truncation finsets `s` and `t` allowed to differ. -/
+theorem coeff_truncFinset_mul_truncFinset_eq_coeff_mul₂ {t : Finset (σ →₀ ℕ)}
+ (hs : IsLowerSet (s : Set (σ →₀ ℕ))) (ht : IsLowerSet (t : Set (σ →₀ ℕ)))
+ {x : σ →₀ ℕ} (f g : MvPowerSeries σ R) (hxs : x ∈ s) (hxt : x ∈ t) :
+ (truncFinset R s f * truncFinset R t g).coeff x = coeff x (f * g) := by
classical
simp only [MvPowerSeries.coeff_mul, MvPolynomial.coeff_mul]
apply sum_congr rfl
rintro ⟨i, j⟩ hij
simp only [mem_antidiagonal] at hij
- rw [coeff_truncFinset_of_mem _ (hs (show i ≤ x by simp [← hij]) hx),
- coeff_truncFinset_of_mem _ (hs (show j ≤ x by simp [← hij]) hx)]
+ rw [coeff_truncFinset_of_mem _ (hs (show i ≤ x by simp [← hij]) hxs),
+ coeff_truncFinset_of_mem _ (ht (show j ≤ x by simp [← hij]) hxt)]
+
+theorem coeff_truncFinset_mul_truncFinset_eq_coeff_mul (hs : IsLowerSet (s : Set (σ →₀ ℕ)))
+ {x : σ →₀ ℕ} (f g : MvPowerSeries σ R) (hx : x ∈ s) :
+ (truncFinset R s f * truncFinset R s g).coeff x = coeff x (f * g) :=
+ coeff_truncFinset_mul_truncFinset_eq_coeff_mul₂ hs hs f g hx hx
theorem truncFinset_truncFinset_pow (hs : IsLowerSet (s : Set (σ →₀ ℕ))) {k : ℕ} (hk : 1 ≤ k)
(p : MvPowerSeries σ R) : truncFinset R s ((truncFinset R s p) ^ k) =
@@ -200,6 +208,21 @@ theorem trunc_C_mul (n : σ →₀ ℕ) (a : R) (p : MvPowerSeries σ R) :
theorem trunc_map [CommSemiring S] (n : σ →₀ ℕ) (f : R →+* S) (p : MvPowerSeries σ R) :
trunc S n (map f p) = MvPolynomial.map f (trunc R n p) := truncFinset_map f p
+/-- A coefficient of a product of truncated power series equals the coefficient of the untruncated
+product, with the two truncation levels `n₁` and `n₂` allowed to differ. -/
+theorem coeff_trunc_mul_trunc_eq_coeff_mul₂ (n₁ n₂ : σ →₀ ℕ)
+ (f g : MvPowerSeries σ R) {m : σ →₀ ℕ} (h₁ : m < n₁) (h₂ : m < n₂) :
+ (trunc R n₁ f * trunc R n₂ g).coeff m = coeff m (f * g) :=
+ coeff_truncFinset_mul_truncFinset_eq_coeff_mul₂ (by grind [IsLowerSet]) (by grind [IsLowerSet])
+ f g (by simpa) (by simpa)
+
+/-- A coefficient of a product of truncated power series equals the coefficient of the untruncated
+product. Both factors are truncated at the same level `n`. -/
+theorem coeff_trunc_mul_trunc_eq_coeff_mul (n : σ →₀ ℕ)
+ (f g : MvPowerSeries σ R) {m : σ →₀ ℕ} (h : m < n) :
+ (trunc R n f * trunc R n g).coeff m = coeff m (f * g) :=
+ coeff_trunc_mul_trunc_eq_coeff_mul₂ n n f g h h
+
end TruncLT
section TruncLE
@@ -232,11 +255,20 @@ theorem trunc'_one (n : σ →₀ ℕ) : trunc' R n 1 = 1 := truncFinset_one (by
theorem trunc'_C (n : σ →₀ ℕ) (a : R) : trunc' R n (C a) = MvPolynomial.C a :=
truncFinset_C (by simp) a
-/-- Coefficients of the truncation of a product of two multivariate power series -/
+/-- A coefficient of a product of truncated power series equals the coefficient of the untruncated
+product, with the two truncation levels `n₁` and `n₂` allowed to differ. -/
+theorem coeff_trunc'_mul_trunc'_eq_coeff_mul₂ (n₁ n₂ : σ →₀ ℕ)
+ (f g : MvPowerSeries σ R) {m : σ →₀ ℕ} (h₁ : m ≤ n₁) (h₂ : m ≤ n₂) :
+ (trunc' R n₁ f * trunc' R n₂ g).coeff m = coeff m (f * g) :=
+ coeff_truncFinset_mul_truncFinset_eq_coeff_mul₂ (by grind [IsLowerSet]) (by grind [IsLowerSet])
+ f g (by simpa) (by simpa)
+
+/-- A coefficient of a product of truncated power series equals the coefficient of the untruncated
+product. Both factors are truncated at the same level `n`. -/
theorem coeff_trunc'_mul_trunc'_eq_coeff_mul (n : σ →₀ ℕ)
(f g : MvPowerSeries σ R) {m : σ →₀ ℕ} (h : m ≤ n) :
(trunc' R n f * trunc' R n g).coeff m = coeff m (f * g) :=
- coeff_truncFinset_mul_truncFinset_eq_coeff_mul (by intro; grind) f g (by simpa)
+ coeff_trunc'_mul_trunc'_eq_coeff_mul₂ n n f g h h
@[deprecated coeff_trunc'_mul_trunc'_eq_coeff_mul (since := "2026-02-20")]
theorem coeff_mul_eq_coeff_trunc'_mul_trunc' (n : σ →₀ ℕ) (f g : MvPowerSeries σ R) {m : σ →₀ ℕ}
From 24434ef5d5de06bd81beb9fc0c864d8d95fb7f65 Mon Sep 17 00:00:00 2001
From: Whysoserioushah <109107491+Whysoserioushah@users.noreply.github.com>
Date: Fri, 26 Jun 2026 12:32:07 +0000
Subject: [PATCH 0388/1300] feat(RepresentationTheory/Basic): add one API
(#41076)
---
Mathlib/RepresentationTheory/Basic.lean | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Mathlib/RepresentationTheory/Basic.lean b/Mathlib/RepresentationTheory/Basic.lean
index b7ba95cae0da1a..ffa02a90949dc8 100644
--- a/Mathlib/RepresentationTheory/Basic.lean
+++ b/Mathlib/RepresentationTheory/Basic.lean
@@ -101,6 +101,10 @@ theorem self_inv_apply (g : G) (x : V) :
ρ g (ρ g⁻¹ x) = x := by
simp [← Module.End.mul_apply, ← map_mul]
+lemma inv_apply_eq_iff {g : G} {x y : V} :
+ ρ g⁻¹ x = y ↔ x = ρ g y := by
+ constructor <;> rintro rfl <;> simp
+
lemma apply_bijective (g : G) :
Function.Bijective (ρ g) :=
Equiv.bijective ⟨ρ g, ρ g⁻¹, inv_self_apply ρ g, self_inv_apply ρ g⟩
From d6772ece85d5c7148c45528396c8ad514db3ff40 Mon Sep 17 00:00:00 2001
From: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com>
Date: Fri, 26 Jun 2026 12:41:43 +0000
Subject: [PATCH 0389/1300] feat(Data/Nat): a number divides a power of its own
radical (#40170)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
A few factorization lemmas, including:
- `∀ n : ℕ`, `n ∣ radical n ^ n`
- `∀ n k : ℕ`, `n ∣ k ^ n ↔ n.primeFactors ⊆ k.primeFactors`
- `∀ n k : ℕ`, `radical n ∣ k ↔ n.primeFactors ⊆ k.primeFactors`
- In any `UniqueFactorizationMonoid M`, `∀ a : M`, `∃ n, a ∣ radical a ^ n`
[#Is there code for X? > A number divides a power of its square-free component](https://leanprover.zulipchat.com/#narrow/channel/217875-Is-there-code-for-X.3F/topic/A.20number.20divides.20a.20power.20of.20its.20square-free.20component/with/599339469)
---
Mathlib/Algebra/Order/Group/Multiset.lean | 24 ++++++++++-------
Mathlib/Data/Nat/Choose/Lucas.lean | 2 +-
Mathlib/Data/Nat/Factorization/Basic.lean | 33 ++++++++++++++++++++---
Mathlib/Data/Nat/MaxPowDiv.lean | 13 +++++++++
Mathlib/Data/Nat/Squarefree.lean | 10 +++++++
Mathlib/Data/ZMod/QuotientRing.lean | 2 +-
Mathlib/RingTheory/Radical/Basic.lean | 22 +++++++++++++++
Mathlib/RingTheory/Radical/NatInt.lean | 14 +++++++---
8 files changed, 102 insertions(+), 18 deletions(-)
diff --git a/Mathlib/Algebra/Order/Group/Multiset.lean b/Mathlib/Algebra/Order/Group/Multiset.lean
index e55ebb61b5dbba..586d3ee1b45cce 100644
--- a/Mathlib/Algebra/Order/Group/Multiset.lean
+++ b/Mathlib/Algebra/Order/Group/Multiset.lean
@@ -63,6 +63,12 @@ lemma mem_nsmul {a : α} {s : Multiset α} {n : ℕ} : a ∈ n • s ↔ n ≠ 0
lemma mem_nsmul_of_ne_zero {a : α} {s : Multiset α} {n : ℕ} (h0 : n ≠ 0) : a ∈ n • s ↔ a ∈ s := by
simp [*]
+theorem smul_subset_self (s : Multiset α) (n : ℕ) : n • s ⊆ s :=
+ subset_iff.mpr fun _ ↦ mem_of_mem_nsmul
+
+theorem subset_smul_self_of_ne_zero (s : Multiset α) {n : ℕ} (hn : n ≠ 0) : s ⊆ n • s :=
+ subset_iff.mpr fun _ ↦ mem_nsmul_of_ne_zero hn |>.mpr
+
lemma nsmul_cons {s : Multiset α} (n : ℕ) (a : α) :
n • (a ::ₘ s) = n • ({a} : Multiset α) + n • s := by
rw [← singleton_add, nsmul_add]
@@ -180,6 +186,14 @@ lemma count_nsmul (a : α) (n s) : count a (n • s) = n * count a s := by
end
+theorem le_card_smul_iff_subset {s t : Multiset α} : s ≤ s.card • t ↔ s ⊆ t := by
+ classical
+ refine ⟨fun hle ↦ Subset.trans (subset_of_le hle) (t.smul_subset_self s.card), ?_⟩
+ refine fun hsub ↦ le_iff_count.mpr fun a ↦ ?_
+ by_cases! has : a ∉ s
+ · simp [count_eq_zero_of_notMem has]
+ grw [count_le_card, count_nsmul, ← one_le_count_iff_mem.mpr <| mem_of_subset hsub has, mul_one]
+
-- TODO: This should be `addMonoidHom_ext`
@[ext]
lemma addHom_ext [AddZeroClass β] ⦃f g : Multiset α →+ β⦄ (h : ∀ x, f {x} = g {x}) : f = g := by
@@ -189,14 +203,6 @@ lemma addHom_ext [AddZeroClass β] ⦃f g : Multiset α →+ β⦄ (h : ∀ x, f
| cons a s ih => simp only [← singleton_add, _root_.map_add, ih, h]
theorem le_smul_dedup [DecidableEq α] (s : Multiset α) : ∃ n : ℕ, s ≤ n • dedup s :=
- ⟨(s.map fun a => count a s).fold max 0,
- le_iff_count.2 fun a => by
- rw [count_nsmul]; by_cases h : a ∈ s
- · grw [← one_le_count_iff_mem.2 <| mem_dedup.2 h]
- have : count a s ≤ fold max 0 (map (fun a => count a s) (a ::ₘ erase s a)) := by
- simp
- rw [cons_erase h] at this
- simpa [mul_succ] using this
- · simp [count_eq_zero.2 h, Nat.zero_le]⟩
+ ⟨s.card, le_card_smul_iff_subset.mpr s.subset_dedup⟩
end Multiset
diff --git a/Mathlib/Data/Nat/Choose/Lucas.lean b/Mathlib/Data/Nat/Choose/Lucas.lean
index cc7f66fdd02fa5..b2192eea6d83e0 100644
--- a/Mathlib/Data/Nat/Choose/Lucas.lean
+++ b/Mathlib/Data/Nat/Choose/Lucas.lean
@@ -208,7 +208,7 @@ theorem gcd_choose_eq_minFac_of_isPrimePow (h : IsPrimePow n) :
have : multiplicity n.minFac ((Icc 1 (n - 1)).gcd n.choose) = 1 := by
refine multiplicity_eq_of_dvd_of_not_dvd ?_ (minFac_sq_ndvd_gcd_choose_of_isPrimePow h)
simpa using minFac_dvd_gcd_choose_of_isPrimePow h
- rw [Nat.prod_pow_primeFactors_factorization ne_zero, primeFactors_gcd_choose_of_isPrimePow h]
+ rw [Nat.prod_primeFactors_coe_pow_factorization ne_zero, primeFactors_gcd_choose_of_isPrimePow h]
simp [← Nat.multiplicity_eq_factorization isPrime ne_zero, this]
/-- For a natural number `n` greater than `1`, assume that `n` is not a prime power, then
diff --git a/Mathlib/Data/Nat/Factorization/Basic.lean b/Mathlib/Data/Nat/Factorization/Basic.lean
index 7ba209ef2b7730..ef73fda01a634c 100644
--- a/Mathlib/Data/Nat/Factorization/Basic.lean
+++ b/Mathlib/Data/Nat/Factorization/Basic.lean
@@ -477,11 +477,16 @@ theorem prod_pow_prime_padicValNat (n : Nat) (hn : n ≠ 0) (m : Nat) (pr : n <
· intro p hp
simp [factorization_def n (prime_of_mem_primeFactors hp)]
-lemma prod_pow_primeFactors_factorization (hn : n ≠ 0) :
+theorem prod_primeFactors_pow_factorization (hn : n ≠ 0) :
+ n = ∏ p ∈ n.primeFactors, p ^ n.factorization p :=
+ prod_factorization_pow_eq_self hn |>.symm.trans <| prod_factorization_eq_prod_primeFactors _
+
+lemma prod_primeFactors_coe_pow_factorization (hn : n ≠ 0) :
n = ∏ (p : n.primeFactors), (p : ℕ) ^ (n.factorization p) := by
- nth_rw 1 [← prod_factorization_pow_eq_self hn]
- rw [prod_factorization_eq_prod_primeFactors _]
- exact prod_subtype n.primeFactors (fun _ ↦ Iff.rfl) fun a ↦ a ^ n.factorization a
+ simpa using prod_primeFactors_pow_factorization hn
+
+@[deprecated (since := "2026-06-24")]
+alias prod_pow_primeFactors_factorization := prod_primeFactors_coe_pow_factorization
lemma pairwise_coprime_pow_primeFactors_factorization :
Pairwise (Function.onFun Nat.Coprime fun (p : n.primeFactors) ↦ p ^ n.factorization p) := by
@@ -491,6 +496,26 @@ lemma pairwise_coprime_pow_primeFactors_factorization :
· exact Nat.prime_of_mem_primeFactors p1.2
· exact Nat.prime_of_mem_primeFactors p2.2
+theorem dvd_prod_primeFactors_pow_self {n : ℕ} (hn : n ≠ 0) :
+ n ∣ (∏ p ∈ n.primeFactors, p) ^ n := by
+ nth_rw 1 [← Finset.prod_pow, prod_primeFactors_pow_factorization hn]
+ refine prod_dvd_prod_of_dvd _ _ fun i hi ↦ pow_dvd_pow i ?_
+ grw [n.factorization_def <| prime_of_mem_primeFactors hi, padicValNat_le_self]
+
+theorem dvd_pow_self_iff {n k : ℕ} (hn : n ≠ 0) (hk : k ≠ 0) :
+ n ∣ k ^ n ↔ n.primeFactors ⊆ k.primeFactors := by
+ refine ⟨fun h ↦ ?_, fun h ↦ ?_⟩
+ · grw [← Nat.primeFactors_pow k hn, Nat.primeFactors_mono h <| pow_ne_zero n hk]
+ · grw [dvd_prod_primeFactors_pow_self hn, prod_dvd_prod_of_subset _ _ _ h, prod_primeFactors_dvd]
+
+theorem exists_dvd_pow_iff {n k : ℕ} (hn : n ≠ 0) (hk : k ≠ 0) :
+ (∃ m, n ∣ k ^ m) ↔ n.primeFactors ⊆ k.primeFactors := by
+ refine ⟨fun ⟨m, h⟩ ↦ ?_, fun h ↦ ⟨n, dvd_pow_self_iff hn hk |>.mpr h⟩⟩
+ rcases eq_or_ne m 0 with (rfl | hm)
+ · simp_all
+ rw [← Nat.primeFactors_pow k hm]
+ exact Nat.primeFactors_mono h <| pow_ne_zero m hk
+
/-! ### Lemmas about factorizations of particular functions -/
/-- Exactly `n / p` naturals in `[1, n]` are multiples of `p`.
diff --git a/Mathlib/Data/Nat/MaxPowDiv.lean b/Mathlib/Data/Nat/MaxPowDiv.lean
index ab67e60daed00f..caa09ce3da8c43 100644
--- a/Mathlib/Data/Nat/MaxPowDiv.lean
+++ b/Mathlib/Data/Nat/MaxPowDiv.lean
@@ -136,6 +136,19 @@ theorem pow_padicValNat_mul_divMaxPow (p n : ℕ) : p ^ padicValNat p n * divMax
theorem _root_.pow_padicValNat_dvd {p n : ℕ} : p ^ padicValNat p n ∣ n :=
⟨divMaxPow n p, by simp⟩
+theorem padicValNat_lt_self {p n : ℕ} (hn : n ≠ 0) : padicValNat p n < n := by
+ match p with
+ | 0 | 1 => simp [Nat.pos_of_ne_zero hn]
+ | p + 2 =>
+ apply (p + 2 |>.pow_lt_pow_iff_right <| by lia).mp
+ apply Nat.lt_of_le_of_lt ?_ <| Nat.lt_pow_self <| by lia
+ exact le_of_dvd (Nat.pos_of_ne_zero hn) pow_padicValNat_dvd
+
+theorem padicValNat_le_self {p : ℕ} (n : ℕ) : padicValNat p n ≤ n := by
+ rcases eq_or_ne n 0 with rfl | hn
+ · simp
+ · exact Nat.le_of_lt <| padicValNat_lt_self hn
+
theorem not_dvd_divMaxPow {p n : ℕ} (hp : 1 < p) (hn : n ≠ 0) : ¬p ∣ divMaxPow n p := by
simp [divMaxPow, maxPowDvdDiv, maxPowDvdDiv.go_spec, *]
diff --git a/Mathlib/Data/Nat/Squarefree.lean b/Mathlib/Data/Nat/Squarefree.lean
index f4fb20713370c4..f856139103cf4a 100644
--- a/Mathlib/Data/Nat/Squarefree.lean
+++ b/Mathlib/Data/Nat/Squarefree.lean
@@ -373,6 +373,16 @@ lemma primeFactors_prod (hs : ∀ p ∈ s, p.Prime) : primeFactors (∏ p ∈ s,
rintro ⟨hp, q, hq, hpq⟩
rwa [← ((hs _ hq).dvd_iff_eq hp.ne_one).1 hpq]
+theorem primeFactors_prod_primeFactors (n : ℕ) :
+ (∏ p ∈ n.primeFactors, p).primeFactors = n.primeFactors :=
+ primeFactors_prod fun _ hp ↦ n.mem_primeFactors.mp hp |>.left
+
+theorem prod_primeFactors_dvd_iff {n k : ℕ} (hk : k ≠ 0) :
+ (∏ p ∈ n.primeFactors, p) ∣ k ↔ n.primeFactors ⊆ k.primeFactors := by
+ refine ⟨fun h ↦ ?_, fun h ↦ ?_⟩
+ · grw [← Nat.primeFactors_mono h hk, primeFactors_prod_primeFactors]
+ · grw [← k.prod_primeFactors_dvd, Finset.prod_dvd_prod_of_subset _ _ _ h]
+
lemma primeFactors_div_gcd (hm : Squarefree m) (hn : n ≠ 0) :
primeFactors (m / m.gcd n) = primeFactors m \ primeFactors n := by
ext p
diff --git a/Mathlib/Data/ZMod/QuotientRing.lean b/Mathlib/Data/ZMod/QuotientRing.lean
index a2902af0327b75..b7161c061e2515 100644
--- a/Mathlib/Data/ZMod/QuotientRing.lean
+++ b/Mathlib/Data/ZMod/QuotientRing.lean
@@ -87,7 +87,7 @@ def ZMod.prodEquivPi {ι : Type*} [Fintype ι] (a : ι → ℕ)
/-- The **Chinese remainder theorem**, version for `ZMod n`. -/
def ZMod.equivPi (hn : n ≠ 0) :
ZMod n ≃+* Π (p : n.primeFactors), ZMod (p ^ (n.factorization p)) :=
- (ringEquivCongr <| Nat.prod_pow_primeFactors_factorization hn).trans
+ (ringEquivCongr <| Nat.prod_primeFactors_coe_pow_factorization hn).trans
<| prodEquivPi (fun (p : n.primeFactors) ↦ (p : ℕ) ^ (n.factorization p))
n.pairwise_coprime_pow_primeFactors_factorization
diff --git a/Mathlib/RingTheory/Radical/Basic.lean b/Mathlib/RingTheory/Radical/Basic.lean
index 66ee3710ca9be3..2b536c44fb969e 100644
--- a/Mathlib/RingTheory/Radical/Basic.lean
+++ b/Mathlib/RingTheory/Radical/Basic.lean
@@ -56,6 +56,12 @@ open scoped Classical in
def primeFactors (a : M) : Finset M :=
(normalizedFactors a).toFinset
+@[simp]
+theorem toFinset_normalizedFactors [DecidableEq M] :
+ (normalizedFactors a).toFinset = primeFactors a := by
+ unfold primeFactors
+ convert rfl
+
lemma mem_primeFactors : a ∈ primeFactors b ↔ a ∈ normalizedFactors b := by
simp only [primeFactors, Multiset.mem_toFinset]
@@ -296,6 +302,22 @@ theorem radical_dvd_iff_primeFactors_subset (hb : b ≠ 0) :
rw [← dvd_radical_iff isRadical_radical hb,
radical_dvd_radical_iff_primeFactors_subset_primeFactors]
+theorem exists_dvd_pow_iff_radical_dvd (ha : a ≠ 0) : (∃ n, a ∣ b ^ n) ↔ radical a ∣ b := by
+ rcases eq_or_ne b 0 with (rfl | hb)
+ · exact ⟨by simp, fun _ ↦ ⟨1, by simp⟩⟩
+ refine ⟨fun ⟨n, hdvd⟩ ↦ ?_, fun h ↦ ⟨normalizedFactors a |>.card, ?_⟩⟩
+ · rcases eq_or_ne n 0 with (rfl | hn)
+ · simp [radical_of_isUnit <| isUnit_of_dvd_one <| pow_zero b ▸ hdvd]
+ grw [radical_dvd_radical hdvd <| pow_ne_zero _ hb, radical_pow b hn, radical_dvd_self]
+ · classical
+ rwa [dvd_iff_normalizedFactors_le_normalizedFactors ha <| pow_ne_zero _ hb,
+ normalizedFactors_pow, Multiset.le_card_smul_iff_subset, ← Multiset.toFinset_subset,
+ toFinset_normalizedFactors, toFinset_normalizedFactors,
+ ← radical_dvd_iff_primeFactors_subset hb]
+
+theorem exists_dvd_radical_self_pow (ha : a ≠ 0) : ∃ n, a ∣ radical a ^ n := by
+ rw [exists_dvd_pow_iff_radical_dvd ha]
+
/-- Radical is multiplicative for relatively prime elements. -/
theorem radical_mul (hc : IsRelPrime a b) :
radical (a * b) = radical a * radical b := by
diff --git a/Mathlib/RingTheory/Radical/NatInt.lean b/Mathlib/RingTheory/Radical/NatInt.lean
index bb14afc78a1ea0..fa1204627a638b 100644
--- a/Mathlib/RingTheory/Radical/NatInt.lean
+++ b/Mathlib/RingTheory/Radical/NatInt.lean
@@ -6,12 +6,10 @@ Authors: Bhavik Mehta, Arend Mellendijk, Jeremy Tan
module
public import Mathlib.Algebra.EuclideanDomain.Int
-public import Mathlib.Algebra.GCDMonoid.Nat
public import Mathlib.Data.Nat.Prime.Int
-public import Mathlib.Data.Nat.PrimeFin
+public import Mathlib.Data.Nat.Squarefree
public import Mathlib.RingTheory.PrincipalIdealDomain
public import Mathlib.RingTheory.Radical.Basic
-public import Mathlib.RingTheory.UniqueFactorizationDomain.Nat
/-!
# The radical in `ℕ` and `ℤ`
@@ -73,6 +71,16 @@ lemma radical_pos (n) : 0 < radical n := pos_of_ne_zero radical_ne_zero
@[simp] lemma self_lt_radical_iff : n < radical n ↔ n = 0 := by
simpa only [not_le, not_not] using radical_le_self_iff.not
+theorem primeFactors_radical (n : ℕ) : (radical n).primeFactors = n.primeFactors := by
+ rw [radical_eq_prod_primeFactors, primeFactors_prod_primeFactors]
+
+theorem radical_dvd_iff {n k : ℕ} (hk : k ≠ 0) :
+ radical n ∣ k ↔ n.primeFactors ⊆ k.primeFactors := by
+ rw [radical_eq_prod_primeFactors, prod_primeFactors_dvd_iff hk]
+
+theorem dvd_radical_pow_self {n : ℕ} (hn : n ≠ 0) : n ∣ radical n ^ n := by
+ grw [radical_eq_prod_primeFactors, ← dvd_prod_primeFactors_pow_self hn]
+
open Qq Lean Mathlib.Meta Finset
namespace Mathlib.Meta.Positivity
From 7f60f15d435f4835fda6e999cbd2d08ad8963bfa Mon Sep 17 00:00:00 2001
From: Marcelo Lynch
Date: Fri, 26 Jun 2026 12:58:25 +0000
Subject: [PATCH 0390/1300] ci: bump actions/checkout to v7.0.0 (#41055)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps `actions/checkout` to v7.0.0 across all workflows.
v7 refuses to check out fork-PR code under `pull_request_target`/`workflow_run` unless `allow-unsafe-pr-checkout: true` is set.
Three steps intentionally check out fork-PR code, and we already defend against the malicious case (no persisted credentials; only trusted, base-built tooling runs against the checkout), so they get the opt-in:
```
┌────────────────────────────────────────────┬──────────────────────────────────┬─────────────────────────────────────────────────────────┐
│ File │ Step │ Checked-out ref │
├────────────────────────────────────────────┼──────────────────────────────────┼─────────────────────────────────────────────────────────┤
│ .github/workflows/add_label_from_diff.yaml │ "Checkout branch to label" (L50) │ ${{ github.event.pull_request.head.sha || github.sha }} │
├────────────────────────────────────────────┼──────────────────────────────────┼─────────────────────────────────────────────────────────┤
│ .github/workflows/PR_summary.yml │ "Checkout code" (L29) │ ${{ github.event.pull_request.head.sha }} │
├────────────────────────────────────────────┼──────────────────────────────────┼─────────────────────────────────────────────────────────┤
│ .github/workflows/decls-diff.yml │ "Checkout new commit" (L67) │ ${{ steps.meta.outputs.new-sha }} │
└────────────────────────────────────────────┴──────────────────────────────────┴─────────────────────────────────────────────────────────┘
```
---
.github/actions/get-mathlib-ci/README.md | 2 +-
.github/actions/get-mathlib-ci/action.yml | 2 +-
.github/actions/get-tools/action.yml | 2 +-
.github/actions/setup-build-env/action.yml | 2 +-
.github/workflows/PR_summary.yml | 7 +++++--
.github/workflows/actionlint.yml | 4 ++--
.github/workflows/add_label_from_diff.yaml | 7 +++++--
.github/workflows/build_template.yml | 12 ++++++------
.github/workflows/cache_test.yml | 2 +-
.github/workflows/check_pr_titles.yaml | 2 +-
.github/workflows/commit_verification.yml | 4 ++--
.github/workflows/daily-master-tag.yml | 2 +-
.github/workflows/daily.yml | 18 +++++++++---------
.github/workflows/decls-diff.yml | 7 +++++--
.github/workflows/docker_build.yml | 2 +-
.github/workflows/lake_cache_shadow.yml | 8 ++++----
.github/workflows/latest_import.yml | 4 ++--
.github/workflows/long_file_report.yml | 4 ++--
.github/workflows/maintainer_bors_wf_run.yml | 2 +-
.github/workflows/maintainer_merge_wf_run.yml | 2 +-
.github/workflows/nightly_bump_and_merge.yml | 4 ++--
.github/workflows/nightly_detect_failure.yml | 8 ++++----
.github/workflows/nightly_merge_master.yml | 2 +-
.github/workflows/nolints.yml | 2 +-
.github/workflows/olean_report.yaml | 8 ++++----
.github/workflows/pr_check_downstream.yml | 2 +-
.github/workflows/pre-commit.yml | 2 +-
.github/workflows/publish_tools.yml | 2 +-
.github/workflows/remove_deprecated_decls.yml | 2 +-
.github/workflows/rm_set_option.yml | 2 +-
.github/workflows/shake.yaml | 2 +-
.github/workflows/technical_debt_metrics.yml | 4 ++--
.github/workflows/update_dependencies.yml | 2 +-
.../workflows/update_dependencies_zulip.yml | 8 ++++----
.../workflows/validate_mathlib_ci_paths.yml | 4 ++--
.github/workflows/weekly-lints.yml | 4 ++--
.github/workflows/zulip_emoji_ci_status.yaml | 2 +-
.github/workflows/zulip_emoji_closed_pr.yaml | 2 +-
.github/workflows/zulip_emoji_labelling.yaml | 2 +-
.../workflows/zulip_emoji_merge_delegate.yaml | 4 ++--
40 files changed, 86 insertions(+), 77 deletions(-)
diff --git a/.github/actions/get-mathlib-ci/README.md b/.github/actions/get-mathlib-ci/README.md
index 77be1f2a6f15c9..96a1e071c4eb0f 100644
--- a/.github/actions/get-mathlib-ci/README.md
+++ b/.github/actions/get-mathlib-ci/README.md
@@ -25,7 +25,7 @@ then use the local action:
```yaml
- name: Checkout local actions
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/actions/get-mathlib-ci/action.yml b/.github/actions/get-mathlib-ci/action.yml
index 0a0ba2f7ebcb9a..6b593d3115f957 100644
--- a/.github/actions/get-mathlib-ci/action.yml
+++ b/.github/actions/get-mathlib-ci/action.yml
@@ -33,7 +33,7 @@ runs:
using: composite
steps:
- name: Get mathlib-ci
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: leanprover-community/mathlib-ci
ref: ${{ inputs.ref }}
diff --git a/.github/actions/get-tools/action.yml b/.github/actions/get-tools/action.yml
index 6e195f21ea308e..185b6b03c4b069 100644
--- a/.github/actions/get-tools/action.yml
+++ b/.github/actions/get-tools/action.yml
@@ -139,7 +139,7 @@ runs:
- name: Checkout tools branch (source build)
if: ${{ steps.finalize.outputs.result == 'build' }}
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ inputs.tools_source_ref }}
path: ${{ inputs.path }}
diff --git a/.github/actions/setup-build-env/action.yml b/.github/actions/setup-build-env/action.yml
index 31298cdb1a88d6..0872e2bd7531de 100644
--- a/.github/actions/setup-build-env/action.yml
+++ b/.github/actions/setup-build-env/action.yml
@@ -64,7 +64,7 @@ runs:
# code we build, so don't leave the GITHUB_TOKEN in pr-branch/.git/config where
# that code could read it.
- name: Checkout PR branch
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ inputs.pr_branch_ref }}
fetch-depth: 1
diff --git a/.github/workflows/PR_summary.yml b/.github/workflows/PR_summary.yml
index e29630eafe1a55..5d73c1426fc510 100644
--- a/.github/workflows/PR_summary.yml
+++ b/.github/workflows/PR_summary.yml
@@ -17,16 +17,19 @@ jobs:
steps:
- name: Checkout code
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
path: pr-branch
# Untrusted (potentially fork) checkout: don't persist the GITHUB_TOKEN into its .git/config.
persist-credentials: false
+ # Only trusted base-repo scripts run against this checkout, so checking out
+ # fork PR code under pull_request_target is safe.
+ allow-unsafe-pr-checkout: true
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml
index be2b1e7f6c7b39..0320272d125fcc 100644
--- a/.github/workflows/actionlint.yml
+++ b/.github/workflows/actionlint.yml
@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: suggester / actionlint
uses: reviewdog/action-actionlint@6fb7acc99f4a1008869fa8a0f09cfca740837d9d # v1.72.0
@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# Using our fork's PR branch until upstream merges the improved error reporting:
# https://github.com/zgosalvez/github-actions-ensure-sha-pinned-actions/pull/288
diff --git a/.github/workflows/add_label_from_diff.yaml b/.github/workflows/add_label_from_diff.yaml
index a2257f8076023d..3738c9c273d046 100644
--- a/.github/workflows/add_label_from_diff.yaml
+++ b/.github/workflows/add_label_from_diff.yaml
@@ -22,7 +22,7 @@ jobs:
if: github.repository == 'leanprover-community/mathlib4'
steps:
- name: Checkout master branch to build autolabel from
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: master
path: tools
@@ -38,13 +38,16 @@ jobs:
run: |
lake build autolabel
- name: Checkout branch to label
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
fetch-depth: 0
path: pr-branch
# Untrusted (potentially fork) checkout: don't persist the GITHUB_TOKEN into its .git/config.
persist-credentials: false
+ # autolabel is built from the trusted base checkout and only reads these files,
+ # so checking out fork PR code under pull_request_target is safe.
+ allow-unsafe-pr-checkout: true
- name: Run autolabel
working-directory: pr-branch
run: |
diff --git a/.github/workflows/build_template.yml b/.github/workflows/build_template.yml
index 9e742026b1aec5..7d05038424111f 100644
--- a/.github/workflows/build_template.yml
+++ b/.github/workflows/build_template.yml
@@ -79,7 +79,7 @@ jobs:
# We just populate the env vars for this step to make them viewable in the logs
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
@@ -394,7 +394,7 @@ jobs:
shell: landrun --rox /usr --ro /etc/timezone --rw /dev --rox /home/lean/.elan --rox /home/lean/actions-runner/_work --rox /home/lean/.cache/mathlib/ --rw pr-branch/.lake/ --env PATH --env HOME --env GITHUB_OUTPUT --env CI -- bash -euxo pipefail {0}
steps:
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
@@ -604,7 +604,7 @@ jobs:
# `build_template` via `pull_request_target`, never this one — so
# `pr_branch_ref` is always a trusted ref here. Fork PRs keep `master`.
- name: Checkout tools branch
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ inputs.tools_branch_ref != '' && inputs.tools_branch_ref || (github.event.pull_request.head.repo.fork && 'master' || inputs.pr_branch_ref) }}
fetch-depth: 1
@@ -674,7 +674,7 @@ jobs:
contents: read
steps:
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ inputs.pr_branch_ref }}
# Untrusted (potentially fork) checkout: don't persist the GITHUB_TOKEN into its .git/config.
@@ -684,7 +684,7 @@ jobs:
# below loads from a trust-rooted source, not from PR-branch-controlled
# content. Mirrors the `Checkout local actions` step in the `build` job.
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
@@ -748,7 +748,7 @@ jobs:
lake exe graph
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/cache_test.yml b/.github/workflows/cache_test.yml
index 3348e82783dbce..69a965e6678d62 100644
--- a/.github/workflows/cache_test.yml
+++ b/.github/workflows/cache_test.yml
@@ -41,7 +41,7 @@ jobs:
run:
shell: bash
steps:
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# Install elan and the toolchain cross-platform. Build/test/lint, the
# Mathlib cache, and the GitHub cache are all disabled, so this is a
diff --git a/.github/workflows/check_pr_titles.yaml b/.github/workflows/check_pr_titles.yaml
index edbe9058ecd2a8..7d995d59b2c69c 100644
--- a/.github/workflows/check_pr_titles.yaml
+++ b/.github/workflows/check_pr_titles.yaml
@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: master
- name: Configure Lean
diff --git a/.github/workflows/commit_verification.yml b/.github/workflows/commit_verification.yml
index b117d8f7235ac6..e14544cb1ad433 100644
--- a/.github/workflows/commit_verification.yml
+++ b/.github/workflows/commit_verification.yml
@@ -33,14 +33,14 @@ jobs:
# This is a quick check to avoid unnecessary runs
steps:
- name: Checkout PR head
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
# Checkout the actual PR head, not the merge commit GitHub creates
ref: ${{ github.event.pull_request.head.sha }}
# Fetch full history to access all PR commits
fetch-depth: 0
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/daily-master-tag.yml b/.github/workflows/daily-master-tag.yml
index 83007e40169d7e..96aeefcb3382ae 100644
--- a/.github/workflows/daily-master-tag.yml
+++ b/.github/workflows/daily-master-tag.yml
@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
if: github.repository == 'leanprover-community/mathlib4'
steps:
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: master
diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml
index cd75a4c5e8c103..d737a444edb805 100644
--- a/.github/workflows/daily.yml
+++ b/.github/workflows/daily.yml
@@ -31,7 +31,7 @@ jobs:
steps:
# Checkout repository, so that we can fetch tags to decide which branch we want.
- name: Checkout branch or tag
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Fetch latest tags (if nightly)
if: matrix.branch_type == 'nightly'
@@ -52,7 +52,7 @@ jobs:
# Checkout the branch or tag we want to test.
- name: Checkout branch or tag
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: ${{ matrix.branch_type == 'nightly' && 'leanprover-community/mathlib4-nightly-testing' || github.repository }}
ref: ${{ env.BRANCH_REF }}
@@ -82,7 +82,7 @@ jobs:
branch_type: [master, nightly]
steps:
- name: Checkout repository
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Get job status and URLs
id: get-status
@@ -156,7 +156,7 @@ jobs:
steps:
# Checkout repository, so that we can fetch tags to decide which branch we want.
- name: Checkout branch or tag
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Fetch latest tags (if nightly)
if: matrix.branch_type == 'nightly'
@@ -177,7 +177,7 @@ jobs:
# Checkout the branch or tag we want to test.
- name: Checkout branch or tag
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: ${{ matrix.branch_type == 'nightly' && 'leanprover-community/mathlib4-nightly-testing' || github.repository }}
ref: ${{ env.BRANCH_REF }}
@@ -205,7 +205,7 @@ jobs:
branch_type: [master, nightly]
steps:
- name: Checkout repository
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Get job status and URLs
id: get-status
@@ -279,7 +279,7 @@ jobs:
steps:
# Checkout repository, so that we can fetch tags to decide which branch we want.
- name: Checkout branch or tag
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Fetch latest tags (if nightly)
if: matrix.branch_type == 'nightly'
@@ -300,7 +300,7 @@ jobs:
# Checkout the branch or tag we want to test.
- name: Checkout branch or tag
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: ${{ matrix.branch_type == 'nightly' && 'leanprover-community/mathlib4-nightly-testing' || github.repository }}
ref: ${{ env.BRANCH_REF }}
@@ -370,7 +370,7 @@ jobs:
branch_type: [master, nightly]
steps:
- name: Checkout repository
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Get job status and URLs
id: get-status
diff --git a/.github/workflows/decls-diff.yml b/.github/workflows/decls-diff.yml
index aca4899a3af3ad..d4405ca5107524 100644
--- a/.github/workflows/decls-diff.yml
+++ b/.github/workflows/decls-diff.yml
@@ -58,10 +58,13 @@ jobs:
} | tee -a "$GITHUB_OUTPUT"
- name: Checkout new commit
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ steps.meta.outputs.new-sha }}
fetch-depth: 0
+ # `new-sha` is fork PR code already built by build_fork.yml; allow the
+ # fork checkout under this workflow_run.
+ allow-unsafe-pr-checkout: true
- name: Resolve merge-base against master
id: resolve
@@ -136,7 +139,7 @@ jobs:
# Tooling is checked out unconditionally: the patcher (from CI_SCRIPTS_DIR)
# is needed on the cache-miss path too, to post the warning notice.
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/docker_build.yml b/.github/workflows/docker_build.yml
index b8b6c0fa81ac43..c46e08fd9934d8 100644
--- a/.github/workflows/docker_build.yml
+++ b/.github/workflows/docker_build.yml
@@ -27,7 +27,7 @@ jobs:
steps:
# documentation at
# https://docs.github.com/en/actions/use-cases-and-examples/publishing-packages/publishing-docker-images
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Log in to the container registry
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
diff --git a/.github/workflows/lake_cache_shadow.yml b/.github/workflows/lake_cache_shadow.yml
index 50ce19d3b21d59..099472ca7b57f0 100644
--- a/.github/workflows/lake_cache_shadow.yml
+++ b/.github/workflows/lake_cache_shadow.yml
@@ -98,13 +98,13 @@ jobs:
uses: dcarbone/install-jq-action@b7ef57d46ece78760b4019dbc4080a1ba2a40b45 # v3.2.0
- name: Checkout tools branch
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: master
path: tools-branch
- name: Checkout mathlib (pr-branch)
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ inputs.mathlib_ref || 'master' }}
fetch-depth: 2
@@ -278,7 +278,7 @@ jobs:
LAKE_CACHE_REVISION_ENDPOINT: ${{ vars.LAKE_CACHE_REVISION_ENDPOINT }}
steps:
- name: Checkout mathlib (for lean-toolchain pin)
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ needs.build_and_stage.outputs.sha }}
path: pr-branch
@@ -410,7 +410,7 @@ jobs:
LAKE_CACHE_REVISION_ENDPOINT: ${{ vars.LAKE_CACHE_REVISION_ENDPOINT_PUBLIC }}
steps:
- name: Checkout mathlib
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ needs.build_and_stage.outputs.sha }}
path: pr-branch
diff --git a/.github/workflows/latest_import.yml b/.github/workflows/latest_import.yml
index eeb9d92bda388a..447160b95b43eb 100644
--- a/.github/workflows/latest_import.yml
+++ b/.github/workflows/latest_import.yml
@@ -26,10 +26,10 @@ jobs:
: # Do nothing on failure, but suppress errors
fi
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/long_file_report.yml b/.github/workflows/long_file_report.yml
index 034ae752cd59f4..f0febf9b6d215f 100644
--- a/.github/workflows/long_file_report.yml
+++ b/.github/workflows/long_file_report.yml
@@ -12,10 +12,10 @@ jobs:
steps:
- name: Checkout code
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/maintainer_bors_wf_run.yml b/.github/workflows/maintainer_bors_wf_run.yml
index 7d4b4f7f62a187..95353cb9b2b22b 100644
--- a/.github/workflows/maintainer_bors_wf_run.yml
+++ b/.github/workflows/maintainer_bors_wf_run.yml
@@ -130,7 +130,7 @@ jobs:
if: ${{ ! steps.inputs.outputs.mOrD == '' &&
( steps.user_permission.outputs.require-result == 'true' ||
steps.inputs.outputs.bot == 'true' ) }}
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/maintainer_merge_wf_run.yml b/.github/workflows/maintainer_merge_wf_run.yml
index 81ed3364171722..44208ccbb8619d 100644
--- a/.github/workflows/maintainer_merge_wf_run.yml
+++ b/.github/workflows/maintainer_merge_wf_run.yml
@@ -163,7 +163,7 @@ jobs:
- name: Checkout local actions
if: ${{ steps.authorized.outputs.authorized == 'true' }}
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/nightly_bump_and_merge.yml b/.github/workflows/nightly_bump_and_merge.yml
index 5418ef6c3ba2fc..a04e35e02d2573 100644
--- a/.github/workflows/nightly_bump_and_merge.yml
+++ b/.github/workflows/nightly_bump_and_merge.yml
@@ -39,14 +39,14 @@ jobs:
# This token is masked by the token minting action and will not be logged accidentally.
- name: Checkout nightly-testing branch
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: nightly-testing
fetch-depth: 0 # Fetch all branches and history
token: ${{ steps.app-token.outputs.token }}
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/nightly_detect_failure.yml b/.github/workflows/nightly_detect_failure.yml
index 8a2010877288a3..9e7159289f748e 100644
--- a/.github/workflows/nightly_detect_failure.yml
+++ b/.github/workflows/nightly_detect_failure.yml
@@ -122,7 +122,7 @@ jobs:
# This token is masked by the token minting action and will not be logged accidentally.
- name: Checkout code
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
# Pin to the SHA whose CI just succeeded, not the current tip of `nightly-testing`,
# which may have advanced while CI was running. Without this, the tag and
@@ -216,7 +216,7 @@ jobs:
# The create-github-app-token README states that this token is masked and will not be logged accidentally.
- name: Checkout Lean repository
if: steps.tag.outputs.is_nightly == 'true'
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: leanprover/lean4
token: ${{ steps.lean-pr-testing-token.outputs.token }}
@@ -418,7 +418,7 @@ jobs:
azure-client-id: ${{ vars.GH_APP_AZURE_CLIENT_ID_NIGHTLY_TESTING }}
azure-tenant-id: ${{ secrets.LPC_AZ_TENANT_ID }}
- name: Checkout Mathlib4 repository
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
if: steps.tag.outputs.is_nightly == 'true' && steps.check_branch.outputs.result == 'false'
with:
ref: nightly-testing # checkout nightly-testing branch (shouldn't matter which)
@@ -427,7 +427,7 @@ jobs:
- name: Checkout local actions
if: steps.tag.outputs.is_nightly == 'true' && steps.check_branch.outputs.result == 'false'
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/nightly_merge_master.yml b/.github/workflows/nightly_merge_master.yml
index e371f9e9b4cd10..39725897ccfa3e 100644
--- a/.github/workflows/nightly_merge_master.yml
+++ b/.github/workflows/nightly_merge_master.yml
@@ -27,7 +27,7 @@ jobs:
# This token is masked by the token minting action and will not be logged accidentally.
- name: Checkout nightly-testing from fork
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: leanprover-community/mathlib4-nightly-testing
ref: nightly-testing
diff --git a/.github/workflows/nolints.yml b/.github/workflows/nolints.yml
index 9716c0526a6205..e1addcf674c915 100644
--- a/.github/workflows/nolints.yml
+++ b/.github/workflows/nolints.yml
@@ -14,7 +14,7 @@ jobs:
contents: read
id-token: write
steps:
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Configure Lean
uses: leanprover/lean-action@38fbc41a8c28c4cbaec22d7f7de508ec2e7c0dd9 # v1.5.0
diff --git a/.github/workflows/olean_report.yaml b/.github/workflows/olean_report.yaml
index 4c3fb07cece678..b517ba84538107 100644
--- a/.github/workflows/olean_report.yaml
+++ b/.github/workflows/olean_report.yaml
@@ -48,7 +48,7 @@ jobs:
- name: Checkout local actions
if: steps.check_trigger.outputs.triggered == 'true'
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
@@ -82,7 +82,7 @@ jobs:
# We fetch full depth so that we can compute the merge base.
- name: Checkout PR head
if: steps.check_trigger.outputs.triggered == 'true'
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: ${{ github.repository }}
ref: refs/pull/${{ github.event.issue.number }}/head
@@ -95,7 +95,7 @@ jobs:
# here so that a single binary can fetch oleans for both checkouts.
- name: Checkout tools branch (master)
if: steps.check_trigger.outputs.triggered == 'true'
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: ${{ github.repository }}
ref: master
@@ -117,7 +117,7 @@ jobs:
- name: Checkout merge base
if: steps.check_trigger.outputs.triggered == 'true'
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: ${{ github.repository }}
ref: ${{ steps.merge_base.outputs.sha }}
diff --git a/.github/workflows/pr_check_downstream.yml b/.github/workflows/pr_check_downstream.yml
index ba7756d551c3e2..b099ba488b7c4b 100644
--- a/.github/workflows/pr_check_downstream.yml
+++ b/.github/workflows/pr_check_downstream.yml
@@ -170,7 +170,7 @@ jobs:
# via a sparse checkout so we can run it; we never need the
# rest of the mathlib4 working tree on this runner.
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml
index 15f6cbbb285324..a9817759d84d61 100644
--- a/.github/workflows/pre-commit.yml
+++ b/.github/workflows/pre-commit.yml
@@ -20,7 +20,7 @@ jobs:
main:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: 3.x
diff --git a/.github/workflows/publish_tools.yml b/.github/workflows/publish_tools.yml
index 028cd32bdc2fb6..999cd7a8c676e9 100644
--- a/.github/workflows/publish_tools.yml
+++ b/.github/workflows/publish_tools.yml
@@ -40,7 +40,7 @@ jobs:
# Build under `tools-branch/`, the same directory `build_template.yml` unpacks
# the tools into, in case the build bakes its own location into the binary.
- name: Checkout master
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: master
path: tools-branch
diff --git a/.github/workflows/remove_deprecated_decls.yml b/.github/workflows/remove_deprecated_decls.yml
index 553f222523dcfa..c50056fdc031a2 100644
--- a/.github/workflows/remove_deprecated_decls.yml
+++ b/.github/workflows/remove_deprecated_decls.yml
@@ -112,7 +112,7 @@ jobs:
echo "from_date=$from_date" >> "$GITHUB_OUTPUT"
echo "to_date=$to_date" >> "$GITHUB_OUTPUT"
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Configure Lean
uses: leanprover/lean-action@38fbc41a8c28c4cbaec22d7f7de508ec2e7c0dd9 # v1.5.0
diff --git a/.github/workflows/rm_set_option.yml b/.github/workflows/rm_set_option.yml
index 780af2bdfb07d5..3bfee558a9f5e6 100644
--- a/.github/workflows/rm_set_option.yml
+++ b/.github/workflows/rm_set_option.yml
@@ -31,7 +31,7 @@ jobs:
contents: read
id-token: write
steps:
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Configure Lean
uses: leanprover/lean-action@38fbc41a8c28c4cbaec22d7f7de508ec2e7c0dd9 # v1.5.0
diff --git a/.github/workflows/shake.yaml b/.github/workflows/shake.yaml
index 1bd86610cc16ca..cc3f1e433173e8 100644
--- a/.github/workflows/shake.yaml
+++ b/.github/workflows/shake.yaml
@@ -26,7 +26,7 @@ jobs:
contents: read
id-token: write
steps:
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Configure Lean
uses: leanprover/lean-action@38fbc41a8c28c4cbaec22d7f7de508ec2e7c0dd9 # v1.5.0
diff --git a/.github/workflows/technical_debt_metrics.yml b/.github/workflows/technical_debt_metrics.yml
index 506f868b7a4a1e..f4792818ced7f3 100644
--- a/.github/workflows/technical_debt_metrics.yml
+++ b/.github/workflows/technical_debt_metrics.yml
@@ -12,12 +12,12 @@ jobs:
steps:
- name: Checkout code
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: # checkout all history so that we can compare across commits
fetch-depth: 0
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/update_dependencies.yml b/.github/workflows/update_dependencies.yml
index b6213824640b54..30e6e281b12a8e 100644
--- a/.github/workflows/update_dependencies.yml
+++ b/.github/workflows/update_dependencies.yml
@@ -27,7 +27,7 @@ jobs:
# This token is masked by the token minting action and will not be logged accidentally.
- name: Checkout repository
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
token: ${{ steps.app-token.outputs.token }}
diff --git a/.github/workflows/update_dependencies_zulip.yml b/.github/workflows/update_dependencies_zulip.yml
index 5f440f10c8aac1..d511ecd7ca857a 100644
--- a/.github/workflows/update_dependencies_zulip.yml
+++ b/.github/workflows/update_dependencies_zulip.yml
@@ -17,13 +17,13 @@ jobs:
id-token: write
steps:
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 2 # Need previous commit for diff
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
@@ -108,12 +108,12 @@ jobs:
id-token: write
steps:
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 2 # Need previous commit for diff
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/validate_mathlib_ci_paths.yml b/.github/workflows/validate_mathlib_ci_paths.yml
index 29c0e856c66f4c..96731cdcd805ca 100644
--- a/.github/workflows/validate_mathlib_ci_paths.yml
+++ b/.github/workflows/validate_mathlib_ci_paths.yml
@@ -29,10 +29,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/weekly-lints.yml b/.github/workflows/weekly-lints.yml
index 17615f854293f1..5df2eddf43434a 100644
--- a/.github/workflows/weekly-lints.yml
+++ b/.github/workflows/weekly-lints.yml
@@ -26,12 +26,12 @@ jobs:
: # Do nothing on failure, but suppress errors
fi
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: master
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/zulip_emoji_ci_status.yaml b/.github/workflows/zulip_emoji_ci_status.yaml
index 02d9ed31903483..b2a46fccb8c0d2 100644
--- a/.github/workflows/zulip_emoji_ci_status.yaml
+++ b/.github/workflows/zulip_emoji_ci_status.yaml
@@ -74,7 +74,7 @@ jobs:
- name: Checkout local actions
if: steps.pr.outputs.skip != 'true' && steps.action.outputs.ci_action != 'skip'
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/zulip_emoji_closed_pr.yaml b/.github/workflows/zulip_emoji_closed_pr.yaml
index 91d05bc2cdd41e..b0488d76541fe2 100644
--- a/.github/workflows/zulip_emoji_closed_pr.yaml
+++ b/.github/workflows/zulip_emoji_closed_pr.yaml
@@ -33,7 +33,7 @@ jobs:
- name: Checkout local actions
if: ${{ ! startsWith(github.event.pull_request.title, '[Merged by Bors]') ||
github.event_name == 'reopened' }}
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/zulip_emoji_labelling.yaml b/.github/workflows/zulip_emoji_labelling.yaml
index f92bb24fd007fe..d462d1a878eeb4 100644
--- a/.github/workflows/zulip_emoji_labelling.yaml
+++ b/.github/workflows/zulip_emoji_labelling.yaml
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/zulip_emoji_merge_delegate.yaml b/.github/workflows/zulip_emoji_merge_delegate.yaml
index 176bda34b93689..7d701d874aa6c4 100644
--- a/.github/workflows/zulip_emoji_merge_delegate.yaml
+++ b/.github/workflows/zulip_emoji_merge_delegate.yaml
@@ -15,12 +15,12 @@ jobs:
steps:
- name: Checkout mathlib4 repository history
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0 # download the full repository
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
From 7bc792d5985b7bc725540bf2cf41943da33695d7 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, 26 Jun 2026 13:47:58 +0000
Subject: [PATCH 0391/1300] feat(Topology/Sets): second-countability of
`(Nonempty)Compacts` (#34271)
---
Mathlib/Topology/MetricSpace/Closeds.lean | 76 ---------------------
Mathlib/Topology/Sets/VietorisTopology.lean | 17 +++++
2 files changed, 17 insertions(+), 76 deletions(-)
diff --git a/Mathlib/Topology/MetricSpace/Closeds.lean b/Mathlib/Topology/MetricSpace/Closeds.lean
index 4590e00f483bd8..84b99c80909c4a 100644
--- a/Mathlib/Topology/MetricSpace/Closeds.lean
+++ b/Mathlib/Topology/MetricSpace/Closeds.lean
@@ -277,82 +277,6 @@ theorem isClosed_in_closeds [CompleteSpace α] :
IsClosed (range <| @NonemptyCompacts.toCloseds α _ _) :=
NonemptyCompacts.isClosedEmbedding_toCloseds.isClosed_range
-/-- In a second countable space, the type of nonempty compact subsets is second countable -/
-instance instSecondCountableTopology [SecondCountableTopology α] :
- SecondCountableTopology (NonemptyCompacts α) :=
- haveI : SeparableSpace (NonemptyCompacts α) := by
- /- To obtain a countable dense subset of `NonemptyCompacts α`, start from
- a countable dense subset `s` of α, and then consider all its finite nonempty subsets.
- This set is countable and made of nonempty compact sets. It turns out to be dense:
- by total boundedness, any compact set `t` can be covered by finitely many small balls, and
- approximations in `s` of the centers of these balls give the required finite approximation
- of `t`. -/
- rcases exists_countable_dense α with ⟨s, cs, s_dense⟩
- let v0 := { t : Set α | t.Finite ∧ t ⊆ s }
- let v : Set (NonemptyCompacts α) := { t : NonemptyCompacts α | (t : Set α) ∈ v0 }
- refine ⟨⟨v, ?_, ?_⟩⟩
- · have : v0.Countable := countable_setOf_finite_subset cs
- exact this.preimage SetLike.coe_injective
- · refine fun t => EMetric.mem_closure_iff.2 fun ε εpos => ?_
- -- t is a compact nonempty set, that we have to approximate uniformly by a a set in `v`.
- rcases exists_between εpos with ⟨δ, δpos, δlt⟩
- have δpos' : 0 < δ / 2 := ENNReal.half_pos δpos.ne'
- -- construct a map F associating to a point in α an approximating point in s, up to δ/2.
- have Exy : ∀ x, ∃ y, y ∈ s ∧ edist x y < δ / 2 := by
- intro x
- rcases EMetric.mem_closure_iff.1 (s_dense x) (δ / 2) δpos' with ⟨y, ys, hy⟩
- exact ⟨y, ⟨ys, hy⟩⟩
- let F x := (Exy x).choose
- have Fspec : ∀ x, F x ∈ s ∧ edist x (F x) < δ / 2 := fun x => (Exy x).choose_spec
- -- cover `t` with finitely many balls. Their centers form a set `a`
- have : TotallyBounded (t : Set α) := t.isCompact.totallyBounded
- obtain ⟨a : Set α, af : Set.Finite a, ta : (t : Set α) ⊆ ⋃ y ∈ a, Metric.eball y (δ / 2)⟩ :=
- EMetric.totallyBounded_iff.1 this (δ / 2) δpos'
- -- replace each center by a nearby approximation in `s`, giving a new set `b`
- let b := F '' a
- have : b.Finite := af.image _
- have tb : ∀ x ∈ t, ∃ y ∈ b, edist x y < δ := by
- intro x hx
- rcases mem_iUnion₂.1 (ta hx) with ⟨z, za, Dxz⟩
- exists F z, mem_image_of_mem _ za
- calc
- edist x (F z) ≤ edist x z + edist z (F z) := edist_triangle _ _ _
- _ < δ / 2 + δ / 2 := ENNReal.add_lt_add Dxz (Fspec z).2
- _ = δ := ENNReal.add_halves _
- -- keep only the points in `b` that are close to point in `t`, yielding a new set `c`
- let c := { y ∈ b | ∃ x ∈ t, edist x y < δ }
- have : c.Finite := ‹b.Finite›.subset fun x hx => hx.1
- -- points in `t` are well approximated by points in `c`
- have tc : ∀ x ∈ t, ∃ y ∈ c, edist x y ≤ δ := by
- intro x hx
- rcases tb x hx with ⟨y, yv, Dxy⟩
- have : y ∈ c := by simpa [c, -mem_image] using ⟨yv, ⟨x, hx, Dxy⟩⟩
- exact ⟨y, this, le_of_lt Dxy⟩
- -- points in `c` are well approximated by points in `t`
- have ct : ∀ y ∈ c, ∃ x ∈ t, edist y x ≤ δ := by
- rintro y ⟨_, x, xt, Dyx⟩
- have : edist y x ≤ δ :=
- calc
- edist y x = edist x y := edist_comm _ _
- _ ≤ δ := le_of_lt Dyx
- exact ⟨x, xt, this⟩
- -- it follows that their Hausdorff distance is small
- have : hausdorffEDist (t : Set α) c ≤ δ := hausdorffEDist_le_of_mem_edist tc ct
- have Dtc : hausdorffEDist (t : Set α) c < ε := this.trans_lt δlt
- -- the set `c` is not empty, as it is well approximated by a nonempty set
- have hc : c.Nonempty := nonempty_of_hausdorffEDist_ne_top t.nonempty (ne_top_of_lt Dtc)
- -- let `d` be the version of `c` in the type `NonemptyCompacts α`
- let d : NonemptyCompacts α := ⟨⟨c, ‹c.Finite›.isCompact⟩, hc⟩
- have : c ⊆ s := by
- intro x hx
- rcases (mem_image _ _ _).1 hx.1 with ⟨y, ⟨_, yx⟩⟩
- rw [← yx]
- exact (Fspec y).1
- have : d ∈ v := ⟨‹c.Finite›, this⟩
- -- we have proved that `d` is a good approximation of `t` as requested
- exact ⟨d, ‹d ∈ v›, Dtc⟩
- UniformSpace.secondCountable_of_separable (NonemptyCompacts α)
-
theorem isometry_singleton : Isometry ({·} : α → NonemptyCompacts α) :=
fun _ _ => hausdorffEDist_singleton
diff --git a/Mathlib/Topology/Sets/VietorisTopology.lean b/Mathlib/Topology/Sets/VietorisTopology.lean
index 262594a067bf4c..812e1615152237 100644
--- a/Mathlib/Topology/Sets/VietorisTopology.lean
+++ b/Mathlib/Topology/Sets/VietorisTopology.lean
@@ -560,6 +560,15 @@ theorem regularSpace_iff : RegularSpace (Compacts α) ↔ RegularSpace α :=
theorem t3Space_iff : T3Space (Compacts α) ↔ T3Space α :=
⟨fun _ => isEmbedding_singleton.t3Space, fun _ => inferInstance⟩
+instance [SecondCountableTopology α] : SecondCountableTopology (Compacts α) := by
+ obtain ⟨b, hb₁, -, hb₂⟩ := exists_countable_basis α
+ exact hb₂.compacts.secondCountableTopology <| (countable_setOf_finite_subset hb₁).image _
+
+@[simp]
+theorem secondCountableTopology_iff :
+ SecondCountableTopology (Compacts α) ↔ SecondCountableTopology α :=
+ ⟨fun _ => isEmbedding_singleton.secondCountableTopology, fun _ => inferInstance⟩
+
theorem isCompact_subsets_of_isCompact {K : Set α} (hK : IsCompact K) :
IsCompact {L : Compacts α | ↑L ⊆ K} := by
rw [isEmbedding_coe.isCompact_iff]
@@ -819,6 +828,14 @@ theorem regularSpace_iff : RegularSpace (NonemptyCompacts α) ↔ RegularSpace
theorem t3Space_iff : T3Space (NonemptyCompacts α) ↔ T3Space α :=
⟨fun _ => isEmbedding_singleton.t3Space, fun _ => inferInstance⟩
+instance [SecondCountableTopology α] : SecondCountableTopology (NonemptyCompacts α) :=
+ isEmbedding_toCompacts.secondCountableTopology
+
+@[simp]
+theorem secondCountableTopology_iff :
+ SecondCountableTopology (NonemptyCompacts α) ↔ SecondCountableTopology α :=
+ ⟨fun _ => isEmbedding_singleton.secondCountableTopology, fun _ => inferInstance⟩
+
instance [CompactSpace α] : CompactSpace (NonemptyCompacts α) :=
isClosedEmbedding_toCompacts.compactSpace
From 33633b7d7081460ec2d82103e7dd831faaa4084a Mon Sep 17 00:00:00 2001
From: Bryan Gin-ge Chen <5209952+bryangingechen@users.noreply.github.com>
Date: Fri, 26 Jun 2026 14:08:34 +0000
Subject: [PATCH 0392/1300] ci: revert #41055 (#41078)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This reverts commit 7f60f15d435f4835fda6e999cbd2d08ad8963bfa.
cf. [#PR reviews > CI refusing to check out fork pull request code @ 💬](https://leanprover.zulipchat.com/#narrow/channel/144837-PR-reviews/topic/CI.20refusing.20to.20check.20out.20fork.20pull.20request.20code/near/606724422)
---
.github/actions/get-mathlib-ci/README.md | 2 +-
.github/actions/get-mathlib-ci/action.yml | 2 +-
.github/actions/get-tools/action.yml | 2 +-
.github/actions/setup-build-env/action.yml | 2 +-
.github/workflows/PR_summary.yml | 7 ++-----
.github/workflows/actionlint.yml | 4 ++--
.github/workflows/add_label_from_diff.yaml | 7 ++-----
.github/workflows/build_template.yml | 12 ++++++------
.github/workflows/cache_test.yml | 2 +-
.github/workflows/check_pr_titles.yaml | 2 +-
.github/workflows/commit_verification.yml | 4 ++--
.github/workflows/daily-master-tag.yml | 2 +-
.github/workflows/daily.yml | 18 +++++++++---------
.github/workflows/decls-diff.yml | 7 ++-----
.github/workflows/docker_build.yml | 2 +-
.github/workflows/lake_cache_shadow.yml | 8 ++++----
.github/workflows/latest_import.yml | 4 ++--
.github/workflows/long_file_report.yml | 4 ++--
.github/workflows/maintainer_bors_wf_run.yml | 2 +-
.github/workflows/maintainer_merge_wf_run.yml | 2 +-
.github/workflows/nightly_bump_and_merge.yml | 4 ++--
.github/workflows/nightly_detect_failure.yml | 8 ++++----
.github/workflows/nightly_merge_master.yml | 2 +-
.github/workflows/nolints.yml | 2 +-
.github/workflows/olean_report.yaml | 8 ++++----
.github/workflows/pr_check_downstream.yml | 2 +-
.github/workflows/pre-commit.yml | 2 +-
.github/workflows/publish_tools.yml | 2 +-
.github/workflows/remove_deprecated_decls.yml | 2 +-
.github/workflows/rm_set_option.yml | 2 +-
.github/workflows/shake.yaml | 2 +-
.github/workflows/technical_debt_metrics.yml | 4 ++--
.github/workflows/update_dependencies.yml | 2 +-
.../workflows/update_dependencies_zulip.yml | 8 ++++----
.../workflows/validate_mathlib_ci_paths.yml | 4 ++--
.github/workflows/weekly-lints.yml | 4 ++--
.github/workflows/zulip_emoji_ci_status.yaml | 2 +-
.github/workflows/zulip_emoji_closed_pr.yaml | 2 +-
.github/workflows/zulip_emoji_labelling.yaml | 2 +-
.../workflows/zulip_emoji_merge_delegate.yaml | 4 ++--
40 files changed, 77 insertions(+), 86 deletions(-)
diff --git a/.github/actions/get-mathlib-ci/README.md b/.github/actions/get-mathlib-ci/README.md
index 96a1e071c4eb0f..77be1f2a6f15c9 100644
--- a/.github/actions/get-mathlib-ci/README.md
+++ b/.github/actions/get-mathlib-ci/README.md
@@ -25,7 +25,7 @@ then use the local action:
```yaml
- name: Checkout local actions
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/actions/get-mathlib-ci/action.yml b/.github/actions/get-mathlib-ci/action.yml
index 6b593d3115f957..0a0ba2f7ebcb9a 100644
--- a/.github/actions/get-mathlib-ci/action.yml
+++ b/.github/actions/get-mathlib-ci/action.yml
@@ -33,7 +33,7 @@ runs:
using: composite
steps:
- name: Get mathlib-ci
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
repository: leanprover-community/mathlib-ci
ref: ${{ inputs.ref }}
diff --git a/.github/actions/get-tools/action.yml b/.github/actions/get-tools/action.yml
index 185b6b03c4b069..6e195f21ea308e 100644
--- a/.github/actions/get-tools/action.yml
+++ b/.github/actions/get-tools/action.yml
@@ -139,7 +139,7 @@ runs:
- name: Checkout tools branch (source build)
if: ${{ steps.finalize.outputs.result == 'build' }}
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ inputs.tools_source_ref }}
path: ${{ inputs.path }}
diff --git a/.github/actions/setup-build-env/action.yml b/.github/actions/setup-build-env/action.yml
index 0872e2bd7531de..31298cdb1a88d6 100644
--- a/.github/actions/setup-build-env/action.yml
+++ b/.github/actions/setup-build-env/action.yml
@@ -64,7 +64,7 @@ runs:
# code we build, so don't leave the GITHUB_TOKEN in pr-branch/.git/config where
# that code could read it.
- name: Checkout PR branch
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ inputs.pr_branch_ref }}
fetch-depth: 1
diff --git a/.github/workflows/PR_summary.yml b/.github/workflows/PR_summary.yml
index 5d73c1426fc510..e29630eafe1a55 100644
--- a/.github/workflows/PR_summary.yml
+++ b/.github/workflows/PR_summary.yml
@@ -17,19 +17,16 @@ jobs:
steps:
- name: Checkout code
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
path: pr-branch
# Untrusted (potentially fork) checkout: don't persist the GITHUB_TOKEN into its .git/config.
persist-credentials: false
- # Only trusted base-repo scripts run against this checkout, so checking out
- # fork PR code under pull_request_target is safe.
- allow-unsafe-pr-checkout: true
- name: Checkout local actions
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml
index 0320272d125fcc..be2b1e7f6c7b39 100644
--- a/.github/workflows/actionlint.yml
+++ b/.github/workflows/actionlint.yml
@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: suggester / actionlint
uses: reviewdog/action-actionlint@6fb7acc99f4a1008869fa8a0f09cfca740837d9d # v1.72.0
@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# Using our fork's PR branch until upstream merges the improved error reporting:
# https://github.com/zgosalvez/github-actions-ensure-sha-pinned-actions/pull/288
diff --git a/.github/workflows/add_label_from_diff.yaml b/.github/workflows/add_label_from_diff.yaml
index 3738c9c273d046..a2257f8076023d 100644
--- a/.github/workflows/add_label_from_diff.yaml
+++ b/.github/workflows/add_label_from_diff.yaml
@@ -22,7 +22,7 @@ jobs:
if: github.repository == 'leanprover-community/mathlib4'
steps:
- name: Checkout master branch to build autolabel from
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: master
path: tools
@@ -38,16 +38,13 @@ jobs:
run: |
lake build autolabel
- name: Checkout branch to label
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
fetch-depth: 0
path: pr-branch
# Untrusted (potentially fork) checkout: don't persist the GITHUB_TOKEN into its .git/config.
persist-credentials: false
- # autolabel is built from the trusted base checkout and only reads these files,
- # so checking out fork PR code under pull_request_target is safe.
- allow-unsafe-pr-checkout: true
- name: Run autolabel
working-directory: pr-branch
run: |
diff --git a/.github/workflows/build_template.yml b/.github/workflows/build_template.yml
index 7d05038424111f..9e742026b1aec5 100644
--- a/.github/workflows/build_template.yml
+++ b/.github/workflows/build_template.yml
@@ -79,7 +79,7 @@ jobs:
# We just populate the env vars for this step to make them viewable in the logs
- name: Checkout local actions
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
@@ -394,7 +394,7 @@ jobs:
shell: landrun --rox /usr --ro /etc/timezone --rw /dev --rox /home/lean/.elan --rox /home/lean/actions-runner/_work --rox /home/lean/.cache/mathlib/ --rw pr-branch/.lake/ --env PATH --env HOME --env GITHUB_OUTPUT --env CI -- bash -euxo pipefail {0}
steps:
- name: Checkout local actions
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
@@ -604,7 +604,7 @@ jobs:
# `build_template` via `pull_request_target`, never this one — so
# `pr_branch_ref` is always a trusted ref here. Fork PRs keep `master`.
- name: Checkout tools branch
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ inputs.tools_branch_ref != '' && inputs.tools_branch_ref || (github.event.pull_request.head.repo.fork && 'master' || inputs.pr_branch_ref) }}
fetch-depth: 1
@@ -674,7 +674,7 @@ jobs:
contents: read
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ inputs.pr_branch_ref }}
# Untrusted (potentially fork) checkout: don't persist the GITHUB_TOKEN into its .git/config.
@@ -684,7 +684,7 @@ jobs:
# below loads from a trust-rooted source, not from PR-branch-controlled
# content. Mirrors the `Checkout local actions` step in the `build` job.
- name: Checkout local actions
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
@@ -748,7 +748,7 @@ jobs:
lake exe graph
- name: Checkout local actions
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/cache_test.yml b/.github/workflows/cache_test.yml
index 69a965e6678d62..3348e82783dbce 100644
--- a/.github/workflows/cache_test.yml
+++ b/.github/workflows/cache_test.yml
@@ -41,7 +41,7 @@ jobs:
run:
shell: bash
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# Install elan and the toolchain cross-platform. Build/test/lint, the
# Mathlib cache, and the GitHub cache are all disabled, so this is a
diff --git a/.github/workflows/check_pr_titles.yaml b/.github/workflows/check_pr_titles.yaml
index 7d995d59b2c69c..edbe9058ecd2a8 100644
--- a/.github/workflows/check_pr_titles.yaml
+++ b/.github/workflows/check_pr_titles.yaml
@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: master
- name: Configure Lean
diff --git a/.github/workflows/commit_verification.yml b/.github/workflows/commit_verification.yml
index e14544cb1ad433..b117d8f7235ac6 100644
--- a/.github/workflows/commit_verification.yml
+++ b/.github/workflows/commit_verification.yml
@@ -33,14 +33,14 @@ jobs:
# This is a quick check to avoid unnecessary runs
steps:
- name: Checkout PR head
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
# Checkout the actual PR head, not the merge commit GitHub creates
ref: ${{ github.event.pull_request.head.sha }}
# Fetch full history to access all PR commits
fetch-depth: 0
- name: Checkout local actions
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/daily-master-tag.yml b/.github/workflows/daily-master-tag.yml
index 96aeefcb3382ae..83007e40169d7e 100644
--- a/.github/workflows/daily-master-tag.yml
+++ b/.github/workflows/daily-master-tag.yml
@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
if: github.repository == 'leanprover-community/mathlib4'
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: master
diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml
index d737a444edb805..cd75a4c5e8c103 100644
--- a/.github/workflows/daily.yml
+++ b/.github/workflows/daily.yml
@@ -31,7 +31,7 @@ jobs:
steps:
# Checkout repository, so that we can fetch tags to decide which branch we want.
- name: Checkout branch or tag
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Fetch latest tags (if nightly)
if: matrix.branch_type == 'nightly'
@@ -52,7 +52,7 @@ jobs:
# Checkout the branch or tag we want to test.
- name: Checkout branch or tag
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
repository: ${{ matrix.branch_type == 'nightly' && 'leanprover-community/mathlib4-nightly-testing' || github.repository }}
ref: ${{ env.BRANCH_REF }}
@@ -82,7 +82,7 @@ jobs:
branch_type: [master, nightly]
steps:
- name: Checkout repository
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Get job status and URLs
id: get-status
@@ -156,7 +156,7 @@ jobs:
steps:
# Checkout repository, so that we can fetch tags to decide which branch we want.
- name: Checkout branch or tag
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Fetch latest tags (if nightly)
if: matrix.branch_type == 'nightly'
@@ -177,7 +177,7 @@ jobs:
# Checkout the branch or tag we want to test.
- name: Checkout branch or tag
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
repository: ${{ matrix.branch_type == 'nightly' && 'leanprover-community/mathlib4-nightly-testing' || github.repository }}
ref: ${{ env.BRANCH_REF }}
@@ -205,7 +205,7 @@ jobs:
branch_type: [master, nightly]
steps:
- name: Checkout repository
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Get job status and URLs
id: get-status
@@ -279,7 +279,7 @@ jobs:
steps:
# Checkout repository, so that we can fetch tags to decide which branch we want.
- name: Checkout branch or tag
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Fetch latest tags (if nightly)
if: matrix.branch_type == 'nightly'
@@ -300,7 +300,7 @@ jobs:
# Checkout the branch or tag we want to test.
- name: Checkout branch or tag
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
repository: ${{ matrix.branch_type == 'nightly' && 'leanprover-community/mathlib4-nightly-testing' || github.repository }}
ref: ${{ env.BRANCH_REF }}
@@ -370,7 +370,7 @@ jobs:
branch_type: [master, nightly]
steps:
- name: Checkout repository
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Get job status and URLs
id: get-status
diff --git a/.github/workflows/decls-diff.yml b/.github/workflows/decls-diff.yml
index d4405ca5107524..aca4899a3af3ad 100644
--- a/.github/workflows/decls-diff.yml
+++ b/.github/workflows/decls-diff.yml
@@ -58,13 +58,10 @@ jobs:
} | tee -a "$GITHUB_OUTPUT"
- name: Checkout new commit
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ steps.meta.outputs.new-sha }}
fetch-depth: 0
- # `new-sha` is fork PR code already built by build_fork.yml; allow the
- # fork checkout under this workflow_run.
- allow-unsafe-pr-checkout: true
- name: Resolve merge-base against master
id: resolve
@@ -139,7 +136,7 @@ jobs:
# Tooling is checked out unconditionally: the patcher (from CI_SCRIPTS_DIR)
# is needed on the cache-miss path too, to post the warning notice.
- name: Checkout local actions
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/docker_build.yml b/.github/workflows/docker_build.yml
index c46e08fd9934d8..b8b6c0fa81ac43 100644
--- a/.github/workflows/docker_build.yml
+++ b/.github/workflows/docker_build.yml
@@ -27,7 +27,7 @@ jobs:
steps:
# documentation at
# https://docs.github.com/en/actions/use-cases-and-examples/publishing-packages/publishing-docker-images
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Log in to the container registry
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
diff --git a/.github/workflows/lake_cache_shadow.yml b/.github/workflows/lake_cache_shadow.yml
index 099472ca7b57f0..50ce19d3b21d59 100644
--- a/.github/workflows/lake_cache_shadow.yml
+++ b/.github/workflows/lake_cache_shadow.yml
@@ -98,13 +98,13 @@ jobs:
uses: dcarbone/install-jq-action@b7ef57d46ece78760b4019dbc4080a1ba2a40b45 # v3.2.0
- name: Checkout tools branch
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: master
path: tools-branch
- name: Checkout mathlib (pr-branch)
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ inputs.mathlib_ref || 'master' }}
fetch-depth: 2
@@ -278,7 +278,7 @@ jobs:
LAKE_CACHE_REVISION_ENDPOINT: ${{ vars.LAKE_CACHE_REVISION_ENDPOINT }}
steps:
- name: Checkout mathlib (for lean-toolchain pin)
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ needs.build_and_stage.outputs.sha }}
path: pr-branch
@@ -410,7 +410,7 @@ jobs:
LAKE_CACHE_REVISION_ENDPOINT: ${{ vars.LAKE_CACHE_REVISION_ENDPOINT_PUBLIC }}
steps:
- name: Checkout mathlib
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ needs.build_and_stage.outputs.sha }}
path: pr-branch
diff --git a/.github/workflows/latest_import.yml b/.github/workflows/latest_import.yml
index 447160b95b43eb..eeb9d92bda388a 100644
--- a/.github/workflows/latest_import.yml
+++ b/.github/workflows/latest_import.yml
@@ -26,10 +26,10 @@ jobs:
: # Do nothing on failure, but suppress errors
fi
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Checkout local actions
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/long_file_report.yml b/.github/workflows/long_file_report.yml
index f0febf9b6d215f..034ae752cd59f4 100644
--- a/.github/workflows/long_file_report.yml
+++ b/.github/workflows/long_file_report.yml
@@ -12,10 +12,10 @@ jobs:
steps:
- name: Checkout code
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Checkout local actions
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/maintainer_bors_wf_run.yml b/.github/workflows/maintainer_bors_wf_run.yml
index 95353cb9b2b22b..7d4b4f7f62a187 100644
--- a/.github/workflows/maintainer_bors_wf_run.yml
+++ b/.github/workflows/maintainer_bors_wf_run.yml
@@ -130,7 +130,7 @@ jobs:
if: ${{ ! steps.inputs.outputs.mOrD == '' &&
( steps.user_permission.outputs.require-result == 'true' ||
steps.inputs.outputs.bot == 'true' ) }}
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/maintainer_merge_wf_run.yml b/.github/workflows/maintainer_merge_wf_run.yml
index 44208ccbb8619d..81ed3364171722 100644
--- a/.github/workflows/maintainer_merge_wf_run.yml
+++ b/.github/workflows/maintainer_merge_wf_run.yml
@@ -163,7 +163,7 @@ jobs:
- name: Checkout local actions
if: ${{ steps.authorized.outputs.authorized == 'true' }}
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/nightly_bump_and_merge.yml b/.github/workflows/nightly_bump_and_merge.yml
index a04e35e02d2573..5418ef6c3ba2fc 100644
--- a/.github/workflows/nightly_bump_and_merge.yml
+++ b/.github/workflows/nightly_bump_and_merge.yml
@@ -39,14 +39,14 @@ jobs:
# This token is masked by the token minting action and will not be logged accidentally.
- name: Checkout nightly-testing branch
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: nightly-testing
fetch-depth: 0 # Fetch all branches and history
token: ${{ steps.app-token.outputs.token }}
- name: Checkout local actions
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/nightly_detect_failure.yml b/.github/workflows/nightly_detect_failure.yml
index 9e7159289f748e..8a2010877288a3 100644
--- a/.github/workflows/nightly_detect_failure.yml
+++ b/.github/workflows/nightly_detect_failure.yml
@@ -122,7 +122,7 @@ jobs:
# This token is masked by the token minting action and will not be logged accidentally.
- name: Checkout code
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
# Pin to the SHA whose CI just succeeded, not the current tip of `nightly-testing`,
# which may have advanced while CI was running. Without this, the tag and
@@ -216,7 +216,7 @@ jobs:
# The create-github-app-token README states that this token is masked and will not be logged accidentally.
- name: Checkout Lean repository
if: steps.tag.outputs.is_nightly == 'true'
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
repository: leanprover/lean4
token: ${{ steps.lean-pr-testing-token.outputs.token }}
@@ -418,7 +418,7 @@ jobs:
azure-client-id: ${{ vars.GH_APP_AZURE_CLIENT_ID_NIGHTLY_TESTING }}
azure-tenant-id: ${{ secrets.LPC_AZ_TENANT_ID }}
- name: Checkout Mathlib4 repository
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
if: steps.tag.outputs.is_nightly == 'true' && steps.check_branch.outputs.result == 'false'
with:
ref: nightly-testing # checkout nightly-testing branch (shouldn't matter which)
@@ -427,7 +427,7 @@ jobs:
- name: Checkout local actions
if: steps.tag.outputs.is_nightly == 'true' && steps.check_branch.outputs.result == 'false'
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/nightly_merge_master.yml b/.github/workflows/nightly_merge_master.yml
index 39725897ccfa3e..e371f9e9b4cd10 100644
--- a/.github/workflows/nightly_merge_master.yml
+++ b/.github/workflows/nightly_merge_master.yml
@@ -27,7 +27,7 @@ jobs:
# This token is masked by the token minting action and will not be logged accidentally.
- name: Checkout nightly-testing from fork
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
repository: leanprover-community/mathlib4-nightly-testing
ref: nightly-testing
diff --git a/.github/workflows/nolints.yml b/.github/workflows/nolints.yml
index e1addcf674c915..9716c0526a6205 100644
--- a/.github/workflows/nolints.yml
+++ b/.github/workflows/nolints.yml
@@ -14,7 +14,7 @@ jobs:
contents: read
id-token: write
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Configure Lean
uses: leanprover/lean-action@38fbc41a8c28c4cbaec22d7f7de508ec2e7c0dd9 # v1.5.0
diff --git a/.github/workflows/olean_report.yaml b/.github/workflows/olean_report.yaml
index b517ba84538107..4c3fb07cece678 100644
--- a/.github/workflows/olean_report.yaml
+++ b/.github/workflows/olean_report.yaml
@@ -48,7 +48,7 @@ jobs:
- name: Checkout local actions
if: steps.check_trigger.outputs.triggered == 'true'
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
@@ -82,7 +82,7 @@ jobs:
# We fetch full depth so that we can compute the merge base.
- name: Checkout PR head
if: steps.check_trigger.outputs.triggered == 'true'
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
repository: ${{ github.repository }}
ref: refs/pull/${{ github.event.issue.number }}/head
@@ -95,7 +95,7 @@ jobs:
# here so that a single binary can fetch oleans for both checkouts.
- name: Checkout tools branch (master)
if: steps.check_trigger.outputs.triggered == 'true'
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
repository: ${{ github.repository }}
ref: master
@@ -117,7 +117,7 @@ jobs:
- name: Checkout merge base
if: steps.check_trigger.outputs.triggered == 'true'
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
repository: ${{ github.repository }}
ref: ${{ steps.merge_base.outputs.sha }}
diff --git a/.github/workflows/pr_check_downstream.yml b/.github/workflows/pr_check_downstream.yml
index b099ba488b7c4b..ba7756d551c3e2 100644
--- a/.github/workflows/pr_check_downstream.yml
+++ b/.github/workflows/pr_check_downstream.yml
@@ -170,7 +170,7 @@ jobs:
# via a sparse checkout so we can run it; we never need the
# rest of the mathlib4 working tree on this runner.
- name: Checkout local actions
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml
index a9817759d84d61..15f6cbbb285324 100644
--- a/.github/workflows/pre-commit.yml
+++ b/.github/workflows/pre-commit.yml
@@ -20,7 +20,7 @@ jobs:
main:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: 3.x
diff --git a/.github/workflows/publish_tools.yml b/.github/workflows/publish_tools.yml
index 999cd7a8c676e9..028cd32bdc2fb6 100644
--- a/.github/workflows/publish_tools.yml
+++ b/.github/workflows/publish_tools.yml
@@ -40,7 +40,7 @@ jobs:
# Build under `tools-branch/`, the same directory `build_template.yml` unpacks
# the tools into, in case the build bakes its own location into the binary.
- name: Checkout master
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: master
path: tools-branch
diff --git a/.github/workflows/remove_deprecated_decls.yml b/.github/workflows/remove_deprecated_decls.yml
index c50056fdc031a2..553f222523dcfa 100644
--- a/.github/workflows/remove_deprecated_decls.yml
+++ b/.github/workflows/remove_deprecated_decls.yml
@@ -112,7 +112,7 @@ jobs:
echo "from_date=$from_date" >> "$GITHUB_OUTPUT"
echo "to_date=$to_date" >> "$GITHUB_OUTPUT"
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Configure Lean
uses: leanprover/lean-action@38fbc41a8c28c4cbaec22d7f7de508ec2e7c0dd9 # v1.5.0
diff --git a/.github/workflows/rm_set_option.yml b/.github/workflows/rm_set_option.yml
index 3bfee558a9f5e6..780af2bdfb07d5 100644
--- a/.github/workflows/rm_set_option.yml
+++ b/.github/workflows/rm_set_option.yml
@@ -31,7 +31,7 @@ jobs:
contents: read
id-token: write
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Configure Lean
uses: leanprover/lean-action@38fbc41a8c28c4cbaec22d7f7de508ec2e7c0dd9 # v1.5.0
diff --git a/.github/workflows/shake.yaml b/.github/workflows/shake.yaml
index cc3f1e433173e8..1bd86610cc16ca 100644
--- a/.github/workflows/shake.yaml
+++ b/.github/workflows/shake.yaml
@@ -26,7 +26,7 @@ jobs:
contents: read
id-token: write
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Configure Lean
uses: leanprover/lean-action@38fbc41a8c28c4cbaec22d7f7de508ec2e7c0dd9 # v1.5.0
diff --git a/.github/workflows/technical_debt_metrics.yml b/.github/workflows/technical_debt_metrics.yml
index f4792818ced7f3..506f868b7a4a1e 100644
--- a/.github/workflows/technical_debt_metrics.yml
+++ b/.github/workflows/technical_debt_metrics.yml
@@ -12,12 +12,12 @@ jobs:
steps:
- name: Checkout code
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with: # checkout all history so that we can compare across commits
fetch-depth: 0
- name: Checkout local actions
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/update_dependencies.yml b/.github/workflows/update_dependencies.yml
index 30e6e281b12a8e..b6213824640b54 100644
--- a/.github/workflows/update_dependencies.yml
+++ b/.github/workflows/update_dependencies.yml
@@ -27,7 +27,7 @@ jobs:
# This token is masked by the token minting action and will not be logged accidentally.
- name: Checkout repository
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
fetch-depth: 0
token: ${{ steps.app-token.outputs.token }}
diff --git a/.github/workflows/update_dependencies_zulip.yml b/.github/workflows/update_dependencies_zulip.yml
index d511ecd7ca857a..5f440f10c8aac1 100644
--- a/.github/workflows/update_dependencies_zulip.yml
+++ b/.github/workflows/update_dependencies_zulip.yml
@@ -17,13 +17,13 @@ jobs:
id-token: write
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
fetch-depth: 2 # Need previous commit for diff
- name: Checkout local actions
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
@@ -108,12 +108,12 @@ jobs:
id-token: write
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
fetch-depth: 2 # Need previous commit for diff
- name: Checkout local actions
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/validate_mathlib_ci_paths.yml b/.github/workflows/validate_mathlib_ci_paths.yml
index 96731cdcd805ca..29c0e856c66f4c 100644
--- a/.github/workflows/validate_mathlib_ci_paths.yml
+++ b/.github/workflows/validate_mathlib_ci_paths.yml
@@ -29,10 +29,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Checkout local actions
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/weekly-lints.yml b/.github/workflows/weekly-lints.yml
index 5df2eddf43434a..17615f854293f1 100644
--- a/.github/workflows/weekly-lints.yml
+++ b/.github/workflows/weekly-lints.yml
@@ -26,12 +26,12 @@ jobs:
: # Do nothing on failure, but suppress errors
fi
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: master
- name: Checkout local actions
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/zulip_emoji_ci_status.yaml b/.github/workflows/zulip_emoji_ci_status.yaml
index b2a46fccb8c0d2..02d9ed31903483 100644
--- a/.github/workflows/zulip_emoji_ci_status.yaml
+++ b/.github/workflows/zulip_emoji_ci_status.yaml
@@ -74,7 +74,7 @@ jobs:
- name: Checkout local actions
if: steps.pr.outputs.skip != 'true' && steps.action.outputs.ci_action != 'skip'
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/zulip_emoji_closed_pr.yaml b/.github/workflows/zulip_emoji_closed_pr.yaml
index b0488d76541fe2..91d05bc2cdd41e 100644
--- a/.github/workflows/zulip_emoji_closed_pr.yaml
+++ b/.github/workflows/zulip_emoji_closed_pr.yaml
@@ -33,7 +33,7 @@ jobs:
- name: Checkout local actions
if: ${{ ! startsWith(github.event.pull_request.title, '[Merged by Bors]') ||
github.event_name == 'reopened' }}
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/zulip_emoji_labelling.yaml b/.github/workflows/zulip_emoji_labelling.yaml
index d462d1a878eeb4..f92bb24fd007fe 100644
--- a/.github/workflows/zulip_emoji_labelling.yaml
+++ b/.github/workflows/zulip_emoji_labelling.yaml
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout local actions
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/zulip_emoji_merge_delegate.yaml b/.github/workflows/zulip_emoji_merge_delegate.yaml
index 7d701d874aa6c4..176bda34b93689 100644
--- a/.github/workflows/zulip_emoji_merge_delegate.yaml
+++ b/.github/workflows/zulip_emoji_merge_delegate.yaml
@@ -15,12 +15,12 @@ jobs:
steps:
- name: Checkout mathlib4 repository history
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
fetch-depth: 0 # download the full repository
- name: Checkout local actions
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
From 571b8a8e54219b4d393f75f4b8653fac08197fcc Mon Sep 17 00:00:00 2001
From: Sebastien Gouezel <10818434+sgouezel@users.noreply.github.com>
Date: Fri, 26 Jun 2026 14:27:24 +0000
Subject: [PATCH 0393/1300] feat: more API for set integrals wrt vector
measures (#40808)
This is essentially an adapted copy of the API we already have for the Bochner integral
---
.../VectorMeasure/SetIntegral.lean | 311 ++++++++++++++++++
1 file changed, 311 insertions(+)
diff --git a/Mathlib/MeasureTheory/VectorMeasure/SetIntegral.lean b/Mathlib/MeasureTheory/VectorMeasure/SetIntegral.lean
index 9bf93cfca754e6..82fd1a22fb2ad9 100644
--- a/Mathlib/MeasureTheory/VectorMeasure/SetIntegral.lean
+++ b/Mathlib/MeasureTheory/VectorMeasure/SetIntegral.lean
@@ -277,4 +277,315 @@ theorem integral_singleton [MeasurableSingletonClass X] {a : X} [CompleteSpace G
∫ᵛ a in {a}, f a ∂[B; μ] = B (f a) (μ {a}) := by
simp only [restrict_singleton, integral_dirac]
+theorem setIntegral_union_eq_left_of_ae (hs : MeasurableSet s) (ht : MeasurableSet t)
+ (ht_eq : ∀ᵐ x ∂μ.variation.restrict t, f x = 0) :
+ ∫ᵛ x in s ∪ t, f x ∂[B; μ] = ∫ᵛ x in s, f x ∂[B; μ] := by
+ classical
+ rw [← integral_indicator hs, ← integral_indicator (hs.union ht)]
+ apply integral_congr_ae
+ rw [ae_restrict_iff' ht] at ht_eq
+ filter_upwards [ht_eq] with x hx
+ classical
+ simp only [indicator_apply, mem_union]
+ grind
+
+theorem setIntegral_union_eq_left_of_forall (hs : MeasurableSet s) (ht : MeasurableSet t)
+ (ht_eq : ∀ x ∈ t, f x = 0) : ∫ᵛ x in s ∪ t, f x ∂[B; μ] = ∫ᵛ x in s, f x ∂[B; μ] := by
+ apply setIntegral_union_eq_left_of_ae hs ht
+ rw [ae_restrict_iff' ht]
+ filter_upwards with x using ht_eq x
+
+theorem setIntegral_eq_of_subset_of_ae_sdiff_eq_zero (hs : MeasurableSet s) (ht : MeasurableSet t)
+ (hts : s ⊆ t) (h't : ∀ᵐ x ∂μ.variation.restrict (t \ s), f x = 0) :
+ ∫ᵛ x in t, f x ∂[B; μ] = ∫ᵛ x in s, f x ∂[B; μ] := by
+ rwa [← union_sdiff_cancel hts, setIntegral_union_eq_left_of_ae hs (ht.diff hs)]
+
+/-- If a function vanishes on `t \ s` with `s ⊆ t`, then its integrals on `s`
+and `t` coincide. -/
+theorem setIntegral_eq_of_subset_of_forall_sdiff_eq_zero
+ (hs : MeasurableSet s) (ht : MeasurableSet t) (hts : s ⊆ t)
+ (h't : ∀ x ∈ t \ s, f x = 0) : ∫ᵛ x in t, f x ∂[B; μ] = ∫ᵛ x in s, f x ∂[B; μ] := by
+ apply setIntegral_eq_of_subset_of_ae_sdiff_eq_zero hs ht hts
+ apply (ae_restrict_iff' (ht.diff hs)).2
+ filter_upwards with x using h't x
+
+/-- If a function vanishes almost everywhere on `sᶜ`, then its integral on `s`
+coincides with its integral on the whole space. -/
+theorem setIntegral_eq_integral_of_ae_compl_eq_zero (hs : MeasurableSet s)
+ (h : ∀ᵐ x ∂μ.variation, x ∉ s → f x = 0) :
+ ∫ᵛ x in s, f x ∂[B; μ] = ∫ᵛ x, f x ∂[B; μ] := by
+ symm
+ nth_rw 1 [← setIntegral_univ]
+ apply setIntegral_eq_of_subset_of_ae_sdiff_eq_zero hs MeasurableSet.univ (subset_univ _)
+ apply (ae_restrict_iff' (MeasurableSet.univ.diff hs)).2
+ filter_upwards [h] with x hx h'x using hx h'x.2
+
+/-- If a function vanishes on `sᶜ`, then its integral on `s` coincides with its integral on the
+whole space. -/
+theorem setIntegral_eq_integral_of_forall_compl_eq_zero (hs : MeasurableSet s)
+ (h : ∀ x, x ∉ s → f x = 0) :
+ ∫ᵛ x in s, f x ∂[B; μ] = ∫ᵛ x, f x ∂[B; μ] :=
+ setIntegral_eq_integral_of_ae_compl_eq_zero hs (Eventually.of_forall h)
+
+theorem setIntegral_const [CompleteSpace G] [IsFiniteMeasure (μ.variation.restrict s)]
+ (c : E) : ∫ᵛ _ in s, c ∂[B; μ] = B c (μ s) := by
+ by_cases hs : MeasurableSet s
+ · have : IsFiniteMeasure (μ.restrict s).variation := by
+ rwa [variation_restrict hs]
+ rw [integral_const, restrict_apply _ hs MeasurableSet.univ, univ_inter]
+ · simp [setIntegral_eq_zero_of_not_measurableSet hs, μ.not_measurable hs]
+
+@[simp]
+theorem integral_indicator_const [CompleteSpace G]
+ (e : E) ⦃s : Set X⦄ [IsFiniteMeasure (μ.variation.restrict s)]
+ (s_meas : MeasurableSet s) :
+ ∫ᵛ x, s.indicator (fun _ : X ↦ e) x ∂[B; μ] = B e (μ s) := by
+ rw [integral_indicator s_meas, ← setIntegral_const]
+
+theorem setIntegral_map {β : Type*} [MeasurableSpace β]
+ {φ : X → β} (hφ : Measurable φ) {f : β → E} {s : Set β} (hs : MeasurableSet s)
+ (hfm : AEStronglyMeasurable f ((μ.restrict (φ ⁻¹' s)).variation.map φ))
+ (hfi' : μ.Integrable (f ∘ φ)) :
+ ∫ᵛ y in s, f y ∂[B; μ.map φ] = ∫ᵛ x in φ ⁻¹' s, f (φ x) ∂[B; μ] := by
+ rw [restrict_map μ hφ hs, integral_map hφ hfm hfi'.integrableOn]
+
+theorem _root_.MeasurableEmbedding.setIntegral_map_vectorMeasure {β : Type*} [MeasurableSpace β]
+ {φ : X → β} {f : β → E} (hφ : MeasurableEmbedding φ) {s : Set β} (hs : MeasurableSet s) :
+ ∫ᵛ y in s, f y ∂[B; μ.map φ] = ∫ᵛ x in φ ⁻¹' s, f (φ x) ∂[B; μ] := by
+ rw [restrict_map μ hφ.measurable hs, hφ.integral_map_vectorMeasure]
+
+theorem _root_.Topology.IsClosedEmbedding.setIntegral_map_vectorMeasure
+ [TopologicalSpace X] [BorelSpace X] {β : Type*}
+ [MeasurableSpace β] [TopologicalSpace β] [BorelSpace β] {φ : X → β} {f : β → E} {s : Set β}
+ (hs : MeasurableSet s) (hφ : IsClosedEmbedding φ) :
+ ∫ᵛ y in s, f y ∂[B; μ.map φ] = ∫ᵛ x in φ ⁻¹' s, f (φ x) ∂[B; μ] :=
+ hφ.measurableEmbedding.setIntegral_map_vectorMeasure hs
+
+theorem setIntegral_map_equiv {β : Type*} [MeasurableSpace β] {e : X ≃ᵐ β} {f : β → E} {s : Set β}
+ (hs : MeasurableSet s) :
+ ∫ᵛ y in s, f y ∂[B; μ.map e] = ∫ᵛ x in e ⁻¹' s, f (e x) ∂[B; μ] :=
+ e.measurableEmbedding.setIntegral_map_vectorMeasure hs
+
+theorem continuousLinearMap_apply_integral
+ [CompleteSpace G] [CompleteSpace H]
+ {C : G →L[ℝ] H} (hf : Integrable f μ.variation) :
+ C (∫ᵛ y, f y ∂[B; μ]) = ∫ᵛ y, f y ∂[((compL ℝ F G H C) ∘L B); μ] := by
+ apply hf.induction (P := fun f ↦ C (∫ᵛ y, f y ∂[B; μ]) = ∫ᵛ y, f y ∂[((compL ℝ F G H C) ∘L B); μ])
+ · intro c s hs hc
+ have : IsFiniteMeasure (μ.variation.restrict s) := ⟨by simpa⟩
+ simp [integral_indicator_const _ hs]
+ · intro f g _ f_int g_int hf hg
+ simp only [Pi.add_apply]
+ simp [integral_fun_add, f_int, g_int, hf, hg]
+ · apply isClosed_eq
+ · apply C.continuous.comp continuous_integral
+ · exact continuous_integral
+ · intro f g hfg _ hf
+ rw [← integral_congr_ae hfg, ← integral_congr_ae hfg, hf]
+
+theorem integral_continuousLinearMap_comp
+ {f : X → H} {C : H →L[ℝ] E} (hf : Integrable f μ.variation) :
+ ∫ᵛ y, C (f y) ∂[B; μ] = ∫ᵛ y, f y ∂[B ∘L C; μ] := by
+ by_cases hG : CompleteSpace G; swap
+ · simp [integral_of_not_completeSpace hG]
+ apply hf.induction (P := fun f ↦ ∫ᵛ y, C (f y) ∂[B; μ] = ∫ᵛ y, f y ∂[B ∘L C; μ])
+ · intro c s hs hc
+ have : IsFiniteMeasure (μ.variation.restrict s) := ⟨by simpa⟩
+ rw [integral_indicator_const _ hs]
+ have : (fun y ↦ C (s.indicator (fun x ↦ c) y)) = s.indicator (fun x ↦ C c) := by
+ ext; simp only [indicator]; grind
+ simp_rw [this]
+ rw [integral_indicator_const _ hs]
+ rfl
+ · intro f g _ f_int g_int hf hg
+ simp only [Pi.add_apply, _root_.map_add]
+ rw [integral_fun_add (C.integrable_comp f_int) (C.integrable_comp g_int), hf, hg,
+ integral_fun_add f_int g_int]
+ · apply isClosed_eq
+ · have I (f : Lp H 1 μ.variation) : ∫ᵛ x, C (f x) ∂[B; μ] = ∫ᵛ x, (C.compLp f) x ∂[B; μ] :=
+ (integral_congr_ae (coeFn_compLp _ _)).symm
+ simp_rw [I]
+ exact continuous_integral.comp (C.compLpL 1 μ.variation).continuous
+ · exact continuous_integral
+ · intro f g hfg _ hf
+ have : ∀ᵐ x ∂μ.variation, C (f x) = C (g x) := by
+ filter_upwards [hfg] with x hx using by simp [hx]
+ rw [← integral_congr_ae hfg, ← integral_congr_ae this, hf]
+
+theorem enorm_setIntegral_le_of_enorm_le_const_ae {C : ℝ≥0∞}
+ (hC : ∀ᵐ x ∂μ.variation.restrict s, ‖f x‖ₑ ≤ C) :
+ ‖∫ᵛ x in s, f x ∂[B; μ]‖ₑ ≤ C * ‖B‖ₑ * μ.variation s := by
+ by_cases hs : MeasurableSet s; swap
+ · simp [setIntegral_eq_zero_of_not_measurableSet hs]
+ rw [← variation_restrict hs] at hC
+ apply (enorm_integral_le_of_enorm_le_const hC).trans
+ rw [variation_restrict hs, Measure.restrict_apply MeasurableSet.univ]
+ simp
+
+theorem enorm_setIntegral_le_of_enorm_le_const {C : ℝ≥0∞}
+ (hC : ∀ x ∈ s, ‖f x‖ₑ ≤ C) :
+ ‖∫ᵛ x in s, f x ∂[B; μ]‖ₑ ≤ C * ‖B‖ₑ * μ.variation s := by
+ by_cases hs : MeasurableSet s; swap
+ · simp [setIntegral_eq_zero_of_not_measurableSet hs]
+ apply enorm_setIntegral_le_of_enorm_le_const_ae
+ apply (ae_restrict_iff' hs).2
+ filter_upwards with x using hC x
+
+theorem norm_setIntegral_le_of_norm_le_const_ae {C : ℝ}
+ [h : IsFiniteMeasure (μ.variation.restrict s)]
+ (hC : ∀ᵐ x ∂μ.variation.restrict s, ‖f x‖ ≤ C) :
+ ‖∫ᵛ x in s, f x ∂[B; μ]‖ ≤ C * ‖B‖ * μ.variation.real s := by
+ by_cases hs : MeasurableSet s; swap
+ · simp only [setIntegral_eq_zero_of_not_measurableSet hs, norm_zero]
+ by_cases h's : μ.variation s = 0
+ · simp [Measure.real, h's]
+ · have : NeBot (ae (μ.variation.restrict s)) := by simpa using h's
+ obtain ⟨x, hx⟩ : ∃ x, ‖f x‖ ≤ C := hC.exists
+ have : 0 ≤ C := le_trans (norm_nonneg _) hx
+ positivity
+ rw [← variation_restrict hs] at hC h
+ apply (norm_integral_le_of_norm_le_const hC).trans_eq
+ simp [variation_restrict hs]
+
+theorem norm_setIntegral_le_of_norm_le_const {C : ℝ}
+ [h : IsFiniteMeasure (μ.variation.restrict s)]
+ (hC : ∀ x ∈ s, ‖f x‖ ≤ C) :
+ ‖∫ᵛ x in s, f x ∂[B; μ]‖ ≤ C * ‖B‖ * μ.variation.real s := by
+ rcases eq_empty_or_nonempty s with rfl | ⟨x, hx⟩
+ · simp
+ by_cases hs : MeasurableSet s; swap
+ · simp only [setIntegral_eq_zero_of_not_measurableSet hs, norm_zero]
+ have : 0 ≤ C := le_trans (norm_nonneg _) (hC x hx)
+ positivity
+ apply norm_setIntegral_le_of_norm_le_const_ae
+ filter_upwards [ae_restrict_mem hs] with x hx using hC x hx
+
+theorem enorm_setIntegral_le_lintegral_enorm :
+ ‖∫ᵛ x in s, f x ∂[B; μ]‖ₑ ≤ ‖B‖ₑ * ∫⁻ x in s, ‖f x‖ₑ ∂μ.variation := by
+ grw [enorm_integral_le_lintegral_enorm, variation_restrict_le]
+
+theorem enorm_setIntegral_le_lintegral_enorm_transpose :
+ ‖∫ᵛ x in s, f x ∂[B; μ]‖ₑ ≤ ∫⁻ x in s, ‖f x‖ₑ ∂(μ.transpose B).variation := by
+ grw [enorm_integral_le_lintegral_enorm_transpose, transpose_restrict,variation_restrict_le]
+
+private theorem hasSum_setIntegral_iUnion_nat {s : ℕ → Set X}
+ (hm : ∀ i, MeasurableSet (s i)) (hd : Pairwise (Disjoint on s))
+ (hfi : μ.IntegrableOn f (⋃ i, s i)) :
+ HasSum (fun n ↦ ∫ᵛ x in s n, f x ∂[B; μ]) (∫ᵛ x in ⋃ n, s n, f x ∂[B; μ]) := by
+ by_cases hG : CompleteSpace G; swap
+ · simp [integral_of_not_completeSpace hG]
+ have I : ∑' i, ‖B‖ₑ * ∫⁻ x in s i, ‖f x‖ₑ ∂μ.variation < ∞ := calc
+ ∑' i, ‖B‖ₑ * ∫⁻ x in s i, ‖f x‖ₑ ∂μ.variation
+ _ = ‖B‖ₑ * ∫⁻ x in (⋃ i, s i), ‖f x‖ₑ ∂μ.variation := by
+ rw [ENNReal.tsum_mul_left, lintegral_iUnion hm hd]
+ _ < ∞ := by
+ simp only [VectorMeasure.IntegrableOn, VectorMeasure.Integrable,
+ variation_restrict (MeasurableSet.iUnion hm)] at hfi
+ exact ENNReal.mul_lt_top (by simp) hfi.2
+ have : Summable (fun n ↦ ∫ᵛ x in s n, f x ∂[B; μ]) := by
+ apply Summable.of_enorm (lt_of_le_of_lt _ I).ne
+ gcongr
+ exact enorm_setIntegral_le_lintegral_enorm
+ apply (Summable.hasSum_iff_tendsto_nat this).2
+ simp_rw [tendsto_iff_edist_tendsto_0, edist_eq_enorm_sub, enorm_sub_rev]
+ apply tendsto_of_tendsto_of_tendsto_of_le_of_le tendsto_const_nhds
+ (ENNReal.tendsto_sum_nat_add _ I.ne) (by positivity) (fun N ↦ ?_)
+ have : ⋃ n, s n = (⋃ n ∈ Finset.range N, s n) ∪ (⋃ n, s (n + N)) := by
+ ext x
+ have : (∃ i, x ∈ s (i + N)) ↔ (∃ i ≥ N, x ∈ s i) :=
+ ⟨fun ⟨i, hi⟩ ↦ ⟨i + N, by grind⟩, fun ⟨i, hi, h'i⟩ ↦ ⟨i - N, by grind⟩⟩
+ simp only [mem_iUnion, Finset.mem_range, mem_union, exists_prop, this, ge_iff_le]
+ grind
+ rw [this, setIntegral_union]; rotate_left
+ · simp only [Finset.mem_range, disjoint_iUnion_right, disjoint_iUnion_left]
+ intro i j hi
+ apply hd (by grind)
+ · apply MeasurableSet.biUnion (Finset.countable_toSet _) (fun i hi ↦ hm i)
+ · apply MeasurableSet.iUnion (fun i ↦ hm _)
+ · apply hfi.mono (MeasurableSet.iUnion hm) (by simp [subset_iUnion s])
+ · apply hfi.mono (MeasurableSet.iUnion hm) (by simp [subset_iUnion s])
+ rw [setIntegral_biUnion_finset]; rotate_left
+ · exact fun i hi ↦ hm i
+ · exact fun i hi j hj hij ↦ hd hij
+ · exact fun i hi ↦ hfi.mono (MeasurableSet.iUnion hm) (by simp [subset_iUnion s])
+ simp only [add_sub_cancel_left]
+ apply enorm_setIntegral_le_lintegral_enorm.trans_eq
+ rw [lintegral_iUnion (fun i ↦ hm _), ENNReal.tsum_mul_left]
+ exact fun i j hij ↦ hd (by grind)
+
+theorem hasSum_setIntegral_iUnion {ι : Type*} [Countable ι] {s : ι → Set X}
+ (hm : ∀ i, MeasurableSet (s i)) (hd : Pairwise (Disjoint on s))
+ (hfi : μ.IntegrableOn f (⋃ i, s i)) :
+ HasSum (fun n ↦ ∫ᵛ x in s n, f x ∂[B; μ]) (∫ᵛ x in ⋃ n, s n, f x ∂[B; μ]) := by
+ classical
+ rcases finite_or_infinite ι with hι | hι
+ · letI : Fintype ι := Fintype.ofFinite ι
+ have : ∫ᵛ x in ⋃ n, s n, f x ∂[B; μ] = ∑ i, ∫ᵛ x in s i, f x ∂[B; μ] := by
+ rw [setIntegral_iUnion_fintype hm hd (fun i ↦ ?_)]
+ exact hfi.mono (MeasurableSet.iUnion hm) (by simp [subset_iUnion s])
+ rw [this]
+ apply hasSum_fintype
+ obtain ⟨e⟩ : Nonempty (ι ≃ ℕ) := nonempty_equiv_of_countable
+ rw [← e.symm.surjective.iUnion_comp, ← e.symm.hasSum_iff]
+ apply hasSum_setIntegral_iUnion_nat (fun i ↦ hm _) (fun i j hij ↦ hd (by simp [hij]))
+ rwa [e.symm.surjective.iUnion_comp]
+
+theorem integral_iUnion {ι : Type*} [Countable ι] {s : ι → Set X} (hm : ∀ i, MeasurableSet (s i))
+ (hd : Pairwise (Disjoint on s)) (hfi : μ.IntegrableOn f (⋃ i, s i)) :
+ ∫ᵛ x in ⋃ n, s n, f x ∂[B; μ] = ∑' n, ∫ᵛ x in s n, f x ∂[B; μ] :=
+ (HasSum.tsum_eq (hasSum_setIntegral_iUnion hm hd hfi)).symm
+
+@[simp] theorem setIntegral_toSignedMeasure {μ : Measure X} [IsFiniteMeasure μ]
+ {f : X → G} {s : Set X} (hs : MeasurableSet s) :
+ ∫ᵛ x in s, f x ∂<•μ.toSignedMeasure = ∫ x in s, f x ∂μ := by
+ rw [← integral_toSignedMeasure, restrict_toSignedMeasure hs]
+
+/-- If `f` is integrable, then `∫ᵛ x in s, f x ∂[B; μ]` is absolutely continuous in `s`:
+it tends to zero as `μ.variation s` tends to zero. -/
+theorem Integrable.tendsto_setIntegral_nhds_zero {ι : Type*}
+ (hf : μ.Integrable f) {l : Filter ι} {s : ι → Set X}
+ (hs : Tendsto (μ.variation ∘ s) l (𝓝 0)) :
+ Tendsto (fun i ↦ ∫ᵛ x in s i, f x ∂[B; μ]) l (𝓝 0) := by
+ rw [tendsto_zero_iff_norm_tendsto_zero]
+ simp_rw [← coe_nnnorm, ← NNReal.coe_zero, NNReal.tendsto_coe, ← ENNReal.tendsto_coe,
+ ENNReal.coe_zero]
+ have : Tendsto (fun i ↦ ‖B‖ₑ * ∫⁻ (x : X) in s i, ‖f x‖ₑ ∂μ.variation) l (𝓝 (‖B‖ₑ * 0)) :=
+ ENNReal.Tendsto.const_mul (tendsto_setLIntegral_zero (ne_of_lt hf.2) hs) (by simp)
+ rw [mul_zero] at this
+ apply tendsto_of_tendsto_of_tendsto_of_le_of_le tendsto_const_nhds this (fun i ↦ zero_le)
+ intro i
+ apply enorm_integral_le_lintegral_enorm.trans
+ dsimp
+ gcongr
+ exact variation_restrict_le
+
+/-- If `F i → f` in `L1`, then `∫ᵛ x in s, F i x ∂[B; μ] → ∫ᵛ x in s, f x ∂[B; μ]`. -/
+lemma tendsto_setIntegral_of_L1 {ι} (f : X → E)
+ (hfi : AEStronglyMeasurable f μ.variation) {F : ι → X → E}
+ {l : Filter ι} (hFi : ∀ᶠ i in l, μ.Integrable (F i))
+ (hF : Tendsto (fun i ↦ ∫⁻ x, ‖F i x - f x‖ₑ ∂μ.variation) l (𝓝 0))
+ (s : Set X) :
+ Tendsto (fun i ↦ ∫ᵛ x in s, F i x ∂[B; μ]) l (𝓝 (∫ᵛ x in s, f x ∂[B; μ])) := by
+ refine tendsto_integral_of_L1 f ?_ ?_ ?_
+ · apply hfi.mono_measure
+ grw [variation_restrict_le, Measure.restrict_le_self]
+ · filter_upwards [hFi] with i hi using hi.restrict
+ · simp_rw [← eLpNorm_one_eq_lintegral_enorm] at hF ⊢
+ apply tendsto_of_tendsto_of_tendsto_of_le_of_le tendsto_const_nhds hF (fun _ ↦ zero_le)
+ (fun i ↦ ?_)
+ apply eLpNorm_mono_measure
+ grw [variation_restrict_le]
+ apply Measure.restrict_le_self
+
+/-- If `F i → f` in `L1`, then `∫ᵛ x in s, F i x ∂[B; μ] → ∫ᵛ x in s, f x ∂[B; μ]`. -/
+lemma tendsto_setIntegral_of_L1' {ι} (f : X → E)
+ (hfi : AEStronglyMeasurable f μ.variation) {F : ι → X → E}
+ {l : Filter ι} (hFi : ∀ᶠ i in l, μ.Integrable (F i))
+ (hF : Tendsto (fun i ↦ eLpNorm (F i - f) 1 μ.variation) l (𝓝 0))
+ (s : Set X) :
+ Tendsto (fun i ↦ ∫ᵛ x in s, F i x ∂[B; μ]) l (𝓝 (∫ᵛ x in s, f x ∂[B; μ])) := by
+ refine tendsto_setIntegral_of_L1 f hfi hFi ?_ s
+ simp_rw [eLpNorm_one_eq_lintegral_enorm, Pi.sub_apply] at hF
+ exact hF
+
end MeasureTheory.VectorMeasure
From 69b0edc5e8efbd9c94db5367ce91754a5e1cb064 Mon Sep 17 00:00:00 2001
From: Marcelo Lynch
Date: Fri, 26 Jun 2026 17:48:43 +0000
Subject: [PATCH 0394/1300] ci: bump actions/checkout to v7.0.0 (#41084)
Bumps `actions/checkout` to v7.0.0 across all workflows.
v7 refuses to check out fork-PR code under `pull_request_target` / `workflow_run` unless `allow-unsafe-pr-checkout: true` is set. Five steps intentionally check out fork-PR code in those contexts. Each is already hardened, the fork code is either built inside the landrun sandbox or run with only `contents: read`, while trust-rooted tooling is loaded from the base-repo checkout.
| File | Step | Checked-out ref | Trigger |
|---|---|---|---|
| `.github/actions/setup-build-env/action.yml` | Checkout PR branch | `inputs.pr_branch_ref` | build_fork (`pull_request_target`) |
| `.github/workflows/build_template.yml` | `post_steps` checkout | `inputs.pr_branch_ref` | build_fork (`pull_request_target`) |
| `.github/workflows/PR_summary.yml` | Checkout code | `github.event.pull_request.head.sha` | `pull_request_target` |
| `.github/workflows/add_label_from_diff.yaml` | Checkout branch to label | `github.event.pull_request.head.sha \|\| github.sha` | `pull_request_target` |
| `.github/workflows/decls-diff.yml` | Checkout new commit | `steps.meta.outputs.new-sha` | `workflow_run` |
Reapplies #41055 (reverted in #41078)
#41055 opted in the three workflow-file steps but missed the two on the fork-build path `setup-build-env`'s `Checkout PR branch` (used by the `build` and `test_lint` jobs) and `build_template.yml`'s `post_steps` checkout.
---
.github/actions/get-mathlib-ci/README.md | 2 +-
.github/actions/get-mathlib-ci/action.yml | 2 +-
.github/actions/get-tools/action.yml | 2 +-
.github/actions/setup-build-env/action.yml | 5 ++++-
.github/workflows/PR_summary.yml | 7 +++++--
.github/workflows/actionlint.yml | 4 ++--
.github/workflows/add_label_from_diff.yaml | 7 +++++--
.github/workflows/build_template.yml | 15 +++++++++------
.github/workflows/cache_test.yml | 2 +-
.github/workflows/check_pr_titles.yaml | 2 +-
.github/workflows/commit_verification.yml | 4 ++--
.github/workflows/daily-master-tag.yml | 2 +-
.github/workflows/daily.yml | 18 +++++++++---------
.github/workflows/decls-diff.yml | 7 +++++--
.github/workflows/docker_build.yml | 2 +-
.github/workflows/lake_cache_shadow.yml | 8 ++++----
.github/workflows/latest_import.yml | 4 ++--
.github/workflows/long_file_report.yml | 4 ++--
.github/workflows/maintainer_bors_wf_run.yml | 2 +-
.github/workflows/maintainer_merge_wf_run.yml | 2 +-
.github/workflows/nightly_bump_and_merge.yml | 4 ++--
.github/workflows/nightly_detect_failure.yml | 8 ++++----
.github/workflows/nightly_merge_master.yml | 2 +-
.github/workflows/nolints.yml | 2 +-
.github/workflows/olean_report.yaml | 8 ++++----
.github/workflows/pr_check_downstream.yml | 2 +-
.github/workflows/pre-commit.yml | 2 +-
.github/workflows/publish_tools.yml | 2 +-
.github/workflows/remove_deprecated_decls.yml | 2 +-
.github/workflows/rm_set_option.yml | 2 +-
.github/workflows/shake.yaml | 2 +-
.github/workflows/technical_debt_metrics.yml | 4 ++--
.github/workflows/update_dependencies.yml | 2 +-
.../workflows/update_dependencies_zulip.yml | 8 ++++----
.../workflows/validate_mathlib_ci_paths.yml | 4 ++--
.github/workflows/weekly-lints.yml | 4 ++--
.github/workflows/zulip_emoji_ci_status.yaml | 2 +-
.github/workflows/zulip_emoji_closed_pr.yaml | 2 +-
.github/workflows/zulip_emoji_labelling.yaml | 2 +-
.../workflows/zulip_emoji_merge_delegate.yaml | 4 ++--
40 files changed, 92 insertions(+), 77 deletions(-)
diff --git a/.github/actions/get-mathlib-ci/README.md b/.github/actions/get-mathlib-ci/README.md
index 77be1f2a6f15c9..96a1e071c4eb0f 100644
--- a/.github/actions/get-mathlib-ci/README.md
+++ b/.github/actions/get-mathlib-ci/README.md
@@ -25,7 +25,7 @@ then use the local action:
```yaml
- name: Checkout local actions
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/actions/get-mathlib-ci/action.yml b/.github/actions/get-mathlib-ci/action.yml
index 0a0ba2f7ebcb9a..6b593d3115f957 100644
--- a/.github/actions/get-mathlib-ci/action.yml
+++ b/.github/actions/get-mathlib-ci/action.yml
@@ -33,7 +33,7 @@ runs:
using: composite
steps:
- name: Get mathlib-ci
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: leanprover-community/mathlib-ci
ref: ${{ inputs.ref }}
diff --git a/.github/actions/get-tools/action.yml b/.github/actions/get-tools/action.yml
index 6e195f21ea308e..185b6b03c4b069 100644
--- a/.github/actions/get-tools/action.yml
+++ b/.github/actions/get-tools/action.yml
@@ -139,7 +139,7 @@ runs:
- name: Checkout tools branch (source build)
if: ${{ steps.finalize.outputs.result == 'build' }}
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ inputs.tools_source_ref }}
path: ${{ inputs.path }}
diff --git a/.github/actions/setup-build-env/action.yml b/.github/actions/setup-build-env/action.yml
index 31298cdb1a88d6..f4a0e092a7eb50 100644
--- a/.github/actions/setup-build-env/action.yml
+++ b/.github/actions/setup-build-env/action.yml
@@ -64,12 +64,15 @@ runs:
# code we build, so don't leave the GITHUB_TOKEN in pr-branch/.git/config where
# that code could read it.
- name: Checkout PR branch
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ inputs.pr_branch_ref }}
fetch-depth: 1
path: pr-branch
persist-credentials: false
+ # The build runs this fork PR code sandboxed (landrun) with no persisted
+ # credentials, so checking it out under pull_request_target is safe.
+ allow-unsafe-pr-checkout: true
# Create empty directories so landrun doesn't complain.
- name: Create empty directories
diff --git a/.github/workflows/PR_summary.yml b/.github/workflows/PR_summary.yml
index e29630eafe1a55..5d73c1426fc510 100644
--- a/.github/workflows/PR_summary.yml
+++ b/.github/workflows/PR_summary.yml
@@ -17,16 +17,19 @@ jobs:
steps:
- name: Checkout code
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
path: pr-branch
# Untrusted (potentially fork) checkout: don't persist the GITHUB_TOKEN into its .git/config.
persist-credentials: false
+ # Only trusted base-repo scripts run against this checkout, so checking out
+ # fork PR code under pull_request_target is safe.
+ allow-unsafe-pr-checkout: true
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml
index be2b1e7f6c7b39..0320272d125fcc 100644
--- a/.github/workflows/actionlint.yml
+++ b/.github/workflows/actionlint.yml
@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: suggester / actionlint
uses: reviewdog/action-actionlint@6fb7acc99f4a1008869fa8a0f09cfca740837d9d # v1.72.0
@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# Using our fork's PR branch until upstream merges the improved error reporting:
# https://github.com/zgosalvez/github-actions-ensure-sha-pinned-actions/pull/288
diff --git a/.github/workflows/add_label_from_diff.yaml b/.github/workflows/add_label_from_diff.yaml
index a2257f8076023d..3738c9c273d046 100644
--- a/.github/workflows/add_label_from_diff.yaml
+++ b/.github/workflows/add_label_from_diff.yaml
@@ -22,7 +22,7 @@ jobs:
if: github.repository == 'leanprover-community/mathlib4'
steps:
- name: Checkout master branch to build autolabel from
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: master
path: tools
@@ -38,13 +38,16 @@ jobs:
run: |
lake build autolabel
- name: Checkout branch to label
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
fetch-depth: 0
path: pr-branch
# Untrusted (potentially fork) checkout: don't persist the GITHUB_TOKEN into its .git/config.
persist-credentials: false
+ # autolabel is built from the trusted base checkout and only reads these files,
+ # so checking out fork PR code under pull_request_target is safe.
+ allow-unsafe-pr-checkout: true
- name: Run autolabel
working-directory: pr-branch
run: |
diff --git a/.github/workflows/build_template.yml b/.github/workflows/build_template.yml
index 9e742026b1aec5..3ab58099ebbc78 100644
--- a/.github/workflows/build_template.yml
+++ b/.github/workflows/build_template.yml
@@ -79,7 +79,7 @@ jobs:
# We just populate the env vars for this step to make them viewable in the logs
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
@@ -394,7 +394,7 @@ jobs:
shell: landrun --rox /usr --ro /etc/timezone --rw /dev --rox /home/lean/.elan --rox /home/lean/actions-runner/_work --rox /home/lean/.cache/mathlib/ --rw pr-branch/.lake/ --env PATH --env HOME --env GITHUB_OUTPUT --env CI -- bash -euxo pipefail {0}
steps:
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
@@ -604,7 +604,7 @@ jobs:
# `build_template` via `pull_request_target`, never this one — so
# `pr_branch_ref` is always a trusted ref here. Fork PRs keep `master`.
- name: Checkout tools branch
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ inputs.tools_branch_ref != '' && inputs.tools_branch_ref || (github.event.pull_request.head.repo.fork && 'master' || inputs.pr_branch_ref) }}
fetch-depth: 1
@@ -674,17 +674,20 @@ jobs:
contents: read
steps:
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ inputs.pr_branch_ref }}
# Untrusted (potentially fork) checkout: don't persist the GITHUB_TOKEN into its .git/config.
persist-credentials: false
+ # This job runs with only `contents: read` and no persisted credentials,
+ # so checking out fork PR code under pull_request_target is safe.
+ allow-unsafe-pr-checkout: true
# Sparse-checkout master's `.github/actions/` so the trust dispatch
# below loads from a trust-rooted source, not from PR-branch-controlled
# content. Mirrors the `Checkout local actions` step in the `build` job.
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
@@ -748,7 +751,7 @@ jobs:
lake exe graph
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/cache_test.yml b/.github/workflows/cache_test.yml
index 3348e82783dbce..69a965e6678d62 100644
--- a/.github/workflows/cache_test.yml
+++ b/.github/workflows/cache_test.yml
@@ -41,7 +41,7 @@ jobs:
run:
shell: bash
steps:
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# Install elan and the toolchain cross-platform. Build/test/lint, the
# Mathlib cache, and the GitHub cache are all disabled, so this is a
diff --git a/.github/workflows/check_pr_titles.yaml b/.github/workflows/check_pr_titles.yaml
index edbe9058ecd2a8..7d995d59b2c69c 100644
--- a/.github/workflows/check_pr_titles.yaml
+++ b/.github/workflows/check_pr_titles.yaml
@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: master
- name: Configure Lean
diff --git a/.github/workflows/commit_verification.yml b/.github/workflows/commit_verification.yml
index b117d8f7235ac6..e14544cb1ad433 100644
--- a/.github/workflows/commit_verification.yml
+++ b/.github/workflows/commit_verification.yml
@@ -33,14 +33,14 @@ jobs:
# This is a quick check to avoid unnecessary runs
steps:
- name: Checkout PR head
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
# Checkout the actual PR head, not the merge commit GitHub creates
ref: ${{ github.event.pull_request.head.sha }}
# Fetch full history to access all PR commits
fetch-depth: 0
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/daily-master-tag.yml b/.github/workflows/daily-master-tag.yml
index 83007e40169d7e..96aeefcb3382ae 100644
--- a/.github/workflows/daily-master-tag.yml
+++ b/.github/workflows/daily-master-tag.yml
@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
if: github.repository == 'leanprover-community/mathlib4'
steps:
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: master
diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml
index cd75a4c5e8c103..d737a444edb805 100644
--- a/.github/workflows/daily.yml
+++ b/.github/workflows/daily.yml
@@ -31,7 +31,7 @@ jobs:
steps:
# Checkout repository, so that we can fetch tags to decide which branch we want.
- name: Checkout branch or tag
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Fetch latest tags (if nightly)
if: matrix.branch_type == 'nightly'
@@ -52,7 +52,7 @@ jobs:
# Checkout the branch or tag we want to test.
- name: Checkout branch or tag
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: ${{ matrix.branch_type == 'nightly' && 'leanprover-community/mathlib4-nightly-testing' || github.repository }}
ref: ${{ env.BRANCH_REF }}
@@ -82,7 +82,7 @@ jobs:
branch_type: [master, nightly]
steps:
- name: Checkout repository
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Get job status and URLs
id: get-status
@@ -156,7 +156,7 @@ jobs:
steps:
# Checkout repository, so that we can fetch tags to decide which branch we want.
- name: Checkout branch or tag
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Fetch latest tags (if nightly)
if: matrix.branch_type == 'nightly'
@@ -177,7 +177,7 @@ jobs:
# Checkout the branch or tag we want to test.
- name: Checkout branch or tag
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: ${{ matrix.branch_type == 'nightly' && 'leanprover-community/mathlib4-nightly-testing' || github.repository }}
ref: ${{ env.BRANCH_REF }}
@@ -205,7 +205,7 @@ jobs:
branch_type: [master, nightly]
steps:
- name: Checkout repository
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Get job status and URLs
id: get-status
@@ -279,7 +279,7 @@ jobs:
steps:
# Checkout repository, so that we can fetch tags to decide which branch we want.
- name: Checkout branch or tag
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Fetch latest tags (if nightly)
if: matrix.branch_type == 'nightly'
@@ -300,7 +300,7 @@ jobs:
# Checkout the branch or tag we want to test.
- name: Checkout branch or tag
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: ${{ matrix.branch_type == 'nightly' && 'leanprover-community/mathlib4-nightly-testing' || github.repository }}
ref: ${{ env.BRANCH_REF }}
@@ -370,7 +370,7 @@ jobs:
branch_type: [master, nightly]
steps:
- name: Checkout repository
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Get job status and URLs
id: get-status
diff --git a/.github/workflows/decls-diff.yml b/.github/workflows/decls-diff.yml
index aca4899a3af3ad..d4405ca5107524 100644
--- a/.github/workflows/decls-diff.yml
+++ b/.github/workflows/decls-diff.yml
@@ -58,10 +58,13 @@ jobs:
} | tee -a "$GITHUB_OUTPUT"
- name: Checkout new commit
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ steps.meta.outputs.new-sha }}
fetch-depth: 0
+ # `new-sha` is fork PR code already built by build_fork.yml; allow the
+ # fork checkout under this workflow_run.
+ allow-unsafe-pr-checkout: true
- name: Resolve merge-base against master
id: resolve
@@ -136,7 +139,7 @@ jobs:
# Tooling is checked out unconditionally: the patcher (from CI_SCRIPTS_DIR)
# is needed on the cache-miss path too, to post the warning notice.
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/docker_build.yml b/.github/workflows/docker_build.yml
index b8b6c0fa81ac43..c46e08fd9934d8 100644
--- a/.github/workflows/docker_build.yml
+++ b/.github/workflows/docker_build.yml
@@ -27,7 +27,7 @@ jobs:
steps:
# documentation at
# https://docs.github.com/en/actions/use-cases-and-examples/publishing-packages/publishing-docker-images
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Log in to the container registry
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
diff --git a/.github/workflows/lake_cache_shadow.yml b/.github/workflows/lake_cache_shadow.yml
index 50ce19d3b21d59..099472ca7b57f0 100644
--- a/.github/workflows/lake_cache_shadow.yml
+++ b/.github/workflows/lake_cache_shadow.yml
@@ -98,13 +98,13 @@ jobs:
uses: dcarbone/install-jq-action@b7ef57d46ece78760b4019dbc4080a1ba2a40b45 # v3.2.0
- name: Checkout tools branch
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: master
path: tools-branch
- name: Checkout mathlib (pr-branch)
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ inputs.mathlib_ref || 'master' }}
fetch-depth: 2
@@ -278,7 +278,7 @@ jobs:
LAKE_CACHE_REVISION_ENDPOINT: ${{ vars.LAKE_CACHE_REVISION_ENDPOINT }}
steps:
- name: Checkout mathlib (for lean-toolchain pin)
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ needs.build_and_stage.outputs.sha }}
path: pr-branch
@@ -410,7 +410,7 @@ jobs:
LAKE_CACHE_REVISION_ENDPOINT: ${{ vars.LAKE_CACHE_REVISION_ENDPOINT_PUBLIC }}
steps:
- name: Checkout mathlib
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ needs.build_and_stage.outputs.sha }}
path: pr-branch
diff --git a/.github/workflows/latest_import.yml b/.github/workflows/latest_import.yml
index eeb9d92bda388a..447160b95b43eb 100644
--- a/.github/workflows/latest_import.yml
+++ b/.github/workflows/latest_import.yml
@@ -26,10 +26,10 @@ jobs:
: # Do nothing on failure, but suppress errors
fi
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/long_file_report.yml b/.github/workflows/long_file_report.yml
index 034ae752cd59f4..f0febf9b6d215f 100644
--- a/.github/workflows/long_file_report.yml
+++ b/.github/workflows/long_file_report.yml
@@ -12,10 +12,10 @@ jobs:
steps:
- name: Checkout code
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/maintainer_bors_wf_run.yml b/.github/workflows/maintainer_bors_wf_run.yml
index 7d4b4f7f62a187..95353cb9b2b22b 100644
--- a/.github/workflows/maintainer_bors_wf_run.yml
+++ b/.github/workflows/maintainer_bors_wf_run.yml
@@ -130,7 +130,7 @@ jobs:
if: ${{ ! steps.inputs.outputs.mOrD == '' &&
( steps.user_permission.outputs.require-result == 'true' ||
steps.inputs.outputs.bot == 'true' ) }}
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/maintainer_merge_wf_run.yml b/.github/workflows/maintainer_merge_wf_run.yml
index 81ed3364171722..44208ccbb8619d 100644
--- a/.github/workflows/maintainer_merge_wf_run.yml
+++ b/.github/workflows/maintainer_merge_wf_run.yml
@@ -163,7 +163,7 @@ jobs:
- name: Checkout local actions
if: ${{ steps.authorized.outputs.authorized == 'true' }}
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/nightly_bump_and_merge.yml b/.github/workflows/nightly_bump_and_merge.yml
index 5418ef6c3ba2fc..a04e35e02d2573 100644
--- a/.github/workflows/nightly_bump_and_merge.yml
+++ b/.github/workflows/nightly_bump_and_merge.yml
@@ -39,14 +39,14 @@ jobs:
# This token is masked by the token minting action and will not be logged accidentally.
- name: Checkout nightly-testing branch
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: nightly-testing
fetch-depth: 0 # Fetch all branches and history
token: ${{ steps.app-token.outputs.token }}
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/nightly_detect_failure.yml b/.github/workflows/nightly_detect_failure.yml
index 8a2010877288a3..9e7159289f748e 100644
--- a/.github/workflows/nightly_detect_failure.yml
+++ b/.github/workflows/nightly_detect_failure.yml
@@ -122,7 +122,7 @@ jobs:
# This token is masked by the token minting action and will not be logged accidentally.
- name: Checkout code
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
# Pin to the SHA whose CI just succeeded, not the current tip of `nightly-testing`,
# which may have advanced while CI was running. Without this, the tag and
@@ -216,7 +216,7 @@ jobs:
# The create-github-app-token README states that this token is masked and will not be logged accidentally.
- name: Checkout Lean repository
if: steps.tag.outputs.is_nightly == 'true'
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: leanprover/lean4
token: ${{ steps.lean-pr-testing-token.outputs.token }}
@@ -418,7 +418,7 @@ jobs:
azure-client-id: ${{ vars.GH_APP_AZURE_CLIENT_ID_NIGHTLY_TESTING }}
azure-tenant-id: ${{ secrets.LPC_AZ_TENANT_ID }}
- name: Checkout Mathlib4 repository
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
if: steps.tag.outputs.is_nightly == 'true' && steps.check_branch.outputs.result == 'false'
with:
ref: nightly-testing # checkout nightly-testing branch (shouldn't matter which)
@@ -427,7 +427,7 @@ jobs:
- name: Checkout local actions
if: steps.tag.outputs.is_nightly == 'true' && steps.check_branch.outputs.result == 'false'
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/nightly_merge_master.yml b/.github/workflows/nightly_merge_master.yml
index e371f9e9b4cd10..39725897ccfa3e 100644
--- a/.github/workflows/nightly_merge_master.yml
+++ b/.github/workflows/nightly_merge_master.yml
@@ -27,7 +27,7 @@ jobs:
# This token is masked by the token minting action and will not be logged accidentally.
- name: Checkout nightly-testing from fork
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: leanprover-community/mathlib4-nightly-testing
ref: nightly-testing
diff --git a/.github/workflows/nolints.yml b/.github/workflows/nolints.yml
index 9716c0526a6205..e1addcf674c915 100644
--- a/.github/workflows/nolints.yml
+++ b/.github/workflows/nolints.yml
@@ -14,7 +14,7 @@ jobs:
contents: read
id-token: write
steps:
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Configure Lean
uses: leanprover/lean-action@38fbc41a8c28c4cbaec22d7f7de508ec2e7c0dd9 # v1.5.0
diff --git a/.github/workflows/olean_report.yaml b/.github/workflows/olean_report.yaml
index 4c3fb07cece678..b517ba84538107 100644
--- a/.github/workflows/olean_report.yaml
+++ b/.github/workflows/olean_report.yaml
@@ -48,7 +48,7 @@ jobs:
- name: Checkout local actions
if: steps.check_trigger.outputs.triggered == 'true'
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
@@ -82,7 +82,7 @@ jobs:
# We fetch full depth so that we can compute the merge base.
- name: Checkout PR head
if: steps.check_trigger.outputs.triggered == 'true'
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: ${{ github.repository }}
ref: refs/pull/${{ github.event.issue.number }}/head
@@ -95,7 +95,7 @@ jobs:
# here so that a single binary can fetch oleans for both checkouts.
- name: Checkout tools branch (master)
if: steps.check_trigger.outputs.triggered == 'true'
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: ${{ github.repository }}
ref: master
@@ -117,7 +117,7 @@ jobs:
- name: Checkout merge base
if: steps.check_trigger.outputs.triggered == 'true'
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: ${{ github.repository }}
ref: ${{ steps.merge_base.outputs.sha }}
diff --git a/.github/workflows/pr_check_downstream.yml b/.github/workflows/pr_check_downstream.yml
index ba7756d551c3e2..b099ba488b7c4b 100644
--- a/.github/workflows/pr_check_downstream.yml
+++ b/.github/workflows/pr_check_downstream.yml
@@ -170,7 +170,7 @@ jobs:
# via a sparse checkout so we can run it; we never need the
# rest of the mathlib4 working tree on this runner.
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml
index 15f6cbbb285324..a9817759d84d61 100644
--- a/.github/workflows/pre-commit.yml
+++ b/.github/workflows/pre-commit.yml
@@ -20,7 +20,7 @@ jobs:
main:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: 3.x
diff --git a/.github/workflows/publish_tools.yml b/.github/workflows/publish_tools.yml
index 028cd32bdc2fb6..999cd7a8c676e9 100644
--- a/.github/workflows/publish_tools.yml
+++ b/.github/workflows/publish_tools.yml
@@ -40,7 +40,7 @@ jobs:
# Build under `tools-branch/`, the same directory `build_template.yml` unpacks
# the tools into, in case the build bakes its own location into the binary.
- name: Checkout master
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: master
path: tools-branch
diff --git a/.github/workflows/remove_deprecated_decls.yml b/.github/workflows/remove_deprecated_decls.yml
index 553f222523dcfa..c50056fdc031a2 100644
--- a/.github/workflows/remove_deprecated_decls.yml
+++ b/.github/workflows/remove_deprecated_decls.yml
@@ -112,7 +112,7 @@ jobs:
echo "from_date=$from_date" >> "$GITHUB_OUTPUT"
echo "to_date=$to_date" >> "$GITHUB_OUTPUT"
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Configure Lean
uses: leanprover/lean-action@38fbc41a8c28c4cbaec22d7f7de508ec2e7c0dd9 # v1.5.0
diff --git a/.github/workflows/rm_set_option.yml b/.github/workflows/rm_set_option.yml
index 780af2bdfb07d5..3bfee558a9f5e6 100644
--- a/.github/workflows/rm_set_option.yml
+++ b/.github/workflows/rm_set_option.yml
@@ -31,7 +31,7 @@ jobs:
contents: read
id-token: write
steps:
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Configure Lean
uses: leanprover/lean-action@38fbc41a8c28c4cbaec22d7f7de508ec2e7c0dd9 # v1.5.0
diff --git a/.github/workflows/shake.yaml b/.github/workflows/shake.yaml
index 1bd86610cc16ca..cc3f1e433173e8 100644
--- a/.github/workflows/shake.yaml
+++ b/.github/workflows/shake.yaml
@@ -26,7 +26,7 @@ jobs:
contents: read
id-token: write
steps:
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Configure Lean
uses: leanprover/lean-action@38fbc41a8c28c4cbaec22d7f7de508ec2e7c0dd9 # v1.5.0
diff --git a/.github/workflows/technical_debt_metrics.yml b/.github/workflows/technical_debt_metrics.yml
index 506f868b7a4a1e..f4792818ced7f3 100644
--- a/.github/workflows/technical_debt_metrics.yml
+++ b/.github/workflows/technical_debt_metrics.yml
@@ -12,12 +12,12 @@ jobs:
steps:
- name: Checkout code
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: # checkout all history so that we can compare across commits
fetch-depth: 0
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/update_dependencies.yml b/.github/workflows/update_dependencies.yml
index b6213824640b54..30e6e281b12a8e 100644
--- a/.github/workflows/update_dependencies.yml
+++ b/.github/workflows/update_dependencies.yml
@@ -27,7 +27,7 @@ jobs:
# This token is masked by the token minting action and will not be logged accidentally.
- name: Checkout repository
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
token: ${{ steps.app-token.outputs.token }}
diff --git a/.github/workflows/update_dependencies_zulip.yml b/.github/workflows/update_dependencies_zulip.yml
index 5f440f10c8aac1..d511ecd7ca857a 100644
--- a/.github/workflows/update_dependencies_zulip.yml
+++ b/.github/workflows/update_dependencies_zulip.yml
@@ -17,13 +17,13 @@ jobs:
id-token: write
steps:
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 2 # Need previous commit for diff
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
@@ -108,12 +108,12 @@ jobs:
id-token: write
steps:
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 2 # Need previous commit for diff
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/validate_mathlib_ci_paths.yml b/.github/workflows/validate_mathlib_ci_paths.yml
index 29c0e856c66f4c..96731cdcd805ca 100644
--- a/.github/workflows/validate_mathlib_ci_paths.yml
+++ b/.github/workflows/validate_mathlib_ci_paths.yml
@@ -29,10 +29,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/weekly-lints.yml b/.github/workflows/weekly-lints.yml
index 17615f854293f1..5df2eddf43434a 100644
--- a/.github/workflows/weekly-lints.yml
+++ b/.github/workflows/weekly-lints.yml
@@ -26,12 +26,12 @@ jobs:
: # Do nothing on failure, but suppress errors
fi
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: master
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/zulip_emoji_ci_status.yaml b/.github/workflows/zulip_emoji_ci_status.yaml
index 02d9ed31903483..b2a46fccb8c0d2 100644
--- a/.github/workflows/zulip_emoji_ci_status.yaml
+++ b/.github/workflows/zulip_emoji_ci_status.yaml
@@ -74,7 +74,7 @@ jobs:
- name: Checkout local actions
if: steps.pr.outputs.skip != 'true' && steps.action.outputs.ci_action != 'skip'
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/zulip_emoji_closed_pr.yaml b/.github/workflows/zulip_emoji_closed_pr.yaml
index 91d05bc2cdd41e..b0488d76541fe2 100644
--- a/.github/workflows/zulip_emoji_closed_pr.yaml
+++ b/.github/workflows/zulip_emoji_closed_pr.yaml
@@ -33,7 +33,7 @@ jobs:
- name: Checkout local actions
if: ${{ ! startsWith(github.event.pull_request.title, '[Merged by Bors]') ||
github.event_name == 'reopened' }}
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/zulip_emoji_labelling.yaml b/.github/workflows/zulip_emoji_labelling.yaml
index f92bb24fd007fe..d462d1a878eeb4 100644
--- a/.github/workflows/zulip_emoji_labelling.yaml
+++ b/.github/workflows/zulip_emoji_labelling.yaml
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
diff --git a/.github/workflows/zulip_emoji_merge_delegate.yaml b/.github/workflows/zulip_emoji_merge_delegate.yaml
index 176bda34b93689..7d701d874aa6c4 100644
--- a/.github/workflows/zulip_emoji_merge_delegate.yaml
+++ b/.github/workflows/zulip_emoji_merge_delegate.yaml
@@ -15,12 +15,12 @@ jobs:
steps:
- name: Checkout mathlib4 repository history
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0 # download the full repository
- name: Checkout local actions
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
From 8d0d1cff7b60e52af96ad2efa0d52fcf73570b29 Mon Sep 17 00:00:00 2001
From: Weiyi Wang
Date: Fri, 26 Jun 2026 18:00:15 +0000
Subject: [PATCH 0395/1300] feat(Analysis/Meromorphic): meromorphicOrderAt of
derivative (#40080)
---
Mathlib/Analysis/Calculus/Deriv/ZPow.lean | 4 +++
Mathlib/Analysis/Meromorphic/Order.lean | 40 +++++++++++++++++++++++
2 files changed, 44 insertions(+)
diff --git a/Mathlib/Analysis/Calculus/Deriv/ZPow.lean b/Mathlib/Analysis/Calculus/Deriv/ZPow.lean
index e474eb729abbb5..e52733ae9f6973 100644
--- a/Mathlib/Analysis/Calculus/Deriv/ZPow.lean
+++ b/Mathlib/Analysis/Calculus/Deriv/ZPow.lean
@@ -168,17 +168,21 @@ theorem iter_deriv_inv_linear_sub (k : ℕ) (c d : 𝕜) :
variable {f : E → 𝕜} {t : Set E} {a : E}
+@[fun_prop]
theorem DifferentiableWithinAt.zpow (hf : DifferentiableWithinAt 𝕜 f t a) (h : f a ≠ 0 ∨ 0 ≤ m) :
DifferentiableWithinAt 𝕜 (fun x => f x ^ m) t a :=
(differentiableAt_zpow.2 h).comp_differentiableWithinAt a hf
+@[fun_prop]
theorem DifferentiableAt.zpow (hf : DifferentiableAt 𝕜 f a) (h : f a ≠ 0 ∨ 0 ≤ m) :
DifferentiableAt 𝕜 (fun x => f x ^ m) a :=
(differentiableAt_zpow.2 h).comp a hf
+@[fun_prop]
theorem DifferentiableOn.zpow (hf : DifferentiableOn 𝕜 f t) (h : (∀ x ∈ t, f x ≠ 0) ∨ 0 ≤ m) :
DifferentiableOn 𝕜 (fun x => f x ^ m) t := fun x hx =>
(hf x hx).zpow <| h.imp_left fun h => h x hx
+@[fun_prop]
theorem Differentiable.zpow (hf : Differentiable 𝕜 f) (h : (∀ x, f x ≠ 0) ∨ 0 ≤ m) :
Differentiable 𝕜 fun x => f x ^ m := fun x => (hf x).zpow <| h.imp_left fun h => h x
diff --git a/Mathlib/Analysis/Meromorphic/Order.lean b/Mathlib/Analysis/Meromorphic/Order.lean
index fef6d7fd10eb99..dd421392c0de3e 100644
--- a/Mathlib/Analysis/Meromorphic/Order.lean
+++ b/Mathlib/Analysis/Meromorphic/Order.lean
@@ -892,3 +892,43 @@ lemma meromorphicOrderAt_mul_of_ne_zero {f : 𝕜 → 𝕜} (hg : AnalyticAt
meromorphicOrderAt_smul_of_ne_zero hg hg'
end smul
+
+/-!
+## Order at a Point of the Derivative
+-/
+
+section deriv
+
+/-- The meromorphic order of the derivative is one less than the order of the original function.
+This however is not true if the characteristic of the domain field divides the original order,
+where the order of the derivative can rise to a larger integer. -/
+lemma meromorphicOrderAt_deriv_eq_sub_one [CompleteSpace E] {f : 𝕜 → E} {x : 𝕜} {n : ℤ}
+ (hn : (n : 𝕜) ≠ 0) (hf : meromorphicOrderAt f x = ↑n) :
+ meromorphicOrderAt (deriv f) x = ↑(n - 1) := by
+ have hmero : MeromorphicAt f x := meromorphicAt_of_meromorphicOrderAt_ne_zero (by aesop)
+ rw [meromorphicOrderAt_eq_int_iff hmero] at hf
+ rw [meromorphicOrderAt_eq_int_iff hmero.deriv]
+ obtain ⟨g, hga, hg0, (hg : f =ᶠ[𝓝[≠] x] fun z ↦ (z - x) ^ n • g z)⟩ := hf
+ refine ⟨fun z ↦ (n : 𝕜) • g z + (z - x) • deriv g z, by fun_prop, by simpa using ⟨hn, hg0⟩, ?_⟩
+ filter_upwards [hga.eventually_analyticAt.filter_mono (nhdsWithin_le_nhds),
+ eventually_mem_nhdsWithin, hg.nhdsNE_deriv] with z hgz hmem hz
+ have hzx : z - x ≠ 0 := by simpa [sub_eq_zero] using hmem
+ calc
+ deriv f z = deriv (fun z ↦ (z - x) ^ n • g z) z :=
+ hz
+ _ = (z - x) ^ n • deriv g z + deriv ((· ^ n) ∘ (· - x)) z • g z :=
+ deriv_fun_smul (by fun_prop (disch := grind)) hgz.differentiableAt
+ _ = (z - x) ^ n • deriv g z + (n * (z - x) ^ (n - 1)) • g z := by
+ rw [deriv_comp _ (by fun_prop (disch := grind)) (by fun_prop)]
+ simp [deriv_zpow]
+ _ = (z - x) ^ (n - 1) • ((n : 𝕜) • g z + (z - x) • deriv g z) := by
+ simp [smul_smul, ← zpow_add_one₀ hzx, add_comm, mul_comm]
+
+/-- Equivalent to `meromorphicOrderAt_deriv_eq_sub_one` with a slightly different statement so the
+conclusion matches more targets -/
+lemma meromorphicOrderAt_deriv [CompleteSpace E] {f : 𝕜 → E} {x : 𝕜} {n : ℤ}
+ (hn : (↑(n + 1) : 𝕜) ≠ 0) (hf : meromorphicOrderAt f x = ↑(n + 1)) :
+ meromorphicOrderAt (deriv f) x = ↑n := by
+ simpa using meromorphicOrderAt_deriv_eq_sub_one hn hf
+
+end deriv
From a592298ec0fdbd7c1d2f1e14915b44441039fbfd Mon Sep 17 00:00:00 2001
From: Jireh Loreaux
Date: Fri, 26 Jun 2026 19:10:12 +0000
Subject: [PATCH 0396/1300] feat: introduce typeclass `LinearMap.IsWeak` for
weak topologies induced by bilinear forms (#40489)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Given a bilinear form `B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜`, the weak topology on `E` is the coarsest topology
such that for all `y : F` every map `(B · y)` is continuous; equivalently, it is the topology
on `E` induced by the map `(B · · : E → (F → 𝕜))`.
This file defines a `Prop`-valued typeclass `LinearMap.IsWeak` expressing that an existing topology
on `E` is the weak topology. Although this could be passed around explicitly as a hypothesis
`Topology.IsInducing (B · ·)`, given the ubiquity of weak topologies in functional analysis, the
numerous properties that can be deduced because the inducing map `B` is bilinear, the fact that
several theorems (e.g., one version of the bipolar theorem) require this hypothesis, and we can
instantiate this class for several extant types in Mathlib, we choose to make this a typeclass
instead.
Note that establishing `LinearMap.IsWeak` before proving theorems about a particular type can help
prevent abuse of definitional equalities. This because spaces equipped with a weak topology are
frequently type synonyms of some other type `E'`. For example, suppose `E'` is a type (potentially
with some extant topology other than the weak topology) and `B' : E' →ₗ[𝕜] F →ₗ[𝕜] 𝕜` is a
bilinear form. To consider the weak topology on `E'` induced by `B'`, in practice we must create a
type synonym `E` with an instance `TopologicalSpace E := .induced (B' · ·) Pi.topologicalSpace`.
It would then be tempting to create theorems such as:
```lean
example (y : F) : Continuous (fun x : E ↦ B' x y) := sorry
```
However, this statement contains an abuse of the the definitional equality `E := E'` since `x : E`,
but `B'` has domain `E'`. Morever, one might be tempted to say that `B'.IsWeak`, but this is
impossible because the domain of `B'` is `E'`, which is equipped with the incorrect topology.
Instead, what one should do is to first define a new bilinear form `B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜` by
composing `B'` with the linear equivalence between `E` and `E'`, and then establish `B.IsWeak`.
If then one proves theorems about `E` using only the `LinearMap.IsWeak` API, then one can have more
confidence that the statements are type correct.
---
Mathlib.lean | 1 +
Mathlib/Topology/Algebra/Module/IsWeak.lean | 180 ++++++++++++++++++++
2 files changed, 181 insertions(+)
create mode 100644 Mathlib/Topology/Algebra/Module/IsWeak.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 1e5e2a2ac83c42..4308c4d1d9eeb9 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -7609,6 +7609,7 @@ public import Mathlib.Topology.Algebra.Module.Determinant
public import Mathlib.Topology.Algebra.Module.Equiv
public import Mathlib.Topology.Algebra.Module.FiniteDimension
public import Mathlib.Topology.Algebra.Module.FiniteDimensionBilinear
+public import Mathlib.Topology.Algebra.Module.IsWeak
public import Mathlib.Topology.Algebra.Module.LinearMap
public import Mathlib.Topology.Algebra.Module.LinearMapPiProd
public import Mathlib.Topology.Algebra.Module.LinearPMap
diff --git a/Mathlib/Topology/Algebra/Module/IsWeak.lean b/Mathlib/Topology/Algebra/Module/IsWeak.lean
new file mode 100644
index 00000000000000..841dc9ef2932b4
--- /dev/null
+++ b/Mathlib/Topology/Algebra/Module/IsWeak.lean
@@ -0,0 +1,180 @@
+/-
+Copyright (c) 2026 Jireh Loreaux. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Jireh Loreaux
+-/
+module
+
+public import Mathlib.Topology.Algebra.Module.ContinuousLinearMap.Basic
+public import Mathlib.Topology.Algebra.Module.Equiv
+public import Mathlib.LinearAlgebra.BilinearMap
+
+/-! # Weak topologies on modules
+
+Given a bilinear form `B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜`, the weak topology on `E` is the coarsest topology
+such that for all `y : F` every map `(B · y)` is continuous; equivalently, it is the topology
+on `E` induced by the map `(B · · : E → (F → 𝕜))`.
+
+This file defines a `Prop`-valued typeclass `LinearMap.IsWeak` expressing that an existing topology
+on `E` is the weak topology. Although this could be passed around explicitly as a hypothesis
+`Topology.IsInducing (B · ·)`, given the ubiquity of weak topologies in functional analysis, the
+numerous properties that can be deduced because the inducing map `B` is bilinear, the fact that
+several theorems (e.g., one version of the bipolar theorem) require this hypothesis, and we can
+instantiate this class for several extant types in Mathlib, we choose to make this a typeclass
+instead.
+
+Note that establishing `LinearMap.IsWeak` before proving theorems about a particular type can help
+prevent abuse of definitional equalities. This because spaces equipped with a weak topology are
+frequently type synonyms of some other type `E'`. For example, suppose `E'` is a type (potentially
+with some extant topology other than the weak topology) and `B' : E' →ₗ[𝕜] F →ₗ[𝕜] 𝕜` is a
+bilinear form. To consider the weak topology on `E'` induced by `B'`, in practice we must create a
+type synonym `E` with an instance `TopologicalSpace E := .induced (B' · ·) Pi.topologicalSpace`.
+It would then be tempting to create theorems such as:
+
+```lean
+example (y : F) : Continuous (fun x : E ↦ B' x y) := sorry
+```
+
+However, this statement contains an abuse of the the definitional equality `E := E'` since `x : E`,
+but `B'` has domain `E'`. Morever, one might be tempted to say that `B'.IsWeak`, but this is
+impossible because the domain of `B'` is `E'`, which is equipped with the incorrect topology.
+Instead, what one should do is to first define a new bilinear form `B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜` by
+composing `B'` with the linear equivalence between `E` and `E'`, and then establish `B.IsWeak`.
+If then one proves theorems about `E` using only the `LinearMap.IsWeak` API, then one can have more
+confidence that the statements are type correct.
+
+## Main definitions
+
++ `LinearMap.IsWeak`: a typeclass expressing that the topology on `E` is the weak topology induced
+ by the bilinear form `B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜`.
++ `LinearMap.IsWeak.eval`: the evaluation map `F →ₗ[𝕜] StrongDual 𝕜 E` sending `y : F` to the
+ continuous linear functional `(B · y)`.
+
+## Main results
+
+We prove the following results characterizing the weak topology:
+
+* `LinearMap.IsWeak.continuous_eval`: For any `y : F`, the evaluation mapping `(B · y)` is
+ continuous.
+* `LinearMap.IsWeak.continuous_of_continuous_eval`: For a mapping to `WeakBilin B` to be continuous,
+ it suffices that its compositions with pairing with `B` at all points `y : F` is continuous.
+* `LinearMap.IsWeak.tendsto_iff_forall_eval_tendsto`: Convergence in `WeakBilin B` can be
+ characterized in terms of convergence of the evaluations at all points `y : F`.
+
+-/
+
+@[expose] public section
+
+open Topology Filter
+
+section Basic
+
+variable {α 𝕜 E F E' F' : Type*} [CommSemiring 𝕜] [TopologicalSpace 𝕜]
+ [AddCommMonoid E] [Module 𝕜 E]
+ [AddCommMonoid F] [Module 𝕜 F]
+
+/-- Typeclass expressing that the topology on `E` is the weak topology induced
+by the bilinear form `B`. -/
+@[mk_iff]
+class LinearMap.IsWeak [t : TopologicalSpace E] (B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜) : Prop where
+ eq_induced : t = .induced (B · ·) Pi.topologicalSpace
+
+variable [inst : TopologicalSpace E] (B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜) [hB : B.IsWeak]
+
+namespace LinearMap.IsWeak
+
+instance : B.flip.flip.IsWeak := hB
+
+/-- The coercion `(B · ·) : E → (F → 𝕜)` is continuous. -/
+theorem coeFn_continuous : Continuous (B · ·) :=
+ hB.eq_induced ▸ continuous_induced_dom
+
+/-- The evaluation map `(B · y) : E → 𝕜` is continuous for each `y : F`. -/
+@[fun_prop]
+lemma continuous_eval (y : F) : Continuous (B · y) :=
+ continuous_pi_iff.mp (coeFn_continuous B) _
+
+/-- A map `f : α → E` is continuous if all the maps `fun a ↦ B (f a) y` are continuous
+for each `y : F`. -/
+lemma continuous_of_continuous_eval {α : Type*} [TopologicalSpace α]
+ {f : α → E} (hf : ∀ y, Continuous (fun x ↦ B (f x) y)) :
+ Continuous f :=
+ hB.eq_induced ▸ continuous_induced_rng.mpr (continuous_pi_iff.mpr hf)
+
+lemma continuous_iff {α : Type*} [TopologicalSpace α] {f : α → E} :
+ Continuous f ↔ ∀ y, Continuous (fun x ↦ B (f x) y) :=
+ ⟨fun _ ↦ by fun_prop, hB.continuous_of_continuous_eval⟩
+
+/-- The coercion `(B · ·) : E → (F → 𝕜)` is an embedding. -/
+theorem isInducing : IsInducing (B · ·) where
+ eq_induced := hB.eq_induced
+
+variable {B} in
+/-- The coercion `(B · ·) : E → (F → 𝕜)` is an embedding. -/
+theorem isEmbedding (hB_inj : Function.Injective B) :
+ IsEmbedding (B · ·) := by
+ convert! (LinearMap.coe_injective.comp hB_inj |>.isEmbedding_induced)
+ exact hB.eq_induced
+
+variable {B} in
+theorem tendsto_iff_forall_eval_tendsto {α : Type*} {l : Filter α} {f : α → E} {x : E}
+ (hB_inj : Function.Injective B) :
+ Tendsto f l (𝓝 x) ↔ ∀ y, Tendsto (fun i ↦ B (f i) y) l (𝓝 (B x y)) := by
+ rw [← tendsto_pi_nhds, (isEmbedding hB_inj).tendsto_nhds_iff, Function.comp_def]
+
+/-- Suppose `B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜` and `B' : E' →ₗ[𝕜] F' →ₗ[𝕜] 𝕜` are bilinear maps such that
+`E ≃L[𝕜] E'` and `F ≃ₗ[𝕜] F'`. If `B.IsWeak`, then so also `B'.IsWeak`. -/
+protected theorem congr [AddCommMonoid E'] [Module 𝕜 E']
+ [AddCommMonoid F'] [Module 𝕜 F'] [TopologicalSpace E']
+ (B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜) (B' : E' →ₗ[𝕜] F' →ₗ[𝕜] 𝕜) (e : E ≃L[𝕜] E') (f : F ≃ₗ[𝕜] F')
+ (hBB' : e.toLinearEquiv.arrowCongr (f.arrowCongr (.refl ..)) B = B') [hB : B.IsWeak] :
+ B'.IsWeak where
+ eq_induced := by
+ rw [e.symm.toHomeomorph.induced_eq.symm]
+ apply congr(TopologicalSpace.induced e.symm $(hB.eq_induced)).trans
+ simp_rw [induced_compose, ← hBB', induced_to_pi]
+ rw [f.toEquiv.iInf_congr]
+ simp
+
+/-- Map `F` into the topological dual of `E` with the weak topology induced by `F` -/
+def eval [ContinuousAdd 𝕜] [ContinuousConstSMul 𝕜 𝕜] : F →ₗ[𝕜] StrongDual 𝕜 E where
+ toFun f := ⟨B.flip f, by fun_prop⟩
+ map_add' _ _ := by ext; simp
+ map_smul' _ _ := by ext; simp
+
+include hB in
+/-- Addition in `E` is continuous when `E` is equipped with a `LinearMap.IsWeak` topology. -/
+theorem continuousAdd [ContinuousAdd 𝕜] : ContinuousAdd E where
+ continuous_add := by
+ let t₁ : TopologicalSpace E := .induced (B · ·) Pi.topologicalSpace
+ have : B.IsWeak := ⟨rfl⟩
+ rw [hB.eq_induced, continuous_induced_rng]
+ simp only [Function.comp_def, map_add, add_apply]
+ fun_prop
+
+include hB in
+/-- Scalar multiplication in `E` is continuous when `E` is equipped with a `LinearMap.IsWeak`
+topology. -/
+theorem continuousSMul [ContinuousSMul 𝕜 𝕜] : ContinuousSMul 𝕜 E where
+ continuous_smul := by
+ let t₁ : TopologicalSpace E := .induced (B · ·) Pi.topologicalSpace
+ have : B.IsWeak := ⟨rfl⟩
+ rw [hB.eq_induced, continuous_induced_rng]
+ simp only [Function.comp_def, map_smul, smul_apply]
+ fun_prop
+
+/-- `E` is a `IsTopologicalAddGroup` when `E` is equipped with a `LinearMap.IsWeak` topology. -/
+theorem isTopologicalAddGroup {𝕜 E F : Type*} [CommRing 𝕜] [TopologicalSpace 𝕜]
+ [AddCommGroup E] [Module 𝕜 E] [AddCommGroup F] [Module 𝕜 F] [TopologicalSpace E]
+ [ContinuousAdd 𝕜] (B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜) [hB : B.IsWeak] : IsTopologicalAddGroup E where
+ toContinuousAdd := continuousAdd B
+ continuous_neg := by
+ let t₁ : TopologicalSpace E := .induced (B · ·) Pi.topologicalSpace
+ have : B.IsWeak := ⟨rfl⟩
+ rw [hB.eq_induced, continuous_induced_rng, continuous_pi_iff]
+ simp_rw [Function.comp_apply, map_neg, neg_apply, ← map_neg (B _)]
+ fun_prop
+
+end LinearMap.IsWeak
+
+end Basic
From ba1e3bb0bf5c36d34a9d7e0b57ca95e8948f18bc Mon Sep 17 00:00:00 2001
From: Bryan Gin-ge Chen <5209952+bryangingechen@users.noreply.github.com>
Date: Fri, 26 Jun 2026 19:29:53 +0000
Subject: [PATCH 0397/1300] chore: extract API from #38807 and golf (#39230)
I wanted to understand why these two proofs in #38807 were long (and also play around more with the API in this corner of the library) so I walked through them with Claude Opus.
prepared with Claude code
---
Mathlib/Order/Bounds/Basic.lean | 11 ++++++++
Mathlib/Order/DirSupClosed.lean | 49 ++++++++-------------------------
2 files changed, 23 insertions(+), 37 deletions(-)
diff --git a/Mathlib/Order/Bounds/Basic.lean b/Mathlib/Order/Bounds/Basic.lean
index bdd91821551caa..8931a089fb9421 100644
--- a/Mathlib/Order/Bounds/Basic.lean
+++ b/Mathlib/Order/Bounds/Basic.lean
@@ -132,6 +132,10 @@ lemma DirectedOn.isCofinalFor_fst_image_prod_snd_image {β : Type*} [Preorder β
obtain ⟨z, hz, hxz, hyz⟩ := hs _ hx _ hy
exact ⟨z, hz, hxz.1, hyz.2⟩
+@[to_dual]
+lemma IsCofinalFor.nonempty (h : IsCofinalFor s t) (hs : s.Nonempty) : t.Nonempty :=
+ let ⟨_, ha⟩ := hs; let ⟨b, hb, _⟩ := h ha; ⟨b, hb⟩
+
theorem IsCofinalFor.union_left (hc : IsCofinalFor s t) : IsCofinalFor (s ∪ t) t := by
rintro a (has | hat)
· exact hc has
@@ -226,6 +230,13 @@ theorem IsLUB.of_subset_of_superset {s t p : Set α} (hs : IsLUB s a) (hp : IsLU
(htp : t ⊆ p) : IsLUB t a :=
⟨upperBounds_mono_set htp hp.1, lowerBounds_mono_set (upperBounds_mono_set hst) hs.2⟩
+/-- The least upper bound of a set is also the least upper bound of any cofinal subset. -/
+@[to_dual /-- The greatest lower bound of a set is also the greatest lower bound of any
+coinitial subset. -/]
+theorem IsLUB.of_isCofinalFor {s t : Set α} (hs : IsLUB s a) (hts : t ⊆ s)
+ (hst : IsCofinalFor s t) : IsLUB t a :=
+ ⟨upperBounds_mono_set hts hs.1, fun _b hb ↦ hs.2 (upperBounds_mono_of_isCofinalFor hst hb)⟩
+
@[to_dual]
theorem IsLeast.mono (ha : IsLeast s a) (hb : IsLeast t b) (hst : s ⊆ t) : b ≤ a :=
hb.2 (hst ha.1)
diff --git a/Mathlib/Order/DirSupClosed.lean b/Mathlib/Order/DirSupClosed.lean
index ead09bf1ac225a..2ea2a0708286fc 100644
--- a/Mathlib/Order/DirSupClosed.lean
+++ b/Mathlib/Order/DirSupClosed.lean
@@ -171,32 +171,14 @@ 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 ⊢
+ wlog h : DirectedOn (· ≤ ·) (d ∩ s) ∧ IsCofinalFor (d ∩ t) (d ∩ s)
+ · rw [union_comm] at hdu 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⟩
+ (directedOn_union_iff.mp (by rwa [hdst])).resolve_right h
+ obtain ⟨hds, hcof⟩ := h
+ have hcof' : IsCofinalFor d (d ∩ s) := hcof.union_right.mono_left hdst.ge
+ exact .inl <| hs (hDL inter_subset_left hD) inter_subset_right
+ (hcof'.nonempty hd₀) hds (ha.of_isCofinalFor inter_subset_left hcof')
theorem DirSupInaccOn.inter (hDL : IsLowerSet D)
(hs : DirSupInaccOn D s) (ht : DirSupInaccOn D t) : DirSupInaccOn D (s ∩ t) := by
@@ -228,18 +210,11 @@ theorem dirSupInaccOn_iff_inter_subset (hDL : IsLowerSet D) :
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)⟩
+ have hcof : IsCofinalFor t (t \ s) := by grind [IsCofinalFor, not_subset]
+ obtain ⟨x, hx, hxs⟩ := h (hDL sdiff_subset hD) (hcof.nonempty ht₀)
+ (ht₁.of_isCofinalFor sdiff_subset hcof)
+ (ha.of_isCofinalFor sdiff_subset hcof) has
+ exact hx.2 hxs
/-- The condition `(d ∩ s).Nonempty` in `DirSupInacc` can be replaced with the stronger
`∃ b ∈ d, Ici b ∩ d ⊆ s`. -/
From 0ee7df2241bfe98d50fd7e434b12ed24e5da648f Mon Sep 17 00:00:00 2001
From: Sebastien Gouezel <10818434+sgouezel@users.noreply.github.com>
Date: Sat, 27 Jun 2026 02:57:04 +0000
Subject: [PATCH 0398/1300] chore: use `NeBot` in left and right limits
(#41079)
Co-authored-by: sgouezel
---
Mathlib/MeasureTheory/Measure/Stieltjes.lean | 2 --
Mathlib/Topology/Order/LeftRightLim.lean | 37 +++++++++-----------
2 files changed, 17 insertions(+), 22 deletions(-)
diff --git a/Mathlib/MeasureTheory/Measure/Stieltjes.lean b/Mathlib/MeasureTheory/Measure/Stieltjes.lean
index f61f16fa6b1d78..eaa70d12d2e15b 100644
--- a/Mathlib/MeasureTheory/Measure/Stieltjes.lean
+++ b/Mathlib/MeasureTheory/Measure/Stieltjes.lean
@@ -148,8 +148,6 @@ theorem iInf_Ioi_eq [OrderTopology R] [DenselyOrdered R] [NoMaxOrder R]
(f : StieltjesFunction R) (x : R) : ⨅ r : Ioi x, f r = f x := by
suffices Function.rightLim f x = ⨅ r : Ioi x, f r by rw [← this, f.rightLim_eq]
rw [f.mono.rightLim_eq_sInf, sInf_image']
- rw [← neBot_iff]
- infer_instance
theorem iInf_rat_gt_eq (f : StieltjesFunction ℝ) (x : ℝ) :
⨅ r : { r' : ℚ // x < r' }, f r = f x := by
diff --git a/Mathlib/Topology/Order/LeftRightLim.lean b/Mathlib/Topology/Order/LeftRightLim.lean
index e423bb8009f047..349daa40030a6a 100644
--- a/Mathlib/Topology/Order/LeftRightLim.lean
+++ b/Mathlib/Topology/Order/LeftRightLim.lean
@@ -64,18 +64,17 @@ noncomputable def Function.rightLim (f : α → β) (a : α) : β :=
open Function
theorem leftLim_eq_of_tendsto [hα : TopologicalSpace α] [h'α : OrderTopology α] [T2Space β]
- {f : α → β} {a : α} {y : β} (h : 𝓝[<] a ≠ ⊥) (h' : Tendsto f (𝓝[<] a) (𝓝 y)) :
+ {f : α → β} {a : α} {y : β} [h : (𝓝[<] a).NeBot] (h' : Tendsto f (𝓝[<] a) (𝓝 y)) :
leftLim f a = y := by
have h'' : ∃ y, Tendsto f (𝓝[<] a) (𝓝 y) := ⟨y, h'⟩
rw [h'α.topology_eq_generate_intervals] at h h' h''
- simp only [leftLim, h, h'', not_true, or_self_iff, if_false]
- haveI := neBot_iff.2 h
+ simp only [leftLim, neBot_iff.mp h, h'', not_true, or_self_iff, if_false]
exact lim_eq h'
theorem rightLim_eq_of_tendsto [TopologicalSpace α] [OrderTopology α] [T2Space β]
- {f : α → β} {a : α} {y : β} (h : 𝓝[>] a ≠ ⊥) (h' : Tendsto f (𝓝[>] a) (𝓝 y)) :
+ {f : α → β} {a : α} {y : β} [h : (𝓝[>] a).NeBot] (h' : Tendsto f (𝓝[>] a) (𝓝 y)) :
Function.rightLim f a = y :=
- leftLim_eq_of_tendsto (α := αᵒᵈ) h h'
+ leftLim_eq_of_tendsto (α := αᵒᵈ) (h := h) h'
theorem leftLim_eq_of_eq_bot [hα : TopologicalSpace α] [h'α : OrderTopology α] (f : α → β) {a : α}
(h : 𝓝[<] a = ⊥) : leftLim f a = f a := by
@@ -111,9 +110,9 @@ theorem rightLim_eq_of_isTop {f : α → β} {a : α} (ha : IsTop a) :
theorem ContinuousWithinAt.leftLim_eq [TopologicalSpace α] [OrderTopology α] [T2Space β]
{f : α → β} {a : α} (hf : ContinuousWithinAt f (Iic a) a) : leftLim f a = f a := by
- rcases eq_or_ne (𝓝[<] a) ⊥ with h' | h'
+ rcases eq_or_neBot (𝓝[<] a) with h' | h'
· simp [leftLim_eq_of_eq_bot f h']
- apply leftLim_eq_of_tendsto h'
+ apply leftLim_eq_of_tendsto
exact hf.tendsto.mono_left (nhdsWithin_mono _ Iio_subset_Iic_self)
theorem ContinuousWithinAt.rightLim_eq [TopologicalSpace α] [OrderTopology α] [T2Space β]
@@ -195,7 +194,7 @@ theorem leftLim_rightLim [TopologicalSpace α] [OrderTopology α] [T3Space β]
{f : α → β} {a : α} (h : Tendsto f (𝓝[<] a) (𝓝 (f.leftLim a))) [h' : (𝓝[<] a).NeBot] :
f.rightLim.leftLim a = f.leftLim a := by
obtain ⟨b, hb⟩ : (Iio a).Nonempty := Filter.nonempty_of_mem (self_mem_nhdsWithin (a := a))
- apply leftLim_eq_of_tendsto (neBot_iff.mp h')
+ apply leftLim_eq_of_tendsto
apply (closed_nhds_basis (f.leftLim a)).tendsto_right_iff.2
rintro s ⟨s_mem, s_closed⟩
obtain ⟨u, au, hu⟩ : ∃ u, u < a ∧ Ioo u a ⊆ {x | f x ∈ s} := by
@@ -273,24 +272,23 @@ variable {α β : Type*} [LinearOrder α] [ConditionallyCompleteLinearOrder β]
[OrderTopology β] {f : α → β} (hf : Monotone f) {x y : α}
include hf
-theorem leftLim_eq_sSup [TopologicalSpace α] [OrderTopology α] (h : 𝓝[<] x ≠ ⊥) :
+theorem leftLim_eq_sSup [TopologicalSpace α] [OrderTopology α] [(𝓝[<] x).NeBot] :
leftLim f x = sSup (f '' Iio x) :=
- leftLim_eq_of_tendsto h (hf.tendsto_nhdsLT x)
+ leftLim_eq_of_tendsto (hf.tendsto_nhdsLT x)
-theorem rightLim_eq_sInf [TopologicalSpace α] [OrderTopology α] (h : 𝓝[>] x ≠ ⊥) :
+theorem rightLim_eq_sInf [TopologicalSpace α] [OrderTopology α] [(𝓝[>] x).NeBot] :
rightLim f x = sInf (f '' Ioi x) :=
- rightLim_eq_of_tendsto h (hf.tendsto_nhdsGT x)
+ rightLim_eq_of_tendsto (hf.tendsto_nhdsGT x)
theorem leftLim_le (h : x ≤ y) : leftLim f x ≤ f y := by
letI : TopologicalSpace α := Preorder.topology α
haveI : OrderTopology α := ⟨rfl⟩
- rcases eq_or_ne (𝓝[<] x) ⊥ with (h' | h')
+ rcases eq_or_neBot (𝓝[<] x) with h' | h'
· simpa [leftLim, h'] using hf h
- haveI A : NeBot (𝓝[<] x) := neBot_iff.2 h'
- rw [leftLim_eq_sSup hf h']
+ rw [leftLim_eq_sSup hf]
refine csSup_le ?_ ?_
· simp only [image_nonempty]
- exact (forall_mem_nonempty_iff_neBot.2 A) _ self_mem_nhdsWithin
+ exact (forall_mem_nonempty_iff_neBot.2 h') _ self_mem_nhdsWithin
· simp only [mem_image, mem_Iio, forall_exists_index, and_imp, forall_apply_eq_imp_iff₂]
intro z hz
exact hf (hz.le.trans h)
@@ -298,10 +296,10 @@ theorem leftLim_le (h : x ≤ y) : leftLim f x ≤ f y := by
theorem le_leftLim (h : x < y) : f x ≤ leftLim f y := by
letI : TopologicalSpace α := Preorder.topology α
haveI : OrderTopology α := ⟨rfl⟩
- rcases eq_or_ne (𝓝[<] y) ⊥ with (h' | h')
+ rcases eq_or_neBot (𝓝[<] y) with h' | h'
· rw [leftLim_eq_of_eq_bot _ h']
exact hf h.le
- rw [leftLim_eq_sSup hf h']
+ rw [leftLim_eq_sSup hf]
refine le_csSup ⟨f y, ?_⟩ (mem_image_of_mem _ h)
simp only [upperBounds, mem_image, mem_Iio, forall_exists_index, and_imp,
forall_apply_eq_imp_iff₂, mem_setOf_eq]
@@ -356,9 +354,8 @@ theorem tendsto_rightLim_within (x : α) : Tendsto f (𝓝[>] x) (𝓝[≥] righ
coincides with the value of the function. -/
theorem continuousWithinAt_Iio_iff_leftLim_eq :
ContinuousWithinAt f (Iio x) x ↔ leftLim f x = f x := by
- rcases eq_or_ne (𝓝[<] x) ⊥ with (h' | h')
+ rcases eq_or_neBot (𝓝[<] x) with h' | h'
· simp [leftLim_eq_of_eq_bot f h', ContinuousWithinAt, h']
- haveI : (𝓝[Iio x] x).NeBot := neBot_iff.2 h'
refine ⟨fun h => tendsto_nhds_unique (hf.tendsto_leftLim x) h.tendsto, fun h => ?_⟩
have := hf.tendsto_leftLim x
rwa [h] at this
From 6568acd0b703246d6858e3a7d189b078389d20b0 Mon Sep 17 00:00:00 2001
From: Christian Merten <136261474+chrisflav@users.noreply.github.com>
Date: Sat, 27 Jun 2026 08:11:23 +0000
Subject: [PATCH 0399/1300] chore(AlgebraicGeometry): add
`LocallyRingedSpace.residue` (#41087)
This aligns the API with the `Scheme` case.
---
.../RingedSpace/LocallyRingedSpace.lean | 2 +-
.../LocallyRingedSpace/ResidueField.lean | 19 +++++++++++++------
2 files changed, 14 insertions(+), 7 deletions(-)
diff --git a/Mathlib/Geometry/RingedSpace/LocallyRingedSpace.lean b/Mathlib/Geometry/RingedSpace/LocallyRingedSpace.lean
index 9cde91f2d687b5..b5d34121cfec62 100644
--- a/Mathlib/Geometry/RingedSpace/LocallyRingedSpace.lean
+++ b/Mathlib/Geometry/RingedSpace/LocallyRingedSpace.lean
@@ -99,7 +99,7 @@ instance : Quiver LocallyRingedSpace :=
/-- A morphism of locally ringed spaces `f : X ⟶ Y` induces
a local ring homomorphism from `Y.stalk (f x)` to `X.stalk x` for any `x : X`.
-/
-noncomputable def Hom.stalkMap {X Y : LocallyRingedSpace.{u}} (f : Hom X Y) (x : X) :
+noncomputable def Hom.stalkMap {X Y : LocallyRingedSpace.{u}} (f : X ⟶ Y) (x : X) :
Y.presheaf.stalk (f.1.1 x) ⟶ X.presheaf.stalk x :=
f.toShHom.hom.stalkMap x
diff --git a/Mathlib/Geometry/RingedSpace/LocallyRingedSpace/ResidueField.lean b/Mathlib/Geometry/RingedSpace/LocallyRingedSpace/ResidueField.lean
index 07e65e02bcb462..4175f420b82bb0 100644
--- a/Mathlib/Geometry/RingedSpace/LocallyRingedSpace/ResidueField.lean
+++ b/Mathlib/Geometry/RingedSpace/LocallyRingedSpace/ResidueField.lean
@@ -48,6 +48,16 @@ def residueField (x : X) : CommRingCat :=
instance (x : X) : Field (X.residueField x) :=
inferInstanceAs <| Field (IsLocalRing.ResidueField (X.presheaf.stalk x))
+/-- The residue map from the stalk to the residue field. -/
+def residue (X : LocallyRingedSpace.{u}) (x : X) : X.presheaf.stalk x ⟶ X.residueField x :=
+ CommRingCat.ofHom (IsLocalRing.residue (X.presheaf.stalk x))
+
+lemma residue_surjective (x : X) : Function.Surjective (X.residue x) :=
+ Ideal.Quotient.mk_surjective
+
+instance (x : X) : Epi (X.residue x) :=
+ ConcreteCategory.epi_of_surjective _ (X.residue_surjective x)
+
/--
If `U` is an open of `X` containing `x`, we have a canonical ring map from the sections
over `U` to the residue field of `x`.
@@ -56,9 +66,7 @@ If we interpret sections over `U` as functions of `X` defined on `U`, then this
corresponds to evaluation at `x`.
-/
def evaluation (x : U) : X.presheaf.obj (op U) ⟶ X.residueField x :=
- -- TODO: make a new definition wrapping
- -- `CommRingCat.ofHom (IsLocalRing.residue (X.presheaf.stalk _))`?
- X.presheaf.germ U x.1 x.2 ≫ CommRingCat.ofHom (IsLocalRing.residue (X.presheaf.stalk _))
+ X.presheaf.germ U x.1 x.2 ≫ X.residue _
/-- The global evaluation map from `Γ(X, ⊤)` to the residue field at `x`. -/
def Γevaluation (x : X) : X.presheaf.obj (op ⊤) ⟶ X.residueField x :=
@@ -97,10 +105,9 @@ a morphism of residue fields in the other direction. -/
def residueFieldMap (x : X) : Y.residueField (f.base x) ⟶ X.residueField x :=
CommRingCat.ofHom (IsLocalRing.ResidueField.map (f.stalkMap x).hom)
+@[reassoc]
lemma residue_comp_residueFieldMap_eq_stalkMap_comp_residue (x : X) :
- CommRingCat.ofHom (IsLocalRing.residue (Y.presheaf.stalk (f.base x))) ≫
- residueFieldMap f x = f.stalkMap x ≫
- CommRingCat.ofHom (IsLocalRing.residue (X.presheaf.stalk x)) := by
+ Y.residue _ ≫ residueFieldMap f x = f.stalkMap x ≫ X.residue _ := by
simp [residueFieldMap]
rfl
From 3b9fc57e284c5399761653bd6c646c78ca6fda95 Mon Sep 17 00:00:00 2001
From: rshlyakh <157648681+rshlyakh@users.noreply.github.com>
Date: Sat, 27 Jun 2026 10:03:15 +0000
Subject: [PATCH 0400/1300] feat(RingTheory/IntegralClosure): add integrality
of kerLift (#41058)
Add `RingHom.IsIntegral.kerLift` which proves that the `kerLift` of an integral ring homomorphism is integral.
---
.../RingTheory/IntegralClosure/IsIntegralClosure/Basic.lean | 3 +++
1 file changed, 3 insertions(+)
diff --git a/Mathlib/RingTheory/IntegralClosure/IsIntegralClosure/Basic.lean b/Mathlib/RingTheory/IntegralClosure/IsIntegralClosure/Basic.lean
index 13ef5f6e66b7ec..865dc6869a9008 100644
--- a/Mathlib/RingTheory/IntegralClosure/IsIntegralClosure/Basic.lean
+++ b/Mathlib/RingTheory/IntegralClosure/IsIntegralClosure/Basic.lean
@@ -599,6 +599,9 @@ theorem isIntegral_quotientMap_iff {I : Ideal S} :
refine this ▸ RingHom.IsIntegral.trans g (Ideal.quotientMap I f le_rfl) ?_ h
exact g.isIntegral_of_surjective Ideal.Quotient.mk_surjective
+theorem RingHom.IsIntegral.kerLift {f : S →+* T} (hf : f.IsIntegral) : f.kerLift.IsIntegral :=
+ RingHom.IsIntegral.tower_top (Ideal.Quotient.mk (RingHom.ker f)) f.kerLift hf
+
theorem RingHom.IsIntegral.isLocalHom {f : R →+* S} (hf : f.IsIntegral)
(inj : Function.Injective f) : IsLocalHom f where
map_nonunit a ha := by
From 0f320b07b214a5ce015b3da2cac08946c4b5a506 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ya=C3=ABl=20Dillies?=
Date: Sat, 27 Jun 2026 13:04:58 +0000
Subject: [PATCH 0401/1300] feat(Topology/MetricSpace): the L^p direct sum of
metric spaces (#40212)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Endow the direct sum `ι →₀ X` of `ι`-many copies of a metric space `X` with the L^p metric for any `1 ≤ p < ∞`.
`p = ∞` is theoretically possible too but currently annoying due to defects in our tactics/`WithTop` API. I am leaving it as future work.
[Zulip](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/What.20topology.20on.20Finsupp.3F/with/600119738)
---
Mathlib.lean | 1 +
.../Algebra/MvPolynomial/SchwartzZippel.lean | 1 -
.../Algebra/Order/Monoid/Canonical/Basic.lean | 9 --
Mathlib/Analysis/InnerProductSpace/PiL2.lean | 2 +-
Mathlib/Analysis/Normed/Lp/Finsupp.lean | 102 ++++++++++++++++++
Mathlib/Analysis/Normed/Lp/WithLp.lean | 3 +
Mathlib/Data/ENNReal/Basic.lean | 6 ++
Mathlib/Data/Finset/Lattice/Fold.lean | 14 ++-
Mathlib/Data/NNReal/Defs.lean | 1 +
9 files changed, 125 insertions(+), 14 deletions(-)
create mode 100644 Mathlib/Analysis/Normed/Lp/Finsupp.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index 4308c4d1d9eeb9..a19ff93a09e8e4 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -2180,6 +2180,7 @@ public import Mathlib.Analysis.Normed.Group.Tannery
public import Mathlib.Analysis.Normed.Group.Ultra
public import Mathlib.Analysis.Normed.Group.Uniform
public import Mathlib.Analysis.Normed.Group.ZeroAtInfty
+public import Mathlib.Analysis.Normed.Lp.Finsupp
public import Mathlib.Analysis.Normed.Lp.LpEquiv
public import Mathlib.Analysis.Normed.Lp.Matrix
public import Mathlib.Analysis.Normed.Lp.MeasurableSpace
diff --git a/Mathlib/Algebra/MvPolynomial/SchwartzZippel.lean b/Mathlib/Algebra/MvPolynomial/SchwartzZippel.lean
index e857251935b284..fd6e51dbdf0f9f 100644
--- a/Mathlib/Algebra/MvPolynomial/SchwartzZippel.lean
+++ b/Mathlib/Algebra/MvPolynomial/SchwartzZippel.lean
@@ -197,7 +197,6 @@ lemma schwartz_zippel_totalDegree {n} {p : MvPolynomial (Fin n) R} (hp : p ≠ 0
_ = p.totalDegree / #S := by
obtain rfl | hs := S.eq_empty_or_nonempty
· simp
- simp only [← _root_.bot_eq_zero, sup_bot]
simp_rw [totalDegree, Nat.cast_finsetSup]
rw [sup_div₀ (by positivity)]
simp [← sum_div, Finsupp.sum_fintype]
diff --git a/Mathlib/Algebra/Order/Monoid/Canonical/Basic.lean b/Mathlib/Algebra/Order/Monoid/Canonical/Basic.lean
index 66c74dc9c18512..d0ba27e66c2599 100644
--- a/Mathlib/Algebra/Order/Monoid/Canonical/Basic.lean
+++ b/Mathlib/Algebra/Order/Monoid/Canonical/Basic.lean
@@ -15,15 +15,6 @@ public import Mathlib.Algebra.Order.Sub.Unbundled.Basic
public section
-namespace Finset
-variable {ι α : Type*} [AddCommMonoid α] [LinearOrder α] [OrderBot α] [CanonicallyOrderedAdd α]
- {s : Finset ι} {f : ι → α}
-
-@[simp] lemma sup_eq_zero : s.sup f = 0 ↔ ∀ i ∈ s, f i = 0 := by simp [← bot_eq_zero']
-@[simp] lemma sup'_eq_zero (hs) : s.sup' hs f = 0 ↔ ∀ i ∈ s, f i = 0 := by simp [sup'_eq_sup]
-
-end Finset
-
namespace Set
variable {α : Type*} [AddCommMonoid α] [PartialOrder α] [CanonicallyOrderedAdd α]
[Sub α] [OrderedSub α] {β : Type*} {f : α → β} {k : α}
diff --git a/Mathlib/Analysis/InnerProductSpace/PiL2.lean b/Mathlib/Analysis/InnerProductSpace/PiL2.lean
index f915ca4763ad8d..d276095cf3211b 100644
--- a/Mathlib/Analysis/InnerProductSpace/PiL2.lean
+++ b/Mathlib/Analysis/InnerProductSpace/PiL2.lean
@@ -1321,7 +1321,7 @@ open Matrix LinearMap EuclideanSpace in
theorem InnerProductSpace.symm_toEuclideanLin_rankOne {𝕜 m n : Type*} [RCLike 𝕜] [Fintype m]
[Fintype n] [DecidableEq n] (x : EuclideanSpace 𝕜 m) (y : EuclideanSpace 𝕜 n) :
toEuclideanLin.symm (rankOne 𝕜 x y) = .vecMulVec x (star y) := by
- simp [toLpLin, toMatrix', ← ext_iff, vecMulVec_apply, inner_single_right, mul_comm]
+ simp [toLpLin, toMatrix', ← Matrix.ext_iff, vecMulVec_apply, inner_single_right, mul_comm]
namespace FiniteDimensional
variable [Unique ι] (h : Module.finrank 𝕜 E = 1) {v : E} (hv : ‖v‖ = 1)
diff --git a/Mathlib/Analysis/Normed/Lp/Finsupp.lean b/Mathlib/Analysis/Normed/Lp/Finsupp.lean
new file mode 100644
index 00000000000000..b9ae2f1afb8f32
--- /dev/null
+++ b/Mathlib/Analysis/Normed/Lp/Finsupp.lean
@@ -0,0 +1,102 @@
+/-
+Copyright (c) 2026 Yaël Dillies. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Yaël Dillies
+-/
+module
+
+public import Mathlib.Algebra.BigOperators.Finsupp.Basic
+public import Mathlib.Analysis.Normed.Lp.WithLp
+public import Mathlib.Analysis.SpecialFunctions.Pow.NNReal
+public import Mathlib.Topology.MetricSpace.Basic
+
+import Mathlib.Algebra.Order.BigOperators.Group.Finset
+import Mathlib.Analysis.MeanInequalities
+import Mathlib.Data.ENNReal.BigOperators
+import Mathlib.Tactic.Positivity.Finset
+
+/-!
+# Direct sum of metric spaces
+
+This files endows the direct sum `ι →₀ X` of `ι`-many copies of a metric space `X` with the
+L^p metric.
+
+## TODO
+
+Allow the L^∞ metric too. Currently, there is no easy way to perform the proofs:
+`match` on `ℝ≥0∞` exposes the underlying `Option` and `induction p using ENNReal.recTopCoe` in the
+`EMetricSpace` instance chokes on the `PseudoEMetricSpace` one.
+-/
+
+open scoped ENNReal NNReal
+
+public section
+
+namespace Finsupp
+variable {ι X : Type*} [Zero X] {p : ℝ≥0} [Fact (1 ≤ p)]
+
+/-- The L^1 extended metric on `ι`-many copies of a metric space `X` -/
+noncomputable instance [PseudoEMetricSpace X] : PseudoEMetricSpace (WithLp p <| ι →₀ X) where
+ edist f g :=
+ ((f.ofLp.zipWith edist (edist_self _) g.ofLp).sum fun i r ↦ r ^ (p : ℝ)) ^ (p⁻¹ : ℝ)
+ edist_self f := by
+ have : 0 < p := zero_lt_one.trans_le Fact.out
+ simp [sum, *]
+ edist_comm f g := by
+ simp only [sum, zipWith_apply, edist_comm]
+ congr 2
+ ext i
+ simp [edist_comm]
+ edist_triangle f g h := by
+ classical
+ have : 0 < p := zero_lt_one.trans_le Fact.out
+ let s := f.ofLp.support ∪ g.ofLp.support ∪ h.ofLp.support
+ rw [sum_of_support_subset (s := s) _ (by grind [support_zipWith]) _ (by simp [*]),
+ sum_of_support_subset (s := s) _ (by grind [support_zipWith]) _ (by simp [*]),
+ sum_of_support_subset (s := s) _ (by grind [support_zipWith]) _ (by simp [*])]
+ simp only [zipWith_apply, ← one_div]
+ grw [← ENNReal.Lp_add_le _ _ _ (mod_cast Fact.out)]
+ gcongr
+ exact edist_triangle ..
+
+lemma edist_def [PseudoEMetricSpace X] {p : ℝ≥0} [Fact (1 ≤ p)]
+ (f g : WithLp p <| ι →₀ X) :
+ edist f g =
+ ((f.ofLp.zipWith edist (edist_self _) g.ofLp).sum fun _i r ↦ r ^ (p : ℝ)) ^ (p⁻¹ : ℝ) := rfl
+
+/-- The L^1 extended metric on `ι`-many copies of a metric space `X` -/
+noncomputable instance [EMetricSpace X] : EMetricSpace (WithLp p <| ι →₀ X) where
+ eq_of_edist_eq_zero {f g} hfg := by simp_all [edist_def, sum, WithLp.ext_iff, DFunLike.ext_iff]
+
+/-- The L^1 metric on `ι`-many copies of a metric space `X` -/
+noncomputable instance [PseudoMetricSpace X] : PseudoMetricSpace (WithLp p <| ι →₀ X) :=
+ PseudoEMetricSpace.toPseudoMetricSpaceOfDist
+ (fun f g ↦ ((f.ofLp.zipWith dist (dist_self _) g.ofLp).sum fun i r ↦ r ^ (p : ℝ)) ^ (p⁻¹ : ℝ))
+ (fun f g ↦ by dsimp [sum]; positivity) fun f g ↦ by
+ simp only [edist_def, sum, zipWith_apply, ← coe_nnreal_ennreal_nndist, NNReal.zero_le_coe,
+ ← ENNReal.coe_rpow_of_nonneg, ← ENNReal.ofNNReal_finsetSum, inv_nonneg, ← coe_nndist,
+ ← NNReal.coe_rpow, ← NNReal.coe_sum, ENNReal.ofReal_coe_nnreal, ENNReal.coe_inj]
+ congr! 2
+ ext i
+ simp [← coe_nndist, ← coe_nnreal_ennreal_nndist]
+
+lemma dist_def [PseudoMetricSpace X] (f g : WithLp p <| ι →₀ X) :
+ dist f g =
+ ((f.ofLp.zipWith dist (dist_self _) g.ofLp).sum fun _i r ↦ r ^ (p : ℝ)) ^ (p⁻¹ : ℝ) := rfl
+
+lemma nndist_def [PseudoMetricSpace X] (f g : WithLp p <| ι →₀ X) :
+ nndist f g =
+ ((f.ofLp.zipWith nndist (nndist_self _) g.ofLp).sum fun _i r ↦ r ^ (p : ℝ)) ^ (p⁻¹ : ℝ) := by
+ ext
+ simp only [coe_nndist, dist_def, sum, zipWith_apply, NNReal.coe_sum, NNReal.coe_rpow]
+ congr 2
+ ext i
+ simp [← coe_nndist]
+
+/-- The L^1 metric on `ι`-many copies of a metric space `X` -/
+noncomputable instance [MetricSpace X] : MetricSpace (WithLp p <| ι →₀ X) :=
+ EMetricSpace.toMetricSpaceOfDist
+ (fun f g ↦ ((f.ofLp.zipWith dist (dist_self _) g.ofLp).sum fun i r ↦ r ^ (p : ℝ)) ^ (p⁻¹ : ℝ))
+ (fun f g ↦ by dsimp [sum]; positivity) fun f g ↦ by rw [edist_dist, dist_def]
+
+end Finsupp
diff --git a/Mathlib/Analysis/Normed/Lp/WithLp.lean b/Mathlib/Analysis/Normed/Lp/WithLp.lean
index 847baeb70474e4..cbf50d3dfae4e3 100644
--- a/Mathlib/Analysis/Normed/Lp/WithLp.lean
+++ b/Mathlib/Analysis/Normed/Lp/WithLp.lean
@@ -101,6 +101,9 @@ variable {K V}
lemma ofLp_toLp (x : V) : ofLp (toLp p x) = x := rfl
@[simp] lemma toLp_ofLp (x : WithLp p V) : toLp p (ofLp x) = x := rfl
+lemma ext_iff {x y : WithLp p V} : x = y ↔ x.ofLp = y.ofLp :=
+ (WithLp.equiv p V).injective.eq_iff.symm
+
lemma ofLp_surjective : Function.Surjective (@ofLp p V) :=
Function.RightInverse.surjective <| ofLp_toLp _
diff --git a/Mathlib/Data/ENNReal/Basic.lean b/Mathlib/Data/ENNReal/Basic.lean
index 8679db698faccf..218392836d7fad 100644
--- a/Mathlib/Data/ENNReal/Basic.lean
+++ b/Mathlib/Data/ENNReal/Basic.lean
@@ -191,6 +191,12 @@ instance : Inhabited ℝ≥0∞ := ⟨0⟩
def recTopCoe {C : ℝ≥0∞ → Sort*} (top : C ∞) (coe : ∀ x : ℝ≥0, C x) (x : ℝ≥0∞) : C x :=
WithTop.recTopCoe top coe x
+@[simp] lemma recTopCoe_top {C : ℝ≥0∞ → Sort*} (top : C ∞) (coe : ∀ x : ℝ≥0, C x) :
+ recTopCoe top coe ∞ = top := rfl
+
+@[simp] lemma recTopCoe_ofNNReal {C : ℝ≥0∞ → Sort*} (top : C ∞) (coe : ∀ x : ℝ≥0, C x) (x : ℝ≥0) :
+ recTopCoe top coe x = coe x := rfl
+
instance canLift : CanLift ℝ≥0∞ ℝ≥0 ofNNReal (· ≠ ∞) := WithTop.canLift
@[simp] theorem none_eq_top : (none : ℝ≥0∞) = ∞ := rfl
diff --git a/Mathlib/Data/Finset/Lattice/Fold.lean b/Mathlib/Data/Finset/Lattice/Fold.lean
index b2f3b900f734bb..0f82aa39ee7c54 100644
--- a/Mathlib/Data/Finset/Lattice/Fold.lean
+++ b/Mathlib/Data/Finset/Lattice/Fold.lean
@@ -260,6 +260,10 @@ theorem sup_mem (s : Set α) (w₁ : ⊥ ∈ s) (w₂ : ∀ᵉ (x ∈ s) (y ∈
protected theorem sup_eq_bot_iff (f : β → α) (S : Finset β) : S.sup f = ⊥ ↔ ∀ s ∈ S, f s = ⊥ := by
classical induction S using Finset.induction <;> simp [*]
+@[to_additive (attr := simp)]
+lemma sup_eq_one [One α] [IsBotOneClass α] : s.sup f = 1 ↔ ∀ i ∈ s, f i = 1 := by
+ simp [← bot_eq_one]
+
@[to_dual (attr := simp)]
lemma sup_disjSum (s : Finset β) (t : Finset γ) (f : β ⊕ γ → α) :
(s.disjSum t).sup f = (s.sup fun x ↦ f (.inl x)) ⊔ (t.sup fun x ↦ f (.inr x)) :=
@@ -663,14 +667,18 @@ end Sup'
section Sup
-variable [SemilatticeSup α] [OrderBot α]
+variable [SemilatticeSup α] [OrderBot α] {s : Finset β} {f : β → α}
@[to_dual]
-theorem sup'_eq_sup {s : Finset β} (H : s.Nonempty) (f : β → α) : s.sup' H f = s.sup f :=
+theorem sup'_eq_sup (H : s.Nonempty) (f : β → α) : s.sup' H f = s.sup f :=
le_antisymm (sup'_le H f fun _ => le_sup) (Finset.sup_le fun _ => le_sup' f)
+@[to_additive (attr := simp)]
+lemma sup'_eq_one [One α] [IsBotOneClass α] (hs) : s.sup' hs f = 1 ↔ ∀ i ∈ s, f i = 1 := by
+ simp [sup'_eq_sup]
+
@[to_dual]
-theorem coe_sup_of_nonempty {s : Finset β} (h : s.Nonempty) (f : β → α) :
+theorem coe_sup_of_nonempty (h : s.Nonempty) (f : β → α) :
(↑(s.sup f) : WithBot α) = s.sup ((↑) ∘ f) := by simp only [← sup'_eq_sup h, coe_sup' h]
end Sup
diff --git a/Mathlib/Data/NNReal/Defs.lean b/Mathlib/Data/NNReal/Defs.lean
index 1e2243a3c8284b..f67fe2b24c949d 100644
--- a/Mathlib/Data/NNReal/Defs.lean
+++ b/Mathlib/Data/NNReal/Defs.lean
@@ -165,6 +165,7 @@ theorem _root_.Real.le_coe_toNNReal (r : ℝ) : r ≤ Real.toNNReal r :=
le_max_left r 0
@[bound] theorem coe_nonneg (r : ℝ≥0) : (0 : ℝ) ≤ r := r.2
+@[simp] lemma not_toReal_neg {r : ℝ≥0} : ¬ r.toReal < 0 := r.coe_nonneg.not_gt
@[simp, norm_cast] theorem coe_mk (a : ℝ) (ha) : toReal (.mk a ha) = a := rfl
From 2899f1514b0e12bd6c2dfa77f6f6bb0031cf7f21 Mon Sep 17 00:00:00 2001
From: Aaron Liu
Date: Sat, 27 Jun 2026 15:40:18 +0000
Subject: [PATCH 0402/1300] perf(CategoryTheory/Triangulated/TriangleShift):
replace `cat_disch` by faster tactic (#41106)
Replace `cat_disch` with `intros; ext <;> simp` in `Triangle.shiftFunctorAdd'`. See [Zulip](https://leanprover.zulipchat.com/#narrow/channel/116290-rss/topic/Significant.20commits.20to.20mathlib4/near/606901493).
---
Mathlib/CategoryTheory/Triangulated/TriangleShift.lean | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Mathlib/CategoryTheory/Triangulated/TriangleShift.lean b/Mathlib/CategoryTheory/Triangulated/TriangleShift.lean
index d2307a55e1cbaa..07fe07961a8c2f 100644
--- a/Mathlib/CategoryTheory/Triangulated/TriangleShift.lean
+++ b/Mathlib/CategoryTheory/Triangulated/TriangleShift.lean
@@ -111,7 +111,7 @@ noncomputable def Triangle.shiftFunctorAdd' (a b n : ℤ) (h : a + b = n) :
← dsimp% (CategoryTheory.shiftFunctorAdd' C a b (a + b) rfl).hom.naturality_assoc]
simp only [shiftFunctorAdd'_eq_shiftFunctorAdd, Int.negOnePow_add,
shiftFunctorComm_hom_app_comp_shift_shiftFunctorAdd_hom_app, add_comm a]))
- (by cat_disch)
+ (by intros; ext <;> simp)
set_option backward.defeqAttrib.useBackward true in
set_option backward.isDefEq.respectTransparency false in
From 46c74195cd0b364ba4a771726ae86743748c8bcb Mon Sep 17 00:00:00 2001
From: Justus Springer <50165510+justus-springer@users.noreply.github.com>
Date: Sat, 27 Jun 2026 18:13:43 +0000
Subject: [PATCH 0403/1300] feat(AlgebraicGeometry/Birational): Birationality
and rationality of schemes (#39122)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This is a first step (of hopefully many) towards some basic birational geometry. This PR adds `Birational/Birational.lean`, which defines predicates `Birational`, `BirationalOver` and `IsRationalOver` for arbitrary schemes and provides basic API (e.g. that they are equivalence relations, and that affine space is rational).
Some notes on the choice of definitions: There are multiple ways to define what it means for
two schemes to be birational to each other. A common one is: "There exists a
rational map with a rational inverse". However, this would require defining composition of
rational maps, which is not always defined (In order to compose `f : X ⤏ Y` with `g : Y ⤏ Z`, you
need at least `X` preirreducible, `Y` nonempty and `f` dominant). On the other hand, I can define
"There exist dense subsets `U : Opens X` and `V : Opens Y` such that `U ≅ V` as schemes" for
any two schemes `X` and `Y`, with no conditions. Hence I chose that as a definition. I'm also
working on defining composition of rational maps (#39445), and once that's done, there should be a theorem
connecting the two definitions.
- [x] depends on: #39316
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
---
Mathlib.lean | 1 +
.../Birational/Birational.lean | 292 ++++++++++++++++++
Mathlib/AlgebraicGeometry/Restrict.lean | 2 +
3 files changed, 295 insertions(+)
create mode 100644 Mathlib/AlgebraicGeometry/Birational/Birational.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index a19ff93a09e8e4..e16fd6872a03c4 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -1349,6 +1349,7 @@ public import Mathlib.AlgebraicGeometry.AffineTransitionLimit
public import Mathlib.AlgebraicGeometry.AlgClosed.Basic
public import Mathlib.AlgebraicGeometry.AlgebraicCycle.Basic
public import Mathlib.AlgebraicGeometry.Artinian
+public import Mathlib.AlgebraicGeometry.Birational.Birational
public import Mathlib.AlgebraicGeometry.Birational.Dominant
public import Mathlib.AlgebraicGeometry.Birational.RationalMap
public import Mathlib.AlgebraicGeometry.ColimitsOver
diff --git a/Mathlib/AlgebraicGeometry/Birational/Birational.lean b/Mathlib/AlgebraicGeometry/Birational/Birational.lean
new file mode 100644
index 00000000000000..a191e2e295f390
--- /dev/null
+++ b/Mathlib/AlgebraicGeometry/Birational/Birational.lean
@@ -0,0 +1,292 @@
+/-
+Copyright (c) 2026 Justus Springer. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Justus Springer
+-/
+module
+
+public import Mathlib.AlgebraicGeometry.AffineSpace
+public import Mathlib.AlgebraicGeometry.Birational.RationalMap
+
+/-!
+# Birationality and Rationality of schemes.
+
+This file defines partial isomorphisms between schemes and uses them to formalize
+birationality and rationality.
+
+## Main definitions
+
+- `Scheme.PartialIso X Y`: an isomorphism between a dense open subscheme of `X` and a
+ dense open subscheme of `Y`.
+- `Scheme.Birational X Y`: `X` and `Y` are birational, i.e. there exists a `PartialIso X Y`.
+- `Scheme.BirationalOver sX sY`: `X` and `Y` are birational over `S` via structure maps
+ `sX : X ⟶ S` and `sY : Y ⟶ S`.
+- `Scheme.IsRationalOver sX`: `X` is rational over `S` via structure map `sX : X ⟶ S`,
+ i.e. birational over `S` to some affine space `𝔸(n; S)`.
+
+-/
+
+@[expose] public section
+
+universe u
+
+open CategoryTheory
+
+namespace AlgebraicGeometry.Scheme
+
+/-- A partial isomorphism from `X` to `Y` is an isomorphism between dense open subschemes
+of `X` and `Y`. -/
+structure PartialIso (X Y : Scheme.{u}) where
+ /-- The source open subscheme of a partial isomorphism. -/
+ source : X.Opens
+ dense_source : Dense (source : Set X)
+ /-- The target open subscheme of a partial isomorphism. -/
+ target : Y.Opens
+ dense_target : Dense (target : Set Y)
+ /-- The underlying isomorphism of a partial isomorphism. -/
+ iso : source.toScheme ≅ target.toScheme
+
+namespace PartialIso
+
+variable {X Y Z S : Scheme.{u}} {sX : X ⟶ S} {sY : Y ⟶ S} {sZ : Z ⟶ S}
+
+variable (sX sY) in
+/-- A partial iso is an `S`-map if the underlying morphism is. -/
+abbrev IsOver (f : X.PartialIso Y) : Prop :=
+ f.iso.hom ≫ f.target.ι ≫ sY = f.source.ι ≫ sX
+
+lemma ext_iff (f g : X.PartialIso Y) :
+ f = g ↔ ∃ (e : f.source = g.source) (e' : g.target = f.target),
+ f.iso = X.isoOfEq e ≪≫ g.iso ≪≫ Y.isoOfEq e' := by
+ constructor
+ · rintro rfl
+ simp
+ · obtain ⟨U₁, hU₁, U₂, hU₂, f⟩ := f
+ obtain ⟨V₁, hV₁, V₂, hU₂, g⟩ := g
+ simp only [forall_exists_index]
+ rintro rfl rfl e
+ simpa using e
+
+@[ext]
+lemma ext (f g : X.PartialIso Y) (e : f.source = g.source) (e' : g.target = f.target)
+ (H : f.iso = X.isoOfEq e ≪≫ g.iso ≪≫ Y.isoOfEq e') : f = g := by
+ rw [ext_iff]
+ exact ⟨e, e', H⟩
+
+variable (X) in
+/-- The identity partial isomorphism on `X`, defined on all of `X`. -/
+@[refl, simps]
+def refl : X.PartialIso X where
+ source := ⊤
+ dense_source := dense_univ
+ target := ⊤
+ dense_target := dense_univ
+ iso := Iso.refl _
+
+/-- The inverse of a partial isomorphism. -/
+@[symm, simps]
+def symm (f : X.PartialIso Y) : Y.PartialIso X where
+ source := f.target
+ dense_source := f.dense_target
+ target := f.source
+ dense_target := f.dense_source
+ iso := f.iso.symm
+
+set_option backward.defeqAttrib.useBackward true in
+lemma IsOver.symm {f : X.PartialIso Y} (hf : f.IsOver sX sY) : f.symm.IsOver sY sX := by
+ simpa [IsOver, ← cancel_epi f.iso.hom] using Eq.symm hf
+
+/-- Compose two partial isomorphisms along a proof that the target of `f` equals the source
+of `g`. See `trans` for the version that does not require this. -/
+@[simps]
+noncomputable def trans' (f : X.PartialIso Y) (g : Y.PartialIso Z) (e : f.target = g.source) :
+ X.PartialIso Z where
+ source := f.source
+ dense_source := f.dense_source
+ target := g.target
+ dense_target := g.dense_target
+ iso := f.iso ≪≫ Y.isoOfEq e ≪≫ g.iso
+
+set_option backward.defeqAttrib.useBackward true in
+lemma IsOver.trans' {f : X.PartialIso Y} {g : Y.PartialIso Z} {e : f.target = g.source}
+ (hf : f.IsOver sX sY) (hg : g.IsOver sY sZ) : (trans' f g e).IsOver sX sZ := by
+ simp [IsOver, ← hf, hg]
+
+/-- Restrict the source of a partial isomorphism to a smaller dense open. -/
+@[simps]
+noncomputable def restrictSource (f : X.PartialIso Y) (U : Opens X) (hU : Dense (U : Set X))
+ (hU' : U ≤ f.source) : X.PartialIso Y where
+ source := U
+ dense_source := hU
+ target := f.target.ι ''ᵁ f.iso.hom ''ᵁ f.source.ι ⁻¹ᵁ U
+ dense_target :=
+ have := Opens.isDominant_ι f.dense_target
+ f.target.ι.denseRange.dense_image f.target.ι.continuous <|
+ f.iso.hom.denseRange.dense_image f.iso.hom.continuous <|
+ hU.preimage f.source.ι.isOpenEmbedding.isOpenMap
+ iso := (Opens.isoOfLE hU').symm ≪≫
+ (f.iso.hom.isoImage (f.source.ι ⁻¹ᵁ U)) ≪≫
+ (f.target.ι.isoImage (f.iso.hom ''ᵁ f.source.ι ⁻¹ᵁ U))
+
+set_option backward.defeqAttrib.useBackward true in
+lemma IsOver.restrictSource {f : X.PartialIso Y} (hf : f.IsOver sX sY) (U : Opens X)
+ (hU : Dense (U : Set X)) (hU' : U ≤ f.source) :
+ (f.restrictSource U hU hU').IsOver sX sY := by
+ simp [IsOver, hf]
+
+/-- Restrict the target of a partial isomorphism to a smaller dense open. -/
+@[simps! source target iso]
+noncomputable def restrictTarget (f : X.PartialIso Y) (U : Opens Y) (hU : Dense (U : Set Y))
+ (hU' : U ≤ f.target) : X.PartialIso Y :=
+ (f.symm.restrictSource U hU hU').symm
+
+lemma IsOver.restrictTarget {f : X.PartialIso Y} (hf : f.IsOver sX sY) (U : Opens Y)
+ (hU : Dense (U : Set Y)) (hU' : U ≤ f.target) :
+ (f.restrictTarget U hU hU').IsOver sX sY :=
+ (hf.symm.restrictSource U hU hU').symm
+
+/-- Compose two partial isomorphisms, restricting to the intersection of the intermediate opens. -/
+@[trans, simps! source target iso]
+noncomputable def trans (f : X.PartialIso Y) (g : Y.PartialIso Z) : X.PartialIso Z :=
+ have := f.dense_target.inter_of_isOpen_right g.dense_source g.source.2
+ (f.restrictTarget _ this inf_le_left).trans' (g.restrictSource _ this inf_le_right) rfl
+
+lemma IsOver.trans {f : X.PartialIso Y} {g : Y.PartialIso Z} (hf : f.IsOver sX sY)
+ (hg : g.IsOver sY sZ) : (f.trans g).IsOver sX sZ :=
+ (hf.restrictTarget _ _ _).trans' (hg.restrictSource _ _ _)
+
+/-- The underlying partial map of a partial isomorphism. -/
+@[simps]
+def toPartialMap (f : X.PartialIso Y) : X.PartialMap Y where
+ domain := f.source
+ dense_domain := f.dense_source
+ hom := f.iso.hom ≫ f.target.ι
+
+/-- The underlying rational map of a partial isomorphism. -/
+abbrev toRationalMap (f : X.PartialIso Y) : X ⤏ Y := f.toPartialMap.toRationalMap
+
+/-- A scheme isomorphism viewed as a partial isomorphism defined on all of `X` and `Y`. -/
+@[simps]
+noncomputable def ofIso (f : X ≅ Y) : X.PartialIso Y where
+ source := ⊤
+ dense_source := dense_univ
+ target := ⊤
+ dense_target := dense_univ
+ iso := X.topIso ≪≫ f ≪≫ Y.topIso.symm
+
+end PartialIso
+
+/-- `X` and `Y` are birational if there exists a partial isomorphism between them. -/
+@[stacks 0A20 "(1)"]
+def Birational (X Y : Scheme.{u}) : Prop := Nonempty (PartialIso X Y)
+
+/-- Choose a partial isomorphism witnessing that `X` and `Y` are birational. -/
+noncomputable def Birational.partialIso {X Y : Scheme.{u}} (h : Birational X Y) :
+ PartialIso X Y :=
+ Classical.choice h
+
+@[refl]
+lemma Birational.refl (X : Scheme.{u}) : Birational X X :=
+ ⟨.refl X⟩
+
+@[symm]
+lemma Birational.symm {X Y : Scheme.{u}} (h : Birational X Y) : Birational Y X :=
+ ⟨h.partialIso.symm⟩
+
+@[trans]
+lemma Birational.trans {X Y Z : Scheme.{u}} (h₁ : Birational X Y) (h₂ : Birational Y Z) :
+ Birational X Z :=
+ ⟨h₁.partialIso.trans h₂.partialIso⟩
+
+/-- `X` and `Y` are birational over `S` if there exists a partial isomorphism between them
+that is compatible with the structure maps to `S`. -/
+def BirationalOver {S X Y : Scheme.{u}} (sX : X ⟶ S) (sY : Y ⟶ S) : Prop :=
+ ∃ f : PartialIso X Y, f.IsOver sX sY
+
+/-- Choose a partial isomorphism witnessing that `X` and `Y` are birational over `S`. -/
+noncomputable def BirationalOver.partialIso {S X Y : Scheme.{u}} (sX : X ⟶ S) (sY : Y ⟶ S)
+ (h : BirationalOver sX sY) :=
+ h.choose
+
+lemma BirationalOver.partialIso_isOver {S X Y : Scheme.{u}} (sX : X ⟶ S) (sY : Y ⟶ S)
+ (h : BirationalOver sX sY) : h.partialIso.IsOver sX sY :=
+ h.choose_spec
+
+set_option backward.defeqAttrib.useBackward true in
+lemma BirationalOver.refl {S X : Scheme.{u}} (sX : X ⟶ S) : BirationalOver sX sX :=
+ ⟨.refl X, by simp [PartialIso.IsOver]⟩
+
+lemma BirationalOver.symm {S X Y : Scheme.{u}} {sX : X ⟶ S} {sY : Y ⟶ S}
+ (h : BirationalOver sX sY) : BirationalOver sY sX :=
+ ⟨h.partialIso.symm, h.partialIso_isOver.symm⟩
+
+lemma BirationalOver.trans {S X Y Z : Scheme.{u}} {sX : X ⟶ S} {sY : Y ⟶ S} {sZ : Z ⟶ S}
+ (h₁ : BirationalOver sX sY) (h₂ : BirationalOver sY sZ) :
+ BirationalOver sX sZ :=
+ ⟨h₁.partialIso.trans h₂.partialIso, h₁.partialIso_isOver.trans h₂.partialIso_isOver⟩
+
+/-- `X` is rational over `S` (or `S`-rational) if it is birational over `S` to some
+affine space `𝔸(n; S)`. Note that we do not require `n` to be finite here. -/
+@[mk_iff]
+class IsRationalOver {S X : Scheme.{u}} (sX : X ⟶ S) : Prop where
+ exists_birationalOver_affineSpace (sX) : ∃ (n : Type u), BirationalOver sX (𝔸(n; S) ↘ S)
+
+instance (S : Scheme.{u}) (n : Type u) : IsRationalOver (𝔸(n; S) ↘ S) where
+ exists_birationalOver_affineSpace := ⟨n, .refl _⟩
+
+/-- If a scheme `X` is `S`-birational to an `S`-rational scheme `Y`, then `X` is `S`-rational. -/
+lemma BirationalOver.isRationalOver {S X Y : Scheme.{u}} (sX : X ⟶ S) (sY : Y ⟶ S)
+ [IsRationalOver sY] (h : BirationalOver sX sY) : IsRationalOver sX := by
+ obtain ⟨n, hn⟩ := IsRationalOver.exists_birationalOver_affineSpace sY
+ exact ⟨n, h.trans hn⟩
+
+section DenseOpen
+
+variable {X S : Scheme.{u}} (U : Opens X) (sX : X ⟶ S)
+
+/-- A dense open set `U : Opens X` induces a partial isomorphism between `U` and `X`. -/
+@[simps]
+def Opens.partialIsoOfDense (hU : Dense (U : Set X)) : PartialIso U X where
+ source := ⊤
+ dense_source := dense_univ
+ target := U
+ dense_target := hU
+ iso := U.toScheme.topIso
+
+/-- A dense open set `U : Opens X` is birational to `X`. -/
+lemma Opens.birational_of_dense (hU : Dense (U : Set X)) : Birational U X :=
+ ⟨U.partialIsoOfDense hU⟩
+
+set_option backward.defeqAttrib.useBackward true in
+/-- A dense open set `U : Opens X` of a scheme `X` over `S` is `S`-birational to `X`. -/
+lemma Opens.birationalOver_of_dense (hU : Dense (U : Set X)) : BirationalOver (U.ι ≫ sX) sX :=
+ ⟨U.partialIsoOfDense hU, by simp [PartialIso.IsOver]⟩
+
+/-- A dense open set `U : Opens X` of a `S`-rational scheme `X` is `S`-rational. -/
+lemma Opens.isRationalOver_of_dense (hU : Dense (U : Set X)) [IsRationalOver sX] :
+ IsRationalOver (U.ι ≫ sX) := by
+ obtain ⟨n, hn⟩ := IsRationalOver.exists_birationalOver_affineSpace sX
+ exact ⟨n, (U.birationalOver_of_dense sX hU).trans hn⟩
+
+end DenseOpen
+
+section OpenImmersion
+
+variable {X U S : Scheme.{u}}
+
+/-- A dominant open immersion `f : U ⟶ X` induces a partial isomorphism between `U` and `X`. -/
+@[simps! source target iso]
+noncomputable def Hom.partialIso (f : U ⟶ X) [IsOpenImmersion f] [IsDominant f] : U.PartialIso X :=
+ (PartialIso.ofIso f.isoOpensRange).trans' (f.opensRange.partialIsoOfDense f.denseRange) rfl
+
+lemma Hom.birational (f : U ⟶ X) [IsOpenImmersion f] [IsDominant f] : Birational U X :=
+ ⟨f.partialIso⟩
+
+set_option backward.defeqAttrib.useBackward true in
+lemma Hom.birationalOver (f : U ⟶ X) [IsOpenImmersion f] [IsDominant f] (sX : X ⟶ S) (sU : U ⟶ S)
+ (hf : f ≫ sX = sU) : BirationalOver sU sX :=
+ ⟨f.partialIso, by simp [PartialIso.IsOver, hf]⟩
+
+end OpenImmersion
+
+end AlgebraicGeometry.Scheme
diff --git a/Mathlib/AlgebraicGeometry/Restrict.lean b/Mathlib/AlgebraicGeometry/Restrict.lean
index bb23bc5250d9e7..6e9b51c80d71e1 100644
--- a/Mathlib/AlgebraicGeometry/Restrict.lean
+++ b/Mathlib/AlgebraicGeometry/Restrict.lean
@@ -59,6 +59,8 @@ instance : IsOpenImmersion U.ι := inferInstanceAs (IsOpenImmersion (X.ofRestric
@[simps! over] instance : U.toScheme.CanonicallyOver X where
hom := U.ι
+lemma ι_comp_over (S : Scheme.{u}) [X.Over S] : U.ι ≫ X ↘ S = U.toScheme ↘ S := rfl
+
instance (U : X.Opens) : U.ι.IsOver X where
lemma toScheme_carrier : (U : Type u) = (U : Set X) := rfl
From e4bf531c0c6668a99814005f730f3b8ada39feee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Iv=C3=A1n=20Renison?=
<85908989+IvanRenison@users.noreply.github.com>
Date: Sat, 27 Jun 2026 18:57:53 +0000
Subject: [PATCH 0404/1300] feat(Combinatorics/SimpleGraph/Coloring): add
lemmas about coloring and maps (#37598)
---
.../SimpleGraph/Coloring/Vertex.lean | 36 ++++++++++++++-----
Mathlib/Combinatorics/SimpleGraph/Maps.lean | 3 ++
2 files changed, 30 insertions(+), 9 deletions(-)
diff --git a/Mathlib/Combinatorics/SimpleGraph/Coloring/Vertex.lean b/Mathlib/Combinatorics/SimpleGraph/Coloring/Vertex.lean
index 0d06d645712f83..f8091d4860295c 100644
--- a/Mathlib/Combinatorics/SimpleGraph/Coloring/Vertex.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/Coloring/Vertex.lean
@@ -127,6 +127,11 @@ theorem Coloring.isIndepSet_colorClass (c : α) : G.IsIndepSet <| C.colorClass c
theorem Coloring.color_classes_independent (c : α) : IsAntichain G.Adj (C.colorClass c) :=
C.isIndepSet_colorClass c
+/-- Coloring induced from a homomorphism to a colored graph. -/
+abbrev Coloring.comap {V' : Type*} {G' : SimpleGraph V'} {α : Type*} (C : G'.Coloring α)
+ (f : G →g G') : G.Coloring α :=
+ C.comp f
+
-- TODO make this computable
noncomputable instance [Fintype V] [Fintype α] : Fintype (Coloring G α) := by
classical
@@ -289,9 +294,9 @@ noncomputable def Colorable.toColoring [Fintype α] {n : ℕ} (hc : G.Colorable
rw [← Fintype.card_fin n] at hn
exact G.recolorOfCardLE hn hc.some
-theorem Colorable.of_hom {V' : Type*} {G' : SimpleGraph V'} (f : G →g G') {n : ℕ}
+theorem Colorable.of_hom {V' : Type*} {G' : SimpleGraph V'} {n : ℕ} (f : G →g G')
(h : G'.Colorable n) : G.Colorable n :=
- ⟨(h.toColoring (by simp)).comp f⟩
+ ⟨h.some.comap f⟩
theorem colorable_iff_exists_bdd_nat_coloring (n : ℕ) :
G.Colorable n ↔ ∃ C : G.Coloring ℕ, ∀ v, C v < n := by
@@ -406,9 +411,9 @@ theorem chromaticNumber_mono (G' : SimpleGraph V)
(h : G ≤ G') : G.chromaticNumber ≤ G'.chromaticNumber :=
chromaticNumber_le_of_forall_imp fun _ => Colorable.mono_left h
-theorem chromaticNumber_mono_of_hom {V' : Type*} {G' : SimpleGraph V'}
- (f : G →g G') : G.chromaticNumber ≤ G'.chromaticNumber :=
- chromaticNumber_le_of_forall_imp fun _ => Colorable.of_hom f
+theorem chromaticNumber_mono_of_hom {V' : Type*} {G' : SimpleGraph V'} (f : G →g G') :
+ G.chromaticNumber ≤ G'.chromaticNumber :=
+ chromaticNumber_le_of_forall_imp fun _ hc => hc.of_hom f
lemma card_le_chromaticNumber_iff_forall_surjective [Fintype α] :
card α ≤ G.chromaticNumber ↔ ∀ C : G.Coloring α, Surjective C := by
@@ -615,10 +620,23 @@ theorem colorable_of_cliqueFree (f : ∀ (i : ι), V i)
end completeMultipartiteGraph
+variable {W : Type*} {H : SimpleGraph W}
+
/-- If `H` is not `n`-colorable and `G` is `n`-colorable, then `G` is `H.Free`. -/
-theorem free_of_colorable {W : Type*} {H : SimpleGraph W}
- (nhc : ¬H.Colorable n) (hc : G.Colorable n) : H.Free G := by
- contrapose nhc with hc'
- exact ⟨hc.some.comp hc'.some.toHom⟩
+theorem free_of_colorable (nhc : ¬H.Colorable n) (hc : G.Colorable n) : H.Free G := by
+ contrapose! nhc with hc'
+ exact hc.of_hom hc'.some.toHom
+
+/-! ### Isomorphisms -/
+
+/-- Equivalence of colorings induced by isomorphisms of graphs and equivalence of colors. -/
+def coloringCongr (f : G ≃g H) (g : α ≃ β) : G.Coloring α ≃ H.Coloring β :=
+ f.homCongr (Iso.completeGraph g)
+
+lemma colorable_congr (f : G ≃g H) : G.Colorable n ↔ H.Colorable n :=
+ ⟨fun hc ↦ hc.of_hom f.symm.toHom, fun hc ↦ hc.of_hom f.toHom⟩
+
+lemma chromaticNumber_congr (f : G ≃g H) : G.chromaticNumber = H.chromaticNumber :=
+ le_antisymm (chromaticNumber_mono_of_hom f.toHom) (chromaticNumber_mono_of_hom f.symm.toHom)
end SimpleGraph
diff --git a/Mathlib/Combinatorics/SimpleGraph/Maps.lean b/Mathlib/Combinatorics/SimpleGraph/Maps.lean
index 07a8cc9ab92df0..404a8eca0f79ef 100644
--- a/Mathlib/Combinatorics/SimpleGraph/Maps.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/Maps.lean
@@ -730,6 +730,9 @@ theorem toEmbedding_completeGraph {α β : Type*} (f : α ≃ β) :
variable {G'' : SimpleGraph X} {G''' : SimpleGraph Y}
+/-- Equivalence of homomorphisms induced by isomorphisms of graphs. -/
+abbrev homCongr (f' : G'' ≃g G''') : G →g G'' ≃ G' →g G''' := RelIso.relHomCongr f f'
+
/-- Composition of graph isomorphisms. -/
abbrev comp (f' : G' ≃g G'') (f : G ≃g G') : G ≃g G'' :=
f.trans f'
From 48fca283afb57d96c5a4dbd7c5dbe5efcf13154c Mon Sep 17 00:00:00 2001
From: Marcelo Lynch
Date: Sat, 27 Jun 2026 19:41:43 +0000
Subject: [PATCH 0405/1300] chore(ci): bump pinned GitHub Actions to latest
(#41089)
- actions/attest-build-provenance: v4.1.0 -> v4.1.1
- actions/setup-python: v6.2.0 -> v6.3.0
- softprops/action-gh-release: v3.0.0 -> v3.0.1
- actions/cache: v5.0.5 -> v6.1.0 (ESM migration + read-only cache handling)
- zulip/github-actions-zulip/send-message: v2.0.1 -> v2.0.2
- leanprover-community/privilege-escalation-bridge: v1.2.0 -> v1.3.0
- leanprover-community/gh-problem-matcher-wrap: pin to the node24 build (clears the Node 20 deprecation warning)
- kim-em/github-actions-ensure-sha-pinned-actions: pin to v5.0.0 instead of a feature branch
- dcarbone/install-jq-action: v3.2.0 -> v4.0.1 (default jq -> 1.8.2)
actions/checkout was already bumped to v7.0.0 on master (#41084).
---
.github/actions/setup-build-env/action.yml | 2 +-
.github/workflows/PR_summary.yml | 2 +-
.github/workflows/build_template.yml | 12 ++++++------
.github/workflows/commit_verification.yml | 2 +-
.github/workflows/commit_verification_wf_run.yml | 2 +-
.github/workflows/daily.yml | 12 ++++++------
.github/workflows/docker_build.yml | 2 +-
.github/workflows/lake_cache_shadow.yml | 4 ++--
.github/workflows/latest_import.yml | 4 ++--
.github/workflows/long_file_report.yml | 2 +-
.github/workflows/maintainer_bors.yml | 2 +-
.github/workflows/maintainer_bors_wf_run.yml | 4 ++--
.github/workflows/maintainer_merge.yml | 2 +-
.github/workflows/maintainer_merge_wf_run.yml | 4 ++--
.github/workflows/nightly_bump_and_merge.yml | 2 +-
.github/workflows/nightly_detect_failure.yml | 6 +++---
.github/workflows/olean_report.yaml | 2 +-
.github/workflows/olean_report_wf_run.yaml | 2 +-
.github/workflows/pre-commit.yml | 2 +-
.github/workflows/release.yml | 2 +-
.github/workflows/remove_deprecated_decls.yml | 6 +++---
.github/workflows/rm_set_option.yml | 8 ++++----
.github/workflows/shake.yaml | 4 ++--
.github/workflows/technical_debt_metrics.yml | 2 +-
.github/workflows/update_dependencies.yml | 2 +-
.github/workflows/update_dependencies_zulip.yml | 8 ++++----
.github/workflows/weekly-lints.yml | 6 +++---
.github/workflows/zulip_emoji_ci_status.yaml | 2 +-
.github/workflows/zulip_emoji_closed_pr.yaml | 2 +-
.github/workflows/zulip_emoji_labelling.yaml | 2 +-
.github/workflows/zulip_emoji_merge_delegate.yaml | 2 +-
31 files changed, 58 insertions(+), 58 deletions(-)
diff --git a/.github/actions/setup-build-env/action.yml b/.github/actions/setup-build-env/action.yml
index f4a0e092a7eb50..e24ab2e2a92876 100644
--- a/.github/actions/setup-build-env/action.yml
+++ b/.github/actions/setup-build-env/action.yml
@@ -45,7 +45,7 @@ runs:
# The Hoskinson runners may not have jq installed, so do that now.
- name: 'Setup jq'
- uses: dcarbone/install-jq-action@b7ef57d46ece78760b4019dbc4080a1ba2a40b45 # v3.2.0
+ uses: dcarbone/install-jq-action@4fcb5062d7ce9bc4382d1a352d19ba3ba2c317c1 # v4.0.1
# Compute the trust-classified container target and read fallback for this
# job. Sets MATHLIB_CACHE_FROM / MATHLIB_CACHE_PRIMARY in env so every
diff --git a/.github/workflows/PR_summary.yml b/.github/workflows/PR_summary.yml
index 5d73c1426fc510..4565d89484f7cd 100644
--- a/.github/workflows/PR_summary.yml
+++ b/.github/workflows/PR_summary.yml
@@ -67,7 +67,7 @@ jobs:
fi
- name: Set up Python
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
+ uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: 3.12
diff --git a/.github/workflows/build_template.yml b/.github/workflows/build_template.yml
index 3ab58099ebbc78..de0366348b2616 100644
--- a/.github/workflows/build_template.yml
+++ b/.github/workflows/build_template.yml
@@ -190,7 +190,7 @@ jobs:
lake exe mk_all --check
- name: begin gh-problem-match-wrap for build step
- uses: leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23
+ uses: leanprover-community/gh-problem-matcher-wrap@65a654fcdf7b64ff7633bc7a558f7b46d59a27bf # 2026-06-25
with:
action: add # In order to be able to run a multiline script, we need to add/remove the problem matcher before and after.
linters: lean
@@ -211,7 +211,7 @@ jobs:
../tools-branch/scripts/lake-build-with-retry.sh Mathlib
# results of build at pr-branch/.lake/build_summary_Mathlib.json
- name: end gh-problem-match-wrap for build step
- uses: leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23
+ uses: leanprover-community/gh-problem-matcher-wrap@65a654fcdf7b64ff7633bc7a558f7b46d59a27bf # 2026-06-25
with:
action: remove
linters: lean
@@ -463,7 +463,7 @@ jobs:
# from the build job's outputs, and the problem-matcher wrap is gated to match.
- name: begin gh-problem-match-wrap for test step
if: ${{ needs.build.outputs.build-outcome == 'success' && needs.build.outputs.mk_all-outcome == 'success' && needs.build.outputs.archive-outcome == 'success' && needs.build.outputs.counterexamples-outcome == 'success' }}
- uses: leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23
+ uses: leanprover-community/gh-problem-matcher-wrap@65a654fcdf7b64ff7633bc7a558f7b46d59a27bf # 2026-06-25
with:
action: add # In order to be able to run a multiline script, we need to add/remove the problem matcher before and after.
linters: lean
@@ -475,7 +475,7 @@ jobs:
../tools-branch/scripts/lake-build-wrapper.py .lake/build_summary_MathlibTest.json lake --iofail test
- name: end gh-problem-match-wrap for test step
if: ${{ needs.build.outputs.build-outcome == 'success' && needs.build.outputs.mk_all-outcome == 'success' && needs.build.outputs.archive-outcome == 'success' && needs.build.outputs.counterexamples-outcome == 'success' }}
- uses: leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23
+ uses: leanprover-community/gh-problem-matcher-wrap@65a654fcdf7b64ff7633bc7a558f7b46d59a27bf # 2026-06-25
with:
action: remove
linters: lean
@@ -485,7 +485,7 @@ jobs:
# lint feedback is still reported. The problem-matcher wrap is gated to match.
- name: begin gh-problem-match-wrap for shake and lint steps
if: ${{ always() && (needs.build.outputs.build-outcome == 'success' || needs.build.outputs.build-outcome == 'failure') }}
- uses: leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23
+ uses: leanprover-community/gh-problem-matcher-wrap@65a654fcdf7b64ff7633bc7a558f7b46d59a27bf # 2026-06-25
with:
action: add # In order to be able to run a multiline script, we need to add/remove the problem matcher before and after.
linters: gcc
@@ -538,7 +538,7 @@ jobs:
- name: end gh-problem-match-wrap for shake and lint steps
if: ${{ always() && (needs.build.outputs.build-outcome == 'success' || needs.build.outputs.build-outcome == 'failure') }}
- uses: leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23
+ uses: leanprover-community/gh-problem-matcher-wrap@65a654fcdf7b64ff7633bc7a558f7b46d59a27bf # 2026-06-25
with:
action: remove
linters: gcc
diff --git a/.github/workflows/commit_verification.yml b/.github/workflows/commit_verification.yml
index e14544cb1ad433..d62c03386f5ba2 100644
--- a/.github/workflows/commit_verification.yml
+++ b/.github/workflows/commit_verification.yml
@@ -111,7 +111,7 @@ jobs:
}' > bridge-outputs.json
- name: Emit bridge artifact
- uses: leanprover-community/privilege-escalation-bridge/emit@f5dfe313a79647c07315b451b2dc2a81a161a50d # v1.2.0
+ uses: leanprover-community/privilege-escalation-bridge/emit@ea7d63d1c8ece92a8e89b6a6d8fda40603167a91 # v1.3.0
with:
artifact: workflow-data
outputs_file: bridge-outputs.json
diff --git a/.github/workflows/commit_verification_wf_run.yml b/.github/workflows/commit_verification_wf_run.yml
index 622820aae6f452..dec43f0e0a8bdb 100644
--- a/.github/workflows/commit_verification_wf_run.yml
+++ b/.github/workflows/commit_verification_wf_run.yml
@@ -25,7 +25,7 @@ jobs:
steps:
- name: Consume bridge artifact
id: bridge
- uses: leanprover-community/privilege-escalation-bridge/consume@f5dfe313a79647c07315b451b2dc2a81a161a50d # v1.2.0
+ uses: leanprover-community/privilege-escalation-bridge/consume@ea7d63d1c8ece92a8e89b6a6d8fda40603167a91 # v1.3.0
with:
token: ${{ github.token }}
artifact: workflow-data
diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml
index d737a444edb805..3865c77fd5e2b4 100644
--- a/.github/workflows/daily.yml
+++ b/.github/workflows/daily.yml
@@ -122,7 +122,7 @@ jobs:
- name: Post success message for leanchecker on Zulip
if: steps.get-status.outputs.job_conclusion == 'success'
- uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1
+ uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2
with:
api-key: ${{ secrets.ZULIP_API_KEY }}
email: 'github-mathlib4-bot@leanprover.zulipchat.com'
@@ -135,7 +135,7 @@ jobs:
- name: Post failure / cancelled message for leanchecker on Zulip
if: steps.get-status.outputs.job_conclusion != 'success'
- uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1
+ uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2
with:
api-key: ${{ secrets.ZULIP_API_KEY }}
email: 'github-mathlib4-bot@leanprover.zulipchat.com'
@@ -245,7 +245,7 @@ jobs:
- name: Post success message for mathlib_test_executable on Zulip
if: steps.get-status.outputs.job_conclusion == 'success'
- uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1
+ uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2
with:
api-key: ${{ secrets.ZULIP_API_KEY }}
email: 'github-mathlib4-bot@leanprover.zulipchat.com'
@@ -258,7 +258,7 @@ jobs:
- name: Post failure / cancelled message for mathlib_test_executable on Zulip
if: steps.get-status.outputs.job_conclusion != 'success'
- uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1
+ uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2
with:
api-key: ${{ secrets.ZULIP_API_KEY }}
email: 'github-mathlib4-bot@leanprover.zulipchat.com'
@@ -410,7 +410,7 @@ jobs:
- name: Post success message for nanoda on Zulip
if: steps.get-status.outputs.job_conclusion == 'success'
- uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1
+ uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2
with:
api-key: ${{ secrets.ZULIP_API_KEY }}
email: 'github-mathlib4-bot@leanprover.zulipchat.com'
@@ -423,7 +423,7 @@ jobs:
- name: Post failure / cancelled message for nanoda on Zulip
if: steps.get-status.outputs.job_conclusion != 'success'
- uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1
+ uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2
with:
api-key: ${{ secrets.ZULIP_API_KEY }}
email: 'github-mathlib4-bot@leanprover.zulipchat.com'
diff --git a/.github/workflows/docker_build.yml b/.github/workflows/docker_build.yml
index c46e08fd9934d8..3e0c28a07aaa7c 100644
--- a/.github/workflows/docker_build.yml
+++ b/.github/workflows/docker_build.yml
@@ -59,7 +59,7 @@ jobs:
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- name: Generate artifact attestation
- uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
+ uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1
with:
subject-name: ${{ env.REGISTRY }}/${{ env.REPO_NAME }}/${{ matrix.image }}
subject-digest: ${{ steps.push.outputs.digest }}
diff --git a/.github/workflows/lake_cache_shadow.yml b/.github/workflows/lake_cache_shadow.yml
index 099472ca7b57f0..68a7710e79bc1c 100644
--- a/.github/workflows/lake_cache_shadow.yml
+++ b/.github/workflows/lake_cache_shadow.yml
@@ -95,7 +95,7 @@ jobs:
run: echo "::notice::Lake cache shadow on ref ${{ inputs.mathlib_ref || 'master' }} run ${{ github.run_id }}"
- name: Setup jq
- uses: dcarbone/install-jq-action@b7ef57d46ece78760b4019dbc4080a1ba2a40b45 # v3.2.0
+ uses: dcarbone/install-jq-action@4fcb5062d7ce9bc4382d1a352d19ba3ba2c317c1 # v4.0.1
- name: Checkout tools branch
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
@@ -544,7 +544,7 @@ jobs:
} >> "$GITHUB_OUTPUT"
- name: Send to Zulip
- uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1
+ uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2
with:
api-key: ${{ secrets.ZULIP_API_KEY }}
email: 'github-mathlib4-bot@leanprover.zulipchat.com'
diff --git a/.github/workflows/latest_import.yml b/.github/workflows/latest_import.yml
index 447160b95b43eb..8aacc26bd9584d 100644
--- a/.github/workflows/latest_import.yml
+++ b/.github/workflows/latest_import.yml
@@ -68,7 +68,7 @@ jobs:
- name: build mathlib
id: build
- uses: leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23
+ uses: leanprover-community/gh-problem-matcher-wrap@65a654fcdf7b64ff7633bc7a558f7b46d59a27bf # 2026-06-25
with:
linters: lean
run: |
@@ -86,7 +86,7 @@ jobs:
tee "$GITHUB_OUTPUT"
- name: Post output to Zulip
- uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1
+ uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2
with:
api-key: ${{ secrets.ZULIP_API_KEY }}
email: 'github-mathlib4-bot@leanprover.zulipchat.com'
diff --git a/.github/workflows/long_file_report.yml b/.github/workflows/long_file_report.yml
index f0febf9b6d215f..03e0a37bf3400c 100644
--- a/.github/workflows/long_file_report.yml
+++ b/.github/workflows/long_file_report.yml
@@ -30,7 +30,7 @@ jobs:
printf $'summary<> "$GITHUB_OUTPUT"
- name: Post output to Zulip
- uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1
+ uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2
with:
api-key: ${{ secrets.ZULIP_API_KEY }}
email: 'github-mathlib4-bot@leanprover.zulipchat.com'
diff --git a/.github/workflows/update_dependencies.yml b/.github/workflows/update_dependencies.yml
index 30e6e281b12a8e..5974d14528aab1 100644
--- a/.github/workflows/update_dependencies.yml
+++ b/.github/workflows/update_dependencies.yml
@@ -206,7 +206,7 @@ jobs:
- name: Send Zulip message (failure)
if: ${{ failure() }}
- uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1
+ uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2
with:
api-key: ${{ secrets.ZULIP_API_KEY }}
email: 'github-mathlib4-bot@leanprover.zulipchat.com'
diff --git a/.github/workflows/update_dependencies_zulip.yml b/.github/workflows/update_dependencies_zulip.yml
index d511ecd7ca857a..f0520210cfa84d 100644
--- a/.github/workflows/update_dependencies_zulip.yml
+++ b/.github/workflows/update_dependencies_zulip.yml
@@ -33,7 +33,7 @@ jobs:
uses: ./workflow-actions/.github/actions/get-mathlib-ci
- name: Set up Python
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
+ uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: '3.x'
@@ -89,7 +89,7 @@ jobs:
return output;
- name: Send Zulip message
- uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1
+ uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2
with:
api-key: ${{ secrets.ZULIP_API_KEY }}
email: 'github-mathlib4-bot@leanprover.zulipchat.com'
@@ -123,7 +123,7 @@ jobs:
uses: ./workflow-actions/.github/actions/get-mathlib-ci
- name: Set up Python
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
+ uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: '3.x'
@@ -168,7 +168,7 @@ jobs:
- name: Send Zulip message
if: ${{ steps.construct_message.outputs.result != '' }}
- uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1
+ uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2
with:
api-key: ${{ secrets.ZULIP_API_KEY }}
email: 'github-mathlib4-bot@leanprover.zulipchat.com'
diff --git a/.github/workflows/weekly-lints.yml b/.github/workflows/weekly-lints.yml
index 5df2eddf43434a..515906d8713e11 100644
--- a/.github/workflows/weekly-lints.yml
+++ b/.github/workflows/weekly-lints.yml
@@ -66,7 +66,7 @@ jobs:
lake build Mathlib.Init
- name: Add GitHub problem matcher wrapper
- uses: leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23
+ uses: leanprover-community/gh-problem-matcher-wrap@65a654fcdf7b64ff7633bc7a558f7b46d59a27bf # 2026-06-25
with:
action: add
linters: lean
@@ -88,13 +88,13 @@ jobs:
"${CI_SCRIPTS_DIR}/reporting/zulip_build_report.sh" "${lean_outfile}" > "${GITHUB_OUTPUT}"
- name: Remove GitHub problem matcher wrapper
- uses: leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23
+ uses: leanprover-community/gh-problem-matcher-wrap@65a654fcdf7b64ff7633bc7a558f7b46d59a27bf # 2026-06-25
with:
action: remove
linters: lean
- name: Post output to Zulip
- uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1
+ uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2
with:
api-key: ${{ secrets.ZULIP_API_KEY }}
email: 'github-mathlib4-bot@leanprover.zulipchat.com'
diff --git a/.github/workflows/zulip_emoji_ci_status.yaml b/.github/workflows/zulip_emoji_ci_status.yaml
index b2a46fccb8c0d2..8ab30ec4c2ef23 100644
--- a/.github/workflows/zulip_emoji_ci_status.yaml
+++ b/.github/workflows/zulip_emoji_ci_status.yaml
@@ -86,7 +86,7 @@ jobs:
- name: Set up Python
if: steps.pr.outputs.skip != 'true' && steps.action.outputs.ci_action != 'skip'
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
+ uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: '3.x'
diff --git a/.github/workflows/zulip_emoji_closed_pr.yaml b/.github/workflows/zulip_emoji_closed_pr.yaml
index b0488d76541fe2..2dbf54efff0875 100644
--- a/.github/workflows/zulip_emoji_closed_pr.yaml
+++ b/.github/workflows/zulip_emoji_closed_pr.yaml
@@ -47,7 +47,7 @@ jobs:
- name: Set up Python
if: ${{ ! startsWith(github.event.pull_request.title, '[Merged by Bors]') ||
github.event_name == 'reopened' }}
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
+ uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: '3.x'
diff --git a/.github/workflows/zulip_emoji_labelling.yaml b/.github/workflows/zulip_emoji_labelling.yaml
index d462d1a878eeb4..5a019050928fdc 100644
--- a/.github/workflows/zulip_emoji_labelling.yaml
+++ b/.github/workflows/zulip_emoji_labelling.yaml
@@ -27,7 +27,7 @@ jobs:
uses: ./workflow-actions/.github/actions/get-mathlib-ci
- name: Set up Python
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
+ uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: '3.x'
diff --git a/.github/workflows/zulip_emoji_merge_delegate.yaml b/.github/workflows/zulip_emoji_merge_delegate.yaml
index 7d701d874aa6c4..9883c1f0263082 100644
--- a/.github/workflows/zulip_emoji_merge_delegate.yaml
+++ b/.github/workflows/zulip_emoji_merge_delegate.yaml
@@ -30,7 +30,7 @@ jobs:
uses: ./workflow-actions/.github/actions/get-mathlib-ci
- name: Set up Python
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
+ uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: '3.x'
From b2b425516745501699f012f5a96fb50a826b3281 Mon Sep 17 00:00:00 2001
From: Michael Rothgang <10105016+grunweg@users.noreply.github.com>
Date: Sat, 27 Jun 2026 21:54:37 +0000
Subject: [PATCH 0406/1300] chore: use more `mkApp(Opt)M` in the differential
geometry elaborators (#40933)
This is more concise (and perhaps even more efficient) than converting an existing expression to Syntax and re-elaborating that. No need for this.
---
Mathlib/Geometry/Manifold/Notation.lean | 37 +++++--------------
.../Notation/Advanced.lean | 12 +++---
.../DifferentialGeometry/Notation/Basic.lean | 4 +-
.../DifferentialGeometry/Notation/Sphere.lean | 4 +-
4 files changed, 19 insertions(+), 38 deletions(-)
diff --git a/Mathlib/Geometry/Manifold/Notation.lean b/Mathlib/Geometry/Manifold/Notation.lean
index 3c34b70a359791..a3d519596a8156 100644
--- a/Mathlib/Geometry/Manifold/Notation.lean
+++ b/Mathlib/Geometry/Manifold/Notation.lean
@@ -430,7 +430,7 @@ where
let some K ← guessBaseFieldForNormedSpace F
| throwError "Couldn't find a `NormedSpace` structure on `{F}`"
let tgtMod ← mkAppOptM ``modelWithCornersSelf #[K, none, F, none, none]
- mkAppM ``ModelWithCorners.prod #[baseModel, tgtMod]
+ mkAppM ``ModelWithCorners.prod #[baseModel, tgtMod]
| _ =>
throwError s!"{e} is a TotalSpace {F} {V}, but {V} is not a pi type --- \
could not infer base of the bundle"
@@ -440,18 +440,14 @@ where
match_expr V with
| TangentSpace _k _ _E _ _ _H _ I M _ _ => do
trace[Elab.DiffGeo.MDiff] "`{V}` is the total space of the `TangentBundle` of `{M}`"
- let srcIT : Term ← Term.exprToSyntax I
- let resTerm : Term ← ``(ModelWithCorners.tangent $srcIT)
- Term.elabTerm resTerm none
+ mkAppM ``ModelWithCorners.tangent #[I]
| _ => throwError "`{V}` is not a `TangentSpace`"
/-- Attempt to find a model on a `TangentBundle` -/
fromTangentBundle : TermElabM Expr := do
match_expr e with
| TangentBundle _k _ _E _ _ _H _ I M _ _ => do
trace[Elab.DiffGeo.MDiff] "`{e}` is a `TangentBundle` over model `{I}` on `{M}`"
- let srcIT : Term ← Term.exprToSyntax I
- let resTerm : Term ← ``(ModelWithCorners.tangent $srcIT)
- Term.elabTerm resTerm none
+ mkAppM ``ModelWithCorners.tangent #[I]
| _ => throwError "`{e}` is not a `TangentBundle`"
/-- Attempt to find the trivial model on a normed space. -/
fromNormedSpace : TermElabM FindModelResult := do
@@ -534,18 +530,13 @@ where
-- the standard model with corners.
-- Therefore, we only check definitional equality at reducible transparency.
let (k, _E, _F) ← isCLMReduciblyDefeqCoefficients e
- let eK : Term ← Term.exprToSyntax k
- let eT : Term ← Term.exprToSyntax e
- let iTerm : Term ← ``(𝓘($eK, $eT))
- Term.elabTerm iTerm none
+ mkAppOptM ``modelWithCornersSelf #[k, none, e, none, none]
/-- Attempt to find a model with corners on a Euclidean space, half-space or quadrant -/
fromEuclideanSpace : TermElabM Expr := do
-- We don't use `match_expr` to avoid importing `EuclideanHalfSpace`.
match (← instantiateMVars e).cleanupAnnotations with
| mkApp2 (.const `EuclideanSpace _) k _n =>
- let eK : Term ← Term.exprToSyntax k
- let eT : Term ← Term.exprToSyntax e
- Term.elabTerm (← ``(𝓘($eK, $eT))) none
+ mkAppOptM ``modelWithCornersSelf #[k, none, e, none, none]
| mkApp2 (.const `EuclideanHalfSpace _) n _ =>
mkAppOptM `modelWithCornersEuclideanHalfSpace #[n, none]
| mkApp (.const `EuclideanQuadrant _) n =>
@@ -606,9 +597,7 @@ where
| _ => return none
if let some (k, R) := searchNormedAlgebra then
trace[Elab.DiffGeo.MDiff] "found a normed algebra: `{α}` is a normed `{k}`-algebra"
- let eK : Term ← Term.exprToSyntax k
- let eR : Term ← Term.exprToSyntax R
- Term.elabTerm (← ``(𝓘($eK, $eR))) none
+ mkAppOptM ``modelWithCornersSelf #[k, none, R, none, none]
else
trace[Elab.DiffGeo.MDiff] "`{α}` is not a normed algebra on the nose: try via a space of \
continuous linear maps"
@@ -633,9 +622,7 @@ where
match normedSpace? with
| some (k, _R) =>
trace[Elab.DiffGeo.MDiff] "found a normed space: `{V}` is a normed space over `{k}`"
- let eK : Term ← Term.exprToSyntax k
- let eα : Term ← Term.exprToSyntax α
- Term.elabTerm (← ``(𝓘($eK, $eα))) none
+ mkAppOptM ``modelWithCornersSelf #[k, none, α, none, none]
| _ => throwError "Found no `NormedSpace` structure on `{V}` among local instances"
else
-- NB. If further instances of `NormedAlgebra` arise in practice, adding another check
@@ -724,10 +711,7 @@ where
| _ => throwError "`{e}` is not a sphere in a real normed space"
/-- Attempt to find a model with corners from a normed field.
We attempt to find a global instance here. -/
- fromNormedField : TermElabM Expr := do
- let eT : Term ← Term.exprToSyntax e
- let iTerm : Term ← ``(𝓘($eT, $eT))
- Term.elabTerm iTerm none
+ fromNormedField : TermElabM Expr := mkAppOptM ``modelWithCornersSelf #[e, none, e, none, none]
/-- Try to find a `ModelWithCorners` instance on a type (represented by an expression `e`),
using the local context to infer the appropriate instance.
@@ -802,10 +786,7 @@ where
throwError "`{e}` is a product of normed spaces, so there are two potential models with \
corners\nFor now, please specify the model by hand."
-- Otherwise, we are not a normed space, and normally form the product model.
- let eTerm : Term ← Term.exprToSyntax srcE
- let fTerm : Term ← Term.exprToSyntax srcF
- let iTerm : Term ← ``(ModelWithCorners.prod $eTerm $fTerm)
- return some { model := ← Term.elabTerm iTerm none }
+ return some { model := ← mkAppM ``ModelWithCorners.prod #[srcE, srcF] }
| Sum E F =>
trace[Elab.DiffGeo.MDiff] "Expression `{e}` is a direct sum of `{E}` and `{F}`\n\
We assume the models match, and only look into the first summand"
diff --git a/MathlibTest/DifferentialGeometry/Notation/Advanced.lean b/MathlibTest/DifferentialGeometry/Notation/Advanced.lean
index f6e277d4f8ae00..1f0384072fa830 100644
--- a/MathlibTest/DifferentialGeometry/Notation/Advanced.lean
+++ b/MathlibTest/DifferentialGeometry/Notation/Advanced.lean
@@ -378,10 +378,10 @@ trace: [Elab.DiffGeo.MDiff] Finding a model with corners for: `M`
`ContinuousLinearMap id' E'' E'''` is not a coercion of a set to a type
[Elab.DiffGeo.MDiff] 💥️ NormedField
[Elab.DiffGeo.MDiff] Failed with error:
- failed to synthesize instance of type class
+ failed to synthesize
NontriviallyNormedField (ContinuousLinearMap id' E'' E''')
⏎
- Hint: Type class instance resolution failures can be inspected with the `set_option trace.Meta.synthInstance true` command.
+ Hint: Additional diagnostic information may be available using the `set_option diagnostics true` command.
[Elab.DiffGeo.MDiff] 💥️ InnerProductSpace
[Elab.DiffGeo.MDiff] Failed with error:
Couldn't find an `InnerProductSpace` structure on `ContinuousLinearMap id' E'' E'''` among local instances.
@@ -481,10 +481,10 @@ trace: [Elab.DiffGeo.MDiff] Finding a model with corners for: `M`
`ContinuousLinearMap σ E'' E''''` is not a coercion of a set to a type
[Elab.DiffGeo.MDiff] 💥️ NormedField
[Elab.DiffGeo.MDiff] Failed with error:
- failed to synthesize instance of type class
+ failed to synthesize
NontriviallyNormedField (ContinuousLinearMap σ E'' E'''')
⏎
- Hint: Type class instance resolution failures can be inspected with the `set_option trace.Meta.synthInstance true` command.
+ Hint: Additional diagnostic information may be available using the `set_option diagnostics true` command.
[Elab.DiffGeo.MDiff] 💥️ InnerProductSpace
[Elab.DiffGeo.MDiff] Failed with error:
Couldn't find an `InnerProductSpace` structure on `ContinuousLinearMap σ E'' E''''` among local instances.
@@ -686,10 +686,10 @@ trace: [Elab.DiffGeo.MDiff] Finding a model with corners for: `↑(Set.Icc x y)`
`Set.Icc x y` is not a sphere in a real normed space
[Elab.DiffGeo.MDiff] 💥️ NormedField
[Elab.DiffGeo.MDiff] Failed with error:
- failed to synthesize instance of type class
+ failed to synthesize
NontriviallyNormedField ↑(Set.Icc x y)
⏎
- Hint: Type class instance resolution failures can be inspected with the `set_option trace.Meta.synthInstance true` command.
+ Hint: Additional diagnostic information may be available using the `set_option diagnostics true` command.
[Elab.DiffGeo.MDiff] 💥️ InnerProductSpace
[Elab.DiffGeo.MDiff] Failed with error:
Couldn't find an `InnerProductSpace` structure on `↑(Set.Icc x y)` among local instances.
diff --git a/MathlibTest/DifferentialGeometry/Notation/Basic.lean b/MathlibTest/DifferentialGeometry/Notation/Basic.lean
index 4461fab938c58c..055a66b9f12458 100644
--- a/MathlibTest/DifferentialGeometry/Notation/Basic.lean
+++ b/MathlibTest/DifferentialGeometry/Notation/Basic.lean
@@ -1470,10 +1470,10 @@ trace: [Elab.DiffGeo.MDiff] Finding a model with corners for: `Unit`
`Unit` is not a coercion of a set to a type
[Elab.DiffGeo.MDiff] 💥️ NormedField
[Elab.DiffGeo.MDiff] Failed with error:
- failed to synthesize instance of type class
+ failed to synthesize
NontriviallyNormedField Unit
⏎
- Hint: Type class instance resolution failures can be inspected with the `set_option trace.Meta.synthInstance true` command.
+ Hint: Additional diagnostic information may be available using the `set_option diagnostics true` command.
[Elab.DiffGeo.MDiff] 💥️ InnerProductSpace
[Elab.DiffGeo.MDiff] Failed with error:
Couldn't find an `InnerProductSpace` structure on `Unit` among local instances.
diff --git a/MathlibTest/DifferentialGeometry/Notation/Sphere.lean b/MathlibTest/DifferentialGeometry/Notation/Sphere.lean
index 8d22d6a5804056..854d1cd7dcfd97 100644
--- a/MathlibTest/DifferentialGeometry/Notation/Sphere.lean
+++ b/MathlibTest/DifferentialGeometry/Notation/Sphere.lean
@@ -230,10 +230,10 @@ trace: [Elab.DiffGeo.MDiff] Finding a model with corners for: `↑(Metric.sphere
Found no fact `finrank ℝ E'' = n + 1` in the local context
[Elab.DiffGeo.MDiff] 💥️ NormedField
[Elab.DiffGeo.MDiff] Failed with error:
- failed to synthesize instance of type class
+ failed to synthesize
NontriviallyNormedField ↑(Metric.sphere 0 1)
⏎
- Hint: Type class instance resolution failures can be inspected with the `set_option trace.Meta.synthInstance true` command.
+ Hint: Additional diagnostic information may be available using the `set_option diagnostics true` command.
[Elab.DiffGeo.MDiff] 💥️ InnerProductSpace
[Elab.DiffGeo.MDiff] Failed with error:
Couldn't find an `InnerProductSpace` structure on `↑(Metric.sphere 0 1)` among local instances.
From 70ba12a7d8c6241f0e608ca5896504c856f671d2 Mon Sep 17 00:00:00 2001
From: Rida Hamadani
Date: Sun, 28 Jun 2026 09:16:42 +0000
Subject: [PATCH 0407/1300] feat(SimpleGraph): `dropLast` of a cycle is a path
(#35295)
---
Mathlib/Combinatorics/SimpleGraph/Paths.lean | 3 +++
1 file changed, 3 insertions(+)
diff --git a/Mathlib/Combinatorics/SimpleGraph/Paths.lean b/Mathlib/Combinatorics/SimpleGraph/Paths.lean
index a5813a65160535..62a598da27c8db 100644
--- a/Mathlib/Combinatorics/SimpleGraph/Paths.lean
+++ b/Mathlib/Combinatorics/SimpleGraph/Paths.lean
@@ -358,6 +358,9 @@ lemma IsPath.tail {p : G.Walk u v} (hp : p.IsPath) : p.tail.IsPath := by
| cons hadj p =>
simp_all [Walk.isPath_def]
+theorem IsCycle.isPath_dropLast {p : G.Walk u u} (h : p.IsCycle) : p.dropLast.IsPath :=
+ .mk' <| p.support_dropLast h.not_nil ▸ h.nodup_dropLast_support
+
theorem IsPath.dropLast (hp : p.IsPath) : p.dropLast.IsPath :=
hp.take _
From c8b35d4b1b468c70850db3b6e9fd0332fc9ed5e0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ya=C3=ABl=20Dillies?=
Date: Sun, 28 Jun 2026 09:35:23 +0000
Subject: [PATCH 0408/1300] feat(RingTheory): adjoining a root is preserved
under base change (#40360)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
From Toric
Co-authored-by: Andrew Yang
Co-authored-by: Michał Mrugała
---
Mathlib/RingTheory/AdjoinRoot.lean | 36 ++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/Mathlib/RingTheory/AdjoinRoot.lean b/Mathlib/RingTheory/AdjoinRoot.lean
index 42bca0ed2495bc..bf58bd9bd4f793 100644
--- a/Mathlib/RingTheory/AdjoinRoot.lean
+++ b/Mathlib/RingTheory/AdjoinRoot.lean
@@ -977,6 +977,42 @@ theorem quotEquivQuotMap_symm_apply_mk (f g : R[X]) (I : Ideal R) :
end
+section TensorProduct
+variable {R S T U : Type*} [CommRing R] [CommRing S] [CommRing T] [Algebra R S] [Algebra R T]
+ [CommRing U] [Algebra R U] {p : Polynomial S}
+
+open Algebra TensorProduct
+
+variable (p) in
+/-- Adjoining a root is preserved under base change. -/
+def tensorAlgEquiv (p : S[X]) (q : (T ⊗[R] S)[X]) (h : p.map includeRight.toRingHom = q) :
+ T ⊗[R] AdjoinRoot p ≃ₐ[T] AdjoinRoot q := by
+ refine .ofAlgHom
+ (Algebra.TensorProduct.lift (algHom T T _)
+ (mapAlgHom includeRight p q <| by exact h.symm.dvd) fun _ _ ↦ .all ..)
+ (liftAlgHom _ (Algebra.TensorProduct.map (AlgHom.id T T)
+ (((Algebra.ofId S (AdjoinRoot p))).restrictScalars R)) (1 ⊗ₜ root _) ?_) ?_ ?_
+ · simp only [← h, AlgHom.toRingHom_eq_coe]
+ rw [Polynomial.eval₂_map]
+ change Polynomial.eval₂ ((Algebra.TensorProduct.map (AlgHom.id R T) _).comp _).toRingHom _ _ = _
+ simp only [map_comp_includeRight, AlgHom.toRingHom_eq_coe, AlgHom.comp_toRingHom,
+ AlgHom.coe_restrictScalars, ← Polynomial.eval₂_map]
+ change Polynomial.eval₂ _ ((RingHomClass.toRingHom includeRight) (root p)) (p.map (of _)) = _
+ rw [Polynomial.eval₂_hom]
+ simp [Polynomial.eval_map]
+ · ext
+ · simp [Algebra.ofId_apply]
+ simp
+ · ext : 3 <;> simp
+
+@[simp] lemma tensorAlgEquiv_root (p : S[X]) (q : Polynomial (T ⊗[R] S)) (h) :
+ tensorAlgEquiv p q h (1 ⊗ₜ root p) = root q := by simp [tensorAlgEquiv]
+
+@[simp] lemma tensorAlgEquiv_of (p : S[X]) (q : Polynomial (T ⊗[R] S)) (h) {x : S} :
+ tensorAlgEquiv p q h (1 ⊗ₜ of p x) = of q (1 ⊗ₜ x):= by simp [tensorAlgEquiv]
+
+end TensorProduct
+
end AdjoinRoot
namespace PowerBasis
From b3efe7e8a0863c414a6eb8b1fa19c429f95a81e2 Mon Sep 17 00:00:00 2001
From: Michael Rothgang <10105016+grunweg@users.noreply.github.com>
Date: Sun, 28 Jun 2026 14:03:13 +0000
Subject: [PATCH 0409/1300] chore: adaptations for batteries#1863 (#40820)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
With the `defLemma` being removed, we delete all corresponding `nolint` entries.
Co-authored-by: mathlib-nightly-testing[bot]
Co-authored-by: F. G. Dorais
Co-authored-by: François G. Dorais
---
Mathlib/CategoryTheory/Category/Preorder.lean | 2 +-
Mathlib/Combinatorics/Hindman.lean | 6 +++---
Mathlib/Data/Nat/Fib/Zeckendorf.lean | 2 +-
Mathlib/Logic/Basic.lean | 2 +-
Mathlib/Topology/Algebra/WithZeroTopology.lean | 4 ----
lake-manifest.json | 2 +-
6 files changed, 7 insertions(+), 11 deletions(-)
diff --git a/Mathlib/CategoryTheory/Category/Preorder.lean b/Mathlib/CategoryTheory/Category/Preorder.lean
index 8e91d464d4b631..4f84c09560328b 100644
--- a/Mathlib/CategoryTheory/Category/Preorder.lean
+++ b/Mathlib/CategoryTheory/Category/Preorder.lean
@@ -81,7 +81,7 @@ theorem leOfHom {x y : X} (h : x ⟶ y) : x ≤ y :=
h.down.down
set_option linter.defProp false in
-@[nolint defLemma, inherit_doc leOfHom]
+@[inherit_doc leOfHom]
abbrev _root_.Quiver.Hom.le := @leOfHom
@[simp]
diff --git a/Mathlib/Combinatorics/Hindman.lean b/Mathlib/Combinatorics/Hindman.lean
index 8e12c26ea959be..1faeeab5012304 100644
--- a/Mathlib/Combinatorics/Hindman.lean
+++ b/Mathlib/Combinatorics/Hindman.lean
@@ -107,17 +107,17 @@ variable {M} [Semigroup M] (a : Stream' M) (m : M) (h : FP a.tail m)
set_option linter.defProp false in
/-- Constructor for `FP`. This is the preferred spelling over `FP.head'`. -/
-@[to_additive (attr := match_pattern, nolint defLemma)
+@[to_additive (attr := match_pattern)
/-- Constructor for `FS`. This is the preferred spelling over `FS.head'`. -/]
abbrev FP.head : a.head ∈ FP a := FP.head' a
set_option linter.defProp false in
/-- Constructor for `FP`. This is the preferred spelling over `FP.tail'`. -/
-@[to_additive (attr := match_pattern, nolint defLemma)
+@[to_additive (attr := match_pattern)
/-- Constructor for `FS`. This is the preferred spelling over `FS.tail'`. -/]
abbrev FP.tail : m ∈ FP a := FP.tail' a m h
set_option linter.defProp false in
/-- Constructor for `FP`. This is the preferred spelling over `FP.cons'`. -/
-@[to_additive (attr := match_pattern, nolint defLemma)
+@[to_additive (attr := match_pattern)
/-- Constructor for `FS`. This is the preferred spelling over `FS.cons'`. -/]
abbrev FP.cons : a.head * m ∈ FP a := FP.cons' a m h
diff --git a/Mathlib/Data/Nat/Fib/Zeckendorf.lean b/Mathlib/Data/Nat/Fib/Zeckendorf.lean
index baf33830ad15dc..5e417a6752f26e 100644
--- a/Mathlib/Data/Nat/Fib/Zeckendorf.lean
+++ b/Mathlib/Data/Nat/Fib/Zeckendorf.lean
@@ -37,7 +37,7 @@ fibonacci, zeckendorf, digit
open List Nat
-- TODO: The `local` attribute makes this not considered as an instance by linters
-@[nolint defLemma docBlame]
+@[nolint docBlame]
local instance : IsTrans ℕ fun a b ↦ b + 2 ≤ a where
trans _a _b _c hba hcb := hcb.trans <| le_self_add.trans hba
diff --git a/Mathlib/Logic/Basic.lean b/Mathlib/Logic/Basic.lean
index b20343ec1ec3f1..e1f659e30a1de0 100644
--- a/Mathlib/Logic/Basic.lean
+++ b/Mathlib/Logic/Basic.lean
@@ -734,7 +734,7 @@ lemma eq_true_intro {a : Prop} (h : a) : a = True := propext (iff_true_intro h)
lemma eq_false_intro {a : Prop} (h : ¬a) : a = False := propext (iff_false_intro h)
-- FIXME: `alias` creates `def Iff.eq := propext` instead of `lemma Iff.eq := propext`
-@[nolint defLemma] alias Iff.eq := propext
+alias Iff.eq := propext
lemma iff_eq_eq {a b : Prop} : (a ↔ b) = (a = b) := propext ⟨propext, Eq.to_iff⟩
diff --git a/Mathlib/Topology/Algebra/WithZeroTopology.lean b/Mathlib/Topology/Algebra/WithZeroTopology.lean
index 1fdd828d8d99c0..760e23fee1833b 100644
--- a/Mathlib/Topology/Algebra/WithZeroTopology.lean
+++ b/Mathlib/Topology/Algebra/WithZeroTopology.lean
@@ -140,7 +140,6 @@ theorem isOpen_Iio {a : Γ₀} : IsOpen (Iio a) :=
/-- The topology on a linearly ordered group with zero element adjoined is compatible with the order
structure: the set `{p : Γ₀ × Γ₀ | p.1 ≤ p.2}` is closed. -/
-@[nolint defLemma]
scoped instance (priority := 100) orderClosedTopology : OrderClosedTopology Γ₀ where
isClosed_le' := by
simp only [← isOpen_compl_iff, compl_setOf, not_le, isOpen_iff_mem_nhds]
@@ -149,7 +148,6 @@ scoped instance (priority := 100) orderClosedTopology : OrderClosedTopology Γ
exact Iio_mem_nhds hab
/-- The topology on a linearly ordered group with zero element adjoined is T₅. -/
-@[nolint defLemma]
scoped instance (priority := 100) t5Space : T5Space Γ₀ where
completely_normal := fun s t h₁ h₂ => by
by_cases hs : 0 ∈ s
@@ -159,7 +157,6 @@ scoped instance (priority := 100) t5Space : T5Space Γ₀ where
/-- The topology on a linearly ordered group with zero element adjoined makes it a topological
monoid. -/
-@[nolint defLemma]
scoped instance (priority := 100) : ContinuousMul Γ₀ where
continuous_mul := by
simp only [continuous_iff_continuousAt, ContinuousAt]
@@ -182,7 +179,6 @@ scoped instance (priority := 100) : ContinuousMul Γ₀ where
rw [nhds_prod_eq, nhds_of_ne_zero hx, nhds_of_ne_zero hy, prod_pure_pure]
exact pure_le_nhds (x * y)
-@[nolint defLemma]
scoped instance (priority := 100) : ContinuousInv₀ Γ₀ :=
⟨fun γ h => by
rw [ContinuousAt, nhds_of_ne_zero h]
diff --git a/lake-manifest.json b/lake-manifest.json
index 1dfeee55bef0ed..4291b508ab7539 100644
--- a/lake-manifest.json
+++ b/lake-manifest.json
@@ -65,7 +65,7 @@
"type": "git",
"subDir": null,
"scope": "leanprover-community",
- "rev": "09c267c2706119a09606e6cde3f6cef5bb2ab72a",
+ "rev": "77d3cc514f987c1f42f2bbd8a8d56855012dc115",
"name": "batteries",
"manifestFile": "lake-manifest.json",
"inputRev": "main",
From 8681f8234faba644788c28a4980fecd4a794b6c0 Mon Sep 17 00:00:00 2001
From: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com>
Date: Sun, 28 Jun 2026 14:58:17 +0000
Subject: [PATCH 0410/1300] doc(Algebra/Group/Defs): fix typo in `NSMul`s
docstring (#41059)
`SMUl` -> `SMul`
---
Mathlib/Algebra/Group/Defs.lean | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Mathlib/Algebra/Group/Defs.lean b/Mathlib/Algebra/Group/Defs.lean
index fce530fbd241ac..d948baeee2b0ef 100644
--- a/Mathlib/Algebra/Group/Defs.lean
+++ b/Mathlib/Algebra/Group/Defs.lean
@@ -638,7 +638,7 @@ theorem npowRec_eq_npowBinRec : @npowRecAuto = @npowBinRecAuto := by
rfl
/-- `NSMul` is an implementation detail of `AddMonoid`. It is needed because it is
-impossible to extend `SMUl ℕ M` and `SMul ℤ M` at the same time. -/
+impossible to extend `SMul ℕ M` and `SMul ℤ M` at the same time. -/
class NSMul (M : Type u) where
/-- Multiplication by a natural number.
Set this to `nsmulRec` unless `Module` diamonds are possible. -/
From 0723fe72056c374fc4b2873821900910dbd766f3 Mon Sep 17 00:00:00 2001
From: Thomas Browning <13339017+tb65536@users.noreply.github.com>
Date: Sun, 28 Jun 2026 14:58:19 +0000
Subject: [PATCH 0411/1300] chore(FieldTheory/IsGaloisGroup): move
`mulEquivAlgEquiv` to ring theory folder (#41071)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The isomorphism `G ≃* Gal(B/A)` does not require Galois theory to be proved so it can be moved to the ring theory folder.
I also cleaned up some messed up variables left over from the file split.
Co-authored-by: tb65536
---
Mathlib/FieldTheory/Galois/IsGaloisGroup.lean | 15 -------------
Mathlib/RingTheory/IsGaloisGroup/Basic.lean | 21 +++++++++++++++----
2 files changed, 17 insertions(+), 19 deletions(-)
diff --git a/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean b/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean
index dd462f855e2f75..f560e44b014c81 100644
--- a/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean
+++ b/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean
@@ -126,21 +126,6 @@ theorem card_eq_finrank' : Nat.card G = Module.finrank A B := by
rw [IsGaloisGroup.card_eq_finrank G (FractionRing A) (FractionRing B),
Algebra.IsAlgebraic.finrank_of_isFractionRing A (FractionRing A) B (FractionRing B)]
-attribute [local instance] FractionRing.liftAlgebra in
-/-- If `G` is a finite Galois group for `B/A`, then `G` is isomorphic to `Gal(B/A)`. -/
-@[simps!] noncomputable def mulEquivAlgEquiv : G ≃* Gal(B/A) :=
- MulEquiv.ofBijective (MulSemiringAction.toAlgAut G A B) (by
- have := IsDomain.of_faithfulSMul A B
- letI K := FractionRing A
- letI L := FractionRing B
- letI := IsFractionRing.mulSemiringAction G B L
- have := isGalois G K L
- have := finiteDimensional G K L
- refine .of_comp_left ?_ (IsFractionRing.fieldEquivOfAlgEquivHom_injective A B K L)
- rw [Nat.bijective_iff_injective_and_card, card_eq_finrank G K L,
- IsGalois.card_aut_eq_finrank K L]
- exact ⟨fun _ _ ↦ (faithful K).eq_of_smul_eq_smul ∘ DFunLike.ext_iff.mp, rfl⟩)
-
@[simp]
theorem map_mulEquivAlgEquiv_fixingSubgroup [IsGaloisGroup G K L] (F : IntermediateField K L) :
(fixingSubgroup G (F : Set L)).map (mulEquivAlgEquiv G K L) = F.fixingSubgroup := by
diff --git a/Mathlib/RingTheory/IsGaloisGroup/Basic.lean b/Mathlib/RingTheory/IsGaloisGroup/Basic.lean
index f2df00cd008376..8dd8974d9fb075 100644
--- a/Mathlib/RingTheory/IsGaloisGroup/Basic.lean
+++ b/Mathlib/RingTheory/IsGaloisGroup/Basic.lean
@@ -126,16 +126,27 @@ instance IsGaloisGroup.toFractionRing [IsDomain A] [IsDomain B] [Finite G]
end Field
-variable (G G' K L : Type*) [Group G] [Group G'] [Field K] [Field L] [Algebra K L]
- [MulSemiringAction G L] [MulSemiringAction G' L]
+variable (G : Type*) [Group G]
namespace IsGaloisGroup
section IsDomain
variable (A B : Type*) [CommRing A] [CommRing B] [IsDomain B] [Algebra A B] [FaithfulSMul A B]
- [MulSemiringAction G B] [MulSemiringAction G' B] [IsGaloisGroup G A B] [IsGaloisGroup G' A B]
- [Finite G] [Finite G']
+ [MulSemiringAction G B] [IsGaloisGroup G A B] [Finite G]
+
+attribute [local instance] FractionRing.liftAlgebra in
+/-- If `G` is a finite Galois group for `B/A`, then `G` is isomorphic to `Gal(B/A)`. -/
+@[simps!] noncomputable def mulEquivAlgEquiv : G ≃* Gal(B/A) :=
+ MulEquiv.ofBijective (MulSemiringAction.toAlgAut G A B) (by
+ have := IsDomain.of_faithfulSMul A B
+ have : FaithfulSMul G B := IsGaloisGroup.faithful A
+ refine ⟨fun _ _ ↦ eq_of_smul_eq_smul ∘ DFunLike.ext_iff.mp, fun φ ↦ ?_⟩
+ obtain ⟨g, hg⟩ := Ideal.Quotient.stabilizerHom_surjective G ⊥ ⊥
+ (Ideal.Quotient.algEquivOfEqMap (⊥ : Ideal A) φ Ideal.map_bot.symm)
+ use g
+ rw [AlgEquiv.ext_iff] at hg ⊢
+ exact fun x ↦ (AlgEquiv.quotientBot A B).symm.injective (hg x))
end IsDomain
@@ -234,6 +245,8 @@ def smulCommClassQuotient [N.Normal] [Algebra A B] [IsScalarTower A B C] [SMulCo
end Semiring
+variable {K L : Type*} [Field K] [Field L] [Algebra K L] [MulSemiringAction G L]
+
variable (F : IntermediateField K L) (N : Subgroup G) [N.Normal] [IsGaloisGroup N F L]
noncomputable instance : MulSemiringAction (G ⧸ N) F :=
From c402bebf78ab971d9749ba6fe8186f265d1705c0 Mon Sep 17 00:00:00 2001
From: Felix Pernegger <188575194+felixpernegger@users.noreply.github.com>
Date: Sun, 28 Jun 2026 14:58:21 +0000
Subject: [PATCH 0412/1300] chore: remove redundant `backward.proofsInPublic`
exceptions (#41096)
These are all (no more exceptions).
I guess it is now possible to remove them due to some proofs having been golfed
Co-authored-by: Batixx
---
Mathlib/CategoryTheory/Sites/EffectiveEpimorphic.lean | 2 --
1 file changed, 2 deletions(-)
diff --git a/Mathlib/CategoryTheory/Sites/EffectiveEpimorphic.lean b/Mathlib/CategoryTheory/Sites/EffectiveEpimorphic.lean
index 1b889bb5c7f259..7f49ecb19f8495 100644
--- a/Mathlib/CategoryTheory/Sites/EffectiveEpimorphic.lean
+++ b/Mathlib/CategoryTheory/Sites/EffectiveEpimorphic.lean
@@ -82,7 +82,6 @@ lemma Presieve.EffectiveEpimorphic.isSheafFor_of_isRepresentable {X : C} {R : Pr
exact hR _
set_option backward.defeqAttrib.useBackward true in
-set_option backward.proofsInPublic true in
/--
Implementation: This is a construction which will be used in the proof that
the sieve generated by a single arrow is effective epimorphic if and only if
@@ -197,7 +196,6 @@ lemma Sieve.generateFamily_eq {B : C} {α : Type*} (X : α → C) (π : (a : α)
exact ⟨_, g, π a, ⟨a⟩, rfl⟩
set_option backward.defeqAttrib.useBackward true in
-set_option backward.proofsInPublic true in
/--
Implementation: This is a construction which will be used in the proof that
the sieve generated by a family of arrows is effective epimorphic if and only if
From 21c622e6836d174a64c0c9b4f76d1fb74825736b Mon Sep 17 00:00:00 2001
From: Felix Pernegger <188575194+felixpernegger@users.noreply.github.com>
Date: Sun, 28 Jun 2026 14:58:23 +0000
Subject: [PATCH 0413/1300] chore: fix some adaption notes (#41117)
By restoring the original proofs when they still work, or removing the workarounds they describe.
Co-authored-by: Batixx
---
.../Algebra/Homology/SpectralObject/Page.lean | 5 +----
Mathlib/Analysis/Convex/StdSimplex.lean | 8 ++------
.../Limits/Shapes/Multiequalizer.lean | 17 -----------------
Mathlib/Topology/Algebra/PontryaginDual.lean | 4 ----
.../Category/TopCat/GrothendieckTopology.lean | 4 +---
5 files changed, 4 insertions(+), 34 deletions(-)
diff --git a/Mathlib/Algebra/Homology/SpectralObject/Page.lean b/Mathlib/Algebra/Homology/SpectralObject/Page.lean
index 4b7662ad43bb1e..e238bd05a5dfd6 100644
--- a/Mathlib/Algebra/Homology/SpectralObject/Page.lean
+++ b/Mathlib/Algebra/Homology/SpectralObject/Page.lean
@@ -236,11 +236,8 @@ noncomputable def πE (hn₁ : n₀ + 1 = n₁ := by lia) (hn₂ : n₁ + 1 = n
(X.cyclesIso f₁ f₂ f₃ n₀ n₁ n₂).inv ≫
(X.shortComplex f₁ f₂ f₃ n₀ n₁ n₂).homologyπ
-#adaptation_note /-- nightly-2026-03-04
-The `deriving` keyword on a `def` should just apply `noncomputable` to all
-instances automatically if the main `def` is already `noncomputable`. -/
set_option backward.isDefEq.respectTransparency false in
-deriving noncomputable instance Epi for πE
+deriving instance Epi for πE
set_option backward.isDefEq.respectTransparency false in
@[reassoc (attr := simp)]
diff --git a/Mathlib/Analysis/Convex/StdSimplex.lean b/Mathlib/Analysis/Convex/StdSimplex.lean
index 9836e956e90d08..46b59101d93b89 100644
--- a/Mathlib/Analysis/Convex/StdSimplex.lean
+++ b/Mathlib/Analysis/Convex/StdSimplex.lean
@@ -87,17 +87,13 @@ theorem ite_eq_mem_stdSimplex (i : ι) : (if i = · then (1 : 𝕜) else 0) ∈
variable [IsOrderedRing 𝕜]
set_option linter.overlappingInstances false
-
-#adaptation_note /-- nightly-2024-03-11
-we need a type annotation on the segment in the following two lemmas. -/
-
/-- The edges are contained in the simplex. -/
lemma segment_single_subset_stdSimplex (i j : ι) :
- ([Pi.single i 1 -[𝕜] Pi.single j 1] : Set (ι → 𝕜)) ⊆ stdSimplex 𝕜 ι :=
+ [Pi.single i 1 -[𝕜] Pi.single j 1] ⊆ stdSimplex 𝕜 ι :=
(convex_stdSimplex 𝕜 ι).segment_subset (single_mem_stdSimplex _ _) (single_mem_stdSimplex _ _)
lemma stdSimplex_fin_two :
- stdSimplex 𝕜 (Fin 2) = ([Pi.single 0 1 -[𝕜] Pi.single 1 1] : Set (Fin 2 → 𝕜)) := by
+ stdSimplex 𝕜 (Fin 2) = [Pi.single 0 1 -[𝕜] Pi.single 1 1] := by
refine Subset.antisymm ?_ (segment_single_subset_stdSimplex 𝕜 (0 : Fin 2) 1)
rintro f ⟨hf₀, hf₁⟩
rw [Fin.sum_univ_two] at hf₁
diff --git a/Mathlib/CategoryTheory/Limits/Shapes/Multiequalizer.lean b/Mathlib/CategoryTheory/Limits/Shapes/Multiequalizer.lean
index aa78bf468b47b3..4ea64dd9d69f3e 100644
--- a/Mathlib/CategoryTheory/Limits/Shapes/Multiequalizer.lean
+++ b/Mathlib/CategoryTheory/Limits/Shapes/Multiequalizer.lean
@@ -689,28 +689,11 @@ def ofPiFork
| WalkingMulticospan.left _ => a.ι ≫ c.proj _
| WalkingMulticospan.right _ => a.ι ≫ I.fstPiMapOfIsLimit c hd ≫ d.proj _
π.naturality := by
- #adaptation_note /-- Proof repaired after leanprover/lean4#13363.
- The proof used to finish from this point as
- ```
rintro (_ | _) (_ | _) (_ | _ | _)
· simp
· simp
· dsimp; rw [a.condition_assoc]; simp
· simp
- ```
- The replacement proof is a short-term fix, and we request that the authors/maintainers of
- this file review the proof, and either approve it by removing this note, revise
- the proof or the prerequisites appropriately, or minimize a problem in lean4 that still
- needs addressing. -/
- rintro (_ | _) (_ | _) (_ | _ | _)
- · simp only [WalkingMulticospan.Hom.id_eq_id, Functor.map_id,
- Functor.const_obj_map, Category.comp_id]
- exact Category.id_comp _
- · simp
- · dsimp; rw [a.condition_assoc]; simp
- · simp only [WalkingMulticospan.Hom.id_eq_id, Functor.map_id,
- Functor.const_obj_map, Category.comp_id]
- exact Category.id_comp _
@[simp]
theorem ofPiFork_ι (a : Fork (I.fstPiMapOfIsLimit c hd) (I.sndPiMapOfIsLimit c hd)) (i) :
diff --git a/Mathlib/Topology/Algebra/PontryaginDual.lean b/Mathlib/Topology/Algebra/PontryaginDual.lean
index 769f32e134617a..2dd281ea6672e0 100644
--- a/Mathlib/Topology/Algebra/PontryaginDual.lean
+++ b/Mathlib/Topology/Algebra/PontryaginDual.lean
@@ -61,10 +61,6 @@ namespace PontryaginDual
open ContinuousMonoidHom
-#adaptation_note /-- nightly-2026-03-31
-This `set_option` is necessary because of a compiler bug.
--/
-set_option backward.inferInstanceAs.wrap.data false in
instance : CommGroup (PontryaginDual A) := inferInstanceAs (CommGroup (A →ₜ* Circle))
deriving instance
diff --git a/Mathlib/Topology/Category/TopCat/GrothendieckTopology.lean b/Mathlib/Topology/Category/TopCat/GrothendieckTopology.lean
index 4de1c96a203ce1..cf9d1ffe5faa15 100644
--- a/Mathlib/Topology/Category/TopCat/GrothendieckTopology.lean
+++ b/Mathlib/Topology/Category/TopCat/GrothendieckTopology.lean
@@ -66,9 +66,7 @@ def precoverage : Precoverage TopCat.{u} :=
Types.jointlySurjectivePrecoverage.comap (forget TopCat) ⊓ isOpenEmbedding.precoverage
deriving Precoverage.HasIsos, Precoverage.IsStableUnderComposition
-#adaptation_note /-- nightly-2026-03-04: Strange we need `noncomputable` for a `Prop` instance.
-Will be fixed by https://github.com/leanprover/lean4/pull/12789 -/
-deriving noncomputable instance Precoverage.IsStableUnderBaseChange for precoverage
+deriving instance Precoverage.IsStableUnderBaseChange for precoverage
/-- The Grothendieck topology on the category of topological spaces is the topology given by
jointly surjective open embeddings. -/
From 9e03f6c352bcc289b28edeba64b80fb591d5cf63 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=ABl=20Riou?=
<37772949+joelriou@users.noreply.github.com>
Date: Sun, 28 Jun 2026 15:48:49 +0000
Subject: [PATCH 0414/1300] =?UTF-8?q?feat(Algebra/Homology):=20the=20right?=
=?UTF-8?q?=20derived=20functor=20`RF=20:=20D^+(C)=20=E2=A5=A4=20D^+(D)`?=
=?UTF-8?q?=20(#40863)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
If `F : C ⥤ D` is an additive functor between abelian categories, where `C` has enough injectives, we define the right derived functor `F.rightDerivedFunctorPlus : DerivedCategory.Plus C ⥤ DerivedCategory.Plus D` between the corresponding bounded below derived categories.
This definition follows from the fact that any functor from the homotopy category `K^+(C)` admits a right derived functor. In order to show this, we use the model category structure on bounded below cochain complexes in `C` #38850 and construct derivability structures on the category of bounded below cochain complexes and its homotopy category.
---
Mathlib.lean | 1 +
.../DerivabilityStructureInjectives.lean | 11 ++-
.../RightDerivedFunctorPlus.lean | 70 +++++++++++++++++++
3 files changed, 81 insertions(+), 1 deletion(-)
create mode 100644 Mathlib/Algebra/Homology/DerivedCategory/RightDerivedFunctorPlus.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index e16fd6872a03c4..a8fa4a3ffd3f6d 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -591,6 +591,7 @@ public import Mathlib.Algebra.Homology.DerivedCategory.KInjective
public import Mathlib.Algebra.Homology.DerivedCategory.KProjective
public import Mathlib.Algebra.Homology.DerivedCategory.Linear
public import Mathlib.Algebra.Homology.DerivedCategory.Plus
+public import Mathlib.Algebra.Homology.DerivedCategory.RightDerivedFunctorPlus
public import Mathlib.Algebra.Homology.DerivedCategory.ShortExact
public import Mathlib.Algebra.Homology.DerivedCategory.SingleTriangle
public import Mathlib.Algebra.Homology.DerivedCategory.SmallShiftedHom
diff --git a/Mathlib/Algebra/Homology/DerivedCategory/DerivabilityStructureInjectives.lean b/Mathlib/Algebra/Homology/DerivedCategory/DerivabilityStructureInjectives.lean
index d5764ce6531e99..2699d12c106b74 100644
--- a/Mathlib/Algebra/Homology/DerivedCategory/DerivabilityStructureInjectives.lean
+++ b/Mathlib/Algebra/Homology/DerivedCategory/DerivabilityStructureInjectives.lean
@@ -373,7 +373,7 @@ variable [HasDerivedCategory C] (F' : DerivedCategory.Plus C ⥤ H)
(α : F ⟶ DerivedCategory.Plus.Qh ⋙ F')
[F'.IsRightDerivedFunctor α (HomotopyCategory.Plus.quasiIso C)]
-instance (K : HomotopyCategory.Plus C) [(∀ (n : ℤ), Injective (K.obj.as.X n))] :
+instance (K : HomotopyCategory.Plus C) [∀ (n : ℤ), Injective (K.obj.as.X n)] :
IsIso (α.app K) := by
have (Y : HomotopyCategory.Plus (InjectiveObject C)) :
IsIso (α.app ((InjectiveObject.ι C).mapHomotopyCategoryPlus.obj Y)) :=
@@ -389,6 +389,15 @@ instance (K : HomotopyCategory.Plus C) [(∀ (n : ℤ), Injective (K.obj.as.X n)
rw [← NatTrans.isIso_app_iff_of_iso α e]
infer_instance
+instance (K : CochainComplex.Plus C) (n : ℤ) [Injective (K.obj.X n)] :
+ Injective (((HomotopyCategory.Plus.quotient C).obj K).obj.as.X n) := by
+ assumption
+
+instance (K : CochainComplex.Plus (InjectiveObject C)) (n : ℤ) :
+ Injective (((HomotopyCategory.Plus.quotient C).obj
+ ((InjectiveObject.ι C).mapCochainComplexPlus.obj K)).obj.as.X n) :=
+ (K.obj.X n).property
+
example (X : HomotopyCategory.Plus (InjectiveObject C)) :
IsIso ((F.totalRightDerivedUnit DerivedCategory.Plus.Qh
(HomotopyCategory.Plus.quasiIso C)).app
diff --git a/Mathlib/Algebra/Homology/DerivedCategory/RightDerivedFunctorPlus.lean b/Mathlib/Algebra/Homology/DerivedCategory/RightDerivedFunctorPlus.lean
new file mode 100644
index 00000000000000..1fbd95aa538737
--- /dev/null
+++ b/Mathlib/Algebra/Homology/DerivedCategory/RightDerivedFunctorPlus.lean
@@ -0,0 +1,70 @@
+/-
+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.Homology.DerivedCategory.DerivabilityStructureInjectives
+
+/-!
+# The right derived functor on the bounded below derived category
+
+If `F : C ⥤ D` is an additive functor between abelian categories,
+where `C` has enough injectives, we define the right derived functor
+`F.rightDerivedFunctorPlus : DerivedCategory.Plus C ⥤ DerivedCategory.Plus D`
+between the corresponding bounded below derived categories.
+
+TODO(@joelriou): show that this functor is triangulated and refactor
+the definiton of `Functor.rightDerived`
+
+-/
+
+@[expose] public section
+
+namespace CategoryTheory
+
+namespace Functor
+
+variable {C D : Type*} [Category* C] [Category* D] [Abelian C] [Abelian D]
+ [HasDerivedCategory C] [HasDerivedCategory D]
+ (F : C ⥤ D) [F.Additive] [EnoughInjectives C]
+
+/-- The right derived functor `DerivedCategory.Plus C ⥤ DerivedCategory.Plus D`
+when `F : C ⥤ D` is an additive functor between abelian categories and
+`C` has enough injectives. -/
+noncomputable def rightDerivedFunctorPlus :
+ DerivedCategory.Plus C ⥤ DerivedCategory.Plus D :=
+ (F.mapHomotopyCategoryPlus ⋙ DerivedCategory.Plus.Qh).totalRightDerived DerivedCategory.Plus.Qh
+ (HomotopyCategory.Plus.quasiIso C)
+
+/-- The natural transformation that is part of the data of
+the right derived functor `DerivedCategory.Plus C ⥤ DerivedCategory.Plus D`
+when `F : C ⥤ D` is an additive functor between abelian categories and
+`C` has enough injectives. -/
+noncomputable def rightDerivedFunctorPlusUnit :
+ F.mapHomotopyCategoryPlus ⋙ DerivedCategory.Plus.Qh ⟶
+ DerivedCategory.Plus.Qh ⋙ F.rightDerivedFunctorPlus :=
+ (F.mapHomotopyCategoryPlus ⋙ DerivedCategory.Plus.Qh).totalRightDerivedUnit
+ DerivedCategory.Plus.Qh (HomotopyCategory.Plus.quasiIso C)
+
+instance :
+ F.rightDerivedFunctorPlus.IsRightDerivedFunctor
+ F.rightDerivedFunctorPlusUnit (HomotopyCategory.Plus.quasiIso C) := by
+ dsimp only [rightDerivedFunctorPlus, rightDerivedFunctorPlusUnit]
+ infer_instance
+
+example (X : HomotopyCategory.Plus (InjectiveObject C)) :
+ IsIso (F.rightDerivedFunctorPlusUnit.app
+ ((InjectiveObject.ι C).mapHomotopyCategoryPlus.obj X)) := by
+ infer_instance
+
+example (K : CochainComplex.Plus (InjectiveObject C)) :
+ IsIso (F.rightDerivedFunctorPlusUnit.app
+ ((HomotopyCategory.Plus.quotient C).obj
+ ((InjectiveObject.ι C).mapCochainComplexPlus.obj K))) := by
+ infer_instance
+
+end Functor
+
+end CategoryTheory
From a730529ad47aa327c8315453cfe41815ca7d429c Mon Sep 17 00:00:00 2001
From: Felix Pernegger <188575194+felixpernegger@users.noreply.github.com>
Date: Sun, 28 Jun 2026 17:46:34 +0000
Subject: [PATCH 0415/1300] chore: remove unused `linter.unusedVariables`
exception (#41003)
Seems like at some point the option was changed to allowed name instances, so this works now. (This is the only instance of the option in mathlib where we can remove it.)
Co-authored-by: Batixx
---
Mathlib/NumberTheory/NumberField/Cyclotomic/Basic.lean | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/Mathlib/NumberTheory/NumberField/Cyclotomic/Basic.lean b/Mathlib/NumberTheory/NumberField/Cyclotomic/Basic.lean
index c2a381225da3d1..0c4e4d90f647ed 100644
--- a/Mathlib/NumberTheory/NumberField/Cyclotomic/Basic.lean
+++ b/Mathlib/NumberTheory/NumberField/Cyclotomic/Basic.lean
@@ -224,9 +224,7 @@ theorem integralPowerBasisOfPrimePow_gen [hcycl : IsCyclotomicExtension {p ^ k}
simp only [adjoinEquivRingOfIntegersOfPrimePow_apply, IsIntegralClosure.algebraMap_lift]
rfl
-set_option linter.unusedVariables false in
-/- We name `hcycl` so it can be used as a named argument, but this is unused in the declaration
-otherwise, so we need to disable the linter. -/
+/- We name `hcycl` so it can be used as a named argument. -/
@[simp]
theorem integralPowerBasisOfPrimePow_dim [hcycl : IsCyclotomicExtension {p ^ k} ℚ K]
(hζ : IsPrimitiveRoot ζ (p ^ k)) : hζ.integralPowerBasisOfPrimePow.dim = φ (p ^ k) := by
From aaedc74a09fbe1da58b11cf4d27806a6fa1a86eb Mon Sep 17 00:00:00 2001
From: Whysoserioushah <109107491+Whysoserioushah@users.noreply.github.com>
Date: Sun, 28 Jun 2026 18:04:23 +0000
Subject: [PATCH 0416/1300] chore(RepresentationTheory): remove some set_option
backwards (#41057)
Removes several `set_option backward.defeqAttrib.useBackward true` / `set_option backward.isDefEq.respectTransparency false` workarounds in `Mathlib/RepresentationTheory`, replacing them with the `@[implicit_reducible]` attribute on the relevant functor definitions and simplifying a few proofs accordingly.
---
Mathlib/RepresentationTheory/Coinduced.lean | 8 ++------
Mathlib/RepresentationTheory/Coinvariants.lean | 7 +------
Mathlib/RepresentationTheory/FiniteIndex.lean | 17 +++++------------
Mathlib/RepresentationTheory/Induced.lean | 4 +---
Mathlib/RepresentationTheory/Invariants.lean | 3 +--
Mathlib/RepresentationTheory/Rep/Basic.lean | 2 +-
6 files changed, 11 insertions(+), 30 deletions(-)
diff --git a/Mathlib/RepresentationTheory/Coinduced.lean b/Mathlib/RepresentationTheory/Coinduced.lean
index e23be2bcc66fa5..51fb493a80f113 100644
--- a/Mathlib/RepresentationTheory/Coinduced.lean
+++ b/Mathlib/RepresentationTheory/Coinduced.lean
@@ -128,13 +128,11 @@ noncomputable abbrev coindMap {A B : Rep k G} (f : A ⟶ B) : coind φ A ⟶ coi
variable (k) in
/-- Given a monoid homomorphism `φ : G →* H`, this is the functor sending a `G`-representation `A`
to the coinduced `H`-representation `coind φ A`, with action on maps given by postcomposition. -/
-@[simps obj map]
+@[implicit_reducible, simps obj map]
noncomputable def coindFunctor : Rep.{t} k G ⥤ Rep k H where
obj A := coind φ A
map f := coindMap φ f
-set_option backward.defeqAttrib.useBackward true in
-set_option backward.isDefEq.respectTransparency false in
instance {G : Type v'} [Group G] (S : Subgroup G) :
(coindFunctor k S.subtype).PreservesEpimorphisms where
preserves {X Y} f := (epi_iff_surjective _).2 fun y => by
@@ -199,7 +197,7 @@ noncomputable def coindMap' {A B : Rep k G} (f : A ⟶ B) : coind' φ A ⟶ coin
variable (k) in
/-- Given a monoid homomorphism `φ : G →* H`, this is the functor sending a `G`-representation `A`
to the coinduced `H`-representation `coind' φ A`, with action on maps given by postcomposition. -/
-@[simps obj map]
+@[implicit_reducible, simps obj map]
noncomputable def coindFunctor' : Rep k G ⥤ Rep k H where
obj A := coind' φ A
map f := coindMap' φ f
@@ -228,8 +226,6 @@ noncomputable def coindVEquiv :
noncomputable def coindIso : coind φ A ≅ coind' φ A :=
Rep.mkIso <| .mk (coindVEquiv φ A) fun h => by ext; simp [homEquiv]
-set_option backward.defeqAttrib.useBackward true in
-set_option backward.isDefEq.respectTransparency false in
/-- Given a monoid homomorphism `φ : G →* H`, the coinduction functors `Rep k G ⥤ Rep k H` given by
`coindFunctor k φ` and `coindFunctor' k φ` are naturally isomorphic, with isomorphism on objects
given by `coindIso φ`. -/
diff --git a/Mathlib/RepresentationTheory/Coinvariants.lean b/Mathlib/RepresentationTheory/Coinvariants.lean
index 93c225bec61f57..7182029e0d4dc9 100644
--- a/Mathlib/RepresentationTheory/Coinvariants.lean
+++ b/Mathlib/RepresentationTheory/Coinvariants.lean
@@ -343,7 +343,7 @@ end
variable (k G) [Monoid G] (A B : Rep.{w} k G)
/-- The functor sending a representation to its coinvariants. -/
-@[simps! obj_carrier map_hom]
+@[implicit_reducible, simps! obj_carrier map_hom]
noncomputable def coinvariantsFunctor : Rep.{w} k G ⥤ ModuleCat k where
obj A := ModuleCat.of k A.ρ.Coinvariants
map f := ModuleCat.ofHom (Representation.Coinvariants.map _ _ f.hom)
@@ -378,7 +378,6 @@ instance : (coinvariantsFunctor k G).Additive where
instance : (coinvariantsFunctor k G).Linear k where
set_option backward.defeqAttrib.useBackward true in
-set_option backward.isDefEq.respectTransparency false in
/-- The adjunction between the functor sending a representation to its coinvariants and the functor
equipping a module with the trivial representation. -/
@[simps]
@@ -394,7 +393,6 @@ theorem coinvariantsAdjunction_homEquiv_apply_hom {X : Rep.{w} k G} {Y : ModuleC
rfl
set_option backward.defeqAttrib.useBackward true in
-set_option backward.isDefEq.respectTransparency false in
@[simp]
theorem coinvariantsAdjunction_homEquiv_symm_apply_hom {X : Rep.{w} k G} {Y : ModuleCat k}
(f : X ⟶ (trivialFunctor k G).obj Y) :
@@ -437,7 +435,6 @@ section
variable (k : Type u) {G : Type v} [CommRing k] [Group G]
-set_option backward.isDefEq.respectTransparency false in
/-- Given a normal subgroup `S ≤ G`, this is the functor sending a `G`-representation `A` to the
`G ⧸ S`-representation it induces on `A_S`. -/
@[simps! obj_V map_hom_toLinearMap]
@@ -466,7 +463,6 @@ noncomputable def coinvariantsTensorFreeToFinsupp :
variable {α}
-set_option backward.isDefEq.respectTransparency false in
@[simp]
lemma coinvariantsTensorFreeToFinsupp_mk_tmul_single (x : A) (i : α) (g : G) (r : k) :
DFunLike.coe (F := (A.ρ.tprod (Representation.free k G α)).Coinvariants →ₗ[k] α →₀ A.V)
@@ -487,7 +483,6 @@ noncomputable def finsuppToCoinvariantsTensorFree :
variable {A α}
set_option backward.defeqAttrib.useBackward true in
-set_option backward.isDefEq.respectTransparency false in
@[simp]
lemma finsuppToCoinvariantsTensorFree_single (i : α) (x : A) :
DFunLike.coe (F := (α →₀ A.V) →ₗ[k] (A.ρ.tprod (Representation.free k G α)).Coinvariants)
diff --git a/Mathlib/RepresentationTheory/FiniteIndex.lean b/Mathlib/RepresentationTheory/FiniteIndex.lean
index 8e0ccd7172bf78..49c52f3cd7e9cd 100644
--- a/Mathlib/RepresentationTheory/FiniteIndex.lean
+++ b/Mathlib/RepresentationTheory/FiniteIndex.lean
@@ -177,10 +177,9 @@ noncomputable def indCoindIso (A : Rep.{max w u} k S) :
variable (k S)
-set_option backward.defeqAttrib.useBackward true in
/-- Given a finite index subgroup `S ≤ G`, this is a natural isomorphism between the `Ind_S^G` and
`Coind_G^S` functors `Rep k S ⥤ Rep k G`. -/
-@[simps! hom_app inv_app]
+@[implicit_reducible, simps! hom_app inv_app]
noncomputable def indCoindNatIso :
indFunctor k S.subtype ≅ coindFunctor.{max w u} k S.subtype :=
NatIso.ofComponents (fun (A : Rep k S) => indCoindIso A) fun f => by
@@ -194,7 +193,6 @@ noncomputable def resIndAdjunction :
resFunctor.{max w u v} S.subtype ⊣ indFunctor.{max w u v} k S.subtype :=
(resCoindAdjunction.{max w u v} k S.subtype).ofNatIsoRight (indCoindNatIso.{max w u v} k S).symm
-
omit [DecidableRel (QuotientGroup.rightRel S)] in
@[instance] -- Note: we must use `@[instance] theorem` here due to [lean4#5595](https://github.com/leanprover/lean4/issues/5595).
theorem instIsRightAdjointSubtypeMemSubgroupIndFunctorSubtype :
@@ -215,8 +213,6 @@ lemma resIndAdjunction_unit_app (B : Rep.{max w u v} k G) :
(resCoindAdjunction.{max w u} k S.subtype).unit.app B ≫
(indCoindIso.{max w (max u v)} (res S.subtype B)).inv := rfl
-set_option backward.defeqAttrib.useBackward true in
-set_option backward.isDefEq.respectTransparency false in
lemma resIndAdjunction_homEquiv_apply (A : Rep.{max w u v} k S)
{B : Rep.{max w u v} k G} (f : res S.subtype B ⟶ A) :
(resIndAdjunction.{w, u, v} k S).homEquiv _ _ f =
@@ -228,7 +224,7 @@ lemma resIndAdjunction_homEquiv_symm_apply (A : Rep.{max w u v} k S)
{B : Rep.{max w u v} k G}
(f : B ⟶ (indFunctor k S.subtype).obj A) :
((resIndAdjunction k S).homEquiv _ _).symm f =
- (resCoindHomEquiv.{max w u v} S.subtype B A).symm (f ≫ (indCoindIso.{max w u v} A).hom) := by
+ (resCoindHomEquiv.{max w u v} S.subtype B A).symm (f ≫ (indCoindIso.{max w u v} A).hom) :=
rfl
variable (k S) in
@@ -248,7 +244,7 @@ theorem instIsLeftAdjointSubtypeMemSubgroupCoindFunctorSubtype :
lemma coindResAdjunction_counit_app (B : Rep.{max w u v} k G) :
(coindResAdjunction.{w, u, v} k S).counit.app B =
(indCoindIso.{max w u v} (res S.subtype B)).inv ≫
- (indResAdjunction k S.subtype).counit.app B := by
+ (indResAdjunction k S.subtype).counit.app B :=
rfl
set_option backward.isDefEq.respectTransparency false in
@@ -257,8 +253,7 @@ lemma coindResAdjunction_unit_app (A : Rep.{max w u v} k S) :
(coindResAdjunction k S).unit.app A = (indResAdjunction k S.subtype).unit.app A ≫
(resFunctor S.subtype).map (indCoindIso.{max w u v} A).hom := by
ext
- simp [coindResAdjunction, Adjunction.ofNatIsoLeft,
- indResAdjunction, indCoindIso]
+ simp [coindResAdjunction]
lemma coindResAdjunction_homEquiv_apply (A : Rep.{max w u v} k S)
{B : Rep k G} (f : coind S.subtype A ⟶ B) :
@@ -270,9 +265,7 @@ lemma coindResAdjunction_homEquiv_symm_apply (A : Rep.{max w u v} k S)
{B : Rep k G} (f : A ⟶ res S.subtype B) :
((coindResAdjunction.{max w u v} k S).homEquiv _ _).symm f =
(indCoindIso.{max w u v} A).inv ≫ (indResHomEquiv S.subtype A B).symm f := by
- simp only [coindResAdjunction, indResAdjunction,
+ simp [coindResAdjunction, indResHomEquiv, indResAdjunction,
Adjunction.homEquiv_ofNatIsoLeft_symm_apply _]
- simp
- rfl
end Rep
diff --git a/Mathlib/RepresentationTheory/Induced.lean b/Mathlib/RepresentationTheory/Induced.lean
index 9033419a235a77..2645fae382cf50 100644
--- a/Mathlib/RepresentationTheory/Induced.lean
+++ b/Mathlib/RepresentationTheory/Induced.lean
@@ -110,7 +110,7 @@ noncomputable def indMap {A B : Rep k G} (f : A ⟶ B) : ind φ A ⟶ ind φ B :
variable (k) in
/-- Given a group homomorphism `φ : G →* H`, this is the functor sending a `G`-representation `A`
to the induced `H`-representation `ind φ A`, with action on maps induced by left tensoring. -/
-@[simps obj map]
+@[implicit_reducible, simps obj map]
noncomputable def indFunctor : Rep.{w} k G ⥤ Rep k H where
obj A := ind φ A
map f := indMap φ f
@@ -152,8 +152,6 @@ noncomputable def indResHomEquiv (A : Rep.{max w v' u} k G) (B : Rep.{max w v' u
simpa using (hom_comm_apply f h⁻¹ (IndV.mk φ A.ρ 1 a)).symm
right_inv _ := by ext; simp
-set_option backward.defeqAttrib.useBackward true in
-set_option backward.isDefEq.respectTransparency false in
variable (k) in
/-- Given a group homomorphism `φ : G →* H`, the induction functor `Rep k G ⥤ Rep k H` is left
adjoint to the restriction functor along `φ`. -/
diff --git a/Mathlib/RepresentationTheory/Invariants.lean b/Mathlib/RepresentationTheory/Invariants.lean
index f6394781abb975..33aa95d7e1c96f 100644
--- a/Mathlib/RepresentationTheory/Invariants.lean
+++ b/Mathlib/RepresentationTheory/Invariants.lean
@@ -246,7 +246,7 @@ abbrev quotientToInvariants : Rep k (G ⧸ S) := Rep.of (A.ρ.quotientToInvarian
variable (k G)
/-- The functor sending a representation to its submodule of invariants. -/
-@[simps! obj_carrier map_hom]
+@[implicit_reducible, simps! obj_carrier map_hom]
noncomputable def invariantsFunctor : Rep.{w} k G ⥤ ModuleCat k where
obj A := ModuleCat.of k A.ρ.invariants
map {A B} f := ModuleCat.ofHom <| (f.hom ∘ₗ A.ρ.invariants.subtype).codRestrict
@@ -258,7 +258,6 @@ instance : (invariantsFunctor k G).PreservesZeroMorphisms where
instance : (invariantsFunctor k G).Additive where
instance : (invariantsFunctor k G).Linear k where
-set_option backward.isDefEq.respectTransparency false in
variable {G} in
/-- Given a normal subgroup S ≤ G, this is the functor sending a `G`-representation `A` to the
`G ⧸ S`-representation it induces on `A^S`. -/
diff --git a/Mathlib/RepresentationTheory/Rep/Basic.lean b/Mathlib/RepresentationTheory/Rep/Basic.lean
index fdbabd2bf828ba..4733606c6e9568 100644
--- a/Mathlib/RepresentationTheory/Rep/Basic.lean
+++ b/Mathlib/RepresentationTheory/Rep/Basic.lean
@@ -436,7 +436,7 @@ end setup
variable (k G) in
/-- The functor equipping a module with the trivial representation. -/
-@[simps! obj_V map_hom]
+@[implicit_reducible, simps! obj_V map_hom]
def trivialFunctor : ModuleCat.{w} k ⥤ Rep.{w} k G where
obj V := trivial k G V
map f := ofHom ⟨f.hom, fun _ ↦ rfl⟩
From 6cc0969466dc9a1043de33485f78471e3aaa1bd8 Mon Sep 17 00:00:00 2001
From: Jack McCarthy <37917934+Deicyde@users.noreply.github.com>
Date: Mon, 29 Jun 2026 05:09:39 +0000
Subject: [PATCH 0417/1300] doc: add wikidata attributes (#40970)
This PR adds a batch of 8 `@[wikidata]` attributes.
Claude helped generate the list of crossrefs (by scanning Wikidata + Mathlib). Comments are generated by [crossref-report](https://github.com/jcommelin/mathlib-crossref-report) and Wikilean.
See https://wikilean.jackmccarthy.org/review?pr=40970 for reviewer UI.
Co-authored-by: wikilean-bot
---
Mathlib/Analysis/InnerProductSpace/PiL2.lean | 1 +
Mathlib/Analysis/Normed/Operator/Compact/Basic.lean | 2 ++
Mathlib/Data/Tree/Basic.lean | 1 +
Mathlib/Geometry/Convex/Cone/Basic.lean | 2 ++
Mathlib/MeasureTheory/Constructions/Polish/Basic.lean | 2 ++
Mathlib/Probability/ConditionalProbability.lean | 2 ++
Mathlib/Probability/Distributions/Exponential.lean | 2 ++
7 files changed, 12 insertions(+)
diff --git a/Mathlib/Analysis/InnerProductSpace/PiL2.lean b/Mathlib/Analysis/InnerProductSpace/PiL2.lean
index d276095cf3211b..a50a9e11c88999 100644
--- a/Mathlib/Analysis/InnerProductSpace/PiL2.lean
+++ b/Mathlib/Analysis/InnerProductSpace/PiL2.lean
@@ -156,6 +156,7 @@ theorem EuclideanSpace.real_norm_sq_eq {n : Type*} [Fintype n] (x : EuclideanSpa
‖x‖ ^ 2 = ∑ i, (x i) ^ 2 := by
simp [EuclideanSpace.norm_sq_eq]
+@[wikidata Q847073]
theorem EuclideanSpace.dist_eq {𝕜 : Type*} [RCLike 𝕜] {n : Type*} [Fintype n]
(x y : EuclideanSpace 𝕜 n) : dist x y = √(∑ i, dist (x i) (y i) ^ 2) :=
PiLp.dist_eq_of_L2 x y
diff --git a/Mathlib/Analysis/Normed/Operator/Compact/Basic.lean b/Mathlib/Analysis/Normed/Operator/Compact/Basic.lean
index 7a8e40734c48b4..9a8ca0e373bacc 100644
--- a/Mathlib/Analysis/Normed/Operator/Compact/Basic.lean
+++ b/Mathlib/Analysis/Normed/Operator/Compact/Basic.lean
@@ -6,6 +6,7 @@ Authors: Anatole Dedecker
module
public import Mathlib.Analysis.LocallyConvex.Bounded
+public import Mathlib.Tactic.CrossRefAttribute
public import Mathlib.Topology.Algebra.Module.Spaces.ContinuousLinearMap
/-!
@@ -66,6 +67,7 @@ but we choose a definition which involves fewer existential quantifiers and repl
with preimages.
We prove the equivalence in `isCompactOperator_iff_exists_mem_nhds_image_subset_compact`. -/
+@[wikidata Q1780743]
def IsCompactOperator {M₁ M₂ : Type*} [Zero M₁] [TopologicalSpace M₁] [TopologicalSpace M₂]
(f : M₁ → M₂) : Prop :=
∃ K, IsCompact K ∧ f ⁻¹' K ∈ (𝓝 0 : Filter M₁)
diff --git a/Mathlib/Data/Tree/Basic.lean b/Mathlib/Data/Tree/Basic.lean
index 993a21a4c1953e..2a510b039f784b 100644
--- a/Mathlib/Data/Tree/Basic.lean
+++ b/Mathlib/Data/Tree/Basic.lean
@@ -6,6 +6,7 @@ Authors: Mario Carneiro, Wojciech Nawrocki
module
public import Mathlib.Data.Nat.Notation
+public import Mathlib.Tactic.CrossRefAttribute
public import Mathlib.Util.CompileInductive
import Batteries.Tactic.Alias
diff --git a/Mathlib/Geometry/Convex/Cone/Basic.lean b/Mathlib/Geometry/Convex/Cone/Basic.lean
index 835f9e94b4093c..71b6c33e5bdce0 100644
--- a/Mathlib/Geometry/Convex/Cone/Basic.lean
+++ b/Mathlib/Geometry/Convex/Cone/Basic.lean
@@ -6,6 +6,7 @@ Authors: Yury Kudryashov, Frédéric Dupuis
module
public import Mathlib.Analysis.Convex.Hull
+public import Mathlib.Tactic.CrossRefAttribute
/-!
# Convex cones
@@ -55,6 +56,7 @@ variable [Semiring R] [PartialOrder R]
variable (R M) in
/-- A convex cone is a subset `s` of an `R`-module such that `a • x + b • y ∈ s` whenever `a, b > 0`
and `x, y ∈ s`. -/
+@[wikidata Q2256541]
structure ConvexCone [AddCommMonoid M] [SMul R M] where
/-- The **carrier set** underlying this cone: the set of points contained in it -/
carrier : Set M
diff --git a/Mathlib/MeasureTheory/Constructions/Polish/Basic.lean b/Mathlib/MeasureTheory/Constructions/Polish/Basic.lean
index ea39c5a407cc7f..c1c595f7b275db 100644
--- a/Mathlib/MeasureTheory/Constructions/Polish/Basic.lean
+++ b/Mathlib/MeasureTheory/Constructions/Polish/Basic.lean
@@ -6,6 +6,7 @@ Authors: Sébastien Gouëzel, Felix Weilacher
module
public import Mathlib.MeasureTheory.Constructions.BorelSpace.Metrizable
+public import Mathlib.Tactic.CrossRefAttribute
public import Mathlib.Topology.MetricSpace.Perfect
public import Mathlib.Topology.Separation.CountableSeparatingOn
@@ -76,6 +77,7 @@ the natural topology in a space is non-Polish.
To endow a standard Borel space `α` with a compatible Polish topology, use
`letI := upgradeStandardBorel α`. One can then use `eq_borel_upgradeStandardBorel α` to
rewrite the `MeasurableSpace α` instance to `borel α t`, where `t` is the new topology. -/
+@[wikidata Q25378068]
class StandardBorelSpace [MeasurableSpace α] : Prop where
/-- There exists a compatible Polish topology. -/
polish : ∃ _ : TopologicalSpace α, BorelSpace α ∧ PolishSpace α
diff --git a/Mathlib/Probability/ConditionalProbability.lean b/Mathlib/Probability/ConditionalProbability.lean
index ce97e0f5c8dae4..75bba5fe9df894 100644
--- a/Mathlib/Probability/ConditionalProbability.lean
+++ b/Mathlib/Probability/ConditionalProbability.lean
@@ -6,6 +6,7 @@ Authors: Rishikesh Vaishnav
module
public import Mathlib.MeasureTheory.Measure.Typeclasses.Probability
+public import Mathlib.Tactic.CrossRefAttribute
/-!
# Conditional Probability
@@ -71,6 +72,7 @@ variable (μ) in
/-- The conditional probability measure of measure `μ` on set `s` is `μ` restricted to `s`
and scaled by the inverse of `μ s` (to make it a probability measure):
`(μ s)⁻¹ • μ.restrict s`. -/
+@[wikidata Q327069]
def cond (s : Set Ω) : Measure Ω :=
(μ s)⁻¹ • μ.restrict s
diff --git a/Mathlib/Probability/Distributions/Exponential.lean b/Mathlib/Probability/Distributions/Exponential.lean
index 5f03844c8318b2..20f6e8d2b2bedc 100644
--- a/Mathlib/Probability/Distributions/Exponential.lean
+++ b/Mathlib/Probability/Distributions/Exponential.lean
@@ -7,6 +7,7 @@ module
public import Mathlib.Probability.CDF
public import Mathlib.Probability.Distributions.Gamma
+public import Mathlib.Tactic.CrossRefAttribute
/-! # Exponential distributions over ℝ
@@ -90,6 +91,7 @@ end ExponentialPDF
open MeasureTheory
/-- Measure defined by the exponential distribution -/
+@[wikidata Q237193]
noncomputable
def expMeasure (r : ℝ) : Measure ℝ := gammaMeasure 1 r
From 41066cdc724cae81659908b18e47b58879c034c7 Mon Sep 17 00:00:00 2001
From: Seewoo Lee <49933279+seewoo5@users.noreply.github.com>
Date: Mon, 29 Jun 2026 08:46:34 +0000
Subject: [PATCH 0418/1300] feat(ModularForms): SL2 action and Serre derivative
(#36963)
---
.../Complex/UpperHalfPlane/Manifold.lean | 14 +++
.../NumberTheory/ModularForms/Derivative.lean | 87 ++++++++++++++++++-
2 files changed, 97 insertions(+), 4 deletions(-)
diff --git a/Mathlib/Analysis/Complex/UpperHalfPlane/Manifold.lean b/Mathlib/Analysis/Complex/UpperHalfPlane/Manifold.lean
index 2585e242b97ebe..44e3e2b3ed77f9 100644
--- a/Mathlib/Analysis/Complex/UpperHalfPlane/Manifold.lean
+++ b/Mathlib/Analysis/Complex/UpperHalfPlane/Manifold.lean
@@ -158,6 +158,20 @@ TODO(MR): investigate if using `mvfderiv` can avoid the "pain" above, and be a c
section Complex
+/-- Derivative of `z ↦ (denom g z) ^ k`: $\frac{d}{dz}[(cz+d)^k] = k \cdot c \cdot (cz+d)^{k-1}$. -/
+lemma hasDerivAt_denom_zpow (g : GL (Fin 2) ℝ) (k : ℤ) (τ : ℍ) :
+ HasDerivAt (fun z ↦ denom g z ^ k) (k * g 1 0 * denom g τ ^ (k - 1)) τ := by
+ have hd : HasDerivAt (denom g ·) (g 1 0) τ := by
+ simpa [denom] using hasDerivAt_id _ |>.const_mul _ |>.add_const (g 1 1 : ℂ)
+ have := (hasDerivAt_zpow k (denom g τ) (Or.inl (denom_ne_zero g τ))).comp _ hd
+ simpa only [Function.comp_def, mul_right_comm] using this
+
+/-- Derivative of `z ↦ (denom g z) ^ k`:
+$\frac{d}{dz}[(cz+d)^k] = k \cdot c \cdot (cz+d)^{k-1}$. -/
+lemma deriv_denom_zpow (g : GL (Fin 2) ℝ) (k : ℤ) (τ : ℍ) :
+ deriv (fun z ↦ denom g z ^ k) τ = k * g 1 0 * denom g τ ^ (k - 1) :=
+ (hasDerivAt_denom_zpow g k τ).deriv
+
lemma hasStrictDerivAt_smul {g : GL (Fin 2) ℝ} (hg : 0 < g.val.det) (τ : ℍ) :
HasStrictDerivAt (fun z ↦ ↑(g • ofComplex z) : ℂ → ℂ) (g.val.det / denom g τ ^ 2) τ := by
suffices HasStrictDerivAt (num g / denom g) (g.val.det / denom g τ ^ 2) τ by
diff --git a/Mathlib/NumberTheory/ModularForms/Derivative.lean b/Mathlib/NumberTheory/ModularForms/Derivative.lean
index e7f4df6c11143a..4fca1821ea6dde 100644
--- a/Mathlib/NumberTheory/ModularForms/Derivative.lean
+++ b/Mathlib/NumberTheory/ModularForms/Derivative.lean
@@ -6,12 +6,19 @@ Authors: Seewoo Lee
module
public import Mathlib.NumberTheory.ModularForms.EisensteinSeries.E2.MDifferentiable
+public import Mathlib.NumberTheory.ModularForms.EisensteinSeries.E2.Transform
/-!
# Derivatives of modular forms
This file defines normalized derivative $D = \frac{1}{2\pi i} \frac{d}{dz}$
-and serre dervative $\partial_k := D - \frac{k}{12} E_2$ of modular forms.
+and (Ramanujan-)Serre derivative $\partial_k := D - \frac{k}{12} E_2$ of modular forms.
+
+## Main Definitions and Theorems
+
+- `normalizedDerivOfComplex`: $D = \frac{1}{2\pi i} \frac{d}{dz}$
+- `serreDerivative`: $\partial_k F := D F - \frac{k}{12} E_2 F$
+- `serreDerivative_slash_equivariant`: Serre derivative is equivariant under the slash action.
TODO:
- Serre derivative preserves modularity, i.e. $\partial_k (M_k) \subseteq M_{k+2}$.
@@ -21,7 +28,7 @@ TODO:
open UpperHalfPlane hiding I
open Real Complex
-open scoped Manifold
+open scoped Manifold MatrixGroups ModularForm Topology
namespace Derivative
@@ -151,8 +158,8 @@ theorem serreDerivative_smul (k : ℂ) (c : ℂ) (F : ℍ → ℂ) (hF : MDiff F
ring_nf
theorem serreDerivative_mul (k₁ k₂ : ℂ) (F G : ℍ → ℂ) (hF : MDiff F) (hG : MDiff G) :
- serreDerivative (k₁ + k₂) (F * G) = (serreDerivative k₁ F) * G + F * (serreDerivative k₂ G)
- := by
+ serreDerivative (k₁ + k₂) (F * G) =
+ (serreDerivative k₁ F) * G + F * (serreDerivative k₂ G) := by
ext z
simp [serreDerivative, normalizedDerivOfComplex_mul F G hF hG]
ring_nf
@@ -169,6 +176,78 @@ theorem serreDerivative_mdifferentiable {F : ℍ → ℂ} (k : ℂ) (hF : MDiff
MDiff (fun z ↦ (k * 12⁻¹) * (EisensteinSeries.E2 z * F z)))
simp [Pi.mul_apply, mul_assoc, mul_left_comm, mul_comm]
+open ModularGroup
+
+/-- How `D` interacts with the slash action. -/
+lemma normalizedDerivOfComplex_slash {k : ℤ} {F : ℍ → ℂ} (hF : MDiff F)
+ {g : GL (Fin 2) ℝ} (hg : 0 < g.val.det) :
+ D (F ∣[k] g) = fun z : ℍ ↦ (g.val.det : ℂ)⁻¹ * (D F ∣[k + 2] g) z -
+ (k : ℂ) * (2 * π * I)⁻¹ * (g 1 0 / denom g z) * (F ∣[k] g) z := by
+ have hdet : g.det.val = g.val.det := Matrix.GeneralLinearGroup.val_det_apply g
+ have hdetℂ : (g.val.det : ℂ) ≠ 0 := Complex.ofReal_ne_zero.mpr hg.ne'
+ have hσ (x) : σ g x = x := by grind [σ, ContinuousAlgEquiv.refl_apply]
+ ext z
+ simp only [normalizedDerivOfComplex, ModularForm.slash_apply]
+ have hz := denom_ne_zero g z
+ have h_smul : HasDerivAt (fun w ↦ ↑(g • ofComplex w) : ℂ → ℂ)
+ ((g.val.det : ℂ) / denom g z ^ 2) ↑z := (hasStrictDerivAt_smul hg z).hasDerivAt
+ have h_F : HasDerivAt (F ∘ ofComplex) (deriv (F ∘ ofComplex) ↑(g • ofComplex (z : ℂ)))
+ ↑(g • ofComplex (z : ℂ)) :=
+ (ofComplex_apply z).symm ▸ (mdifferentiableAt_iff.mp (hF (g • z))).hasDerivAt
+ have h_denom : HasDerivAt (fun w ↦ (denom g w) ^ (-k))
+ (-k * (g 1 0 : ℂ) * (denom g z) ^ (-k - 1)) ↑z := by
+ simpa using hasDerivAt_denom_zpow g (-k) z
+ have hcomp : ((F ∣[k] g) ∘ ofComplex) =ᶠ[𝓝 ↑z]
+ fun w ↦ (g.val.det : ℂ) ^ (k - 1) *
+ ((F ∘ ofComplex) ↑(g • ofComplex w) * (denom g w) ^ (-k)) := by
+ filter_upwards [isOpen_upperHalfPlaneSet.mem_nhds z.im_pos] with w hw
+ grind [ofComplex_apply_of_im_pos, ofComplex_apply, ModularForm.slash_apply]
+ rw [((((h_F.comp (z : ℂ) h_smul).mul h_denom).const_mul _).congr_of_eventuallyEq hcomp).deriv]
+ simp only [hσ, hdet, abs_of_pos hg, ofComplex_apply, Function.comp_apply]
+ rw [show k + 2 - 1 = (k - 1) + 2 by ring, show -(k + 2) = -k + -2 by ring,
+ zpow_add₀ hdetℂ, zpow_add₀ hz, zpow_sub_one₀ hz]
+ field
+
+/-- The `SL(2, ℤ)` case of `normalizedDerivOfComplex_slash`, where the determinant factor is `1`. -/
+lemma normalizedDerivOfComplex_SL_slash {k : ℤ} {F : ℍ → ℂ} (hF : MDiff F) {γ : SL(2, ℤ)} :
+ D (F ∣[k] γ) = (D F ∣[k + 2] γ) -
+ (fun z : ℍ ↦ (k : ℂ) * (2 * π * I)⁻¹ * (γ 1 0 / denom γ z) * (F ∣[k] γ) z) := by
+ have hdet : (γ : GL (Fin 2) ℝ).val.det = 1 := by
+ rw [← Matrix.GeneralLinearGroup.val_det_apply]; simp
+ ext z
+ have := congrFun
+ (normalizedDerivOfComplex_slash (k := k) hF (g := (γ : GL (Fin 2) ℝ)) (by grind)) z
+ rw [hdet] at this
+ simpa [ModularForm.SL_slash] using this
+
+/--
+Serre derivative is equivariant under the slash action. More precisely,
+$\partial_k (F ∣[k] γ) = (\partial_k F) ∣[k + 2] \gamma$ for all $\gamma \in SL(2, \mathbb{Z})$.
+-/
+theorem serreDerivative_slash_equivariant {k : ℤ} {F : ℍ → ℂ} (hF : MDiff F) {γ : SL(2, ℤ)} :
+ serreDerivative k F ∣[k + 2] γ = serreDerivative k (F ∣[k] γ) := by
+ ext z
+ have hLHS : (serreDerivative (k : ℂ) F ∣[k + 2] γ) z =
+ (D F ∣[k + 2] γ) z - ↑k * 12⁻¹ * ((EisensteinSeries.E2 ∣[(2 : ℤ)] γ) z * (F ∣[k] γ) z) := by
+ grind [ModularForm.SL_slash_apply, serreDerivative_apply, Pi.mul_apply,
+ congrFun (ModularForm.mul_slash_SL2 2 k γ EisensteinSeries.E2 F) z]
+ have hDz : (D (F ∣[k] γ)) z = (D F ∣[k + 2] γ) z -
+ (k * (2 * π * I)⁻¹ * (γ 1 0 / denom γ z) * (F ∣[k] γ) z) := by
+ simp [normalizedDerivOfComplex_SL_slash hF]
+ have hE2z : (EisensteinSeries.E2 ∣[(2 : ℤ)] γ) z =
+ EisensteinSeries.E2 z - 1 / (2 * riemannZeta 2) * EisensteinSeries.D2 γ z := by
+ simp [EisensteinSeries.E2_slash_action]
+ grind [serreDerivative_apply, EisensteinSeries.D2, riemannZeta_two, I_sq]
+
+/--
+As a corollary, if `F` is invariant under the slash action of weight `k`, then
+`serreDerivative k F` is invariant under the slash action of weight `k + 2`.
+-/
+theorem serreDerivative_slash_invariant {k : ℤ} {F : ℍ → ℂ} (hF : MDiff F) {γ : SL(2, ℤ)}
+ (h : F ∣[k] γ = F) :
+ serreDerivative k F ∣[k + 2] γ = serreDerivative k F := by
+ grind [serreDerivative_slash_equivariant]
+
end
end Derivative
From e752928d1223d1202d969b623a6f27cc79866e9c Mon Sep 17 00:00:00 2001
From: Yongle Hu
Date: Mon, 29 Jun 2026 08:56:20 +0000
Subject: [PATCH 0419/1300] feat(RingTheory): UFD criteria via height `1` prime
ideals and localization (#36739)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
We prove the following UFD criteria via height `1` prime ideals and localization:
1. Let `R` be a Noetherian domain. Then `R` is a UFD if and only if every height `1` prime ideal is principal.
2. Let `R` be a Noetherian domain, `x ∈ R` be a prime element. If `Rₓ` is a UFD, then `R` is also a UFD.
Co-authored-by: Thmoas-Guan <150537269+Thmoas-Guan@users.noreply.github.com>
---
Mathlib.lean | 2 +
Mathlib/RingTheory/Ideal/Height.lean | 28 ++++-
Mathlib/RingTheory/Ideal/Maximal.lean | 3 +
Mathlib/RingTheory/Ideal/UFD.lean | 112 ++++++++++++++++++
.../RingTheory/Localization/Away/Basic.lean | 10 +-
.../RingTheory/Localization/Away/Lemmas.lean | 24 ++++
.../UniqueFactorizationDomain/Defs.lean | 8 ++
.../Localization.lean | 50 ++++++++
8 files changed, 235 insertions(+), 2 deletions(-)
create mode 100644 Mathlib/RingTheory/Ideal/UFD.lean
create mode 100644 Mathlib/RingTheory/UniqueFactorizationDomain/Localization.lean
diff --git a/Mathlib.lean b/Mathlib.lean
index a8fa4a3ffd3f6d..7d538bd87f4316 100644
--- a/Mathlib.lean
+++ b/Mathlib.lean
@@ -6670,6 +6670,7 @@ public import Mathlib.RingTheory.Ideal.Quotient.Operations
public import Mathlib.RingTheory.Ideal.Quotient.Over
public import Mathlib.RingTheory.Ideal.Quotient.PowTransition
public import Mathlib.RingTheory.Ideal.Span
+public import Mathlib.RingTheory.Ideal.UFD
public import Mathlib.RingTheory.IdealFilter.Basic
public import Mathlib.RingTheory.IdealFilter.Topology
public import Mathlib.RingTheory.Idempotents
@@ -7054,6 +7055,7 @@ public import Mathlib.RingTheory.UniqueFactorizationDomain.Finsupp
public import Mathlib.RingTheory.UniqueFactorizationDomain.GCDMonoid
public import Mathlib.RingTheory.UniqueFactorizationDomain.Ideal
public import Mathlib.RingTheory.UniqueFactorizationDomain.Kaplansky
+public import Mathlib.RingTheory.UniqueFactorizationDomain.Localization
public import Mathlib.RingTheory.UniqueFactorizationDomain.Moebius
public import Mathlib.RingTheory.UniqueFactorizationDomain.Multiplicative
public import Mathlib.RingTheory.UniqueFactorizationDomain.Multiplicity
diff --git a/Mathlib/RingTheory/Ideal/Height.lean b/Mathlib/RingTheory/Ideal/Height.lean
index 457a89a3fc05a2..6fc30fe2b79b4e 100644
--- a/Mathlib/RingTheory/Ideal/Height.lean
+++ b/Mathlib/RingTheory/Ideal/Height.lean
@@ -6,7 +6,6 @@ Authors: Wanyi He, Jiedong Jiang, Jingting Wang, Andrew Yang, Shouxin Zhang
module
public import Mathlib.Algebra.Module.SpanRank
-public import Mathlib.RingTheory.Ideal.MinimalPrime.Localization
public import Mathlib.RingTheory.Ideal.MinimalPrime.Noetherian
public import Mathlib.RingTheory.Spectrum.Prime.Topology
@@ -50,6 +49,12 @@ lemma Ideal.height_eq_inf_minimalPrimes : I.height = ⨅ J ∈ I.minimalPrimes,
have := hp.isPrime
exact (Ideal.height_eq_primeHeight _).symm
+lemma Ideal.exists_isPrime_height_eq {I : Ideal R} {n : ℕ} (hI : I.height = n) :
+ ∃ (p : Ideal R) (_ : p.IsPrime) (_ : I ≤ p), p.height = n := by
+ simp only [Ideal.height, ENat.iInf_eq_coe_iff] at hI
+ rcases hI with ⟨⟨p, ⟨⟨⟨hpp, hIp⟩, _⟩, h⟩, -⟩, -⟩
+ exact ⟨p, hpp, hIp, h ▸ p.height_eq_primeHeight⟩
+
/-- An ideal has finite height if it is either the unit ideal or its height is finite.
We include the unit ideal in order to have the instance `IsNoetherianRing R → FiniteHeight I`. -/
@[mk_iff]
@@ -247,6 +252,17 @@ lemma Ideal.height_bot [Nontrivial R] : (⊥ : Ideal R).height = 0 := by
simp only [ENat.iInf_eq_zero]
refine ⟨p, hp, haveI := hp.isPrime; height_eq_zero_iff.mpr hp⟩
+@[simp]
+lemma Ideal.height_eq_zero_iff_eq_bot [IsDomain R] {I : Ideal R} : I.height = 0 ↔ I = ⊥ := by
+ refine ⟨fun hI ↦ ?_, fun hI0 ↦ by simp [hI0]⟩
+ rcases exists_isPrime_height_eq hI with ⟨p, _, hIp, hp0⟩
+ rw [CharP.cast_eq_zero, height_eq_zero_iff, IsDomain.minimalPrimes_eq_singleton_bot,
+ Set.mem_singleton_iff] at hp0
+ exact bot_unique (hIp.trans_eq hp0)
+
+theorem Ideal.ne_bot_of_height_eq_one [IsDomain R] {I : Ideal R} (h : I.height = 1) : I ≠ ⊥ :=
+ I.height_eq_zero_iff_eq_bot.not.mp (ne_zero_of_eq_one h)
+
/-- In a trivial commutative ring, the height of any ideal is `∞`. -/
@[simp, nontriviality]
lemma Ideal.height_of_subsingleton [Subsingleton R] : I.height = ⊤ := by
@@ -542,3 +558,13 @@ lemma Ring.krullDimLE_of_isLocalization_maximal {n : ℕ}
exact h P
end isLocalization
+
+lemma Ideal.eq_span_singleton_of_height_eq_one [IsDomain R] {p : Ideal R} [p.IsPrime]
+ (h1 : p.height = 1) {x : R} (hx : x ∈ p) (hxp : Prime x) : p = span {x} := by
+ have : (span {x}).IsPrime := by simp [span_singleton_prime hxp.ne_zero, hxp]
+ have : p.FiniteHeight := by simp [p.finiteHeight_iff, h1]
+ by_contra! hne
+ apply hxp.ne_zero
+ rw [← span_singleton_eq_bot, ← height_eq_zero_iff_eq_bot, ← Order.lt_one_iff, ← h1]
+ refine height_strict_mono_of_isPrime_of_isPrime (lt_of_le_of_ne ?_ hne.symm)
+ simp only [p.span_singleton_le_iff_mem, hx]
diff --git a/Mathlib/RingTheory/Ideal/Maximal.lean b/Mathlib/RingTheory/Ideal/Maximal.lean
index c6cae02a1be57a..f4cfefe052d189 100644
--- a/Mathlib/RingTheory/Ideal/Maximal.lean
+++ b/Mathlib/RingTheory/Ideal/Maximal.lean
@@ -148,6 +148,9 @@ variable [CommSemiring α] (I : Ideal α)
theorem span_singleton_prime {p : α} (hp : p ≠ 0) : IsPrime (span ({p} : Set α)) ↔ Prime p := by
simp [isPrime_iff, Prime, span_singleton_eq_top, hp, mem_span_singleton]
+theorem isPrime_span_singleton_of_prime {p : α} (hp : Prime p) : (span {p}).IsPrime := by
+ simp [Ideal.span_singleton_prime hp.ne_zero, hp]
+
theorem IsMaximal.isPrime {I : Ideal α} (H : I.IsMaximal) : I.IsPrime :=
⟨H.1.1, @fun x y hxy =>
or_iff_not_imp_left.2 fun hx => by
diff --git a/Mathlib/RingTheory/Ideal/UFD.lean b/Mathlib/RingTheory/Ideal/UFD.lean
new file mode 100644
index 00000000000000..5581830415eec3
--- /dev/null
+++ b/Mathlib/RingTheory/Ideal/UFD.lean
@@ -0,0 +1,112 @@
+/-
+Copyright (c) 2026 Yongle Hu. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Yongle Hu
+-/
+module
+
+public import Mathlib.RingTheory.Ideal.KrullsHeightTheorem
+public import Mathlib.RingTheory.Localization.Away.Lemmas
+public import Mathlib.RingTheory.UniqueFactorizationDomain.Localization
+
+/-!
+# UFD criteria via height `1` prime ideals and localization
+
+## Main results
+* `UniqueFactorizationMonoid.iff_forall_isPrincipal_of_height_eq_one` : Let `R` be a
+ Noetherian domain. Then `R` is a UFD if and only if every height `1` prime ideal is principal.
+
+* `UniqueFactorizationMonoid.iff_localizationAway_of_prime` : Let `R` be a Noetherian domain,
+ `x ∈ R` be a prime element. Then `R` is a UFD if and only if `Rₓ` is a UFD.
+-/
+
+public section
+
+variable {R : Type*} [CommRing R] [IsDomain R]
+
+namespace Ideal
+
+variable [WfDvdMonoid R] {x : R} (hx : Prime x) {p : Ideal R} [p.IsPrime] (hxp : x ∉ p)
+
+include hx hxp
+
+theorem isPrincipal_of_isPrincipal_isLocalizationAway_of_prime
+ (S : Type*) [CommRing S] [Algebra R S] [IsLocalization.Away x S]
+ (hp : (map (algebraMap R S) p).IsPrincipal) : p.IsPrincipal := by
+ have := (disjoint_powers_iff_notMem_of_isPrime x).mpr hxp
+ by_cases hpbot : p = ⊥
+ · simp [hpbot, bot_isPrincipal]
+ · have hi := IsLocalization.injective S (powers_le_nonZeroDivisors_of_noZeroDivisors hx.ne_zero)
+ have hpb : map (algebraMap R S) p ≠ ⊥ := by simp [Ideal.map_eq_bot_iff_of_injective hi, hpbot]
+ obtain ⟨g, hg⟩ := hp
+ have hg0 : g ≠ 0 := fun hg0 ↦ hpb <| by simp [hg0, hg]
+ obtain ⟨a, n, hxa, hag⟩ := exists_reduced_fraction' x S hg0 hx.irreducible
+ have hu : IsUnit (selfZPow x S n) :=
+ IsUnit.of_mul_eq_one (selfZPow x S (- n)) (selfZPow_mul_neg x S n)
+ refine ⟨a, Ideal.eq_of_map_algebraMap_le S x ?_ (by simp [IsPrime.mul_mem_left_iff hxp]) ?_⟩
+ · simp [hg, map_span, ← span_singleton_mul_left_unit hu (algebraMap R S a), hag]
+ · intro y hy
+ rw [mem_span_singleton] at hy ⊢
+ exact (hx.left_dvd_or_dvd_right_of_dvd_mul hy).resolve_left hxa
+
+theorem isPrincipal_of_isPrincipal_localizationAway_of_prime
+ (hp : (map (algebraMap R (Localization.Away x)) p).IsPrincipal) : p.IsPrincipal :=
+ p.isPrincipal_of_isPrincipal_isLocalizationAway_of_prime hx hxp (Localization.Away x) hp
+
+end Ideal
+
+namespace UniqueFactorizationMonoid
+
+theorem isPrincipal_of_height_eq_one [UniqueFactorizationMonoid R]
+ {p : Ideal R} [p.IsPrime] (hph : p.height = 1) : p.IsPrincipal := by
+ have hpn : p ≠ ⊥ := p.ne_bot_of_height_eq_one hph
+ obtain ⟨x, hxmem, hxp⟩ := Ideal.IsPrime.exists_mem_prime_of_ne_bot ‹_› hpn
+ exact ⟨x, p.eq_span_singleton_of_height_eq_one hph hxmem hxp⟩
+
+variable [IsNoetherianRing R]
+
+theorem of_forall_isPrincipal_of_height_eq_one
+ (h : ∀ (p : Ideal R) [p.IsPrime], p.height = 1 → p.IsPrincipal) :
+ UniqueFactorizationMonoid R := by
+ rw [iff_exists_prime_mem_of_isPrime]
+ intro I hIn _
+ rcases I.ne_bot_iff.mp hIn with ⟨x, hxI, hx0⟩
+ rcases Ideal.exists_minimalPrimes_le (I.span_singleton_le_iff_mem.mpr hxI) with ⟨p, hpmin, hpl⟩
+ have : p.IsPrime := hpmin.isPrime
+ have hpn : p ≠ ⊥ := fun hpb ↦ hx0 <|
+ Ideal.span_singleton_eq_bot.mp <| bot_unique (hpmin.le.trans_eq hpb)
+ have hpp : p.IsPrincipal := h p <| le_antisymm
+ (Ideal.height_le_one_of_isPrincipal_of_mem_minimalPrimes _ p hpmin)
+ (by simpa [Order.one_le_iff_ne_zero])
+ exact ⟨hpp.generator p, hpl (hpp.generator_mem p), hpp.prime_generator_of_isPrime p hpn⟩
+
+/-- Let `R` be a Noetherian domain. Then `R` is a UFD if and only if every height `1` prime ideal is
+ principal. -/
+@[stacks 0AFT]
+theorem iff_forall_isPrincipal_of_height_eq_one :
+ UniqueFactorizationMonoid R ↔ ∀ (p : Ideal R) [p.IsPrime], p.height = 1 → p.IsPrincipal :=
+ ⟨fun _ _ _ ↦ isPrincipal_of_height_eq_one, of_forall_isPrincipal_of_height_eq_one⟩
+
+theorem iff_of_isLocalizationAway_of_prime {x : R} (hx : Prime x)
+ (S : Type*) [CommRing S] [Algebra R S] [IsLocalization.Away x S] :
+ UniqueFactorizationMonoid R ↔ UniqueFactorizationMonoid S := by
+ have : IsDomain S := IsLocalization.Away.isDomain S hx.ne_zero
+ refine ⟨fun _ ↦ of_isLocalization (Submonoid.powers x) S, fun _ ↦ ?_⟩
+ rw [iff_forall_isPrincipal_of_height_eq_one]
+ intro p hp h1
+ by_cases hxp : x ∈ p
+ · exact ⟨x, p.eq_span_singleton_of_height_eq_one h1 hxp hx⟩
+ · have hd := by rwa [← Ideal.disjoint_powers_iff_notMem_of_isPrime x] at hxp
+ have := IsLocalization.isPrime_of_isPrime_disjoint (Submonoid.powers x) S p hp hd
+ refine p.isPrincipal_of_isPrincipal_isLocalizationAway_of_prime hx hxp S
+ (isPrincipal_of_height_eq_one ?_)
+ rw [← IsLocalization.height_under (Submonoid.powers x),
+ IsLocalization.under_map_of_isPrime_disjoint (Submonoid.powers x) S hp hd, h1]
+
+/-- Let `R` be a Noetherian domain, `x ∈ R` be a prime element. Then `R` is a UFD if and only if
+ `Rₓ` is a UFD. -/
+theorem iff_localizationAway_of_prime {x : R} (hx : Prime x) :
+ UniqueFactorizationMonoid R ↔ UniqueFactorizationMonoid (Localization.Away x) :=
+ iff_of_isLocalizationAway_of_prime hx (Localization.Away x)
+
+end UniqueFactorizationMonoid
diff --git a/Mathlib/RingTheory/Localization/Away/Basic.lean b/Mathlib/RingTheory/Localization/Away/Basic.lean
index e6c6f68d598a64..2a0c932cff60c2 100644
--- a/Mathlib/RingTheory/Localization/Away/Basic.lean
+++ b/Mathlib/RingTheory/Localization/Away/Basic.lean
@@ -5,8 +5,8 @@ Authors: Kenny Lau, Mario Carneiro, Johan Commelin, Amelia Livingston, Anne Baan
-/
module
-public import Mathlib.GroupTheory.MonoidLocalization.Away
public import Mathlib.Algebra.Algebra.Pi
+public import Mathlib.GroupTheory.MonoidLocalization.Away
public import Mathlib.RingTheory.Ideal.Maps
public import Mathlib.RingTheory.Localization.Basic
public import Mathlib.RingTheory.UniqueFactorizationDomain.Multiplicity
@@ -310,6 +310,10 @@ lemma commutes {R : Type*} [CommSemiring R] (S₁ S₂ T : Type*) [CommSemiring
ext x
simp
+theorem isDomain [IsDomain R] {x : R} (hx : x ≠ 0) [IsLocalization.Away x S] : IsDomain S :=
+ IsLocalization.isDomain_of_le_nonZeroDivisors S
+ (powers_le_nonZeroDivisors_of_noZeroDivisors hx)
+
end Away
end Away
@@ -590,6 +594,10 @@ theorem existsUnique_algebraMap_eq_of_span_eq_top (s : Set R) (span_eq : Ideal.s
simp_rw [← map_pow, eq, ← map_mul, Finset.sum_mul, mul_assoc, eq2 _ a, mul_left_comm (c _),
← Finset.mul_sum, ← smul_eq_mul (a := c _), eq1, mul_one]
+/-- If `x ≠ 0`, then the localization of a domain away from `x` is again a domain. -/
+theorem Away.isDomain [IsDomain R] {x : R} (hx : x ≠ 0) : IsDomain (Localization.Away x) :=
+ IsLocalization.Away.isDomain (Localization.Away x) hx
+
end Localization
end CommSemiring
diff --git a/Mathlib/RingTheory/Localization/Away/Lemmas.lean b/Mathlib/RingTheory/Localization/Away/Lemmas.lean
index 70a7c32959e6a2..36c6b0d1ac3b95 100644
--- a/Mathlib/RingTheory/Localization/Away/Lemmas.lean
+++ b/Mathlib/RingTheory/Localization/Away/Lemmas.lean
@@ -68,3 +68,27 @@ lemma quotient_of_isIdempotentElem {e : R} (he : IsIdempotentElem e) :
away_of_isIdempotentElem he Ideal.mk_ker Quotient.mk_surjective
end IsLocalization.Away
+
+section saturated
+
+variable {R : Type*} (S : Type*) [CommSemiring R] [CommSemiring S]
+ [Algebra R S] (x : R) [IsLocalization.Away x S] {I J : Ideal R}
+
+lemma Ideal.le_of_map_algebraMap_le (hle : I.map (algebraMap R S) ≤ J.map (algebraMap R S))
+ (hxJ : ∀ y : R, x * y ∈ J → y ∈ J) : I ≤ J := by
+ intro y hy
+ have hin : algebraMap R S y ∈ I.map (algebraMap R S) := Ideal.mem_map_of_mem (algebraMap R S) hy
+ grw [hle, IsLocalization.algebraMap_mem_map_algebraMap_iff (Submonoid.powers x)] at hin
+ obtain ⟨m, ⟨n, hn, rfl⟩, h⟩ := hin
+ dsimp at h
+ induction n with
+ | zero => simpa using h
+ | succ n ih =>
+ rw [add_comm, pow_add, pow_one, mul_assoc] at h
+ exact ih <| hxJ _ h
+
+lemma Ideal.eq_of_map_algebraMap_le (heq : I.map (algebraMap R S) = J.map (algebraMap R S))
+ (hxI : ∀ y : R, x * y ∈ I → y ∈ I) (hxJ : ∀ y : R, x * y ∈ J → y ∈ J) : I = J :=
+ le_antisymm (le_of_map_algebraMap_le S x heq.le hxJ) (le_of_map_algebraMap_le S x heq.ge hxI)
+
+end saturated
diff --git a/Mathlib/RingTheory/UniqueFactorizationDomain/Defs.lean b/Mathlib/RingTheory/UniqueFactorizationDomain/Defs.lean
index 3da613e7b67574..68c3a1a67d99de 100644
--- a/Mathlib/RingTheory/UniqueFactorizationDomain/Defs.lean
+++ b/Mathlib/RingTheory/UniqueFactorizationDomain/Defs.lean
@@ -174,6 +174,14 @@ end UniqueFactorizationMonoid
namespace UniqueFactorizationMonoid
variable [CommMonoidWithZero α]
+
+variable (α) in
+theorem of_subsingleton [Subsingleton α] : UniqueFactorizationMonoid α where
+ mul_left_cancel_of_ne_zero _ a b _ := Subsingleton.elim a b
+ mul_right_cancel_of_ne_zero _ a b _ := Subsingleton.elim a b
+ wf := ⟨fun a ↦ Acc.intro a fun b ⟨hb, _⟩ ↦ (hb (Subsingleton.elim b 0)).elim⟩
+ irreducible_iff_prime {a} := by simp [Subsingleton.elim a 0]
+
variable [UniqueFactorizationMonoid α]
open Classical in
diff --git a/Mathlib/RingTheory/UniqueFactorizationDomain/Localization.lean b/Mathlib/RingTheory/UniqueFactorizationDomain/Localization.lean
new file mode 100644
index 00000000000000..1c4b0fae487db3
--- /dev/null
+++ b/Mathlib/RingTheory/UniqueFactorizationDomain/Localization.lean
@@ -0,0 +1,50 @@
+/-
+Copyright (c) 2026 Yongle Hu. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Yongle Hu
+-/
+module
+
+public import Mathlib.RingTheory.Localization.Ideal
+public import Mathlib.RingTheory.UniqueFactorizationDomain.Kaplansky
+
+/-!
+# Localization of a UFD
+
+## Main results
+* `UniqueFactorizationMonoid.localization` : The localization of a UFD is still a UFD.
+-/
+
+public section
+
+namespace UniqueFactorizationMonoid
+
+variable {R : Type*} [CommRing R] [UniqueFactorizationMonoid R] [IsDomain R]
+
+/-- If `S` is the localization of a UFD `R`, then `S` is also a UFD. -/
+theorem of_isLocalization (M : Submonoid R)
+ (S : Type*) [CommRing S] [Algebra R S] [IsLocalization M S] : UniqueFactorizationMonoid S := by
+ by_cases h0 : 0 ∈ M
+ · have : Subsingleton S := IsLocalization.subsingleton h0
+ exact of_subsingleton S
+ have hM : M ≤ nonZeroDivisors R := le_nonZeroDivisors_of_noZeroDivisors h0
+ have : IsDomain S := IsLocalization.isDomain_of_le_nonZeroDivisors S hM
+ rw [UniqueFactorizationMonoid.iff_exists_prime_mem_of_isPrime]
+ intro p hpb _
+ obtain ⟨x, hxp, hpx⟩ := Ideal.IsPrime.exists_mem_prime_of_ne_bot
+ inferInstance (IsLocalization.bot_lt_under_prime M S hM p hpb).ne'
+ use algebraMap R S x, hxp
+ rw [← Ideal.span_singleton_prime]
+ · rw [← Set.image_singleton, ← Ideal.map_span]
+ refine IsLocalization.isPrime_of_isPrime_disjoint M S _
+ (Ideal.isPrime_span_singleton_of_prime hpx) ?_
+ rw [← IsLocalization.map_algebraMap_ne_top_iff_disjoint M S]
+ intro h
+ exact Ideal.IsPrime.ne_top' (top_unique (h.symm.trans_le (by simpa [Ideal.map_span] using hxp)))
+ · simp [map_ne_zero_iff _ (IsLocalization.injective S hM), hpx.ne_zero]
+
+/-- The localization of a UFD is still a UFD. -/
+instance localization (M : Submonoid R) : UniqueFactorizationMonoid (Localization M) :=
+ of_isLocalization M (Localization M)
+
+end UniqueFactorizationMonoid
From 8fe98ac2574a5edbc45dfce69f098e2189699d1d Mon Sep 17 00:00:00 2001
From: William Coram