perhaps this is more a question of theory than programming, but I can't find an acceptable answer. I wrote this code in C++, which tridiagonalizes the symmetric matrix A with the function HouseholderMain (which overwrites A and updates PP with the matrix that tridiagonalizes A), then finds the QR decomposition of A with the function QRtridiag (which overwrites A with its upper triangular form and gives the matrix Q of the decomposition) and finally calculates the vector of eigenvalues a and the matrix of eigenvectors X of the upper triangular matrix A with the function AutovTriang.
int main()
{
int n = 4;
double** A = createMatrix(n, n);
double** PP = createMatrix(n, n);
double** Q = createMatrix(n, n);
double** X = createMatrix(n, n);
double* a = createVector(n);
A[0][0] = 1;
A[0][1] = A[1][0] = 2;
A[0][2] = A[2][0] = -3;
A[0][3] = A[3][0] = 4;
A[1][1] = 1;
A[1][2] = A[2][1] = -1;
A[1][3] = A[3][1] = 9;
A[2][2] = 2;
A[2][3] = A[3][2] = 10;
A[3][3] = 0;
HouseholderMain(A, PP, n);
QRtridiag(A, Q, n);
AutovTriang(A, X, a, n);
return 0;
}
void HouseholderMain(double** A, double** PP, int n)
{
int i, j, k, l;
double sum;
double** P = createMatrix(n, n);
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
PP[i][j] = (i == j ? 1 : 0);
}
}
for (int l = 0; l < n - 2; l++) {
Householder2Cycle(A, P, n, l);
double** PA = multMatrices(P, A, n);
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
sum = 0;
for (k = 0; k < n; k++) {
sum += PA[i][k] * P[k][j];
}
A[i][j] = sum;
}
}
double** a = multMatrices(PP, P, n);
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
PP[i][j] = a[i][j];
}
}
}
}
void Householder2Cycle(double** A, double** P, int n, int k)
{
int i, j;
double sum, mod, mod2x, h;
double* x = createVector(n);
for (i = 0; i < k + 1; i++) {
x[i] = 0;
}
for (i = k + 2; i < n; i++) {
x[i] = A[i][k];
}
sum = 0;
for (i = k + 1; i < n; i++) {
sum += A[i][k] * A[i][k];
}
mod = (A[k+1][k] > 0 ? sqrt(sum) : -sqrt(sum));
x[k + 1] = A[k + 1][k] + mod;
sum = 0;
for (i = 0; i < n; i++) {
sum += x[i] * x[i];
}
mod2x = sum;
h = mod2x / 2;
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
P[i][j] = (i == j ? 1 : 0) - x[i] * x[j] / h;
}
}
freeVector(x);
}
void QRtridiag(double** A, double** Q, int n)
{
int i, j;
double theta, c, s, t, p, pp;
for (j = 0; j <= n - 2; j++) {
for (i = j + 1; i < n; i++) {
Q[i][j] = Q[j][i] = 0;
}
}
for (i = 0; i < n; i++) {
Q[i][i] = 1;
}
for (i = 0; i < n - 1; i++) {
theta = A[i][i] / A[i+1][i];
t = 1 / (fabs(theta) + sqrt(theta * theta + 1));
if (theta < 0) t = -t;
c = (1 - t * t) / (1 + t * t);
s = 2 * t / (1 + t * t);
p = A[i][i + 1];
A[i][i] = c * A[i][i] + s * A[i + 1][i];
A[i + 1][i] = 0;
if (i < n - 2) {
A[i][i + 2] = s * A[i + 1][i + 2];
A[i + 1][i + 2] = c * A[i + 1][i + 2];
}
A[i][i + 1] = c * p + s * A[i + 1][i + 1];
A[i + 1][i + 1] = -s * p + c * A[i + 1][i + 1];
for (j = 0; j <= i; j++) {
pp = Q[j][i];
Q[j][i] = c * pp;
Q[j][i+1] = -s * pp;
}
Q[i+1][i] = s;
Q[i + 1][i + 1] = c;
}
}
void AutovTriang(double** A, double** X, double* a, int n)
{
int i, j, k;
for (i = 0; i < n; i++) {
a[i] = A[i][i];
}
for (k = n - 1; k >= 2; k--) {
X[k][k] = 1;
X[k - 1][k] = -A[k - 1][k] / (a[k - 1] - a[k]);
for (i = k - 2; i >= 0; i--) {
X[i][k] = (-A[i][i + 1] * X[i + 1][k] - A[i][i + 2] * X[i + 2][k]) / (a[i] - a[k]);
}
}
X[1][1] = X[0][0] = 1;
X[0][1] = -A[0][1] / (a[0] - a[1]);
for (j = 0; j <= n - 2; j++) {
for (i = j + 1; i < n; i++) {
X[i][j] = 0;
}
}
}
My question is simply the following: how can I relate eigenvalues and eigenvectors of the final upper triangular matrix to those of the original matrix? I tried to understand the QR algorithm but I am not sure it offers what I need. I have the eigensystem of the triangular matrix yet and I think there is an easy way to calculate the eigensystem of the original matrix using the matrices PP and Q, but I cannot figure out how to do it. Can someone help me? Thanks you so much.
A direct relation is that the product of the diagonal elements of R (which are the eignvalues of R) times the determinant of Q (which is +1 or -1), from the QR decomposition, is equal to the product of the eigenvalues of your original A.
There are iterative procedures to get the eigenvalues of QR with repeated applications of the QR decomposition. Those procedures are sped up by the tri-diagonalization you have already done. The eigenvalues of your original A are equal to the eigenvalues of its tridiagonalization, and you can back-convert the eigenvectors using the transformation matrix you accumulated in
PP. If we call A' the tridiagonal form of A, then A' = PPT A PP, so A = PP A' PPT, where that transformation would convert the eigenvectors of A' to A.The simplest procedure to get the eigenvalues of A' is to get its QR decomposition, and compute RQ. Do a QR decomposition on that for a new Q and R, compute RQ again, until the diagonals of successive RQ's have sufficiently converged. The sequence of RQ's will approach diagonal, with the eigenvalues of A or A' on the diagonal.
This can take many iterations. A way to significantly speed up the convergence is by subtracting a constant from the diagonal elements of QR, and then adding that constant back to RQ. Choosing the minimum absolute value of the diagonal elements of QR as the constant works well. In a test I tried, that reduced the iterations from 547 to 32.