Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f4730bf
feat(LinearAlgebra/Matrix/Nondegenerate): generalize `det ≠ 0 → Nonde…
SnirBroshi May 20, 2026
9b16f01
delete `A` and just use `R`
SnirBroshi May 21, 2026
d5aacdd
use `nonZeroDivisors` notation
SnirBroshi May 21, 2026
883dc1d
extract the argument for a `Nondegenerate` hypothesis
SnirBroshi May 21, 2026
1239439
transpose theorems to make mulVec work
SnirBroshi May 21, 2026
0a987cd
cleanup
SnirBroshi May 21, 2026
9717541
generalize further
SnirBroshi May 21, 2026
457f9d6
cleanup
SnirBroshi May 21, 2026
4f59719
`SeparatingLeft`/`SeparatingRight`/`Nondegenerate` are equivalent for…
SnirBroshi May 21, 2026
fe40743
generalize the definitions for good measure
SnirBroshi May 21, 2026
06df011
move `classical` closer to usage
SnirBroshi May 21, 2026
953df37
`variable ... in` is silly
SnirBroshi May 21, 2026
13cb34b
tag transpose lemmas as `@[simp]`
SnirBroshi May 25, 2026
7317b59
remove more `variable ... in`
SnirBroshi May 25, 2026
8824264
use the other theorems in `separatingLeft_iff_separatingRight` to avo…
SnirBroshi May 25, 2026
f195841
extract `variable`s in `ToLinearEquiv.lean`
SnirBroshi May 25, 2026
52a0560
Merge branch 'master' into feature/matrix/nondegenerate-generalize-no…
SnirBroshi Jun 7, 2026
dae7377
`simpa using!`
SnirBroshi Jun 7, 2026
05599b3
rename `nondegenerate_iff_forall_mulVec_and_vecMul_eq_zero`
SnirBroshi Jun 7, 2026
e69a766
move `Fintype` `variable`s
SnirBroshi Jun 7, 2026
b4d4f49
Merge branch 'master' into feature/matrix/nondegenerate-generalize-no…
SnirBroshi Jun 24, 2026
a131348
fix name of `nondegenerate_iff_forall_vecMul_and_mulVec_eq_zero`
SnirBroshi Jun 24, 2026
8956d35
rename transpose aliases
SnirBroshi Jun 24, 2026
6775d92
move `open scoped`
SnirBroshi Jun 24, 2026
87f8a81
golf `*_iff_det_ne_zero`
SnirBroshi Jun 24, 2026
92cd008
reorder `of_` hypotheses in names
SnirBroshi Jun 25, 2026
d991d28
shorten `rw` to `simp`
SnirBroshi Jun 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Mathlib/Data/Matrix/Mul.lean
Original file line number Diff line number Diff line change
Expand Up @@ -1081,13 +1081,13 @@ theorem vecMul_transpose [Fintype n] (A : Matrix m n α) (x : n → α) : x ᵥ*
apply dotProduct_comm

/-- Bilinear form identity: `x ⬝ᵥ Aᵀ *ᵥ y = y ⬝ᵥ A *ᵥ x` for commutative semirings. -/
theorem dotProduct_transpose_mulVec [Fintype m] (A : Matrix m m α) (x y : m → α) :
x ⬝ᵥ Aᵀ *ᵥ y = y ⬝ᵥ A *ᵥ x := by
theorem dotProduct_transpose_mulVec [Fintype m] [Fintype n] (A : Matrix m n α) (x : n → α)
(y : m → α) : x ⬝ᵥ Aᵀ *ᵥ y = y ⬝ᵥ A *ᵥ x := by
rw [dotProduct_mulVec, dotProduct_comm, vecMul_transpose]

/-- Bilinear form identity: `(x ᵥ* Aᵀ) ⬝ᵥ y = (y ᵥ* A) ⬝ᵥ x` for commutative semirings. -/
theorem dotProduct_vecMul_transpose [Fintype m] (A : Matrix m m α) (x y : m → α) :
(x ᵥ* Aᵀ) ⬝ᵥ y = (y ᵥ* A) ⬝ᵥ x := by
theorem dotProduct_vecMul_transpose [Fintype m] [Fintype n] (A : Matrix m n α) (x : n → α)
(y : m → α) : (x ᵥ* Aᵀ) ⬝ᵥ y = (y ᵥ* A) ⬝ᵥ x := by
simpa [dotProduct_mulVec] using dotProduct_transpose_mulVec (A := A) (x := x) (y := y)

theorem mulVec_vecMul [Fintype n] [Fintype o] (A : Matrix m n α) (B : Matrix o n α) (x : o → α) :
Expand Down
123 changes: 95 additions & 28 deletions Mathlib/LinearAlgebra/Matrix/Nondegenerate.lean
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace Matrix

section Finite

variable {m n R A : Type*} [CommRing R] [Finite m] [Finite n] (M : Matrix m n R)
variable {m n R A : Type*} [NonUnitalNonAssocSemiring R] [Finite m] [Finite n] (M : Matrix m n R)

attribute [local instance] Fintype.ofFinite

Expand All @@ -39,29 +39,87 @@ def SeparatingLeft : Prop :=
(∀ v, (∀ w, v ⬝ᵥ M *ᵥ w = 0) → v = 0)

/-- A matrix `M` is nondegenerate if it is both left-separating and right-separating. -/
@[mk_iff]
structure Nondegenerate (M : Matrix m n R) : Prop where
separatingLeft : SeparatingLeft M
separatingRight : SeparatingRight M

end Finite

variable {m n R A : Type*} [CommRing R] [Fintype m] [Fintype n] [CommRing A] [IsDomain A]
{M : Matrix m n R}
variable {m n R : Type*} [CommRing R] {M : Matrix m n R}

lemma separatingRight_def : M.SeparatingRight ↔ (∀ w, (∀ v, v ⬝ᵥ M *ᵥ w = 0) → w = 0) := by
lemma separatingRight_def [Fintype m] [Fintype n] :
M.SeparatingRight ↔ (∀ w, (∀ v, v ⬝ᵥ M *ᵥ w = 0) → w = 0) := by
refine forall_congr' fun w ↦ ⟨fun hM hw ↦ hM ?_, fun hM hw ↦ hM ?_⟩ <;>
convert! hw

lemma separatingLeft_def : M.SeparatingLeft ↔ (∀ v, (∀ w, v ⬝ᵥ M *ᵥ w = 0) → v = 0) := by
lemma separatingLeft_def [Fintype m] [Fintype n] :
M.SeparatingLeft ↔ (∀ v, (∀ w, v ⬝ᵥ M *ᵥ w = 0) → v = 0) := by
refine forall_congr' fun v ↦ ⟨fun hM hv ↦ hM ?_, fun hM hv ↦ hM ?_⟩ <;>
convert! hv

lemma nondegenerate_def : M.Nondegenerate ↔
(∀ v, (∀ w, v ⬝ᵥ M *ᵥ w = 0) → v = 0) ∧ (∀ w, (∀ v, v ⬝ᵥ M *ᵥ w = 0) → w = 0) := by
lemma nondegenerate_def [Fintype m] [Fintype n] :
M.Nondegenerate ↔
(∀ v, (∀ w, v ⬝ᵥ M *ᵥ w = 0) → v = 0) ∧ (∀ w, (∀ v, v ⬝ᵥ M *ᵥ w = 0) → w = 0) := by
constructor
· exact fun h ↦ ⟨separatingLeft_def.mp h.1, separatingRight_def.mp h.2⟩
· exact fun h ↦ ⟨separatingLeft_def.mpr h.1, separatingRight_def.mpr h.2⟩

theorem separatingLeft_iff_forall_vecMul_eq_zero [Fintype m] [Finite n] :
M.SeparatingLeft ↔ ∀ v, v ᵥ* M = 0 → v = 0 := by
have := Fintype.ofFinite n
rw [separatingLeft_def]
refine ⟨fun h v hv ↦ h v fun w ↦ ?_, fun h w hw ↦ h w <| funext fun i ↦ ?_⟩
· simp [dotProduct_mulVec, hv]
· classical simpa using! hw <| Pi.single i 1

theorem separatingRight_iff_forall_mulVec_eq_zero [Finite m] [Fintype n] :
M.SeparatingRight ↔ ∀ v, M *ᵥ v = 0 → v = 0 := by
have := Fintype.ofFinite m
rw [separatingRight_def]
refine ⟨fun h v hv ↦ h v fun w ↦ ?_, fun h w hw ↦ h w <| funext fun i ↦ ?_⟩
· simp [hv]
· classical simpa using hw <| Pi.single i 1

theorem SeparatingLeft.eq_zero_of_vecMul_eq_zero [Fintype m] [Finite n] (hM : M.SeparatingLeft)
{v : m → R} (hv : v ᵥ* M = 0) : v = 0 :=
separatingLeft_iff_forall_vecMul_eq_zero.mp hM v hv

theorem SeparatingRight.eq_zero_of_mulVec_eq_zero [Finite m] [Fintype n] (hM : M.SeparatingRight)
{v : n → R} (hv : M *ᵥ v = 0) : v = 0 :=
separatingRight_iff_forall_mulVec_eq_zero.mp hM v hv

theorem nondegenerate_iff_forall_vecMul_and_mulVec_eq_zero [Fintype m] [Fintype n] :
M.Nondegenerate ↔ (∀ v, v ᵥ* M = 0 → v = 0) ∧ (∀ v, M *ᵥ v = 0 → v = 0) := by
rw [nondegenerate_iff, separatingLeft_iff_forall_vecMul_eq_zero,
separatingRight_iff_forall_mulVec_eq_zero]

@[simp]
theorem separatingLeft_transpose_iff [Finite m] [Finite n] :
Mᵀ.SeparatingLeft ↔ M.SeparatingRight := by
have := Fintype.ofFinite m
have := Fintype.ofFinite n
simp_rw [separatingLeft_def, separatingRight_def, dotProduct_transpose_mulVec]

alias ⟨_, SeparatingRight.separatingLeft_transpose⟩ := separatingLeft_transpose_iff

@[simp]
theorem separatingRight_transpose_iff [Finite m] [Finite n] :
Mᵀ.SeparatingRight ↔ M.SeparatingLeft := by
have := Fintype.ofFinite m
have := Fintype.ofFinite n
simp_rw [separatingRight_def, separatingLeft_def, dotProduct_transpose_mulVec]

alias ⟨_, SeparatingLeft.separatingRight_transpose⟩ := separatingRight_transpose_iff

@[simp]
theorem nondegenerate_transpose_iff [Finite m] [Finite n] : Mᵀ.Nondegenerate ↔ M.Nondegenerate := by
Comment thread
SnirBroshi marked this conversation as resolved.
simp [nondegenerate_iff, and_comm]

alias ⟨_, Nondegenerate.transpose⟩ := nondegenerate_transpose_iff

Comment thread
SnirBroshi marked this conversation as resolved.
variable [Fintype m] [Fintype n]

/-- If `M` is nondegenerate and `w * M * v = 0` for all `w`, then `v = 0`. -/
theorem Nondegenerate.eq_zero_of_ortho (hM : Nondegenerate M) {v : m → R}
(hv : ∀ w, v ⬝ᵥ M *ᵥ w = 0) : v = 0 :=
Expand All @@ -73,42 +131,51 @@ theorem Nondegenerate.exists_not_ortho_of_ne_zero (hM : Nondegenerate M)
not_forall.mp (mt hM.eq_zero_of_ortho hv)

/-- If `M` is nondegenerate and `w * M * v = 0` for all `v`, then `w = 0`. -/
theorem Nondegenerate.eq_zero_of_ortho' {M : Matrix m n R} (hM : Nondegenerate M) {w : n → R}
theorem Nondegenerate.eq_zero_of_ortho' (hM : Nondegenerate M) {w : n → R}
(hw : ∀ v, v ⬝ᵥ M *ᵥ w = 0) : w = 0 :=
(nondegenerate_def.mp hM).2 w hw

/-- If `M` is nondegenerate and `w ≠ 0`, then there is some `v` such that `v * M * w ≠ 0`. -/
theorem Nondegenerate.exists_not_ortho_of_ne_zero' {M : Matrix m n R} (hM : Nondegenerate M)
{w : n → R} (hw : w ≠ 0) : ∃ v, v ⬝ᵥ M *ᵥ w ≠ 0 :=
theorem Nondegenerate.exists_not_ortho_of_ne_zero' (hM : Nondegenerate M) {w : n → R} (hw : w ≠ 0) :
∃ v, v ⬝ᵥ M *ᵥ w ≠ 0 :=
not_forall.mp (mt hM.eq_zero_of_ortho' hw)

section Determinant
variable [DecidableEq m] {M : Matrix m m A}
variable [DecidableEq m] {M : Matrix m m R}

open scoped nonZeroDivisors

private theorem SeparatingLeft.of_det_mem_nonZeroDivisors (hM : M.det ∈ R⁰) : M.SeparatingLeft := by
refine separatingLeft_def.mpr fun v h ↦ funext fun i ↦ mem_nonZeroDivisors_iff_left.mp hM _ ?_
simpa using h <| M.cramer <| Pi.single i 1

theorem Nondegenerate.of_det_mem_nonZeroDivisors (hM : M.det ∈ R⁰) : M.Nondegenerate where
separatingLeft := .of_det_mem_nonZeroDivisors hM
separatingRight := separatingLeft_transpose_iff.mp <| .of_det_mem_nonZeroDivisors <| by simpa

/-- If `M` is square and has nonzero determinant, then `M` as a bilinear form on `n → A` is
/-- If `M` is square and has nonzero determinant, then `M` as a bilinear form on `n → R` is
nondegenerate. The "iff" implication, `nondegenerate_iff_det_ne_zero`, is proved in a later file.

See also `BilinForm.nondegenerateOfDetNeZero'` and `BilinForm.nondegenerateOfDetNeZero`.
-/
theorem nondegenerate_of_det_ne_zero (hM : M.det ≠ 0) : Nondegenerate M := by
refine nondegenerate_def.mpr ⟨fun v h ↦ ?_, fun w h ↦ ?_⟩
· ext i
specialize h (M.cramer (Pi.single i 1))
simp_all
· ext i
contrapose! h
use Pi.single i 1 ᵥ* M.adjugate
rw [dotProduct_mulVec, vecMul_vecMul, adjugate_mul]
simp_all [dotProduct, smul_apply, smul_eq_mul, Matrix.one_apply]

