QR Decomposition

The QR (orthogonal triangular) decomposition is the most effective and widely used method to find all the eigenvalues of a general matrix. It decomposes the matrix into a normal orthogonal matrix QQ and an upper triangular matrix RR, so it is called the QR decomposition. There are two common methods of QR decomposition, one is based on Gram-Schmidt, and the other one is based on Householder reflectors.

QR decomposition based on Gram-Schmidt

Let say a matrix A=(a1a2a3)A=(a_{1} \quad a_{2} \quad a_{3}) for example, then Ax=(a1a2a3)x=bAx=(a_{1} \quad a_{2} \quad a_{3})x=b. To find the QQ and RR for AA, we need to orthogonalize and normalize every vector in AA (* for the first vector a1a_1, only normalization is required).

(1) Normalize a1a_1: g1=a1a1,a1=a1g1\quad g_{1}=\frac{a_{1}}{\parallel a_{1} \parallel}, \quad a_{1}=\left\|a_{1}\right\| g_{1}

(2) Orthogonalize a2a_2: a2=a2(a2g1)g1\quad a_{2}^{\prime}=a_{2}-(a_{2}^{\top}g_{1})g_{1}

(3) Normalize a2a_2: g2=a2a2,a2=(a2g1)g1+a2g2\quad g_{2}=\frac{a_{2}^{\prime}}{\left\|a_{2}^{\prime}\right\|}, \quad a_{2}=\left(a_{2}^{\top} g_{1}\right) g_{1}+\left\|a_{2}^{\prime}\right\| g_{2}

(4) Orthogonalise a3a_{3}: a3=a3(a3g1)g1(a3g2)g2\quad a_{3}^{\prime}=a_{3}-\left(a_{3}^{\top} g_{1}\right) g_{1}-\left(a_{3}^{\top} g_{2}\right) g_{2}

(5) Normalize a3a_{3}: g3=a3a3,a3=(a3g1)g1+(a3g2)g2+a3g3\quad g_{3}=\frac{a_{3}{ }^{\prime}}{\left\|a_{3}\right\|}, \quad a_{3}=\left(a_{3}^{\top} g_{1}\right) g_{1}+\left(a_{3}^{\top} g_{2}\right) g_{2}+\left\|a_{3}^{\prime}\right\| g_{3}

After the orthogonalization and normalization, we get Q=(g1g2g3)Q=\left(\begin{array}{lll} g_{1} & g_{2} & g_{3} \end{array}\right) and R=(a1a2g1a3g10a2a3g200a3)R=\left(\begin{array}{ccc} \left\|a_{1}\right\| & a_{2}^{\top} g_{1} & a_{3}^{\top} g_{1} \\ 0 & \left\|a_{2}\right\| & a_{3}^{\top} g_{2} \\ 0 & 0 & \left\|a_{3}\right\| \end{array}\right)

One problem with Gram-Schmidt is that g1,g2,g3g_{1}, g_{2}, g_{3} won’t be exactly orthogonal due to error in the computations (round-off).

And as g3g_3 depends on g1g_1 and g2g_2, the problem tends to get worse and worse. The vectors get less and less orthogonal in practice. Therefore, it’s better to use Householder reflectors when we apply QR decomposition.

QR decomposition based on Householder reflectors

Householder reflector: H=I2uu=I2vvv22,H2=IH=I-2uu^{\top}=I-\frac{2 v v^{\top}}{\|v\|_{2}{ }^{2}}, \quad H^{2}=I

For every reflector, it is orthogonal and doesn’t change length H2=HH=I\rightarrow H^{2}=H^{\top}H=I

Now consider AA as a 434*3 matrix for example, then QnQ_{n} (n=1,2,3)(n=1,2,3), all are square matrix with size of 444*4. We can find QnQ_{n} one by one following the steps below.

Find Q1Q_1 that makes Q1A=(xxx0xx0xx0xx)Q_{1}A=\left(\begin{array}{ccc}x & x & x \\ 0 & x & x \\ 0 & x & x \\ 0 & x & x\end{array}\right):

v=a1+a1e1\quad v=a_{1}+\left\|a_{1}\right\|e_{1} \quad (a1a_{1} is the first column in AA), Q1=I2vvv22\quad Q_{1}=I - \frac{2 v v^{\top}}{\|v\|_{2}{ }^{2}}

