# Jaccobi Eigenvalue Algorithm
應用時機: 求解**實對稱矩陣**的特徵值與特徵向量,在矩陣階數小於10以下,相對於QR分解快速。
## 原理
如果矩陣 $A$ 是實對稱矩陣,可以透過一系列精心設計過的旋轉矩陣組成正交矩陣 $V$,使得 $D=V^TAV$ 為對角矩陣。該變換實則為相似變換,所以矩陣 $D$ 的對角元素為 $A$ 的特徵值。
Jacobi特徵法由 $D = R_k^T...R_1^TAR_1...R_k$ 形式組成。每個正交變換(**Jacobi rotations**)都是在旋轉平面,主要是在消滅**非對角**元素。
最後該方法收斂的證明詳見 [線代啟示錄-Jacobi 特徵值算法](https://ccjou.wordpress.com/2015/09/17/jacobi-%E7%89%B9%E5%BE%B5%E5%80%BC%E7%AE%97%E6%B3%95/)。
:::spoiler Given Rotation 如何消滅非對角元素?
詳見[Givens 旋轉於 QR 分解的應用](https://ccjou.wordpress.com/2010/02/18/givens-%E6%97%8B%E8%BD%89%E6%96%BC-qr-%E5%88%86%E8%A7%A3%E7%9A%84%E6%87%89%E7%94%A8/)
舉2維空間作為例子,令$c=cos\theta$,$s=sin\theta$。Given Rotation旨在以$x_p$為旋轉軸,消滅$x_q$,如下所述:
$$
\begin{bmatrix}
c & -s\\
s & c \\
\end{bmatrix}
\begin{bmatrix}
x_p \\
x_q \\
\end{bmatrix}
\begin{bmatrix}
r \\
0 \\
\end{bmatrix}
$$
:::
:::spoiler Jacobi Rotation 如何消滅非對角元素?
根據上述式子 $D=V^TAV$,分成兩個部分介紹:$D$ 對角矩陣 以及 $V$ 正交矩陣。
==**(1) $D$ 對角矩陣由來**==
舉2維空間作為例子,精心設計$\theta$,使得$d_{pq}=d_{qp}=0$,求$\theta$:
$$
D=
\begin{bmatrix}
d_{pp} & d_{pq} \\
d_{qp} & d_{qq} \\
\end{bmatrix}
=R^TAR=
\begin{bmatrix}
cos\theta & sin\theta \\
-sin\theta & cos\theta \\
\end{bmatrix}
\begin{bmatrix}
a_{pp} & a_{pq} \\
a_{pq} & a_{qq} \\
\end{bmatrix}
\begin{bmatrix}
cos\theta & -sin\theta \\
sin\theta & cos\theta \\
\end{bmatrix}
$$
乘開可得,並令其為0:
$$
d_{pq}=cos\theta sin\theta (a_{qq}-a_{pp}) + (cos^2\theta - sin^2\theta) a_{pq}=d_{qp}=0
$$
透過倍角公式 $cos2\theta = cos^2\theta - sin^2\theta$,$sin2\theta = 2cos\theta sin\theta$ 以及 $tan2\theta = \frac{2tan\theta}{1-tan^2\theta}$ ,可得:
$$
tan2\theta = \frac{2a_{pq}}{a_{pp}-a_{qq}}=\frac{2tan\theta}{1-tan^2\theta}
$$
令其 ==$\phi=1/tan2\theta$== 以及 ==$t=tan\theta$==,
$$
\phi=\frac{ 1 - t^2 }{ 2t }=\frac{ a_{pp} - a_{qq} }{ 2a_{pq} } \implies t^2+2t\phi -1 = 0
$$
選擇絕對值較小的解 $t_{min}=sgn(\phi)(-|\phi|+\sqrt{\phi^2+1})$,為了避免兩個相近大小的數相減造成[catastrophic cancellation](https://zh.wikipedia.org/wiki/%E7%81%BE%E9%9A%BE%E6%80%A7%E6%8A%B5%E6%B6%88),對 $t_{min}$ 乘上 $\frac{|\phi+\sqrt{\phi^2+1}|}{|\phi+\sqrt{\phi^2+1}|}$ ,最後得出:
<center>
==$t=\frac{sgn(\phi)}{|\phi|+\sqrt{\phi^2+1}}$==
</center>
但如果 $\phi >> 1$ ,恐會讓 $\phi^2$ 造成overflow,因此在該狀況下讓 ==$t=\frac{1}{2\phi}$==。
另外Jacobi特徵法的參數值如下,
<center>
==$c=cos\theta=\frac{1}{\sqrt{1+t^2}}$== , ==$s=sin\theta=t \cdot c$==
</center>
當$2\theta = arctan\frac{2a_{pq}}{a_{qq}-a_{pp}}$時,使得$d_{pq}=d_{qp}=0$。證明了透過jacobi rotation二階實對稱矩陣可正交對角化。
**擴展至n維狀況**:
正交相似變換定義為$A'=R_{pq}^T \cdot A \cdot R_{pq}$,其中旋轉矩陣如下所示:
$$
R_{pq}=
\begin{bmatrix}
1 & & & & & & \\
&...& & & & & \\
& & c &...&-s& & \\
& & : & 1 & :& & \\
& & s &...& c& & \\
& & & & &... & \\
& & & & & & 1\\
\end{bmatrix}
$$
上述操作可知 $R_{pq}^T \cdot A$ **改變**了 $A$ 的==p,q列== 以及$A \cdot R_{pq}$則**改變**了 $A$的==p,q行==。 如下圖所示,黃線處為變動項:
<center>
<img width=45% src=https://hackmd.io/_uploads/HkrP3wEQj.png>
</center>
乘開後便有以下規律:
<center>
\begin{cases}
a'_{rp}=ca_{rp}-sa_{rq}, & \text{$r \neq p, r\neq q$} \\
a'_{rq}=ca_{rq}+sa_{rp}, & \text{$r \neq p, r\neq q$} \\
a'_{pp}=c^2a_{pp}+s^2a_{qq}-2sc\cdot a_{pq} \\
a'_{qq}=s^2a_{pp}+c^2a_{qq}+2sc\cdot a_{pq} \\
a'_{pq} = (c^2-s^2)a_{pq} + sc(a_{pp}-a_{qq})=0
\end{cases}
</center>
整理成:
$$
\begin{bmatrix}
a'_{rp}\\
a'_{rq}\\
\end{bmatrix}=
\begin{bmatrix}
c & -s\\
s & c\\
\end{bmatrix}
\begin{bmatrix}
a_{rp}\\
a_{rq}\\
\end{bmatrix},\quad r \neq p, r\neq q \\
a'_{pp}=a_{pp}-t \cdot a_{pq} \\
a'_{qq}=a_{qq}+t \cdot a_{pq} \\
a'_{pq}=0
$$
==**(2) $V$ 正交矩陣由來**==
從上述迭代式,可知正交矩陣 $V$ 之形式:
$$
A=VDV^T \Longleftrightarrow D=V^TAV=(R_k^T \cdots R_1^T)A(R_1 \cdots R_k)
$$
代表 $V=I\cdot R_1\cdots R_k$,公式整理如下:
$$
\begin{bmatrix}
v'_{rp}\\
v'_{rq}\\
\end{bmatrix}=
\begin{bmatrix}
c & -s\\
s & c\\
\end{bmatrix}
\begin{bmatrix}
v_{rp}\\
v_{rq}\\
\end{bmatrix}
$$
:::
::: spoiler 那些Jacobi Rotations以C語言實現的坑
因為$A$矩陣為實對稱矩陣,實質上也只需要尋訪上三角$n(n-1)/2$個。而因為c語言實現,故旋轉就分成三種case。
<center>
<img width=45% src=https://hackmd.io/_uploads/HJxTlgBXi.png>
</center>
```csharp
//1. Case of rotations 0 ≤ j< p.
for (int j = 0; j<ip; j++)
Rotate(ref A, j, ip, j, iq, c, s);
//2. Case of rotations p+1 ≤ j< q.
for (int j = ip+1; j<iq; j++)
Rotate(ref A, ip, j, j, iq, c, s);
//3. Case of rotations q+1 ≤ j< n.
for (int j = iq+1; j<n; j++)
Rotate(ref A, ip, j, iq, j, c, s);
void Rotate(double[,] A, int r1, int c1, int r2, int c2, double c, double s)
{
double num = A[r1, c1];
double num2 = A[r2, c2];
A[r1, c1] = num * c - num2 * s;
A[r2, c2] = num * s + num2 * c;
}
```
:::
::: spoiler Cyclic Jacobi method 代碼
```csharp=
//From Numerical Recipes in C. Second Edition
public static void JacobiEigenvalueAlgorithm(
double[,] A,
ref double[] W,
ref double[,] V,
out int totalitera)
{
int n = A.GetLength(0);
totalitera = 0;
int Maxitera = n * n * 30;
for (int i = 0; i < n; i++)
{
//Initialize V to the identity matrix
if (V != null)
V[i, i] = 1.0;
//Initialize W to the diagonal of A matrix
W[i] = A[i, i];
}
double Magnitude_OffDiag, h, t, c, s, diagAddtemr;
double threshold;
double sum_OffDiag;
for (int i = 0; i<Maxitera; i++)
{
//Sum off-diagonal elements.
sum_OffDiag=0;
for (int ip = 0; ip<n-1; ip++)
for (int iq = ip+1; iq<n; iq++)
sum_OffDiag +=Math.Abs(A[ip, iq]);
// if the sum of off-diagonal elements is zeros,
// then it reaches convergence.
if (sum_OffDiag <2.2204460492503131E-16)
break;
threshold = (i<4) ? 0.2*sum_OffDiag/(n*n) : 0;
for (int ip = 0; ip<n-1; ip++)
{
for (int iq = ip+1; iq<n; iq++)
{
Magnitude_OffDiag=100.0*Math.Abs(A[ip, iq]);
//After four sweeps,
//skip the rotation
//if the off-diagonal element is small
//(W[ip] and W[iq] >> 100*|A[ip,iq]| ).
if (i>3
&& (Math.Abs(W[ip])+Magnitude_OffDiag)==Math.Abs(W[ip])
&& (Math.Abs(W[iq])+Magnitude_OffDiag)==Math.Abs(W[iq])
)
{
A[ip, iq]=0;
}
else if (Math.Abs(A[ip, iq])>threshold)
{
h=W[iq]-W[ip];
if ((Math.Abs(h)+Magnitude_OffDiag)==Math.Abs(h))
t=A[ip, iq]/h;
else
{
//1. phi = (A[p,p] -A[q,q]) / (2*A[p,q]);
//2. t = tan(theta) = sgn(phi) /(|phi|+sqrt(phi^2 +1));
double phi = 0.5 * h/A[ip, iq];
t = 1.0/(Math.Abs(phi)+Math.Sqrt(1.0+phi*phi));
if (phi<0)
t=-t;
}
//3. c= cos(theta) = 1 / sqrt(1 + tan(theta)^2 )
//4. s= sin(theta) = tan(theta) * cos(theta)
c=1.0/Math.Sqrt(1+t*t);
s=t*c;
// Follow eq(11.1.14)
diagAddtemr=t*A[ip, iq];
W[ip] -= diagAddtemr;
W[iq] += diagAddtemr;
A[ip, iq]=0;
//Case of rotations 0 ≤ j< p.
for (int j = 0; j<ip; j++)
Rotate(ref A, j, ip, j, iq, c, s);
//Case of rotations p+1 ≤ j< q.
for (int j = ip+1; j<iq; j++)
Rotate(ref A, ip, j, j, iq, c, s);
//Case of rotations q+1 ≤ j< n.
for (int j = iq+1; j<n; j++)
Rotate(ref A, ip, j, iq, j, c, s);
if (V != null)
{
for (int j = 0; j<n; j++)
Rotate(ref V, j, ip, j, iq, c, s);
}
totalitera++;
}
}
}
}
//sorting by descent via bubble sort
for (int j = 0; j < n - 1; j++)
{
int key = j;
for (int i = j + 1; i < n; i++)
{
if (Math.Abs(W[key]) < Math.Abs(W[i]))
key = i;
}
if (j != key)
{
swap(ref W[key], ref W[j]);
if (V!=null)
{
for (int i = 0; i < n; i++)
swap(ref V[i, key], ref V[i, j]);
}
}
}
}
private static void Rotate(
ref double[,] A,
int row1, int col1,
int row2, int col2,
double c, double s)
{
double num = A[row1, col1];
double num2 = A[row2, col2];
A[row1, col1] = num * c - num2 * s;
A[row2, col2] = num * s + num2 * c;
}
```
:::
## Reference:
1. Numerical Recipes in C , Second Edition (1992), Ch11.
2. Jacobi eigenvalue algorithm, WIKI
3. [線代啟示錄-Jacobi 特徵值算法](https://ccjou.wordpress.com/2015/09/17/jacobi-%E7%89%B9%E5%BE%B5%E5%80%BC%E7%AE%97%E6%B3%95/)
4. [Givens 旋轉於 QR 分解的應用](https://ccjou.wordpress.com/2010/02/18/givens-%E6%97%8B%E8%BD%89%E6%96%BC-qr-%E5%88%86%E8%A7%A3%E7%9A%84%E6%87%89%E7%94%A8/)
###### tags: `Math`