// QR ITERATION ############################################################# /* QR iteration for finding eigenvalues LANGUAGE: C++0x PRECISION: double AUTHOR: Hendrik Wernecke <wernecke@itp.uni-frankfurt.de> VERSION DATE: October 19, 2016 LICENCE: free to share, distribute and modify INCLUDES: standard libraries (and possible bugs) PRELIMINARIES works so far only with square matrices only valid for matrices with real eigenvalues DESCRIPTION By means of Householder decomposition a m*m matrix A is decomposed A = Q*R where Q is a m*m orthogonal matrix and R is a m*m upper triangular matrix. Using the iteration A_n-1 = Q_n*R_n and A_n = R_n*Q_n with initially A0 = A one can -- under certain circumstances -- converged in the limit lim_{n\to\infty}A_n = A' towards an upper triangular matrix A' which has the same eigenvalues than the original matrix A. */ //########################################################################### /* ### INCLUDES ### */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include <iostream> /* ### FUNCTIONS forward definitions ### */ // definitions below MAIN double** newMatrix(int m, int n); void matrixCopy(double** mat0, double** mat1, int m, int n); void UnitMatrix(double** mat, int m, int n); void printMatrix(double** mat, int m, int n); void printArray(double* arr, int m); double norm(double* arr, int m); void getColumn(double** mat, int k, double* arr, int m, int n); void multiplyMatrices(double** mat1, double** mat2, double** erg, int m, int n); void updateAMatrix(double** Ah, double** Qk, int k, int m, int n); void transposeMatrix(double** mat, double** tmat, int m, int n); bool qrHouseholderDecomp(double** A0, double** Q, double** R, int m, int n, double tol); /* ### MAIN ### */ int main(int argLength, char* argValues[]) { /* SETTINGS */ int maxitnum = 1000; // maximal number of iterations double tol = 1e-4; // tolerance for QR decomposition // and accuracy of eigenvalues /* // example 1) dimension 3*3 const int m = 3, n = 3; // dimensions of matrix (n!=m not yet implemented) double A[m][n] = { // matrix to be decomposed {12, -51, 4}, { 6, 167, -68}, {-4, 24, -41} }; */ /* // example 2) dimension 4*4 const int m = 4, n = 4; // dimensions of matrix (n!=m not yet implemented) double A[m][n] = { // matrix to be decomposed { 4, 1, 3, -2}, { 1, -2, 4, 1}, { 3, 4, 1, 2}, {-2, 1, 2, 3} }; */ // example 3) symmetric matrix int size = 8; if (argLength==2) size = atof(argValues[1]); const int m=size, n=size; double A[m][n]; for (int i=0; i<m; i++) { for (int j=i; j<n; j++) { A[i][j] = rand()%100; A[j][i] = A[i][j]; } } /* COMPUTATIONS */ // map A to real 2D array double** A0 = newMatrix(m, n); for (int i=0; i<m; i++) { for (int j=0; j<n; j++) { A0[i][j] = A[i][j]; } } // allocate matrices for storage of results double** Aprev = newMatrix(m, n); matrixCopy(A0, Aprev, m, n); double** Anext = newMatrix(m, n); double** Q = newMatrix(m, n); double** R = newMatrix(m, n); // some auxiliar variables int itnum = 0; bool check = false; // get started with the iteration while ( itnum<maxitnum ) { //printf("A_%i = \n", itnum); printMatrix(Aprev, m, n); if (not qrHouseholderDecomp(Aprev, Q, R, m, n, tol)) { printf("QR decomposition failed!\n"); return 1; } multiplyMatrices(R, Q, Anext, m, n); // check if converged (comparing diagonal entries) double diff = 0; for (int i=0; i<m && i<n; i++) { diff += fabs(Aprev[i][i]-Anext[i][i]); } //printf("diff = %f\n", diff); if ( diff<=tol ) { check = true; break; } matrixCopy(Anext, Aprev, m, n); itnum++; } /* RESULTS */ // announce final result if (check) { printf("eigenvalues converged within %i steps of iteration\n", itnum); printf("original matrix A_0 = \n"); printMatrix(A0, m, n); printf("iterated matrix A_%i = \n", itnum); printMatrix(Anext, m, n); printf("eigenvalues are: "); for (int i=0; i<m; i++) { printf(" %f, ", Anext[i][i]); } printf("\n"); } else { printf("iteration did not converge in %i steps\nresulting in\n", itnum); printf("A_%i\n", itnum); printMatrix(Anext, m, n); } /* CLEANING UP */ delete [] A0; delete [] Aprev; delete [] Anext; delete [] Q; delete [] R; return 0; } // END of MAIN /* ### FUNCTIONS DEFINITIONS ### */ bool qrHouseholderDecomp(double** A0, double** Q, double** R, const int m, const int n, double tol) // performs a QR decomposition base on Householder reflection // see <https://en.wikipedia.org/wiki/QR_decomposition#Using_Householder_reflections> // takes the matrix <A0> to be decomposed and the matrices <Q>, <R> (resulting composites) // all matrices are <m>*<n> matrices // for selfconsistent check A0=Q*R the tolerance <tol> is encountered { // auxiliar matrix H double** H = newMatrix(m, n); UnitMatrix(H, m, n); // create local copy double** Ak = newMatrix(m, n); matrixCopy(A0, Ak, m, n); for (int k = 0; k<m-1 && k<n ; k++) { //printf("before k = %i, Ah = \n", k); printMatrix(Ak, m, n); // get first column double x[m]; getColumn(Ak, k, x, m, n); //printf("x = \n"); printArray(x, m); // compute alpha as norm of first column double alpha = norm(x, m); //printf("alpha = %f\n", alpha); double e[m]; for (int i=0; i<m; i++) { e[i] = 0; } e[k] = 1; // k-th unit vector double u[m]; for (int i=0; i<m; i++) { u[i] = x[i] - alpha * e[i]; } double unorm = norm(u, m); //printf("u = \n"); printArray(u, m); double v[m]; for (int i=0; i<m; i++) { v[i] = u[i]/unorm; } //printf("v = \n"); printArray(v, m); // assemble l-th Householder matrix Qk = 1-2 v*v^T double** Qk = newMatrix(m, n); for (int i=0; i<m; i++) { for (int j=0; j<n; j++) { if (i==j) { // diagonal Qk[i][j] = 1 - 2*v[i]*v[j]; } else { // non-diagonal Qk[i][j] = - 2*v[i]*v[j]; } } } //printf("Qk = \n"); printMatrix(Qk, m, n); // updating Ah matrix updateAMatrix(Ak, Qk, k, m, n); //printf("A_k = \n"); printMatrix(Ak, m, n); // store product of Q_k (H=Q_n*Q_n-1*...*Q_1) double** Hh = newMatrix(m, n); matrixCopy(H, Hh, m, n); multiplyMatrices(Qk, H, Hh, m, n); H = newMatrix(m, n); matrixCopy(Hh, H, m, n); //printf("H = \n"); printMatrix(H, m, n); delete [] Hh; delete [] Qk; } // Q = (Hn*...*H1)^T = H^T UnitMatrix(Q, m, n); transposeMatrix(H, Q, m, n); //printf("Q = \n"); printMatrix(Q, m, n); // R = Q^T*A = H*A UnitMatrix(R, m, n); multiplyMatrices(H, A0, R, m, n); //printf("R = \n"); printMatrix(R, m, n); // check that A = Q*R double** Ah = newMatrix(m, n); multiplyMatrices(Q, R, Ah, m, n); //printf("A = \n"); printMatrix(Ah, m, n); // check deviation double dev = 0; for (int i=0; i<m; i++) { for (int j=0; j<n; j++) { dev += fabs(Ah[i][j]-A0[i][j]); } } if (dev<=tol) {return true;} else {return false;} } double** newMatrix(int m, int n) // returns pointer to new <m>*<n> matrix { // allocate new matrix double** mat = (double**) malloc(m*sizeof(double)); for (int i=0; i<m; i++) { mat[i] = (double*) malloc(n*sizeof(double)); } return mat; } void matrixCopy(double** mat0, double** mat1, int m, int n) // copy content of matrix <mat0> to matrix <mat1>, both <m>*<n> { // copy entries for (int i=0; i<m; i++) { for (int j=0; j<n; j++) { mat1[i][j] = mat0[i][j]; } } return; } void UnitMatrix(double** mat, int m, int n) // fills the <m>*<n> matrix <mat> with a unity matrix { // write entries for (int i=0; i<m; i++) { for (int j=0; j<n; j++) { if (i==j) { mat[i][j] = 1; } else { mat[i][j] = 0; } } } return; } void printMatrix(double** mat, int m, int n) // prints the matrix <mat> with <m> rows and <n> columns { printf("\n"); for (int i=0; i<m; i++) { for (int j=0; j<n; j++) { printf(" %8.3f", mat[i][j]); } printf("\n"); } printf("\n"); return; } void printArray(double* arr, int m) // prints the array <arr> with length <m> { printf("\n"); for (int i=0; i<m; i++) { printf(" %8.3f", arr[i]); } printf("\n"); return; } double norm(double* arr, int m) // computes the norm of an array <arr> with length <m> { double h = 0; for (int i=0; i<m; i++) { h += arr[i]*arr[i]; } return sqrt(h); } void getColumn(double** mat, int k, double* arr, int m, int n) // extracts the <k>-th column of a <m>*<n> matrix <mat> and saves it to array <arr> { if ( k<0 or k>=n ) { arr = NULL; return; } for (int i=0; i<m; i++) { arr[i] = mat[i][k]; } return; } void multiplyMatrices(double** mat1, double** mat2, double** erg, int m, int n) // multiplies the matrices <mat1> and <mat2> { for (int i=0; i<m; i++) { for (int j=0; j<n; j++) { erg[i][j] = 0; } } // compute product for (int i=0; i<m; i++) { for (int j=0; j<n; j++) { for (int k=0; k<n; k++) { erg[i][j] += mat1[i][k]*mat2[k][j]; } } } return; } void updateAMatrix(double** Ah, double** Qk, int k, int m, int n) // updateds the new matrix <Ah> for next Householder step // takes the Householder matrix <Qk>, both <m>*<n> matrices // and the Householder iteration step number <k> { // copy Qk*A double** QA = newMatrix(m, n); multiplyMatrices(Qk, Ah, QA, m, n); // create dummy double** H = newMatrix(m, n); /* assemble new matrix */ // k+1 zero columns for (int i=0; i<k+1; i++) { for (int j=0; j<n; j++) { H[i][j] = 0; } } // k+1 zero rows for (int i=0; i<m; i++) { for (int j=0; j<k+1; j++) { H[i][j] = 0; } } // k+1 diagonal elements set to one for (int i=0; i<k+1; i++) { H[i][i] = 1; } // rest of the matrix filled with values from QA for (int i=k+1; i<m; i++) { for (int j=k+1; j<n; j++) { H[i][j] = QA[i][j]; } } // copy result to Ah for (int i=0; i<m; i++) { for (int j=0; j<n; j++) { Ah[i][j] = H[i][j]; } } // clean up delete [] QA; delete [] H; return; } void transposeMatrix(double** mat, double** tmat, int m, int n) // transposes the <m>*<n> matrix <mat> // and stores the transpose in the <n>*<m> matrix <tmat> { // transpose for (int j=0; j<n; j++) { for (int i=0; i<m; i++) { tmat[j][i] = mat[i][j]; } } return; } // ### END OF FILE ###