theorem eq_zero_of_vecMul_eq_zero (hM : M.det ≠ 0) {v : m → A}
theorem nondegenerate_of_det_ne_zero [NoZeroDivisors R] (hM : M.det ≠ 0) : M.Nondegenerate :=
.of_det_mem_nonZeroDivisors <| mem_nonZeroDivisors_of_ne_zero hM

theorem eq_zero_of_det_mem_nonZeroDivisors_of_vecMul_eq_zero (hM : M.det ∈ R⁰)
{v : m → R} (hv : v ᵥ* M = 0) : v = 0 :=
Nondegenerate.of_det_mem_nonZeroDivisors hM |>.separatingLeft.eq_zero_of_vecMul_eq_zero hv

theorem eq_zero_of_vecMul_eq_zero [NoZeroDivisors R] (hM : M.det ≠ 0) {v : m → R}
(hv : v ᵥ* M = 0) : v = 0 :=
(nondegenerate_of_det_ne_zero hM).eq_zero_of_ortho fun w => by
rw [dotProduct_mulVec, hv, zero_dotProduct]
nondegenerate_of_det_ne_zero hM |>.separatingLeft.eq_zero_of_vecMul_eq_zero hv
Comment thread
themathqueen marked this conversation as resolved.

theorem eq_zero_of_det_mem_nonZeroDivisors_of_mulVec_eq_zero (hM : M.det ∈ R⁰)
{v : m → R} (hv : M *ᵥ v = 0) : v = 0 :=
Nondegenerate.of_det_mem_nonZeroDivisors hM |>.separatingRight.eq_zero_of_mulVec_eq_zero hv