Find Q2Q_2 that makes Q2(Q1A)=(xxx0xx00x00x)Q_{2}(Q_{1}A)=\left(\begin{array}{ccc}x & x & x \\ 0 & x & x \\ 0 & 0 & x \\ 0 & 0 & x\end{array}\right):

v=a2+a2e1\quad v=a_{2}+\left\|a_{2}\right\|e_{1} \quad (a2a_{2} is last three elements of the second column in Q1AQ_{1}A)

Q2=I2vvv22\quad Q_{2}^{\prime}=I - \frac{2 v v^{\top}}{\|v\|_{2}{ }^{2}}, Q2=(10000Q21,1Q21,2Q21,30Q22,1Q22,2Q22,30Q23,1Q23,2Q23,3)\quad Q_{2}=\left(\begin{array}{cccc}1 & 0 & 0 & 0 \\ 0 & Q_{2_{1,1}}^{\prime} & Q_{2_{1,2}}^{\prime} & Q_{2_{1,3}}^{\prime} \\ 0 & Q_{2_{2,1}}^{\prime} & Q_{2_{2,2}}^{\prime} & Q_{2_{2,3}}^{\prime} \\ 0 & Q_{2_{3,1}}^{\prime} & Q_{2_{3,2}}^{\prime} & Q_{2_{3,3}}^{\prime} \end{array}\right)

Find Q3Q_3 that makes Q3(Q2Q1A)=(xxx0xx00x000)Q_{3}(Q_{2}Q_{1}A)=\left(\begin{array}{ccc}x & x & x \\ 0 & x & x \\ 0 & 0 & x \\ 0 & 0 & 0\end{array}\right):

v=a3+a3e1\quad v=a_{3}+\left\|a_{3}\right\|e_{1} \quad (a3a_{3} is last two elements of the third column in Q2(Q1A)Q_{2}(Q_{1}A))

Q3=I2vvv22\quad Q_{3}^{\prime}=I - \frac{2 v v^{\top}}{\|v\|_{2}{ }^{2}}, Q3=(1000010000Q31,1Q31,200Q32,1Q32,2)\quad Q_{3}=\left(\begin{array}{cccc}1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & Q_{3_{1,1}}^{\prime} & Q_{3_{1,2}}^{\prime} \\ 0 & 0 & Q_{3_{2,1}}^{\prime} & Q_{3_{2,2}}^{\prime} \end{array}\right)

Q3Q2Q1A=RA=Q1Q2Q3R=QRQ_{3} Q_{2} Q_{1} A=R \rightarrow A=Q_{1}^{\top} Q_{2}^{\top} Q_{3}^{\top} R=Q R, and we get Q=Q1Q2Q3Q=Q_{1}^{\top} Q_{2}^{\top} Q_{3}^{\top}

QR Decomposition in Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import math
import argparse
import numpy as np
from typing import Union

# QR decomposition based on Gram-Schmidt
def gram_schmidt(A):
cols = A.shape[1]
Q = np.copy(A)
R = np.zeros((cols, cols))
for col in range(cols):
for i in range(col):
k = np.sum(a[:, col] * Q[:, i]) / np.sum( np.square(Q[:, i]) )
Q[:, col] -= k*Q[:, i]
Q[:, col] /= np.linalg.norm(Q[:, col])
for i in range(cols):
R[col, i] = Q[:, col].dot( A[:, i] )
return Q, R

# QR decomposition based on Householder reflectors
def householder(alpha: float, x: np.ndarray) -> Union[np.ndarray, int]:
s = math.pow(np.linalg.norm(x, ord=2), 2)
v = x
if s == 0:
tau = 0
else:
t = math.sqrt(alpha * alpha + s)
v_one = alpha - t if alpha <= 0 else -s / (alpha + t)
tau = 2 * v_one * v_one / (s + v_one * v_one)
v /= v_one
return v, tau

def qr_decomposition(A: np.ndarray, m: int, n: int) -> Union[np.ndarray, np.ndarray]:
H = []
R = A
Q = A
I = np.eye(m, m)
for j in range(0, n):
# Apply Householder transformation.
x = A[j + 1:m, j]
v_householder, tau = householder(np.linalg.norm(x), x)
v = np.zeros((1, m))
v[0, j] = 1
v[0, j + 1:m] = v_householder
res = I - tau * v * np.transpose(v)
R = np.matmul(res, R)
H.append(res)
return Q, R

All articles in this blog adopt the CC BY-SA 4.0 agreement except for special statements. Please indicate the source for reprinting!