大好きな対称行列をsympyで固有値分解する 

# はじめに

『秋葉原ロボット部 理論グループ Advent Calendar 2023』13日目の投稿です
線形代数が大好きで、スキが高じて量子力学の勉強会にも参加しています
線形代数のなかでも個人的に一番好きな『対称行列』について書いてみました
コメントなどございましたらありがたいです（お手柔らかに）

この記事は、[大好きな対称行列をsympyでスペクトル分解する](https://qiita.com/osawat/items/009c51e4d82c25070234)の前編です

# まずは、転置行列

- 転置行列は元の行列に対して次のように行と列が転置された行列です
    - 転置の転置は元通りです($(A^T)^T=A$)

<img src="https://upload.wikimedia.org/wikipedia/commons/e/e4/Matrix_transpose.gif" width="12%">

【例】

```math
\begin{array}{l}
元々\\
\ \ \ \ A = \begin{bmatrix} 1 & 2  \\ 3 & 4 \\ 5 & 6 \end{bmatrix} \ \ (3\times 2)\\
転置\\
\ \ \ \ A^T = \begin{bmatrix} 1 & 3 & 5  \\ 2 & 4 & 6 \end{bmatrix} \ \ (2\times 3)\\
\end{array}
```


## sympyで試してみる

sympyはPythonで使える数式を扱えるライブラリです
行列の計算はとても手間がかかります、人生を無駄使いしないようツールを使いましょう
jupyter notebookベースで使うことを前提としてsympyでコードを記述します

### 初期設定
お約束です
```py
# 初期設定
from sympy import *
init_printing()

```
### 行列の定義
ここでは2行3列の行列を定義しました
```py 
# 行列の定義
A = Matrix([
        [1,2],
        [3,4],
        [5,6]
    ])
A
```
```math
\begin{bmatrix} 1 & 2  \\ 3 & 4 \\ 5 & 6 \end{bmatrix}
```

### 転置行列
行列$A$を転置するには転置(Transpose)を表す$.T$を$A$の後ろに付けます
```py 
# 転置行列
A.T
```
```math
\begin{bmatrix} 1 & 3 & 5  \\ 2 & 4 & 6 \end{bmatrix}
```


# いよいよ、対称行列

- 対称行列は以下のような行列です
  - 正方行列 (行と列の数が同じ)で
  - 転置したものが同じ$A=A^T$
    - 対角(左上から右下の要素)を中心に対称
    - $a_{i,j}=a_{j,i}$

<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/3/32/Matrix_symmetry_qtl1.svg/640px-Matrix_symmetry_qtl1.svg.png" width="20%">

【例】
```math
\begin{array}{l}
A = \begin{bmatrix} 1 & 2  \\ 2 & 3  \end{bmatrix} \ \ (2\times 2)\\
B = \begin{bmatrix} 1 & 4 & -5 \\
4 & 2 & 6 \\
-5 & 6 & 3 \end{bmatrix} \ \ (3 \times 3)
\end{array}
```


## 対称行列の性質

対称行列は、例えば、以下のようなとても良い性質を持っています

- 固有値はすべて実数
- 正規直交する固有ベクトルを選ぶことができる
    - 選ぶことができるというのが微妙ないいまわしです
- スペクトル定理
    - 別途、記述予定

## sympyで試してみる

### 対称行列の定義

```py
# 対称行列の定義
A = Matrix([
        [3, -1, -1],
        [-1, 3, -1],
        [-1, -1, 3]
    ])
A
```

```math
\begin{bmatrix} 3 & -1 & -1 \\
-1 & 3 & -1 \\
-1 & -1 & 3 \end{bmatrix}
```

一応、転置行列であるかを確認してみる
```py
# 転置
A==A.T
```

```math
True
```
# 対称行列の固有値分解
対称行列を固有値分解することによって対角化やスペクトル分解することができます
3x3行列の対角化について概略を記述します（丁寧ではありません、スイマセン）


```math
\begin{array}{l}
A\vec{x_1} = \lambda_1 \vec{x_1} & (1) \\
A\vec{x_2} = \lambda_2 \vec{x_2} & (2) \\
A\vec{x_3} = \lambda_3 \vec{x_3} & (3) \\
\\ 

(1),(2),(3)は以下のようにまとめられる\\
A \underbrace{[ \vec{x_1} \ \vec{x_2} \ \vec{x_3}}_{S}] =  \underbrace{[ \vec{x_1} \ \vec{x_2} \ \vec{x_3} ]}_{S}
\underbrace{ \begin{bmatrix}
\lambda_1  & 0 & 0 \\
0 & \lambda_2 & 0 \\
0 & 0 & \lambda_3
\end{bmatrix}}_{\Lambda} \\
AS = S \Lambda \\
\\
これから \\
A = S \Lambda S^{-1} \ \ (固有値分解) \\
\Lambda = S^{-1} AS \ \ (対角化)\\
\\
\end{array}
```

## sympyで試してみる
### 固有値分解してみる

行列`A`に対して`.diagonalize()`メソッドを使って固有値分解します
- 戻り値
    - 行列`S`：固有ベクトルが列ベクトルとなっている行列
        - 特に引数として`normalize=True`とすると固有ベクトルが正規化される
    - 行列`Lambdas`： 対角成分が固有値となっている対角行列

```py
# 固有値分解
S, Lambdas = A.diagonalize(normalize=True)
S, Lambdas # 固有ベクトル、固有値ベクトル
```

```math
\left(
\begin{bmatrix}
\frac{\sqrt{3}}{3} & -\frac{\sqrt{2}}{2} & -\frac{\sqrt{2}}{2} \\
\frac{\sqrt{3}}{3} & \frac{\sqrt{2}}{2}, &0 \\
\frac{\sqrt{3}}{3} &  0 & \frac{\sqrt{2}}{2} \\
\end{bmatrix}
 \ , \
\begin{bmatrix}
1 & 0 & 0 \\
0 & 4 & 0 \\
0 & 0 & 4 \\
\end{bmatrix}
\right)
```

### 結果確認

- 固有値は`Lambdas`の対角成分で、$1, 4, 4$ であり$4$が重解となっています
- 固有ベクトルは`S`のそれぞれの列ベクトルです
    - ここでは正規化するよう`normalize=True`としたのでノルムは$1$です

```py
# 固有ベクトルと固有値を取り出す
x1, x2, x3 = S[:,0] , S[:,1] , S[:,2]  # 固有ベクトル
lambda1, lambda2, lambda3 = Lambdas.diagonal()  # 固有値
x1, x2, x3, lambda1, lambda2, lambda3
```

```math
\left(
\begin{bmatrix}
\frac{\sqrt{3}}{3} \\
\frac{\sqrt{3}}{3} \\
\frac{\sqrt{3}}{3} \\
\end{bmatrix}
 \ , \
\begin{bmatrix}
-\frac{\sqrt{2}}{2} \\
\frac{\sqrt{2}}{2} \\
0  \\
\end{bmatrix}
 \ , \
\begin{bmatrix}
-\frac{\sqrt{2}}{2} \\
0 \\
\frac{\sqrt{2}}{2} \\
\end{bmatrix}
\ , \  1 \ , \ 4  \ , \ 4 
\right)
```
- $S$の列ベクトルがそれぞれ固有ベクトルです (`x1, x2, x3`)
- $\Lambda$(Lambdas)の対角成分がそれぞれ固有値です (`lambda1, lambda2, lambda3`)

### ちゃんと固有値分解されているか復元して確認

$A=S\Lambda S^{-1}$からちゃん元の$A$になるかを確認 ($AS=S\Lambda$)

```py
# 検算 : 元通りになるか確認
S * Lambdas * S.inv()
```

```math
\begin{bmatrix} 3 & -1 & -1 \\
-1 & 3 & -1 \\
-1 & -1 & 3 \end{bmatrix}
```
- 復元できました

### ちゃんと固有ベクトルが正規化されているか
`normalize=True`
```py
# 正規化されているか確認
x1.norm(), x2.norm(), x3.norm()
```

```math
(1, 1, 1)
```
- 正規化されていますね

### ちゃんと対角化できるか確認

$\Lambda=S^{-1}AS$で対角化されるかを確認 ($AS=S\Lambda$)
```py
# 対角化の確認
S.inv()*A*S
```
```math
\begin{bmatrix}
1 & 0 & 0 \\
0 & 4 & 0 \\
0 & 0 & 4 \\
\end{bmatrix}
```

- 対角化できました 

### ちゃんと固有ベクトルが直交しているか確認、アレッ！？

固有ベクトルが直交しているかどうかは、内積をとればわかります

```py
# 内積
# (x1,x2), (x1,x3), (x2,x3)
x1.T*x2,  x1.T*x3, x2.T*x3
```

```math
\left([0],[0],[\frac{1}{2}]\right)
```

上記から、以下のことがわかりました

- $x1 \perp x2 \ , \ x1 \perp x3$
- $x2 \not \perp x3$

つまり、$x2$ と $x3$は直交していないってことです
理由は、これらの固有値は重解で同じ$4$であったからですが・・・
- 対称行列の固有ベクトルは
    - 固有値が異なれば必ず直交します
    - 固有値が同じものでも直交するように固有ベクトルを選ぶことができます
    
- 固有値が同じものの固有ベクトルは、ひと手間いるようです
- ひと手間の話は、[大好きな対称行列をsympyでスペクトル分解する](https://qiita.com/osawat/items/009c51e4d82c25070234)に続きます


# まとめ
対称行列は本当にいい仕事をしてくれます
複素行列における対称行列であるエルミート行列は量子力学での主役だ（と思っています）
少しは対称行列が好きになってもらえたらうれしいです
後編では、スペクトル分解と上記の『アレッ！』について記述します

<hr>
参考文献

- 『ストラング線形代数イントロダクション』
- 『量子力学10講』