theorem eq_zero_of_mulVec_eq_zero (hM : M.det ≠ 0) {v : m → A}
theorem eq_zero_of_mulVec_eq_zero [NoZeroDivisors R] (hM : M.det ≠ 0) {v : m → R}
(hv : M *ᵥ v = 0) : v = 0 :=
eq_zero_of_vecMul_eq_zero (by rwa [det_transpose]) ((vecMul_transpose M v).trans hv)
nondegenerate_of_det_ne_zero hM |>.separatingRight.eq_zero_of_mulVec_eq_zero hv

end Determinant

Expand Down
73 changes: 40 additions & 33 deletions Mathlib/LinearAlgebra/Matrix/ToLinearEquiv.lean
Original file line number Diff line number Diff line change
Expand Up @@ -158,54 +158,61 @@ private theorem exists_mulVec_eq_zero_iff' {A : Type*} (K : Type*) [DecidableEq
RingHom.mapMatrix_apply, Pi.smul_apply, smul_eq_mul, Algebra.smul_def]
· rw [mulVec_smul, mul_eq, Pi.smul_apply, Pi.zero_apply, smul_zero]

theorem exists_mulVec_eq_zero_iff {A : Type*} [DecidableEq n] [CommRing A] [IsDomain A]
{M : Matrix n n A} : (∃ v ≠ 0, M *ᵥ v = 0) ↔ M.det = 0 :=
variable {A : Type*} [CommRing A] [IsDomain A] {M N : Matrix n n A}

