/************************************************************************************* Grid physics library, www.github.com/paboyle/Grid Source file: ./lib/algorithms/iterative/Francis.h Copyright (C) 2015 Author: Peter Boyle This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. See the full license in the file "LICENSE" in the top level distribution directory *************************************************************************************/ /* END LEGAL */ #ifndef FRANCIS_H #define FRANCIS_H #include #include #include #include #include #include #include #include #include //#include //#include //#include namespace Grid { template int SymmEigensystem(DenseMatrix &Ain, DenseVector &evals, DenseMatrix &evecs, RealD small); template int Eigensystem(DenseMatrix &Ain, DenseVector &evals, DenseMatrix &evecs, RealD small); /** Find the eigenvalues of an upper hessenberg matrix using the Francis QR algorithm. H = x x x x x x x x x x x x x x x x x x 0 x x x x x x x x 0 0 x x x x x x x 0 0 0 x x x x x x 0 0 0 0 x x x x x 0 0 0 0 0 x x x x 0 0 0 0 0 0 x x x 0 0 0 0 0 0 0 x x Factorization is P T P^H where T is upper triangular (mod cc blocks) and P is orthagonal/unitary. **/ template int QReigensystem(DenseMatrix &Hin, DenseVector &evals, DenseMatrix &evecs, RealD small) { DenseMatrix H = Hin; int N ; SizeSquare(H,N); int M = N; Fill(evals,0); Fill(evecs,0); T s,t,x=0,y=0,z=0; T u,d; T apd,amd,bc; DenseVector p(N,0); T nrm = Norm(H); ///DenseMatrix Norm int n, m; int e = 0; int it = 0; int tot_it = 0; int l = 0; int r = 0; DenseMatrix P; Resize(P,N,N); Unity(P); DenseVector trows(N,0); /// Check if the matrix is really hessenberg, if not abort RealD sth = 0; for(int j=0;j small){ std::cout << "Non hessenberg H = " << sth << " > " << small << std::endl; exit(1); } } } do{ std::cout << "Francis QR Step N = " << N << std::endl; /** Check for convergence x x x x x 0 x x x x 0 0 x x x 0 0 x x x 0 0 0 0 x for this matrix l = 4 **/ do{ l = Chop_subdiag(H,nrm,e,small); r = 0; ///May have converged on more than one eval ///Single eval if(l == N-1){ evals[e] = H[l][l]; N--; e++; r++; it = 0; } ///RealD eval if(l == N-2){ trows[l+1] = 1; ///Needed for UTSolve apd = H[l][l] + H[l+1][l+1]; amd = H[l][l] - H[l+1][l+1]; bc = (T)4.0*H[l+1][l]*H[l][l+1]; evals[e] = (T)0.5*( apd + sqrt(amd*amd + bc) ); evals[e+1] = (T)0.5*( apd - sqrt(amd*amd + bc) ); N-=2; e+=2; r++; it = 0; } } while(r>0); if(N ==0) break; DenseVector ck; Resize(ck,3); DenseVector v; Resize(v,3); for(int m = N-3; m >= l; m--){ ///Starting vector essentially random shift. if(it%10 == 0 && N >= 3 && it > 0){ s = (T)1.618033989*( abs( H[N-1][N-2] ) + abs( H[N-2][N-3] ) ); t = (T)0.618033989*( abs( H[N-1][N-2] ) + abs( H[N-2][N-3] ) ); x = H[m][m]*H[m][m] + H[m][m+1]*H[m+1][m] - s*H[m][m] + t; y = H[m+1][m]*(H[m][m] + H[m+1][m+1] - s); z = H[m+1][m]*H[m+2][m+1]; } ///Starting vector implicit Q theorem else{ s = (H[N-2][N-2] + H[N-1][N-1]); t = (H[N-2][N-2]*H[N-1][N-1] - H[N-2][N-1]*H[N-1][N-2]); x = H[m][m]*H[m][m] + H[m][m+1]*H[m+1][m] - s*H[m][m] + t; y = H[m+1][m]*(H[m][m] + H[m+1][m+1] - s); z = H[m+1][m]*H[m+2][m+1]; } ck[0] = x; ck[1] = y; ck[2] = z; if(m == l) break; /** Some stupid thing from numerical recipies, seems to work**/ // PAB.. for heaven's sake quote page, purpose, evidence it works. // what sort of comment is that!?!?!? u=abs(H[m][m-1])*(abs(y)+abs(z)); d=abs(x)*(abs(H[m-1][m-1])+abs(H[m][m])+abs(H[m+1][m+1])); if ((T)abs(u+d) == (T)abs(d) ){ l = m; break; } //if (u < small){l = m; break;} } if(it > 100000){ std::cout << "QReigensystem: bugger it got stuck after 100000 iterations" << std::endl; std::cout << "got " << e << " evals " << l << " " << N << std::endl; exit(1); } normalize(ck); ///Normalization cancels in PHP anyway T beta; Householder_vector(ck, 0, 2, v, beta); Householder_mult(H,v,beta,0,l,l+2,0); Householder_mult(H,v,beta,0,l,l+2,1); ///Accumulate eigenvector Householder_mult(P,v,beta,0,l,l+2,1); int sw = 0; ///Are we on the last row? for(int k=l;k(ck, 0, 2-sw, v, beta); Householder_mult(H,v, beta,0,k+1,k+3-sw,0); Householder_mult(H,v, beta,0,k+1,k+3-sw,1); ///Accumulate eigenvector Householder_mult(P,v, beta,0,k+1,k+3-sw,1); } it++; tot_it++; }while(N > 1); N = evals.size(); ///Annoying - UT solves in reverse order; DenseVector tmp; Resize(tmp,N); for(int i=0;i int my_Wilkinson(DenseMatrix &Hin, DenseVector &evals, DenseMatrix &evecs, RealD small) { /** Find the eigenvalues of an upper Hessenberg matrix using the Wilkinson QR algorithm. H = x x 0 0 0 0 x x x 0 0 0 0 x x x 0 0 0 0 x x x 0 0 0 0 x x x 0 0 0 0 x x Factorization is P T P^H where T is upper triangular (mod cc blocks) and P is orthagonal/unitary. **/ return my_Wilkinson(Hin, evals, evecs, small, small); } template int my_Wilkinson(DenseMatrix &Hin, DenseVector &evals, DenseMatrix &evecs, RealD small, RealD tol) { int N; SizeSquare(Hin,N); int M = N; ///I don't want to modify the input but matricies must be passed by reference //Scale a matrix by its "norm" //RealD Hnorm = abs( Hin.LargestDiag() ); H = H*(1.0/Hnorm); DenseMatrix H; H = Hin; RealD Hnorm = abs(Norm(Hin)); H = H * (1.0 / Hnorm); // TODO use openmp and memset Fill(evals,0); Fill(evecs,0); T s, t, x = 0, y = 0, z = 0; T u, d; T apd, amd, bc; DenseVector p; Resize(p,N); Fill(p,0); T nrm = Norm(H); ///DenseMatrix Norm int n, m; int e = 0; int it = 0; int tot_it = 0; int l = 0; int r = 0; DenseMatrix P; Resize(P,N,N); Unity(P); DenseVector trows(N, 0); /// Check if the matrix is really symm tridiag RealD sth = 0; for(int j = 0; j < N; ++j) { for(int i = j + 2; i < N; ++i) { if(abs(H[i][j]) > tol || abs(H[j][i]) > tol) { std::cout << "Non Tridiagonal H(" << i << ","<< j << ") = |" << Real( real( H[j][i] ) ) << "| > " << tol << std::endl; std::cout << "Warning tridiagonalize and call again" << std::endl; // exit(1); // see what is going on //return; } } } do{ do{ //Jasper //Check if the subdiagonal term is small enough ( 0); //Jasper //Already converged //-------------- if(N == 0) break; DenseVector ck,v; Resize(ck,2); Resize(v,2); for(int m = N - 3; m >= l; m--) { ///Starting vector essentially random shift. if(it%10 == 0 && N >= 3 && it > 0) { t = abs(H[N - 1][N - 2]) + abs(H[N - 2][N - 3]); x = H[m][m] - t; z = H[m + 1][m]; } else { ///Starting vector implicit Q theorem d = (H[N - 2][N - 2] - H[N - 1][N - 1]) * (T) 0.5; t = H[N - 1][N - 1] - H[N - 1][N - 2] * H[N - 1][N - 2] / (d + sign(d) * sqrt(d * d + H[N - 1][N - 2] * H[N - 1][N - 2])); x = H[m][m] - t; z = H[m + 1][m]; } //Jasper //why it is here???? //----------------------- if(m == l) break; u = abs(H[m][m - 1]) * (abs(y) + abs(z)); d = abs(x) * (abs(H[m - 1][m - 1]) + abs(H[m][m]) + abs(H[m + 1][m + 1])); if ((T)abs(u + d) == (T)abs(d)) { l = m; break; } } //Jasper if(it > 1000000) { std::cout << "Wilkinson: bugger it got stuck after 100000 iterations" << std::endl; std::cout << "got " << e << " evals " << l << " " << N << std::endl; exit(1); } // T s, c; Givens_calc(x, z, c, s); Givens_mult(H, l, l + 1, c, -s, 0); Givens_mult(H, l, l + 1, c, s, 1); Givens_mult(P, l, l + 1, c, s, 1); // for(int k = l; k < N - 2; ++k) { x = H.A[k + 1][k]; z = H.A[k + 2][k]; Givens_calc(x, z, c, s); Givens_mult(H, k + 1, k + 2, c, -s, 0); Givens_mult(H, k + 1, k + 2, c, s, 1); Givens_mult(P, k + 1, k + 2, c, s, 1); } it++; tot_it++; }while(N > 1); N = evals.size(); ///Annoying - UT solves in reverse order; DenseVector tmp(N); for(int i = 0; i < N; ++i) tmp[i] = evals[N-i-1]; evals = tmp; // UTeigenvectors(H, trows, evals, evecs); //UTSymmEigenvectors(H, trows, evals, evecs); for(int i = 0; i < evals.size(); ++i) { evecs[i] = P * evecs[i]; normalize(evecs[i]); evals[i] = evals[i] * Hnorm; } // // FIXME this is to test // Hin.write("evecs3", evecs); // Hin.write("evals3", evals); // // check rsd // for(int i = 0; i < M; i++) { // vector Aevec = Hin * evecs[i]; // RealD norm2(0.); // for(int j = 0; j < M; j++) { // norm2 += (Aevec[j] - evals[i] * evecs[i][j]) * (Aevec[j] - evals[i] * evecs[i][j]); // } // } return tot_it; } template void Hess(DenseMatrix &A, DenseMatrix &Q, int start){ /** turn a matrix A = x x x x x x x x x x x x x x x x x x x x x x x x x into x x x x x x x x x x 0 x x x x 0 0 x x x 0 0 0 x x with householder rotations Slow. */ int N ; SizeSquare(A,N); DenseVector p; Resize(p,N); Fill(p,0); for(int k=start;k ck,v; Resize(ck,N-k-1); Resize(v,N-k-1); for(int i=k+1;i(ck, 0, ck.size()-1, v, beta); ///Householder vector Householder_mult(A,v,beta,start,k+1,N-1,0); ///A -> PA Householder_mult(A,v,beta,start,k+1,N-1,1); ///PA -> PAP^H ///Accumulate eigenvector Householder_mult(Q,v,beta,start,k+1,N-1,1); ///Q -> QP^H } /*for(int l=0;l void Tri(DenseMatrix &A, DenseMatrix &Q, int start){ ///Tridiagonalize a matrix int N; SizeSquare(A,N); Hess(A,Q,start); /*for(int l=0;l void ForceTridiagonal(DenseMatrix &A){ ///Tridiagonalize a matrix int N ; SizeSquare(A,N); for(int l=0;l int my_SymmEigensystem(DenseMatrix &Ain, DenseVector &evals, DenseVector > &evecs, RealD small){ ///Solve a symmetric eigensystem, not necessarily in tridiagonal form int N; SizeSquare(Ain,N); DenseMatrix A; A = Ain; DenseMatrix Q; Resize(Q,N,N); Unity(Q); Tri(A,Q,0); int it = my_Wilkinson(A, evals, evecs, small); for(int k=0;k int Wilkinson(DenseMatrix &Ain, DenseVector &evals, DenseVector > &evecs, RealD small){ return my_Wilkinson(Ain, evals, evecs, small); } template int SymmEigensystem(DenseMatrix &Ain, DenseVector &evals, DenseVector > &evecs, RealD small){ return my_SymmEigensystem(Ain, evals, evecs, small); } template int Eigensystem(DenseMatrix &Ain, DenseVector &evals, DenseVector > &evecs, RealD small){ ///Solve a general eigensystem, not necessarily in tridiagonal form int N = Ain.dim; DenseMatrix A(N); A = Ain; DenseMatrix Q(N);Q.Unity(); Hess(A,Q,0); int it = QReigensystem(A, evals, evecs, small); for(int k=0;k