Skip to content
This repository was archived by the owner on Jul 24, 2024. It is now read-only.

Commit 2941590

Browse files
feat(linear_algebra/matrix): Spectral theorem for matrices (#14231)
Co-authored-by: Eric Wieser <wieser.eric@gmail.com>
1 parent 4e1eeeb commit 2941590

File tree

6 files changed

+305
-4
lines changed

6 files changed

+305
-4
lines changed

src/analysis/inner_product_space/pi_L2.lean

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ open_locale big_operators uniformity topological_space nnreal ennreal complex_co
3939

4040
noncomputable theory
4141

42-
variables {ι : Type*}
42+
variables {ι : Type*} {ι' : Type*}
4343
variables {𝕜 : Type*} [is_R_or_C 𝕜] {E : Type*} [inner_product_space 𝕜 E]
4444
variables {E' : Type*} [inner_product_space 𝕜 E']
4545
variables {F : Type*} [inner_product_space ℝ F]
@@ -112,6 +112,9 @@ instance : inner_product_space 𝕜 (euclidean_space 𝕜 ι) := by apply_instan
112112
lemma finrank_euclidean_space_fin {n : ℕ} :
113113
finite_dimensional.finrank 𝕜 (euclidean_space 𝕜 (fin n)) = n := by simp
114114

115+
lemma euclidean_space.inner_eq_star_dot_product (x y : euclidean_space 𝕜 ι) :
116+
⟪x, y⟫ = matrix.dot_product (star $ pi_Lp.equiv _ _ x) (pi_Lp.equiv _ _ y) := rfl
117+
115118
/-- A finite, mutually orthogonal family of subspaces of `E`, which span `E`, induce an isometry
116119
from `E` to `pi_Lp 2` of the subspaces equipped with the `L2` inner product. -/
117120
def direct_sum.is_internal.isometry_L2_of_orthogonal_family

src/analysis/normed_space/pi_Lp.lean

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,11 @@ to compare the `L^p` and `L^∞` distances through it. -/
8383
protected def equiv : pi_Lp p α ≃ Π (i : ι), α i :=
8484
equiv.refl _
8585

86-
@[simp] lemma equiv_apply (x : pi_Lp p α) (i : ι) : pi_Lp.equiv p α x i = x i := rfl
87-
@[simp] lemma equiv_symm_apply (x : Π i, α i) (i : ι) : (pi_Lp.equiv p α).symm x i = x i := rfl
86+
lemma equiv_apply (x : pi_Lp p α) (i : ι) : pi_Lp.equiv p α x i = x i := rfl
87+
lemma equiv_symm_apply (x : Π i, α i) (i : ι) : (pi_Lp.equiv p α).symm x i = x i := rfl
88+
89+
@[simp] lemma equiv_apply' (x : pi_Lp p α) : pi_Lp.equiv p α x = x := rfl
90+
@[simp] lemma equiv_symm_apply' (x : Π i, α i) : (pi_Lp.equiv p α).symm x = x := rfl
8891

8992
section
9093
/-!

src/linear_algebra/matrix/basis.lean

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,32 @@ lemma basis_to_matrix_mul_linear_map_to_matrix_mul_basis_to_matrix
177177
c.to_matrix c' ⬝ linear_map.to_matrix b' c' f ⬝ b'.to_matrix b = linear_map.to_matrix b c f :=
178178
by rw [basis_to_matrix_mul_linear_map_to_matrix, linear_map_to_matrix_mul_basis_to_matrix]
179179

180+
lemma basis_to_matrix_mul [decidable_eq κ]
181+
(b₁ : basis ι R M) (b₂ : basis ι' R M) (b₃ : basis κ R N) (A : matrix ι' κ R) :
182+
b₁.to_matrix b₂ ⬝ A = linear_map.to_matrix b₃ b₁ (to_lin b₃ b₂ A) :=
183+
begin
184+
have := basis_to_matrix_mul_linear_map_to_matrix b₃ b₁ b₂ (matrix.to_lin b₃ b₂ A),
185+
rwa [linear_map.to_matrix_to_lin] at this
186+
end
187+
188+
lemma mul_basis_to_matrix [decidable_eq ι] [decidable_eq ι']
189+
(b₁ : basis ι R M) (b₂ : basis ι' R M) (b₃ : basis κ R N) (A : matrix κ ι R) :
190+
A ⬝ b₁.to_matrix b₂ = linear_map.to_matrix b₂ b₃ (to_lin b₁ b₃ A) :=
191+
begin
192+
have := linear_map_to_matrix_mul_basis_to_matrix b₂ b₁ b₃ (matrix.to_lin b₁ b₃ A),
193+
rwa [linear_map.to_matrix_to_lin] at this
194+
end
195+
196+
lemma basis_to_matrix_basis_fun_mul (b : basis ι R (ι → R)) (A : matrix ι ι R) :
197+
b.to_matrix (pi.basis_fun R ι) ⬝ A = (λ i j, b.repr (Aᵀ j) i) :=
198+
begin
199+
classical,
200+
simp only [basis_to_matrix_mul _ _ (pi.basis_fun R ι), matrix.to_lin_eq_to_lin'],
201+
ext i j,
202+
rw [linear_map.to_matrix_apply, matrix.to_lin'_apply, pi.basis_fun_apply,
203+
matrix.mul_vec_std_basis_apply]
204+
end
205+
180206
/-- A generalization of `linear_map.to_matrix_id`. -/
181207
@[simp] lemma linear_map.to_matrix_id_eq_basis_to_matrix [decidable_eq ι] :
182208
linear_map.to_matrix b b' id = b'.to_matrix b :=
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/-
2+
Copyright (c) 2022 Alexander Bentkamp. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Alexander Bentkamp
5+
-/
6+
import analysis.inner_product_space.spectrum
7+
8+
/-! # Hermitian matrices
9+
10+
This file defines hermitian matrices and some basic results about them.
11+
12+
## Main definition
13+
14+
* `matrix.is_hermitian `: a matrix `A : matrix n n α` is hermitian if `Aᴴ = A`.
15+
16+
## Tags
17+
18+
self-adjoint matrix, hermitian matrix
19+
20+
-/
21+
22+
namespace matrix
23+
24+
variables {α β : Type*} {m n : Type*} {A : matrix n n α}
25+
26+
open_locale matrix
27+
28+
local notation `⟪`x`, `y`⟫` := @inner α (pi_Lp 2 (λ (_ : n), α)) _ x y
29+
30+
section non_unital_semiring
31+
32+
variables [non_unital_semiring α] [star_ring α] [non_unital_semiring β] [star_ring β]
33+
34+
/-- A matrix is hermitian if it is equal to its conjugate transpose. On the reals, this definition
35+
captures symmetric matrices. -/
36+
def is_hermitian (A : matrix n n α) : Prop := Aᴴ = A
37+
38+
lemma is_hermitian.eq {A : matrix n n α} (h : A.is_hermitian) : Aᴴ = A := h
39+
40+
@[ext]
41+
lemma is_hermitian.ext {A : matrix n n α} : (∀ i j, star (A j i) = A i j) → A.is_hermitian :=
42+
by { intros h, ext i j, exact h i j }
43+
44+
lemma is_hermitian.apply {A : matrix n n α} (h : A.is_hermitian) (i j : n) : star (A j i) = A i j :=
45+
by { unfold is_hermitian at h, rw [← h, conj_transpose_apply, star_star, h] }
46+
47+
lemma is_hermitian.ext_iff {A : matrix n n α} : A.is_hermitian ↔ ∀ i j, star (A j i) = A i j :=
48+
⟨is_hermitian.apply, is_hermitian.ext⟩
49+
50+
lemma is_hermitian_mul_conj_transpose_self [fintype n] (A : matrix n n α) :
51+
(A ⬝ Aᴴ).is_hermitian :=
52+
by rw [is_hermitian, conj_transpose_mul, conj_transpose_conj_transpose]
53+
54+
lemma is_hermitian_transpose_mul_self [fintype n] (A : matrix n n α) :
55+
(Aᴴ ⬝ A).is_hermitian :=
56+
by rw [is_hermitian, conj_transpose_mul, conj_transpose_conj_transpose]
57+
58+
lemma is_hermitian_add_transpose_self (A : matrix n n α) :
59+
(A + Aᴴ).is_hermitian :=
60+
by simp [is_hermitian, add_comm]
61+
62+
lemma is_hermitian_transpose_add_self (A : matrix n n α) :
63+
(Aᴴ + A).is_hermitian :=
64+
by simp [is_hermitian, add_comm]
65+
66+
@[simp] lemma is_hermitian_zero :
67+
(0 : matrix n n α).is_hermitian :=
68+
conj_transpose_zero
69+
70+
-- TODO: move
71+
lemma conj_transpose_map {A : matrix n n α} (f : α → β) (hf : f ∘ star = star ∘ f) :
72+
Aᴴ.map f = (A.map f)ᴴ :=
73+
by rw [conj_transpose, conj_transpose, ←transpose_map, map_map, map_map, hf]
74+
75+
@[simp] lemma is_hermitian.map {A : matrix n n α} (h : A.is_hermitian) (f : α → β)
76+
(hf : f ∘ star = star ∘ f) :
77+
(A.map f).is_hermitian :=
78+
by {refine (conj_transpose_map f hf).symm.trans _, rw h.eq }
79+
80+
@[simp] lemma is_hermitian.transpose {A : matrix n n α} (h : A.is_hermitian) :
81+
Aᵀ.is_hermitian :=
82+
by { rw [is_hermitian, conj_transpose, transpose_map], congr, exact h }
83+
84+
@[simp] lemma is_hermitian.conj_transpose {A : matrix n n α} (h : A.is_hermitian) :
85+
Aᴴ.is_hermitian :=
86+
h.transpose.map _ rfl
87+
88+
@[simp] lemma is_hermitian.add {A B : matrix n n α} (hA : A.is_hermitian) (hB : B.is_hermitian) :
89+
(A + B).is_hermitian :=
90+
(conj_transpose_add _ _).trans (hA.symm ▸ hB.symm ▸ rfl)
91+
92+
@[simp] lemma is_hermitian.minor {A : matrix n n α} (h : A.is_hermitian) (f : m → n) :
93+
(A.minor f f).is_hermitian :=
94+
(conj_transpose_minor _ _ _).trans (h.symm ▸ rfl)
95+
96+
/-- The real diagonal matrix `diagonal v` is hermitian. -/
97+
@[simp] lemma is_hermitian_diagonal [decidable_eq n] (v : n → ℝ) :
98+
(diagonal v).is_hermitian :=
99+
diagonal_conj_transpose _
100+
101+
/-- A block matrix `A.from_blocks B C D` is hermitian,
102+
if `A` and `D` are hermitian and `Bᴴ = C`. -/
103+
lemma is_hermitian.from_blocks
104+
{A : matrix m m α} {B : matrix m n α} {C : matrix n m α} {D : matrix n n α}
105+
(hA : A.is_hermitian) (hBC : Bᴴ = C) (hD : D.is_hermitian) :
106+
(A.from_blocks B C D).is_hermitian :=
107+
begin
108+
have hCB : Cᴴ = B, {rw ← hBC, simp},
109+
unfold matrix.is_hermitian,
110+
rw from_blocks_conj_transpose,
111+
congr;
112+
assumption
113+
end
114+
115+
/-- This is the `iff` version of `matrix.is_hermitian.from_blocks`. -/
116+
lemma is_hermitian_from_blocks_iff
117+
{A : matrix m m α} {B : matrix m n α} {C : matrix n m α} {D : matrix n n α} :
118+
(A.from_blocks B C D).is_hermitian ↔ A.is_hermitian ∧ Bᴴ = C ∧ Cᴴ = B ∧ D.is_hermitian :=
119+
⟨λ h, ⟨congr_arg to_blocks₁₁ h, congr_arg to_blocks₂₁ h,
120+
congr_arg to_blocks₁₂ h, congr_arg to_blocks₂₂ h⟩,
121+
λ ⟨hA, hBC, hCB, hD⟩, is_hermitian.from_blocks hA hBC hD⟩
122+
123+
end non_unital_semiring
124+
125+
section semiring
126+
127+
variables [semiring α] [star_ring α] [semiring β] [star_ring β]
128+
129+
@[simp] lemma is_hermitian_one [decidable_eq n] :
130+
(1 : matrix n n α).is_hermitian :=
131+
conj_transpose_one
132+
133+
end semiring
134+
135+
section ring
136+
137+
variables [ring α] [star_ring α] [ring β] [star_ring β]
138+
139+
@[simp] lemma is_hermitian.neg {A : matrix n n α} (h : A.is_hermitian) :
140+
(-A).is_hermitian :=
141+
(conj_transpose_neg _).trans (congr_arg _ h)
142+
143+
@[simp] lemma is_hermitian.sub {A B : matrix n n α} (hA : A.is_hermitian) (hB : B.is_hermitian) :
144+
(A - B).is_hermitian :=
145+
(conj_transpose_sub _ _).trans (hA.symm ▸ hB.symm ▸ rfl)
146+
147+
end ring
148+
149+
section is_R_or_C
150+
151+
variables [is_R_or_C α] [is_R_or_C β]
152+
153+
/-- A matrix is hermitian iff the corresponding linear map is self adjoint. -/
154+
lemma is_hermitian_iff_is_self_adjoint [fintype n] [decidable_eq n] {A : matrix n n α} :
155+
is_hermitian A ↔ inner_product_space.is_self_adjoint
156+
((pi_Lp.linear_equiv α (λ _ : n, α)).symm.conj A.to_lin' : module.End α (pi_Lp 2 _)) :=
157+
begin
158+
rw [inner_product_space.is_self_adjoint, (pi_Lp.equiv 2 (λ _ : n, α)).symm.surjective.forall₂],
159+
simp only [linear_equiv.conj_apply, linear_map.comp_apply, linear_equiv.coe_coe,
160+
pi_Lp.linear_equiv_apply, pi_Lp.linear_equiv_symm_apply, linear_equiv.symm_symm],
161+
simp_rw [euclidean_space.inner_eq_star_dot_product, equiv.apply_symm_apply, to_lin'_apply,
162+
star_mul_vec, dot_product_mul_vec],
163+
split,
164+
{ rintro (h : Aᴴ = A) x y,
165+
rw h },
166+
{ intro h,
167+
ext i j,
168+
simpa only [(pi.single_star i 1).symm, ← star_mul_vec, mul_one, dot_product_single,
169+
single_vec_mul, star_one, one_mul] using
170+
h (@pi.single _ _ _ (λ i, add_zero_class.to_has_zero α) i 1)
171+
(@pi.single _ _ _ (λ i, add_zero_class.to_has_zero α) j 1) }
172+
end
173+
174+
end is_R_or_C
175+
176+
end matrix
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/-
2+
Copyright (c) 2022 Alexander Bentkamp. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Alexander Bentkamp
5+
-/
6+
import analysis.inner_product_space.spectrum
7+
import linear_algebra.matrix.hermitian
8+
9+
/-! # Spectral theory of hermitian matrices
10+
11+
This file proves the spectral theorem for matrices. The proof of the spectral theorem is based on
12+
the spectral theorem for linear maps (`diagonalization_basis_apply_self_apply`).
13+
14+
## Tags
15+
16+
spectral theorem, diagonalization theorem
17+
18+
-/
19+
20+
namespace matrix
21+
22+
variables {𝕜 : Type*} [is_R_or_C 𝕜] [decidable_eq 𝕜] {n : Type*} [fintype n] [decidable_eq n]
23+
variables {A : matrix n n 𝕜}
24+
25+
open_locale matrix
26+
27+
namespace is_hermitian
28+
29+
variables (hA : A.is_hermitian)
30+
31+
/-- The eigenvalues of a hermitian matrix, indexed by `fin (fintype.card n)` where `n` is the index
32+
type of the matrix. -/
33+
noncomputable def eigenvalues₀ : fin (fintype.card n) → ℝ :=
34+
@inner_product_space.is_self_adjoint.eigenvalues 𝕜 _ _ (pi_Lp 2 (λ (_ : n), 𝕜)) _ A.to_lin'
35+
(is_hermitian_iff_is_self_adjoint.1 hA) _ (fintype.card n) finrank_euclidean_space
36+
37+
/-- The eigenvalues of a hermitian matrix, reusing the index `n` of the matrix entries. -/
38+
noncomputable def eigenvalues : n → ℝ :=
39+
λ i, hA.eigenvalues₀ $ (fintype.equiv_of_card_eq (fintype.card_fin _)).symm i
40+
41+
/-- A choice of an orthonormal basis of eigenvectors of a hermitian matrix. -/
42+
noncomputable def eigenvector_basis : basis n 𝕜 (n → 𝕜) :=
43+
(@inner_product_space.is_self_adjoint.eigenvector_basis 𝕜 _ _
44+
(pi_Lp 2 (λ (_ : n), 𝕜)) _ A.to_lin' (is_hermitian_iff_is_self_adjoint.1 hA) _
45+
(fintype.card n) finrank_euclidean_space).reindex
46+
(fintype.equiv_of_card_eq (fintype.card_fin _))
47+
48+
/-- A matrix whose columns are an orthonormal basis of eigenvectors of a hermitian matrix. -/
49+
noncomputable def eigenvector_matrix : matrix n n 𝕜 :=
50+
(pi.basis_fun 𝕜 n).to_matrix (eigenvector_basis hA)
51+
52+
/-- The inverse of `eigenvector_matrix` -/
53+
noncomputable def eigenvector_matrix_inv : matrix n n 𝕜 :=
54+
(eigenvector_basis hA).to_matrix (pi.basis_fun 𝕜 n)
55+
56+
lemma eigenvector_matrix_mul_inv :
57+
hA.eigenvector_matrix ⬝ hA.eigenvector_matrix_inv = 1 :=
58+
by apply basis.to_matrix_mul_to_matrix_flip
59+
60+
/-- *Diagonalization theorem*, *spectral theorem* for matrices; A hermitian matrix can be
61+
diagonalized by a change of basis.
62+
63+
For the spectral theorem on linear maps, see `diagonalization_basis_apply_self_apply`. -/
64+
theorem spectral_theorem :
65+
hA.eigenvector_matrix_inv ⬝ A =
66+
diagonal (coe ∘ hA.eigenvalues) ⬝ hA.eigenvector_matrix_inv :=
67+
begin
68+
rw [eigenvector_matrix_inv, basis_to_matrix_basis_fun_mul],
69+
ext i j,
70+
convert @inner_product_space.is_self_adjoint.diagonalization_basis_apply_self_apply 𝕜 _ _
71+
(pi_Lp 2 (λ (_ : n), 𝕜)) _ A.to_lin' (is_hermitian_iff_is_self_adjoint.1 hA) _ (fintype.card n)
72+
finrank_euclidean_space (euclidean_space.single j 1)
73+
((fintype.equiv_of_card_eq (fintype.card_fin _)).symm i),
74+
{ rw [eigenvector_basis, inner_product_space.is_self_adjoint.diagonalization_basis,
75+
to_lin'_apply],
76+
simp only [basis.to_matrix, basis.coe_to_orthonormal_basis_repr, basis.equiv_fun_apply],
77+
simp_rw [basis.reindex_repr, euclidean_space.single, pi_Lp.equiv_symm_apply', mul_vec_single,
78+
mul_one],
79+
refl },
80+
{ simp only [diagonal_mul, (∘), eigenvalues, eigenvector_basis,
81+
inner_product_space.is_self_adjoint.diagonalization_basis],
82+
rw [basis.to_matrix_apply, basis.coe_to_orthonormal_basis_repr, basis.reindex_repr,
83+
basis.equiv_fun_apply, pi.basis_fun_apply, eigenvalues₀, linear_map.coe_std_basis,
84+
euclidean_space.single, pi_Lp.equiv_symm_apply'] }
85+
end
86+
87+
end is_hermitian
88+
89+
end matrix

src/linear_algebra/matrix/to_lin.lean

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,14 @@ variables {l m n : Type*}
172172

173173
variables [fintype n] [decidable_eq n]
174174

175-
@[simp] lemma matrix.mul_vec_std_basis (M : matrix m n R) (i j) :
175+
lemma matrix.mul_vec_std_basis (M : matrix m n R) (i j) :
176176
M.mul_vec (std_basis R (λ _, R) j 1) i = M i j :=
177177
(congr_fun (matrix.mul_vec_single _ _ (1 : R)) i).trans $ mul_one _
178178

179+
@[simp] lemma matrix.mul_vec_std_basis_apply (M : matrix m n R) (j) :
180+
M.mul_vec (std_basis R (λ _, R) j 1) = Mᵀ j :=
181+
funext $ λ i, matrix.mul_vec_std_basis M i j
182+
179183
/-- Linear maps `(n → R) →ₗ[R] (m → R)` are linearly equivalent to `matrix m n R`. -/
180184
def linear_map.to_matrix' : ((n → R) →ₗ[R] (m → R)) ≃ₗ[R] matrix m n R :=
181185
{ to_fun := λ f i j, f (std_basis R (λ _, R) j 1) i,

0 commit comments

Comments
 (0)