theorem exists_mulVec_eq_zero_iff [DecidableEq n] : (∃ v ≠ 0, M *ᵥ v = 0) ↔ M.det = 0 :=
exists_mulVec_eq_zero_iff' (FractionRing A)

theorem exists_vecMul_eq_zero_iff {A : Type*} [DecidableEq n] [CommRing A] [IsDomain A]
{M : Matrix n n A} : (∃ v ≠ 0, v ᵥ* M = 0) ↔ M.det = 0 := by
theorem exists_vecMul_eq_zero_iff [DecidableEq n] : (∃ v ≠ 0, v ᵥ* M = 0) ↔ M.det = 0 := by
simpa only [← M.det_transpose, ← mulVec_transpose] using exists_mulVec_eq_zero_iff

theorem nondegenerate_iff_det_ne_zero {A : Type*} [DecidableEq n] [CommRing A] [IsDomain A]
{M : Matrix n n A} : Nondegenerate M ↔ M.det ≠ 0 := by
refine ⟨?_, nondegenerate_of_det_ne_zero⟩
rw [ne_eq, ← exists_vecMul_eq_zero_iff]
push Not
intro hM v hv hMv
obtain ⟨w, hwMv⟩ := hM.exists_not_ortho_of_ne_zero hv
simp [dotProduct_mulVec, hMv, zero_dotProduct, ne_eq] at hwMv

