You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							789 lines
						
					
					
						
							29 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							789 lines
						
					
					
						
							29 KiB
						
					
					
				| /* -*- c++ -*- (enables emacs c++ mode) */ | |
| /*=========================================================================== | |
|   | |
|  Copyright (C) 2003-2012 Yves Renard | |
|   | |
|  This file is a part of GETFEM++ | |
|   | |
|  Getfem++  is  free software;  you  can  redistribute  it  and/or modify it | |
|  under  the  terms  of the  GNU  Lesser General Public License as published | |
|  by  the  Free Software Foundation;  either version 3 of the License,  or | |
|  (at your option) any later version along with the GCC Runtime Library | |
|  Exception either version 3.1 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 Lesser General Public | |
|  License and GCC Runtime Library Exception for more details. | |
|  You  should  have received a copy of the GNU Lesser General Public License | |
|  along  with  this program;  if not, write to the Free Software Foundation, | |
|  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA. | |
|   | |
|  As a special exception, you  may use  this file  as it is a part of a free | |
|  software  library  without  restriction.  Specifically,  if   other  files | |
|  instantiate  templates  or  use macros or inline functions from this file, | |
|  or  you compile this  file  and  link  it  with other files  to produce an | |
|  executable, this file  does  not  by itself cause the resulting executable | |
|  to be covered  by the GNU Lesser General Public License.  This   exception | |
|  does not  however  invalidate  any  other  reasons why the executable file | |
|  might be covered by the GNU Lesser General Public License. | |
|   | |
| ===========================================================================*/ | |
| 
 | |
