Skip to content

Commit 1db185c

Browse files
ocfnashJonBannon
authored andcommitted
feat: define the index of a linear map (leanprover-community#39930)
And add some basic API. Written at the ICERM workshop "Techniques and Tools for the Formalization of Analysis". Co-authored-by: Jon Bannon <jbannon@siena.edu>
1 parent 0dea9c1 commit 1db185c

22 files changed

Lines changed: 303 additions & 18 deletions

File tree

Mathlib.lean

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,8 @@ public import Mathlib.Algebra.EuclideanDomain.Basic
303303
public import Mathlib.Algebra.EuclideanDomain.Defs
304304
public import Mathlib.Algebra.EuclideanDomain.Field
305305
public import Mathlib.Algebra.EuclideanDomain.Int
306-
public import Mathlib.Algebra.Exact
306+
public import Mathlib.Algebra.Exact.Basic
307+
public import Mathlib.Algebra.Exact.Sequence
307308
public import Mathlib.Algebra.Expr
308309
public import Mathlib.Algebra.Field.Action.ConjAct
309310
public import Mathlib.Algebra.Field.Basic
@@ -781,6 +782,7 @@ public import Mathlib.Algebra.Module.LinearMap.Defs
781782
public import Mathlib.Algebra.Module.LinearMap.DivisionRing
782783
public import Mathlib.Algebra.Module.LinearMap.End
783784
public import Mathlib.Algebra.Module.LinearMap.FiniteRange
785+
public import Mathlib.Algebra.Module.LinearMap.Index
784786
public import Mathlib.Algebra.Module.LinearMap.Polynomial
785787
public import Mathlib.Algebra.Module.LinearMap.Prod
786788
public import Mathlib.Algebra.Module.LinearMap.Rat

Mathlib/Algebra/BigOperators/Fin.lean

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,3 +775,28 @@ alias alternatingSum_eq_finset_sum := alternatingSum_eq_finsetSum
775775
alias alternatingProd_eq_finset_prod := alternatingProd_eq_finsetProd
776776

777777
end List
778+
779+
/-- This is a classic "telescoping sum" lemma. It says:
780+
`r₀ - (r₀ + r₁) + (r₁ + r₂) - (r₂+ r₃) + ⋯ ± (rₙ₋₁ + rₙ) ∓ rₙ = 0`.
781+
782+
The chosen spelling, which gives definitional power over `d`, is influenced by downstream
783+
applications such as `Module.sum_neg_one_pow_finrank_eq_zero_of_exact`. -/
784+
lemma Fin.sum_neg_one_pow_eq_zero {α : Type*} [AddCommGroup α]
785+
{n : ℕ} (d : Fin (n + 2) → α) (r : Fin (n + 1) → α)
786+
(h_first : d 0 = r 0)
787+
(h_mid : ∀ i : Fin n, d i.succ.castSucc = r i.castSucc + r i.succ)
788+
(h_last : d (Fin.last _) = r (Fin.last _)) :
789+
∑ i, (-1) ^ i.val • d i = 0 := by
790+
have h₁ : ∑ i : Fin (n + 2), (-1 : ℤ) ^ i.val • d i =
791+
d 0 +
792+
∑ i : Fin n, (-1 : ℤ) ^ (i.val + 1) • (d (Fin.castSucc i).succ) +
793+
(-1 : ℤ) ^ (n + 1) • d (Fin.last (n + 1)) := by
794+
rw [Fin.sum_univ_succ, Fin.sum_univ_castSucc]
795+
simp [add_assoc]
796+
have h₂ : ∑ i : Fin n, (-1 : ℤ) ^ (i.val + 1) • (r (Fin.castSucc i) + r (Fin.succ i)) =
797+
∑ i : Fin n, (-1 : ℤ) ^ (i.val + 1) • r (Fin.castSucc i) +
798+
∑ i : Fin n, (-1 : ℤ) ^ (i.val + 1) • r (Fin.succ i) := by
799+
simp_rw [zsmul_add, Finset.sum_add_distrib]
800+
have h₃ := Fin.sum_univ_castSucc fun i ↦ (-1 : ℤ) ^ i.val • r i
801+
simp_all [Fin.sum_univ_succ, pow_succ']
802+
grind

Mathlib/Algebra/Category/ModuleCat/Kernels.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ module
77

88
public import Mathlib.Algebra.Category.ModuleCat.EpiMono
99
public import Mathlib.CategoryTheory.ConcreteCategory.Elementwise
10-
public import Mathlib.Algebra.Exact
10+
public import Mathlib.Algebra.Exact.Basic
1111
public import Mathlib.LinearAlgebra.Isomorphisms
1212

1313
/-!
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/-
2+
Copyright (c) 2026 Oliver Nash. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Oliver Nash, Jon Bannon
5+
-/
6+
module
7+
8+
public import Mathlib.Algebra.BigOperators.Fin
9+
public import Mathlib.Algebra.Exact.Basic
10+
public import Mathlib.LinearAlgebra.Dimension.Constructions
11+
public import Mathlib.LinearAlgebra.FiniteDimensional.Lemmas
12+
13+
/-! # Exactness of sequences
14+
15+
In this file we provide some API for handling exact sequences.
16+
17+
## Main definitions / results:
18+
19+
* `Module.sum_neg_one_pow_finrank_eq_zero_of_exact`: the Euler characteristic of a finite exact
20+
sequence is zero.
21+
22+
## TODO
23+
24+
Write a simproc to generate unrolled, universe-polymorphic versions of
25+
`Module.sum_neg_one_pow_finrank_eq_zero_of_exact` on the fly and so obviate the need for
26+
`Module.sum_neg_one_pow_finrank_eq_zero_of_exact_six`.
27+
28+
-/
29+
30+
universe u₀ u₁ u₂ u₃ u₄ u₅
31+
32+
namespace Module
33+
34+
open Function
35+
36+
variable {k : Type*} [DivisionRing k]
37+
38+
/-- The Euler characteristic of a finite exact sequence is zero. -/
39+
public lemma sum_neg_one_pow_finrank_eq_zero_of_exact {n : ℕ} (V : Fin (n + 2) → Type*)
40+
[∀ i, AddCommGroup (V i)] [∀ i, Module k (V i)] [∀ i, FiniteDimensional k (V i)]
41+
(f : (i : Fin (n + 1)) → V i.castSucc →ₗ[k] V i.succ)
42+
(inj : Injective (f 0))
43+
(h_exact : ∀ i : Fin n, Exact (f i.castSucc) (f i.succ))
44+
(surj : Surjective (f (Fin.last _))) :
45+
∑ i, (-1) ^ i.val * (finrank k (V i) : ℤ) = 0 := by
46+
replace inj := LinearMap.finrank_range_of_inj inj
47+
replace surj := LinearMap.range_eq_top.mpr surj
48+
simp_rw [← smul_eq_mul]
49+
refine Fin.sum_neg_one_pow_eq_zero _ (fun i ↦ finrank k (f i).range) ?_ (fun i ↦ ?_) ?_
50+
· aesop
51+
· grind [(h_exact i).linearMap_ker_eq, (f i.succ).finrank_range_add_finrank_ker]
52+
· grind [finrank_top]
53+
54+
/- An unrolled version of `Module.sum_neg_one_pow_finrank_eq_zero_of_exact`. This is an auxiliary
55+
lemma en route to `Module.sum_neg_one_pow_finrank_eq_zero_of_exact_six`. -/
56+
private lemma sum_neg_one_pow_finrank_eq_zero_of_exact_six_aux {V₀ V₁ V₂ V₃ V₄ V₅ : Type u₀}
57+
[AddCommGroup V₀] [Module k V₀] [FiniteDimensional k V₀]
58+
[AddCommGroup V₁] [Module k V₁] [FiniteDimensional k V₁]
59+
[AddCommGroup V₂] [Module k V₂] [FiniteDimensional k V₂]
60+
[AddCommGroup V₃] [Module k V₃] [FiniteDimensional k V₃]
61+
[AddCommGroup V₄] [Module k V₄] [FiniteDimensional k V₄]
62+
[AddCommGroup V₅] [Module k V₅] [FiniteDimensional k V₅]
63+
(f₀ : V₀ →ₗ[k] V₁) (f₁ : V₁ →ₗ[k] V₂) (f₂ : V₂ →ₗ[k] V₃) (f₃ : V₃ →ₗ[k] V₄) (f₄ : V₄ →ₗ[k] V₅)
64+
(inj : Injective f₀)
65+
(exact₁ : Exact f₀ f₁)
66+
(exact₂ : Exact f₁ f₂)
67+
(exact₃ : Exact f₂ f₃)
68+
(exact₄ : Exact f₃ f₄)
69+
(surj : Surjective f₄) :
70+
(finrank k V₀ : ℤ) - finrank k V₁ + finrank k V₂ -
71+
finrank k V₃ + finrank k V₄ - finrank k V₅ = 0 := by
72+
letI Vs := ![V₀, V₁, V₂, V₃, V₄, V₅]
73+
letI (i : Fin 6) : AddCommGroup (Vs i) := match i with
74+
| 0 => ‹_› | 1 => ‹_› | 2 => ‹_› | 3 => ‹_› | 4 => ‹_› | 5 => ‹_›
75+
letI (i : Fin 6) : Module k (Vs i) := match i with
76+
| 0 => ‹_› | 1 => ‹_› | 2 => ‹_› | 3 => ‹_› | 4 => ‹_› | 5 => ‹_›
77+
have (i : Fin 6) : FiniteDimensional k (Vs i) := match i with
78+
| 0 => ‹_› | 1 => ‹_› | 2 => ‹_› | 3 => ‹_› | 4 => ‹_› | 5 => ‹_›
79+
letI fs (i : Fin 5) : Vs i.castSucc →ₗ[k] Vs i.succ := match i with
80+
| 0 => f₀ | 1 => f₁ | 2 => f₂ | 3 => f₃ | 4 => f₄
81+
simpa [Fin.sum_univ_six] using Module.sum_neg_one_pow_finrank_eq_zero_of_exact Vs fs inj
82+
(fun i ↦ by fin_cases i; exacts [exact₁, exact₂, exact₃, exact₄]) surj
83+
84+
/-- This is an unrolled, universe-polymorphic version of
85+
`Module.sum_neg_one_pow_finrank_eq_zero_of_exact`. This special case exists because of the role that
86+
this lemma plays in the proof of `LinearMap.index_comp`.
87+
88+
In theory one could write a `simproc` which conjured up this lemma for a sequence of any length and
89+
then one would not need to have this special-case lemma at all. -/
90+
public lemma sum_neg_one_pow_finrank_eq_zero_of_exact_six
91+
{V₀ : Type u₀} [AddCommGroup V₀] [Module k V₀] [FiniteDimensional k V₀]
92+
{V₁ : Type u₁} [AddCommGroup V₁] [Module k V₁] [FiniteDimensional k V₁]
93+
{V₂ : Type u₂} [AddCommGroup V₂] [Module k V₂] [FiniteDimensional k V₂]
94+
{V₃ : Type u₃} [AddCommGroup V₃] [Module k V₃] [FiniteDimensional k V₃]
95+
{V₄ : Type u₄} [AddCommGroup V₄] [Module k V₄] [FiniteDimensional k V₄]
96+
{V₅ : Type u₅} [AddCommGroup V₅] [Module k V₅] [FiniteDimensional k V₅]
97+
(f₀ : V₀ →ₗ[k] V₁) (f₁ : V₁ →ₗ[k] V₂) (f₂ : V₂ →ₗ[k] V₃) (f₃ : V₃ →ₗ[k] V₄) (f₄ : V₄ →ₗ[k] V₅)
98+
(inj : Injective f₀)
99+
(exact₁ : Exact f₀ f₁)
100+
(exact₂ : Exact f₁ f₂)
101+
(exact₃ : Exact f₂ f₃)
102+
(exact₄ : Exact f₃ f₄)
103+
(surj : Surjective f₄) :
104+
(finrank k V₀ : ℤ) - finrank k V₁ + finrank k V₂ -
105+
finrank k V₃ + finrank k V₄ - finrank k V₅ = 0 := by
106+
let W₀ := ULift.{max u₀ u₁ u₂ u₃ u₄ u₅} V₀
107+
let W₁ := ULift.{max u₀ u₁ u₂ u₃ u₄ u₅} V₁
108+
let W₂ := ULift.{max u₀ u₁ u₂ u₃ u₄ u₅} V₂
109+
let W₃ := ULift.{max u₀ u₁ u₂ u₃ u₄ u₅} V₃
110+
let W₄ := ULift.{max u₀ u₁ u₂ u₃ u₄ u₅} V₄
111+
let W₅ := ULift.{max u₀ u₁ u₂ u₃ u₄ u₅} V₅
112+
let g₀ : W₀ →ₗ[k] W₁ := ULift.moduleEquiv.symm.toLinearMap ∘ₗ f₀ ∘ₗ ULift.moduleEquiv.toLinearMap
113+
let g₁ : W₁ →ₗ[k] W₂ := ULift.moduleEquiv.symm.toLinearMap ∘ₗ f₁ ∘ₗ ULift.moduleEquiv.toLinearMap
114+
let g₂ : W₂ →ₗ[k] W₃ := ULift.moduleEquiv.symm.toLinearMap ∘ₗ f₂ ∘ₗ ULift.moduleEquiv.toLinearMap
115+
let g₃ : W₃ →ₗ[k] W₄ := ULift.moduleEquiv.symm.toLinearMap ∘ₗ f₃ ∘ₗ ULift.moduleEquiv.toLinearMap
116+
let g₄ : W₄ →ₗ[k] W₅ := ULift.moduleEquiv.symm.toLinearMap ∘ₗ f₄ ∘ₗ ULift.moduleEquiv.toLinearMap
117+
have := sum_neg_one_pow_finrank_eq_zero_of_exact_six_aux g₀ g₁ g₂ g₃ g₄
118+
(inj := by simpa [g₀]) (surj := by simpa [g₄])
119+
simp only [W₀, W₁, W₂, W₃, W₄, W₅, finrank_ulift] at this
120+
apply this <;>
121+
simpa only [g₀, g₁, g₂, g₃, g₄, LinearEquiv.postcomp_exact_iff_exact,
122+
LinearEquiv.conj_symm_exact_iff_exact, LinearEquiv.precomp_exact_iff_exact]
123+
124+
end Module

Mathlib/Algebra/FiveLemma.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Authors: Christian Merten
55
-/
66
module
77

8-
public import Mathlib.Algebra.Exact
8+
public import Mathlib.Algebra.Exact.Basic
99

1010
/-!
1111
# The five lemma in terms of modules

Mathlib/Algebra/Homology/ShortComplex/Ab.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ module
77

88
public import Mathlib.Algebra.Category.Grp.Abelian
99
public import Mathlib.Algebra.Category.Grp.Kernels
10-
public import Mathlib.Algebra.Exact
10+
public import Mathlib.Algebra.Exact.Basic
1111
public import Mathlib.Algebra.Homology.ShortComplex.ShortExact
1212
public import Mathlib.GroupTheory.QuotientGroup.Finite
1313

Mathlib/Algebra/Lie/Extension.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Authors: Scott Carnahan
55
-/
66
module
77

8-
public import Mathlib.Algebra.Exact
8+
public import Mathlib.Algebra.Exact.Basic
99
public import Mathlib.Algebra.Lie.Cochain
1010

1111
/-!
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/-
2+
Copyright (c) 2026 Oliver Nash. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Oliver Nash
5+
-/
6+
module
7+
8+
public import Mathlib.Algebra.Exact.Sequence
9+
public import Mathlib.Algebra.Module.LinearMap.Defs
10+
public import Mathlib.Algebra.Module.Submodule.Map
11+
public import Mathlib.LinearAlgebra.FiniteDimensional.Lemmas
12+
13+
/-!
14+
# The index of a linear map
15+
16+
In this file we define the index of a linear map and provide some basic API.
17+
18+
## Main definitions / results:
19+
20+
* `LinearMap.index`: the index of a linear map, with sign convention `index = dim ker - dim coker`.
21+
* `LinearMap.index_comp`: the index is additive under composition.
22+
23+
-/
24+
25+
noncomputable section
26+
27+
namespace LinearMap
28+
29+
open Function Module
30+
31+
variable {M N : Type*} [AddCommGroup M] [AddCommGroup N]
32+
33+
section Ring
34+
35+
variable {R : Type*} [Ring R] [Module R M] [Module R N] (f : M →ₗ[R] N)
36+
37+
/-- The index of a linear map with sign convention `index = dim ker - dim coker`.
38+
39+
In the case that either the kernel or cokernel has infinite rank, the value is junk. -/
40+
public def index : ℤ := finrank R f.ker - finrank R (N ⧸ f.range)
41+
42+
variable {f}
43+
44+
public lemma index_eq_finrank_sub :
45+
f.index = finrank R f.ker - finrank R (N ⧸ f.range) := by
46+
rfl
47+
48+
@[nontriviality] public lemma index_of_subsingleton [Subsingleton R] :
49+
f.index = 0 := by
50+
simp [index_eq_finrank_sub]
51+
52+
@[simp] public lemma index_zero :
53+
(0 : M →ₗ[R] N).index = finrank R M - finrank R N := by
54+
rw [index_eq_finrank_sub, ker_zero, range_zero]
55+
simpa using (Submodule.quotEquivOfEqBot _ rfl).finrank_eq
56+
57+
public lemma index_of_injective [Nontrivial R] (hf : Injective f) :
58+
f.index = - finrank R (N ⧸ f.range) := by
59+
simpa [index_eq_finrank_sub] using ker_eq_bot.2 hf ▸ finrank_bot _ _
60+
61+
variable [StrongRankCondition R]
62+
63+
public lemma index_of_surjective (hf : Surjective f) :
64+
f.index = finrank R f.ker := by
65+
rw [index_eq_finrank_sub, range_eq_top.mpr hf]
66+
simp [finrank_eq_zero_of_subsingleton]
67+
68+
@[simp] public lemma index_id :
69+
(id : M →ₗ[R] M).index = 0 := by
70+
nontriviality R
71+
rw [index_eq_finrank_sub, range_id]
72+
simp [finrank_eq_zero_of_subsingleton]
73+
74+
@[simp] public lemma _root_.LinearEquiv.index_eq_zero {e : M ≃ₗ[R] N} :
75+
e.toLinearMap.index = 0 := by
76+
nontriviality R
77+
have := index_of_injective e.injective
78+
have := index_of_surjective e.surjective
79+
lia
80+
81+
end Ring
82+
83+
section DivisionRing
84+
85+
variable {k : Type*} [DivisionRing k] [Module k M] [Module k N] {f : M →ₗ[k] N}
86+
87+
@[simp] public lemma index_neg :
88+
(-f).index = f.index := by
89+
rw [index_eq_finrank_sub, index_eq_finrank_sub, ker_neg, range_neg]
90+
91+
public lemma index_eq_of_finiteDimensional [FiniteDimensional k M] [FiniteDimensional k N] :
92+
f.index = finrank k M - finrank k N := by
93+
-- `0 → f.ker → M → N → f.coker → 0`
94+
rw [index_eq_finrank_sub]
95+
have h₁ := f.range.finrank_quotient_add_finrank
96+
have h₂ := f.quotKerEquivRange.finrank_eq
97+
have h₃ := f.ker.finrank_quotient_add_finrank
98+
lia
99+
100+
open Submodule in
101+
@[simp] public lemma index_comp {P : Type*} [AddCommGroup P] [Module k P] (g : N →ₗ[k] P)
102+
[FiniteDimensional k f.ker] [FiniteDimensional k g.ker]
103+
[FiniteDimensional k (N ⧸ f.range)] [FiniteDimensional k (P ⧸ g.range)] :
104+
(g ∘ₗ f).index = g.index + f.index := by
105+
-- `0 → f.ker → (g ∘ₗ f).ker → g.ker → f.coker → (g ∘ₗ f).coker → g.coker → 0`
106+
have aux : f.range ≤ comap g (g ∘ₗ f).range := by rw [← map_le_iff_le_comap, range_comp]
107+
let f₀ : f.ker →ₗ[k] (g ∘ₗ f).ker := inclusion <| ker_le_ker_comp f g
108+
let f₁ : (g ∘ₗ f).ker →ₗ[k] g.ker := f.restrict <| by simp
109+
let f₂ : g.ker →ₗ[k] N ⧸ f.range := f.range.mkQ ∘ₗ g.ker.subtype
110+
let f₃ : (N ⧸ f.range) →ₗ[k] P ⧸ (g ∘ₗ f).range := f.range.mapQ (g ∘ₗ f).range g aux
111+
let f₄ : (P ⧸ (g ∘ₗ f).range) →ₗ[k] P ⧸ g.range := factor <| range_comp_le_range f g
112+
have h₀ : Injective f₀ := inclusion_injective _
113+
have h₁ : Exact f₀ f₁ := by rw [exact_iff]; simp [f₀, f₁, ker_restrict, range_inclusion]
114+
have h₂ : Exact f₁ f₂ := by rw [exact_iff]; simp [f₁, f₂, ker_comp, map_comap_eq]
115+
have h₃ : Exact f₂ f₃ := by rw [exact_iff]; simp [f₂, f₃, range_comp, ker_mapQ, comap_map_eq]
116+
have h₄ : Exact f₃ f₄ := by rw [exact_iff]; simp [f₃, f₄, factor, ker_mapQ, range_mapQ]
117+
have h₅ : Surjective f₄ := factor_surjective _
118+
have : FiniteDimensional k (g ∘ₗ f).ker := by rw [ker_comp]; infer_instance
119+
have : FiniteDimensional k (P ⧸ (g ∘ₗ f).range) := by rw [range_comp]; infer_instance
120+
grind [index, sum_neg_one_pow_finrank_eq_zero_of_exact_six f₀ f₁ f₂ f₃ f₄ h₀ h₁ h₂ h₃ h₄ h₅]
121+
122+
end DivisionRing
123+
124+
section Field
125+
126+
variable {k : Type*} [Field k] [Module k M] [Module k N] {f : M →ₗ[k] N}
127+
128+
public lemma index_smul (t : k) (ht : t ≠ 0) :
129+
(t • f).index = f.index := by
130+
rw [index_eq_finrank_sub, index_eq_finrank_sub, ker_smul _ _ ht, range_smul _ _ ht]
131+
132+
end Field
133+
134+
end LinearMap

Mathlib/Algebra/Module/LocalizedModule/Exact.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Authors: Andrew Yang, Jujian Zhang
55
-/
66
module
77

8-
public import Mathlib.Algebra.Exact
8+
public import Mathlib.Algebra.Exact.Basic
99
public import Mathlib.Algebra.Module.LocalizedModule.Basic
1010

1111
/-!

0 commit comments

Comments
 (0)