lemma separatingLeft_iff_det_ne_zero {A : Type*} [DecidableEq n] [CommRing A] [IsDomain A]
{M : Matrix n n A} : SeparatingLeft M ↔ M.det ≠ 0 := by
refine ⟨fun h hc ↦ ?_, fun h ↦ (nondegenerate_of_det_ne_zero h).1⟩
obtain ⟨v, hvne, hv⟩ := exists_vecMul_eq_zero_iff.mpr hc
refine hvne (separatingLeft_def.mp h v ?_)
simp [dotProduct_mulVec, hv]

lemma separatingRight_iff_det_ne_zero {A : Type*} [DecidableEq n] [CommRing A] [IsDomain A]
{M : Matrix n n A} : SeparatingRight M ↔ M.det ≠ 0 := by
refine ⟨fun h hc ↦ ?_, fun h ↦ (nondegenerate_of_det_ne_zero h).2⟩
obtain ⟨v, hvne, hv⟩ := exists_mulVec_eq_zero_iff.mpr hc
refine hvne (separatingRight_def.mp h v ?_)
simp [hv]

theorem Nondegenerate.mul_iff_right {A : Type*} [CommRing A] [IsDomain A]
{M N : Matrix n n A} (h : N.Nondegenerate) :
theorem nondegenerate_iff_det_ne_zero [DecidableEq n] : Nondegenerate M ↔ M.det ≠ 0 := by
grind [nondegenerate_iff_forall_vecMul_and_mulVec_eq_zero, exists_mulVec_eq_zero_iff,
exists_vecMul_eq_zero_iff]