| /**@file gmm_dense_qr.h | |
|    @author  Caroline Lecalvez, Caroline.Lecalvez@gmm.insa-tlse.fr, Yves Renard <Yves.Renard@insa-lyon.fr> | |
|    @date September 12, 2003. | |
|    @brief Dense QR factorization. | |
| */ | |
| #ifndef GMM_DENSE_QR_H | |
| #define GMM_DENSE_QR_H | |
|  | |
| #include "gmm_dense_Householder.h" | |
|  | |
| namespace gmm { | |
| 
 | |
| 
 | |
|   /** | |
|      QR factorization using Householder method (complex and real version). | |
|   */ | |
|   template <typename MAT1> | |
|   void qr_factor(const MAT1 &A_) {  | |
|     MAT1 &A = const_cast<MAT1 &>(A_); | |
|     typedef typename linalg_traits<MAT1>::value_type value_type; | |
| 
 | |
|     size_type m = mat_nrows(A), n = mat_ncols(A); | |
|     GMM_ASSERT2(m >= n, "dimensions mismatch"); | |
| 
 | |
|     std::vector<value_type> W(m), V(m); | |
| 
 | |
|     for (size_type j = 0; j < n; ++j) { | |
|       sub_interval SUBI(j, m-j), SUBJ(j, n-j); | |
|       V.resize(m-j); W.resize(n-j); | |
| 
 | |
|       for (size_type i = j; i < m; ++i) V[i-j] = A(i, j); | |
|       house_vector(V); | |
| 
 | |
|       row_house_update(sub_matrix(A, SUBI, SUBJ), V, W); | |
|       for (size_type i = j+1; i < m; ++i) A(i, j) = V[i-j]; | |
|     } | |
|   } | |
| 
 | |
|    | |
|   // QR comes from QR_factor(QR) where the upper triangular part stands for R | |
|   // and the lower part contains the Householder reflectors. | |
|   // A <- AQ | |
|   template <typename MAT1, typename MAT2> | |
|   void apply_house_right(const MAT1 &QR, const MAT2 &A_) {  | |
|     MAT2 &A = const_cast<MAT2 &>(A_); | |
|     typedef typename linalg_traits<MAT1>::value_type T; | |
|     size_type m = mat_nrows(QR), n = mat_ncols(QR); | |
|     GMM_ASSERT2(m == mat_ncols(A), "dimensions mismatch"); | |
|     if (m == 0) return; | |
|     std::vector<T> V(m), W(mat_nrows(A)); | |
|     V[0] = T(1); | |
|     for (size_type j = 0; j < n; ++j) { | |
|       V.resize(m-j); | |
|       for (size_type i = j+1; i < m; ++i) V[i-j] = QR(i, j); | |
|       col_house_update(sub_matrix(A, sub_interval(0, mat_nrows(A)), | |
| 				  sub_interval(j, m-j)), V, W); | |
|     } | |
|   } | |
| 
 | |
|   // QR comes from QR_factor(QR) where the upper triangular part stands for R | |
|   // and the lower part contains the Householder reflectors. | |
|   // A <- Q*A | |
|   template <typename MAT1, typename MAT2> | |
|   void apply_house_left(const MAT1 &QR, const MAT2 &A_) {  | |
|     MAT2 &A = const_cast<MAT2 &>(A_); | |
|     typedef typename linalg_traits<MAT1>::value_type T; | |
|     size_type m = mat_nrows(QR), n = mat_ncols(QR); | |
|     GMM_ASSERT2(m == mat_nrows(A), "dimensions mismatch"); | |
|     if (m == 0) return; | |
|     std::vector<T> V(m), W(mat_ncols(A)); | |
|     V[0] = T(1); | |
|     for (size_type j = 0; j < n; ++j) { | |
|       V.resize(m-j); | |
|       for (size_type i = j+1; i < m; ++i) V[i-j] = QR(i, j); | |
|       row_house_update(sub_matrix(A, sub_interval(j, m-j), | |
| 				  sub_interval(0, mat_ncols(A))), V, W); | |
|     } | |
|   }   | |
| 
 | |
|   /** Compute the QR factorization, where Q is assembled. */ | |
|   template <typename MAT1, typename MAT2, typename MAT3> | |
|     void qr_factor(const MAT1 &A, const MAT2 &QQ, const MAT3 &RR) {  | |
|     MAT2 &Q = const_cast<MAT2 &>(QQ); MAT3 &R = const_cast<MAT3 &>(RR);  | |
|     typedef typename linalg_traits<MAT1>::value_type value_type; | |
| 
 | |
|     size_type m = mat_nrows(A), n = mat_ncols(A); | |
|     GMM_ASSERT2(m >= n, "dimensions mismatch"); | |
|     gmm::copy(A, Q); | |
|      | |
|     std::vector<value_type> W(m); | |
|     dense_matrix<value_type> VV(m, n); | |
| 
 | |
|     for (size_type j = 0; j < n; ++j) { | |
|       sub_interval SUBI(j, m-j), SUBJ(j, n-j); | |
| 
 | |
|       for (size_type i = j; i < m; ++i) VV(i,j) = Q(i, j); | |
|       house_vector(sub_vector(mat_col(VV,j), SUBI)); | |
| 
 | |
|       row_house_update(sub_matrix(Q, SUBI, SUBJ), | |
| 		       sub_vector(mat_col(VV,j), SUBI), sub_vector(W, SUBJ)); | |
|     } | |
| 
 | |
|     gmm::copy(sub_matrix(Q, sub_interval(0, n), sub_interval(0, n)), R); | |
|     gmm::copy(identity_matrix(), Q); | |
|      | |
|     for (size_type j = n-1; j != size_type(-1); --j) { | |
|       sub_interval SUBI(j, m-j), SUBJ(j, n-j); | |
|       row_house_update(sub_matrix(Q, SUBI, SUBJ),  | |
| 		       sub_vector(mat_col(VV,j), SUBI), sub_vector(W, SUBJ)); | |
|     } | |
|   } | |
| 
 | |
|   ///@cond DOXY_SHOW_ALL_FUNCTIONS | |
|   template <typename TA, typename TV, typename Ttol,  | |
| 	    typename MAT, typename VECT> | |
|   void extract_eig(const MAT &A, VECT &V, Ttol tol, TA, TV) { | |
|     size_type n = mat_nrows(A); | |
|     if (n == 0) return; | |
|     tol *= Ttol(2); | |
|     Ttol tol_i = tol * gmm::abs(A(0,0)), tol_cplx = tol_i; | |
|     for (size_type i = 0; i < n; ++i) { | |
|       if (i < n-1) { | |
| 	tol_i = (gmm::abs(A(i,i))+gmm::abs(A(i+1,i+1)))*tol; | |
| 	tol_cplx = std::max(tol_cplx, tol_i); | |
|       } | |
|       if ((i < n-1) && gmm::abs(A(i+1,i)) >= tol_i) { | |
| 	TA tr = A(i,i) + A(i+1, i+1); | |
| 	TA det = A(i,i)*A(i+1, i+1) - A(i,i+1)*A(i+1, i); | |
| 	TA delta = tr*tr - TA(4) * det; | |
| 	if (delta < -tol_cplx) { | |
| 	  GMM_WARNING1("A complex eigenvalue has been detected : " | |
| 		      << std::complex<TA>(tr/TA(2), gmm::sqrt(-delta)/TA(2))); | |
| 	  V[i] = V[i+1] = tr / TA(2); | |
| 	} | |
| 	else { | |
| 	  delta = std::max(TA(0), delta); | |
| 	  V[i  ] = TA(tr + gmm::sqrt(delta))/ TA(2); | |
| 	  V[i+1] = TA(tr -  gmm::sqrt(delta))/ TA(2); | |
| 	} | |
| 	++i; | |
|       } | |
|       else | |
| 	V[i] = TV(A(i,i)); | |
|     } | |
|   } | |
| 
 | |
|   template <typename TA, typename TV, typename Ttol,  | |
| 	    typename MAT, typename VECT> | |
|   void extract_eig(const MAT &A, VECT &V, Ttol tol, TA, std::complex<TV>) { | |
|     size_type n = mat_nrows(A); | |
|     tol *= Ttol(2); | |
|     for (size_type i = 0; i < n; ++i) | |
|       if ((i == n-1) || | |
| 	  gmm::abs(A(i+1,i)) < (gmm::abs(A(i,i))+gmm::abs(A(i+1,i+1)))*tol) | |
| 	V[i] = std::complex<TV>(A(i,i)); | |
|       else { | |
| 	TA tr = A(i,i) + A(i+1, i+1); | |
| 	TA det = A(i,i)*A(i+1, i+1) - A(i,i+1)*A(i+1, i); | |
| 	TA delta = tr*tr - TA(4) * det; | |
| 	if (delta < TA(0)) { | |
| 	  V[i] = std::complex<TV>(tr / TA(2), gmm::sqrt(-delta) / TA(2)); | |
| 	  V[i+1] = std::complex<TV>(tr / TA(2), -gmm::sqrt(-delta)/ TA(2)); | |
| 	} | |
| 	else { | |
| 	  V[i  ] = TA(tr + gmm::sqrt(delta)) / TA(2); | |
| 	  V[i+1] = TA(tr -  gmm::sqrt(delta)) / TA(2); | |
| 	} | |
| 	++i; | |
|       } | |
|   } | |
| 
 | |
|   template <typename TA, typename TV, typename Ttol, | |
| 	    typename MAT, typename VECT> | |
|   void extract_eig(const MAT &A, VECT &V, Ttol tol, std::complex<TA>, TV) { | |
|     typedef std::complex<TA> T; | |
|     size_type n = mat_nrows(A); | |
|     if (n == 0) return; | |
|     tol *= Ttol(2); | |
|     Ttol tol_i = tol * gmm::abs(A(0,0)), tol_cplx = tol_i; | |
|     for (size_type i = 0; i < n; ++i) { | |
|       if (i < n-1) { | |
| 	tol_i = (gmm::abs(A(i,i))+gmm::abs(A(i+1,i+1)))*tol; | |
| 	tol_cplx = std::max(tol_cplx, tol_i); | |
|       } | |
|       if ((i == n-1) || gmm::abs(A(i+1,i)) < tol_i) { | |
| 	if (gmm::abs(std::imag(A(i,i))) > tol_cplx) | |
| 	  GMM_WARNING1("A complex eigenvalue has been detected : " | |
| 		      << T(A(i,i)) << " : "  << gmm::abs(std::imag(A(i,i))) | |
| 		      / gmm::abs(std::real(A(i,i))) << " : " << tol_cplx); | |
| 	V[i] = std::real(A(i,i)); | |
|       } | |
|       else { | |
| 	T tr = A(i,i) + A(i+1, i+1); | |
| 	T det = A(i,i)*A(i+1, i+1) - A(i,i+1)*A(i+1, i); | |
| 	T delta = tr*tr - TA(4) * det; | |
| 	T a1 = (tr + gmm::sqrt(delta)) / TA(2); | |
| 	T a2 = (tr - gmm::sqrt(delta)) / TA(2); | |
| 	if (gmm::abs(std::imag(a1)) > tol_cplx) | |
| 	  GMM_WARNING1("A complex eigenvalue has been detected : " << a1); | |
| 	if (gmm::abs(std::imag(a2)) > tol_cplx) | |
| 	  GMM_WARNING1("A complex eigenvalue has been detected : " << a2); | |
| 
 | |
| 	V[i] = std::real(a1); V[i+1] = std::real(a2); | |
| 	++i; | |
|       } | |
|     } | |
|   } | |
| 
 | |
|   template <typename TA, typename TV, typename Ttol, | |
| 	    typename MAT, typename VECT> | |
|   void extract_eig(const MAT &A, VECT &V, Ttol tol, | |
| 		   std::complex<TA>, std::complex<TV>) { | |
|     size_type n = mat_nrows(A); | |
|     tol *= Ttol(2); | |
|     for (size_type i = 0; i < n; ++i) | |
|       if ((i == n-1) || | |
| 	  gmm::abs(A(i+1,i)) < (gmm::abs(A(i,i))+gmm::abs(A(i+1,i+1)))*tol) | |
| 	V[i] = std::complex<TV>(A(i,i)); | |
|       else { | |
| 	std::complex<TA> tr = A(i,i) + A(i+1, i+1); | |
| 	std::complex<TA> det = A(i,i)*A(i+1, i+1) - A(i,i+1)*A(i+1, i); | |
| 	std::complex<TA> delta = tr*tr - TA(4) * det; | |
| 	V[i] = (tr + gmm::sqrt(delta)) / TA(2); | |
| 	V[i+1] = (tr - gmm::sqrt(delta)) / TA(2); | |
| 	++i; | |
|       } | |
|   } | |
| 
 | |
|   ///@endcond | |
|   /** | |
|      Compute eigenvalue vector. | |
|   */ | |
|   template <typename MAT, typename Ttol, typename VECT> inline | |
|   void extract_eig(const MAT &A, const VECT &V, Ttol tol) { | |
|     extract_eig(A, const_cast<VECT&>(V), tol, | |
| 		typename linalg_traits<MAT>::value_type(), | |
| 		typename linalg_traits<VECT>::value_type()); | |
|   } | |
| 
 | |
|   /* ********************************************************************* */ | |
|   /*    Stop criterion for QR algorithms                                   */ | |
|   /* ********************************************************************* */ | |
| 
 | |
|   template <typename MAT, typename Ttol> | |
|   void qr_stop_criterion(MAT &A, size_type &p, size_type &q, Ttol tol) { | |
|     typedef typename linalg_traits<MAT>::value_type T; | |
|     typedef typename number_traits<T>::magnitude_type R; | |
|     R rmin = default_min(R()) * R(2); | |
|     size_type n = mat_nrows(A); | |
|     if (n <= 2) { q = n; p = 0; } | |
|     else { | |
|       for (size_type i = 1; i < n-q; ++i) | |
| 	if (gmm::abs(A(i,i-1)) < (gmm::abs(A(i,i))+ gmm::abs(A(i-1,i-1)))*tol | |
| 	    || gmm::abs(A(i,i-1)) < rmin) | |
| 	  A(i,i-1) = T(0); | |
|        | |
|       while ((q < n-1 && A(n-1-q, n-2-q) == T(0)) || | |
| 	     (q < n-2 && A(n-2-q, n-3-q) == T(0))) ++q; | |
|       if (q >= n-2) q = n; | |
|       p = n-q; if (p) --p; if (p) --p; | |
|       while (p > 0 && A(p,p-1) != T(0)) --p; | |
|     } | |
|   } | |
|    | |
|   template <typename MAT, typename Ttol> inline | |
|   void symmetric_qr_stop_criterion(const MAT &AA, size_type &p, size_type &q, | |
| 				Ttol tol) { | |
|     typedef typename linalg_traits<MAT>::value_type T; | |
|     typedef typename number_traits<T>::magnitude_type R; | |
|     R rmin = default_min(R()) * R(2); | |
|     MAT& A = const_cast<MAT&>(AA); | |
|     size_type n = mat_nrows(A); | |
|     if (n <= 1) { q = n; p = 0; } | |
|     else { | |
|       for (size_type i = 1; i < n-q; ++i) | |
| 	if (gmm::abs(A(i,i-1)) < (gmm::abs(A(i,i))+ gmm::abs(A(i-1,i-1)))*tol | |
| 	    || gmm::abs(A(i,i-1)) < rmin) | |
| 	  A(i,i-1) = T(0); | |
|        | |
|       while (q < n-1 && A(n-1-q, n-2-q) == T(0)) ++q; | |
|       if (q >= n-1) q = n; | |
|       p = n-q; if (p) --p; if (p) --p; | |
|       while (p > 0 && A(p,p-1) != T(0)) --p; | |
|     } | |
|   } | |
| 
 | |
|   template <typename VECT1, typename VECT2, typename Ttol> inline | |
|   void symmetric_qr_stop_criterion(const VECT1 &diag, const VECT2 &sdiag_, | |
| 				   size_type &p, size_type &q, Ttol tol) { | |
|     typedef typename linalg_traits<VECT2>::value_type T; | |
|     typedef typename number_traits<T>::magnitude_type R; | |
|     R rmin = default_min(R()) * R(2); | |
|     VECT2 &sdiag = const_cast<VECT2 &>(sdiag_); | |
|     size_type n = vect_size(diag); | |
|     if (n <= 1) { q = n; p = 0; return; } | |
|     for (size_type i = 1; i < n-q; ++i) | |
|       if (gmm::abs(sdiag[i-1]) < (gmm::abs(diag[i])+ gmm::abs(diag[i-1]))*tol | |
| 	  || gmm::abs(sdiag[i-1]) < rmin) | |
| 	sdiag[i-1] = T(0); | |
|     while (q < n-1 && sdiag[n-2-q] == T(0)) ++q; | |
|     if (q >= n-1) q = n; | |
|     p = n-q; if (p) --p; if (p) --p; | |
|     while (p > 0 && sdiag[p-1] != T(0)) --p; | |
|   } | |
| 
 | |
|   /* ********************************************************************* */ | |
|   /*    2x2 blocks reduction for Schur vectors                             */ | |
|   /* ********************************************************************* */ | |
| 
 | |
|   template <typename MATH, typename MATQ, typename Ttol> | |
|   void block2x2_reduction(MATH &H, MATQ &Q, Ttol tol) { | |
|     typedef typename linalg_traits<MATH>::value_type T; | |
|     typedef typename number_traits<T>::magnitude_type R; | |
| 
 | |
|     size_type n = mat_nrows(H), nq = mat_nrows(Q); | |
|     sub_interval SUBQ(0, nq), SUBL(0, 2); | |
|     std::vector<T> v(2), w(std::max(n, nq)); v[0] = T(1); | |
|     if (n < 2) return; | |
|     tol *= Ttol(2); | |
|     Ttol tol_i = tol * gmm::abs(H(0,0)), tol_cplx = tol_i; | |
|     for (size_type i = 0; i < n-1; ++i) { | |
|       tol_i = (gmm::abs(H(i,i))+gmm::abs(H(i+1,i+1)))*tol; | |
|       tol_cplx = std::max(tol_cplx, tol_i); | |
|        | |
|       if (gmm::abs(H(i+1,i)) > tol_i) { // 2x2 block detected | |
| 	T tr = (H(i+1, i+1) - H(i,i)) / T(2); | |
| 	T delta = tr*tr + H(i,i+1)*H(i+1, i); | |
| 	 | |
| 	if (is_complex(T()) || gmm::real(delta) >= R(0)) { | |
| 	  sub_interval SUBI(i, 2); | |
| 	  T theta = (tr - gmm::sqrt(delta)) / H(i+1,i); | |
| 	  R a = gmm::abs(theta); | |
| 	  v[1] = (a == R(0)) ? T(-1) | |
| 	    : gmm::conj(theta) * (R(1) - gmm::sqrt(a*a + R(1)) / a); | |
| 	  row_house_update(sub_matrix(H, SUBI), v, sub_vector(w, SUBL)); | |
| 	  col_house_update(sub_matrix(H, SUBI), v, sub_vector(w, SUBL)); | |
| 	  col_house_update(sub_matrix(Q, SUBQ, SUBI), v, sub_vector(w, SUBQ)); | |
| 	} | |
| 	++i; | |
|       } | |
|     } | |
|   } | |
| 
 | |
|   /* ********************************************************************* */ | |
|   /*    Basic qr algorithm.                                                */ | |
|   /* ********************************************************************* */ | |
| 
 | |
|   #define tol_type_for_qr typename number_traits<typename \ | |
|                           linalg_traits<MAT1>::value_type>::magnitude_type | |
|   #define default_tol_for_qr \ | |
|     (gmm::default_tol(tol_type_for_qr()) *  tol_type_for_qr(3)) | |
|  | |
|   // QR method for real or complex square matrices based on QR factorisation. | |
|   // eigval has to be a complex vector if A has complex eigeinvalues. | |
|   // Very slow method. Use implicit_qr_method instead. | |
|   template <typename MAT1, typename VECT, typename MAT2> | |
|     void rudimentary_qr_algorithm(const MAT1 &A, const VECT &eigval_, | |
| 				  const MAT2 &eigvect_, | |
| 				  tol_type_for_qr tol = default_tol_for_qr, | |
| 				  bool compvect = true) { | |
|     VECT &eigval = const_cast<VECT &>(eigval_); | |
|     MAT2 &eigvect = const_cast<MAT2 &>(eigvect_); | |
| 
 | |
|     typedef typename linalg_traits<MAT1>::value_type value_type; | |
| 
 | |
|     size_type n = mat_nrows(A), p, q = 0, ite = 0; | |
|     dense_matrix<value_type> Q(n, n), R(n,n), A1(n,n);  | |
|     gmm::copy(A, A1); | |
| 
 | |
|     Hessenberg_reduction(A1, eigvect, compvect); | |
|     qr_stop_criterion(A1, p, q, tol); | |
| 
 | |
|     while (q < n) { | |
|       qr_factor(A1, Q, R); | |
|       gmm::mult(R, Q, A1); | |
|       if (compvect) { gmm::mult(eigvect, Q, R); gmm::copy(R, eigvect); } | |
|        | |
|       qr_stop_criterion(A1, p, q, tol); | |
|       ++ite; | |
|       GMM_ASSERT1(ite < n*1000, "QR algorithm failed"); | |
|     } | |
|     if (compvect) block2x2_reduction(A1, Q, tol); | |
|     extract_eig(A1, eigval, tol);  | |
|   } | |
| 
 | |
|   template <typename MAT1, typename VECT> | |
|     void rudimentary_qr_algorithm(const MAT1 &a, VECT &eigval, | |
| 				  tol_type_for_qr tol = default_tol_for_qr) { | |
|     dense_matrix<typename linalg_traits<MAT1>::value_type> m(0,0); | |
|     rudimentary_qr_algorithm(a, eigval, m, tol, false);  | |
|   } | |
| 
 | |
|   /* ********************************************************************* */ | |
|   /*    Francis QR step.                                                   */ | |
|   /* ********************************************************************* */ | |
| 
 | |
|   template <typename MAT1, typename MAT2> | |
|     void Francis_qr_step(const MAT1& HH, const MAT2 &QQ, bool compute_Q) { | |
|     MAT1& H = const_cast<MAT1&>(HH); MAT2& Q = const_cast<MAT2&>(QQ); | |
|     typedef typename linalg_traits<MAT1>::value_type value_type; | |
|     size_type n = mat_nrows(H), nq = mat_nrows(Q);  | |
|      | |
|     std::vector<value_type> v(3), w(std::max(n, nq)); | |
| 
 | |
|     value_type s = H(n-2, n-2) + H(n-1, n-1); | |
|     value_type t = H(n-2, n-2) * H(n-1, n-1) - H(n-2, n-1) * H(n-1, n-2); | |
|     value_type x = H(0, 0) * H(0, 0) + H(0,1) * H(1, 0) - s * H(0,0) + t; | |
|     value_type y = H(1, 0) * (H(0,0) + H(1,1) - s); | |
|     value_type z = H(1, 0) * H(2, 1); | |
| 
 | |
|     sub_interval SUBQ(0, nq); | |
| 
 | |
|     for (size_type k = 0; k < n - 2; ++k) { | |
|       v[0] = x; v[1] = y; v[2] = z; | |
|       house_vector(v); | |
|       size_type r = std::min(k+4, n), q = (k==0) ? 0 : k-1; | |
|       sub_interval SUBI(k, 3), SUBJ(0, r), SUBK(q, n-q); | |
|        | |
|       row_house_update(sub_matrix(H, SUBI, SUBK),  v, sub_vector(w, SUBK)); | |
|       col_house_update(sub_matrix(H, SUBJ, SUBI),  v, sub_vector(w, SUBJ)); | |
|        | |
|       if (compute_Q) | |
|        	col_house_update(sub_matrix(Q, SUBQ, SUBI),  v, sub_vector(w, SUBQ)); | |
| 
 | |
|       x = H(k+1, k); y = H(k+2, k); | |
|       if (k < n-3) z = H(k+3, k); | |
|     } | |
|     sub_interval SUBI(n-2,2), SUBJ(0, n), SUBK(n-3,3), SUBL(0, 3); | |
|     v.resize(2); | |
|     v[0] = x; v[1] = y; | |
|     house_vector(v); | |
|     row_house_update(sub_matrix(H, SUBI, SUBK), v, sub_vector(w, SUBL)); | |
|     col_house_update(sub_matrix(H, SUBJ, SUBI), v, sub_vector(w, SUBJ)); | |
|     if (compute_Q) | |
|       col_house_update(sub_matrix(Q, SUBQ, SUBI), v, sub_vector(w, SUBQ)); | |
|   } | |
| 
 | |
|   /* ********************************************************************* */ | |
|   /*    Wilkinson Double shift QR step (from Lapack).                      */ | |
|   /* ********************************************************************* */ | |
| 
 | |
|   template <typename MAT1, typename MAT2, typename Ttol> | |
|   void Wilkinson_double_shift_qr_step(const MAT1& HH, const MAT2 &QQ, | |
| 				      Ttol tol, bool exc, bool compute_Q) { | |
|     MAT1& H = const_cast<MAT1&>(HH); MAT2& Q = const_cast<MAT2&>(QQ); | |
|     typedef typename linalg_traits<MAT1>::value_type T; | |
|     typedef typename number_traits<T>::magnitude_type R; | |
| 
 | |
|     size_type n = mat_nrows(H), nq = mat_nrows(Q), m; | |
|     std::vector<T> v(3), w(std::max(n, nq)); | |
|     const R dat1(0.75), dat2(-0.4375); | |
|     T h33, h44, h43h34, v1(0), v2(0), v3(0); | |
|      | |
|     if (exc) {                    /* Exceptional shift.                    */ | |
|       R s = gmm::abs(H(n-1, n-2)) + gmm::abs(H(n-2, n-3)); | |
|       h33 = h44 = dat1 * s; | |
|       h43h34 = dat2*s*s; | |
|     } | |
|     else {                        /* Wilkinson double shift.               */ | |
|       h44 = H(n-1,n-1); h33 = H(n-2, n-2); | |
|       h43h34 = H(n-1, n-2) * H(n-2, n-1); | |
|     } | |
| 
 | |
|     /* Look for two consecutive small subdiagonal elements.                */ | |
|     /* Determine the effect of starting the double-shift QR iteration at   */ | |
|     /* row m, and see if this would make H(m-1, m-2) negligible.           */ | |
|     for (m = n-2; m != 0; --m) { | |
|       T h11  = H(m-1, m-1), h22  = H(m, m); | |
|       T h21  = H(m, m-1),   h12  = H(m-1, m); | |
|       T h44s = h44 - h11,   h33s = h33 - h11; | |
|       v1 = (h33s*h44s-h43h34) / h21 + h12; | |
|       v2 = h22 - h11 - h33s - h44s; | |
|       v3 = H(m+1, m); | |
|       R s = gmm::abs(v1) + gmm::abs(v2) + gmm::abs(v3); | |
|       v1 /= s; v2 /= s; v3 /= s; | |
|       if (m == 1) break; | |
|       T h00 = H(m-2, m-2); | |
|       T h10 = H(m-1, m-2); | |
|       R tst1 = gmm::abs(v1)*(gmm::abs(h00)+gmm::abs(h11)+gmm::abs(h22)); | |
|       if (gmm::abs(h10)*(gmm::abs(v2)+gmm::abs(v3)) <= tol * tst1) break; | |
|     } | |
| 
 | |
|     /* Double shift QR step.                                               */ | |
|     sub_interval SUBQ(0, nq); | |
|     for (size_type k = (m == 0) ? 0 : m-1; k < n-2; ++k) { | |
|       v[0] = v1; v[1] = v2; v[2] = v3; | |
|       house_vector(v); | |
|       size_type r = std::min(k+4, n), q = (k==0) ? 0 : k-1; | |
|       sub_interval SUBI(k, 3), SUBJ(0, r), SUBK(q, n-q); | |
|        | |
|       row_house_update(sub_matrix(H, SUBI, SUBK),  v, sub_vector(w, SUBK)); | |
|       col_house_update(sub_matrix(H, SUBJ, SUBI),  v, sub_vector(w, SUBJ)); | |
|       if (k > m-1) { H(k+1, k-1) = T(0); if (k < n-3) H(k+2, k-1) = T(0); } | |
|        | |
|       if (compute_Q) | |
|        	col_house_update(sub_matrix(Q, SUBQ, SUBI),  v, sub_vector(w, SUBQ)); | |
| 
 | |
|       v1 = H(k+1, k); v2 = H(k+2, k); | |
|       if (k < n-3) v3 = H(k+3, k); | |
|     } | |
|     sub_interval SUBI(n-2,2), SUBJ(0, n), SUBK(n-3,3), SUBL(0, 3); | |
|     v.resize(2); v[0] = v1; v[1] = v2; | |
|     house_vector(v); | |
|     row_house_update(sub_matrix(H, SUBI, SUBK), v, sub_vector(w, SUBL)); | |
|     col_house_update(sub_matrix(H, SUBJ, SUBI), v, sub_vector(w, SUBJ)); | |
|     if (compute_Q) | |
|       col_house_update(sub_matrix(Q, SUBQ, SUBI), v, sub_vector(w, SUBQ)); | |
|   } | |
| 
 | |
|   /* ********************************************************************* */ | |
|   /*    Implicit QR algorithm.                                             */ | |
|   /* ********************************************************************* */ | |
| 
 | |
|   // QR method for real or complex square matrices based on an | |
|   // implicit QR factorisation. eigval has to be a complex vector | |
|   // if A has complex eigeinvalues. complexity about 10n^3, 25n^3 if | |
|   // eigenvectors are computed | |
|   template <typename MAT1, typename VECT, typename MAT2> | |
|     void implicit_qr_algorithm(const MAT1 &A, const VECT &eigval_, | |
| 			       const MAT2 &Q_,  | |
| 			       tol_type_for_qr tol = default_tol_for_qr, | |
| 			       bool compvect = true) { | |
|     VECT &eigval = const_cast<VECT &>(eigval_); | |
|     MAT2 &Q = const_cast<MAT2 &>(Q_); | |
|     typedef typename linalg_traits<MAT1>::value_type value_type; | |
| 
 | |
|     size_type n(mat_nrows(A)), q(0), q_old, p(0), ite(0), its(0); | |
|     dense_matrix<value_type> H(n,n); | |
|     sub_interval SUBK(0,0); | |
| 
 | |
|     gmm::copy(A, H); | |
|     Hessenberg_reduction(H, Q, compvect); | |
|     qr_stop_criterion(H, p, q, tol); | |
|      | |
|     while (q < n) { | |
|       sub_interval SUBI(p, n-p-q), SUBJ(0, mat_ncols(Q)); | |
|       if (compvect) SUBK = SUBI; | |
| //       Francis_qr_step(sub_matrix(H, SUBI), | |
| // 		      sub_matrix(Q, SUBJ, SUBK), compvect); | |
|       Wilkinson_double_shift_qr_step(sub_matrix(H, SUBI),  | |
| 				     sub_matrix(Q, SUBJ, SUBK), | |
| 				     tol, (its == 10 || its == 20), compvect); | |
|       q_old = q; | |
|       qr_stop_criterion(H, p, q, tol*2); | |
|       if (q != q_old) its = 0; | |
|       ++its; ++ite; | |
|       GMM_ASSERT1(ite < n*100, "QR algorithm failed"); | |
|     } | |
|     if (compvect) block2x2_reduction(H, Q, tol); | |
|     extract_eig(H, eigval, tol); | |
|   } | |
| 
 | |
| 
 | |
|   template <typename MAT1, typename VECT> | |
|     void implicit_qr_algorithm(const MAT1 &a, VECT &eigval, | |
| 			       tol_type_for_qr tol = default_tol_for_qr) { | |
|     dense_matrix<typename linalg_traits<MAT1>::value_type> m(1,1); | |
|     implicit_qr_algorithm(a, eigval, m, tol, false);  | |
|   } | |
| 
 | |
|   /* ********************************************************************* */ | |
|   /*    Implicit symmetric QR step with Wilkinson Shift.                   */ | |
|   /* ********************************************************************* */ | |
| 
 | |
|   template <typename MAT1, typename MAT2>  | |
|     void symmetric_Wilkinson_qr_step(const MAT1& MM, const MAT2 &ZZ, | |
| 				     bool compute_z) { | |
|     MAT1& M = const_cast<MAT1&>(MM); MAT2& Z = const_cast<MAT2&>(ZZ); | |
|     typedef typename linalg_traits<MAT1>::value_type T; | |
|     typedef typename number_traits<T>::magnitude_type R; | |
|     size_type n = mat_nrows(M); | |
| 
 | |
|     for (size_type i = 0; i < n; ++i) { | |
|       M(i, i) = T(gmm::real(M(i, i))); | |
|       if (i > 0) { | |
| 	T a = (M(i, i-1) + gmm::conj(M(i-1, i)))/R(2); | |
| 	M(i, i-1) = a; M(i-1, i) = gmm::conj(a); | |
|       } | |
|     } | |
| 
 | |
|     R d = gmm::real(M(n-2, n-2) - M(n-1, n-1)) / R(2); | |
|     R e = gmm::abs_sqr(M(n-1, n-2)); | |
|     R nu = d + gmm::sgn(d)*gmm::sqrt(d*d+e); | |
|     if (nu == R(0)) { M(n-1, n-2) = T(0); return; } | |
|     R mu = gmm::real(M(n-1, n-1)) - e / nu; | |
|     T x = M(0,0) - T(mu), z = M(1, 0), c, s; | |
| 
 | |
|     for (size_type k = 1; k < n; ++k) { | |
|       Givens_rotation(x, z, c, s); | |
| 
 | |
|       if (k > 1) Apply_Givens_rotation_left(M(k-1,k-2), M(k,k-2), c, s); | |
|       Apply_Givens_rotation_left(M(k-1,k-1), M(k,k-1), c, s); | |
|       Apply_Givens_rotation_left(M(k-1,k  ), M(k,k  ), c, s); | |
|       if (k < n-1) Apply_Givens_rotation_left(M(k-1,k+1), M(k,k+1), c, s); | |
|       if (k > 1) Apply_Givens_rotation_right(M(k-2,k-1), M(k-2,k), c, s); | |
|       Apply_Givens_rotation_right(M(k-1,k-1), M(k-1,k), c, s); | |
|       Apply_Givens_rotation_right(M(k  ,k-1), M(k,k)  , c, s); | |
|       if (k < n-1) Apply_Givens_rotation_right(M(k+1,k-1), M(k+1,k), c, s); | |
| 
 | |
|       if (compute_z) col_rot(Z, c, s, k-1, k); | |
|       if (k < n-1) { x = M(k, k-1); z = M(k+1, k-1); } | |
|     } | |
| 
 | |
|   } | |
| 
 | |
|   template <typename VECT1, typename VECT2, typename MAT>  | |
|   void symmetric_Wilkinson_qr_step(const VECT1& diag_, const VECT2& sdiag_, | |
| 				   const MAT &ZZ, bool compute_z) { | |
|     VECT1& diag = const_cast<VECT1&>(diag_); | |
|     VECT2& sdiag = const_cast<VECT2&>(sdiag_); | |
|     MAT& Z = const_cast<MAT&>(ZZ); | |
|     typedef typename linalg_traits<VECT2>::value_type T; | |
|     typedef typename number_traits<T>::magnitude_type R; | |
| 
 | |
|     size_type n = vect_size(diag); | |
|     R d = (diag[n-2] - diag[n-1]) / R(2); | |
|     R e = gmm::abs_sqr(sdiag[n-2]); | |
|     R nu = d + gmm::sgn(d)*gmm::sqrt(d*d+e); | |
|     if (nu == R(0)) { sdiag[n-2] = T(0); return; } | |
|     R mu = diag[n-1] - e / nu; | |
|     T x = diag[0] - T(mu), z = sdiag[0], c, s; | |
| 
 | |
|     T a01(0), a02(0); | |
|     T a10(0), a11(diag[0]), a12(gmm::conj(sdiag[0])), a13(0); | |
|     T a20(0), a21(sdiag[0]), a22(diag[1]), a23(gmm::conj(sdiag[1])); | |
|     T a31(0), a32(sdiag[1]); | |
| 
 | |
|     for (size_type k = 1; k < n; ++k) { | |
|       Givens_rotation(x, z, c, s); | |
| 
 | |
|       if (k > 1) Apply_Givens_rotation_left(a10, a20, c, s); | |
|       Apply_Givens_rotation_left(a11, a21, c, s); | |
|       Apply_Givens_rotation_left(a12, a22, c, s); | |
|       if (k < n-1) Apply_Givens_rotation_left(a13, a23, c, s); | |
| 
 | |
|       if (k > 1) Apply_Givens_rotation_right(a01, a02, c, s); | |
|       Apply_Givens_rotation_right(a11, a12, c, s); | |
|       Apply_Givens_rotation_right(a21, a22, c, s); | |
|       if (k < n-1) Apply_Givens_rotation_right(a31, a32, c, s); | |
| 
 | |
|       if (compute_z) col_rot(Z, c, s, k-1, k); | |
| 
 | |
|       diag[k-1] = gmm::real(a11); | |
|       diag[k] = gmm::real(a22); | |
|       if (k > 1) sdiag[k-2] = (gmm::conj(a01) + a10) / R(2); | |
|       sdiag[k-1] = (gmm::conj(a12) + a21) / R(2); | |
| 
 | |
|       x = sdiag[k-1]; z = (gmm::conj(a13) + a31) / R(2); | |
| 
 | |
|       a01 = a12; a02 = a13; | |
|       a10 = a21; a11 = a22; a12 = a23; a13 = T(0); | |
|       a20 = a31; a21 = a32; a31 = T(0); | |
| 
 | |
|       if (k < n-1) { | |
| 	sdiag[k] = (gmm::conj(a23) + a32) / R(2); | |
| 	a22 = T(diag[k+1]); a32 = sdiag[k+1]; a23 = gmm::conj(a32); | |
|       } | |
|     } | |
|   } | |
| 
 | |
|   /* ********************************************************************* */ | |
|   /*    Implicit QR algorithm for symmetric or hermitian matrices.         */ | |
|   /* ********************************************************************* */ | |
| 
 | |
|   // implicit QR method for real square symmetric matrices or complex | |
|   // hermitian matrices. | |
|   // eigval has to be a complex vector if A has complex eigeinvalues. | |
|   // complexity about 4n^3/3, 9n^3 if eigenvectors are computed | |
|   template <typename MAT1, typename VECT, typename MAT2> | |
|   void symmetric_qr_algorithm_old(const MAT1 &A, const VECT &eigval_, | |
| 			      const MAT2 &eigvect_, | |
| 			      tol_type_for_qr tol = default_tol_for_qr, | |
| 			      bool compvect = true) { | |
|     VECT &eigval = const_cast<VECT &>(eigval_); | |
|     MAT2 &eigvect = const_cast<MAT2 &>(eigvect_); | |
|     typedef typename linalg_traits<MAT1>::value_type T; | |
|     typedef typename number_traits<T>::magnitude_type R; | |
| 
 | |
|     if (compvect) gmm::copy(identity_matrix(), eigvect); | |
|     size_type n = mat_nrows(A), q = 0, p, ite = 0; | |
|     dense_matrix<T> Tri(n, n); | |
|     gmm::copy(A, Tri); | |
| 
 | |
|     Householder_tridiagonalization(Tri, eigvect, compvect); | |
|      | |
|     symmetric_qr_stop_criterion(Tri, p, q, tol); | |
|      | |
|     while (q < n) { | |
| 
 | |
|       sub_interval SUBI(p, n-p-q), SUBJ(0, mat_ncols(eigvect)), SUBK(p, n-p-q); | |
|       if (!compvect) SUBK = sub_interval(0,0); | |
|       symmetric_Wilkinson_qr_step(sub_matrix(Tri, SUBI),  | |
| 				  sub_matrix(eigvect, SUBJ, SUBK), compvect); | |
|        | |
|       symmetric_qr_stop_criterion(Tri, p, q, tol*R(2)); | |
|       ++ite; | |
|       GMM_ASSERT1(ite < n*100, "QR algorithm failed. Probably, your matrix" | |
| 		  " is not real symmetric or complex hermitian"); | |
|     } | |
|      | |
|     extract_eig(Tri, eigval, tol); | |
|   } | |
| 
 | |
|   template <typename MAT1, typename VECT, typename MAT2> | |
|   void symmetric_qr_algorithm(const MAT1 &A, const VECT &eigval_, | |
| 			      const MAT2 &eigvect_, | |
| 			      tol_type_for_qr tol = default_tol_for_qr, | |
| 			      bool compvect = true) { | |
|     VECT &eigval = const_cast<VECT &>(eigval_); | |
|     MAT2 &eigvect = const_cast<MAT2 &>(eigvect_); | |
|     typedef typename linalg_traits<MAT1>::value_type T; | |
|     typedef typename number_traits<T>::magnitude_type R; | |
| 
 | |
|     size_type n = mat_nrows(A), q = 0, p, ite = 0; | |
|     if (compvect) gmm::copy(identity_matrix(), eigvect); | |
|     if (n == 0) return; | |
|     if (n == 1) { eigval[0]=gmm::real(A(0,0)); return; } | |
|     dense_matrix<T> Tri(n, n); | |
|     gmm::copy(A, Tri); | |
| 
 | |
|     Householder_tridiagonalization(Tri, eigvect, compvect); | |
| 
 | |
|     std::vector<R> diag(n); | |
|     std::vector<T> sdiag(n); | |
|     for (size_type i = 0; i < n; ++i) | |
|       { diag[i] = gmm::real(Tri(i, i)); if (i+1 < n) sdiag[i] = Tri(i+1, i); } | |
|      | |
|     symmetric_qr_stop_criterion(diag, sdiag, p, q, tol); | |
|      | |
|     while (q < n) { | |
|       sub_interval SUBI(p, n-p-q), SUBJ(0, mat_ncols(eigvect)), SUBK(p, n-p-q); | |
|       if (!compvect) SUBK = sub_interval(0,0); | |
|        | |
|       symmetric_Wilkinson_qr_step(sub_vector(diag, SUBI), | |
| 				  sub_vector(sdiag, SUBI), | |
| 				  sub_matrix(eigvect, SUBJ, SUBK), compvect); | |
| 
 | |
|       symmetric_qr_stop_criterion(diag, sdiag, p, q, tol*R(3)); | |
|       ++ite; | |
|       GMM_ASSERT1(ite < n*100, "QR algorithm failed."); | |
|     } | |
|      | |
|     gmm::copy(diag, eigval); | |
|   } | |
| 
 | |
| 
 | |
|   template <typename MAT1, typename VECT> | |
|     void symmetric_qr_algorithm(const MAT1 &a, VECT &eigval, | |
| 				tol_type_for_qr tol = default_tol_for_qr) { | |
|     dense_matrix<typename linalg_traits<MAT1>::value_type> m(0,0); | |
|     symmetric_qr_algorithm(a, eigval, m, tol, false); | |
|   } | |
| 
 | |
| 
 | |
| } | |
| 
 | |
| #endif | |
| 
 |