lemma separatingLeft_iff_det_ne_zero [DecidableEq n] : SeparatingLeft M ↔ M.det ≠ 0 := by
grind [separatingLeft_iff_forall_vecMul_eq_zero, exists_vecMul_eq_zero_iff]

lemma separatingRight_iff_det_ne_zero [DecidableEq n] : SeparatingRight M ↔ M.det ≠ 0 := by
grind [separatingRight_iff_forall_mulVec_eq_zero, exists_mulVec_eq_zero_iff]

omit [Fintype n] in
theorem nondegenerate_iff_separatingLeft [Finite n] : M.Nondegenerate ↔ M.SeparatingLeft := by
classical
have := Fintype.ofFinite n
rw [nondegenerate_iff_det_ne_zero, separatingLeft_iff_det_ne_zero]

alias ⟨_, SeparatingLeft.nondegenerate⟩ := nondegenerate_iff_separatingLeft

omit [Fintype n] in
theorem nondegenerate_iff_separatingRight [Finite n] : M.Nondegenerate ↔ M.SeparatingRight := by
classical
have := Fintype.ofFinite n
rw [nondegenerate_iff_det_ne_zero, separatingRight_iff_det_ne_zero]
Comment thread
SnirBroshi marked this conversation as resolved.

alias ⟨_, SeparatingRight.nondegenerate⟩ := nondegenerate_iff_separatingRight

omit [Fintype n] in
theorem separatingLeft_iff_separatingRight [Finite n] : M.SeparatingLeft ↔ M.SeparatingRight :=
nondegenerate_iff_separatingLeft.symm.trans nondegenerate_iff_separatingRight

alias ⟨SeparatingLeft.separatingRight, SeparatingRight.separatingLeft⟩ :=
separatingLeft_iff_separatingRight

theorem Nondegenerate.mul_iff_right (h : N.Nondegenerate) :
(M * N).Nondegenerate ↔ M.Nondegenerate := by
classical
simp only [nondegenerate_iff_det_ne_zero, det_mul] at h ⊢
exact mul_ne_zero_iff_right h

theorem Nondegenerate.mul_iff_left {A : Type*} [CommRing A] [IsDomain A]
{M N : Matrix n n A} (h : M.Nondegenerate) :
theorem Nondegenerate.mul_iff_left (h : M.Nondegenerate) :
(M * N).Nondegenerate ↔ N.Nondegenerate := by
classical
simp only [nondegenerate_iff_det_ne_zero, det_mul] at h ⊢
exact mul_ne_zero_iff_left h

omit [Fintype n] in
theorem Nondegenerate.smul_iff [Finite n] {A : Type*} [CommRing A] [IsDomain A]
{M : Matrix n n A} {t : A} (h : t ≠ 0) :
theorem Nondegenerate.smul_iff [Finite n] {t : A} (h : t ≠ 0) :
(t • M).Nondegenerate ↔ M.Nondegenerate := by
have := Fintype.ofFinite
rw [nondegenerate_def, nondegenerate_def]
Expand Down
Loading