00001 /* 00002 ------------------------------------------------------------------- 00003 00004 Copyright (C) 2006, 2007, 2008, 2009, Andrew W. Steiner 00005 00006 This file is part of O2scl. 00007 00008 O2scl is free software; you can redistribute it and/or modify 00009 it under the terms of the GNU General Public License as published by 00010 the Free Software Foundation; either version 3 of the License, or 00011 (at your option) any later version. 00012 00013 O2scl is distributed in the hope that it will be useful, 00014 but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 GNU General Public License for more details. 00017 00018 You should have received a copy of the GNU General Public License 00019 along with O2scl. If not, see <http://www.gnu.org/licenses/>. 00020 00021 ------------------------------------------------------------------- 00022 */ 00023 /** 00024 \brief Lanczos diagonalization 00025 00026 This is useful for approximating the largest eigenvalues of a 00027 symmetric matrix. 00028 00029 The vector and matrix types can be any type which provides 00030 access via \c operator[], given suitably constructed allocation 00031 types. 00032 00033 The tridiagonalization routine was rewritten from the EISPACK 00034 routines \c imtql1.f (but uses \c gsl_hypot() instead of \c 00035 pythag.f). 00036 00037 \future The function eigen_tdiag() automatically sorts the 00038 eigenvalues, which may not be necessary. 00039 \future Do something better than the naive matrix-vector product? 00040 */ 00041 template<class vec_t, class mat_t, class alloc_vec_t, class alloc_t> 00042 class lanczos { 00043 00044 public: 00045 00046 lanczos() { 00047 td_iter=30; 00048 td_lasteval=0; 00049 } 00050 00051 /** \brief Number of iterations for finding the eigenvalues of the 00052 tridiagonal matrix (default 30) 00053 */ 00054 size_t td_iter; 00055 00056 /** \brief The index for the last eigenvalue not determined if 00057 tridiagonalization fails 00058 */ 00059 size_t td_lasteval; 00060 00061 /** \brief Approximate the largest eigenvalues of a symmetric 00062 matrix \c mat using the Lanczos method 00063 00064 Given a square matrix \c mat with size \c size, this function 00065 applies \c n_iter iterations of the Lanczos algorithm to 00066 produce \c n_iter approximate eigenvalues stored in \c 00067 eigen. As a by-product, this function also partially 00068 tridiagonalizes the matrix placing the result in \c diag and 00069 \c off_diag. Before calling this function, space must have 00070 already been allocated for \c eigen, \c diag, and \c 00071 off_diag. All three of these arrays must have at least enough 00072 space for \c n_iter elements. 00073 00074 Choosing /c n_iter = \c size will produce all of the exact 00075 eigenvalues and the corresponding tridiagonal matrix, but this 00076 may be slower than diagonalizing the matrix directly. 00077 */ 00078 int eigenvalues(size_t size, mat_t &mat, size_t n_iter, 00079 vec_t &eigen, vec_t &diag, vec_t &off_diag) { 00080 double t; 00081 bool cont=true; 00082 size_t i, j, k; 00083 00084 alloc_vec_t v; 00085 alloc_vec_t w; 00086 alloc_vec_t b3; 00087 alloc_vec_t prod; 00088 alloc_t ao; 00089 ao.allocate(v,size); 00090 ao.allocate(w,size); 00091 ao.allocate(b3,size); 00092 ao.allocate(prod,size); 00093 00094 // Pick a unit vector 00095 O2SCL_IX(w,0)=1.0; 00096 for(i=1;i<size;i++) O2SCL_IX(w,i)=0.0; 00097 00098 for(i=0;i<size;i++) O2SCL_IX(v,i)=0; 00099 j=0; 00100 while (cont) { 00101 if (j!=0) { 00102 for(i=0;i<size;i++) { 00103 t=O2SCL_IX(w,i); 00104 O2SCL_IX(w,i)=O2SCL_IX(v,i)/O2SCL_IX(off_diag,j-1); 00105 O2SCL_IX(v,i)=-O2SCL_IX(off_diag,j-1)*t; 00106 } 00107 } 00108 product(size,mat,w,prod); 00109 for(k=0;k<size;k++) O2SCL_IX(v,k)+=O2SCL_IX(prod,k); 00110 O2SCL_IX(diag,j)=0.0; 00111 O2SCL_IX(off_diag,j)=0.0; 00112 00113 for(k=0;k<size;k++) O2SCL_IX(diag,j)+=O2SCL_IX(w,k)*O2SCL_IX(v,k); 00114 for(k=0;k<size;k++) O2SCL_IX(v,k)-=O2SCL_IX(diag,j)*O2SCL_IX(w,k); 00115 for(k=0;k<size;k++) O2SCL_IX(off_diag,j)+=O2SCL_IX(v,k)*O2SCL_IX(v,k); 00116 O2SCL_IX(off_diag,j)=sqrt(O2SCL_IX(off_diag,j)); 00117 j++; 00118 00119 if (j>=n_iter || O2SCL_IX(off_diag,j-1)==0.0) cont=false; 00120 00121 if (j>0) { 00122 for(k=0;k<size-1;k++) { 00123 O2SCL_IX(b3,k+1)=O2SCL_IX(off_diag,k); 00124 O2SCL_IX(eigen,k)=O2SCL_IX(diag,k); 00125 } 00126 O2SCL_IX(eigen,size-1)=O2SCL_IX(diag,size-1); 00127 if (eigen_tdiag(j,eigen,b3)!=0) { 00128 00129 ao.free(v); 00130 ao.free(w); 00131 ao.free(b3); 00132 ao.free(prod); 00133 00134 O2SCL_ERR_RET("Call to eigen_tdiag() in eigenvalues() failed.", 00135 o2scl::gsl_efailed); 00136 } 00137 } 00138 } 00139 00140 ao.free(v); 00141 ao.free(w); 00142 ao.free(b3); 00143 ao.free(prod); 00144 00145 return 0; 00146 } 00147 00148 /** 00149 \brief In-place diagonalization of a tri-diagonal matrix 00150 00151 On input, the vectors \c diag and \c off_diag should both be 00152 vectors of size \c n. The diagonal entries stored in \c diag, 00153 and the \f$ n-1 \f$ off-diagonal entries should be stored in 00154 \c off_diag, starting with \c off_diag[1]. The value in \c 00155 off_diag[0] is unused. The vector \c off_diag is destroyed by 00156 the computation. 00157 00158 This uses an implict QL method from the EISPACK routine \c 00159 imtql1. The value of \c ierr from the original Fortran routine 00160 is stored in \ref td_lasteval. 00161 00162 */ 00163 int eigen_tdiag(size_t n, vec_t &diag, vec_t &off_diag) { 00164 00165 // 'i' is set to zero here because Cygwin complained 00166 // about uninit'ed variables. This is probably ok, 00167 // but it would be nice to double check that there 00168 // isn't a problem with setting i=0 here. 00169 int i=0,j,l,m,mml; 00170 double b,c,f,g,p,r,s,tst1,tst2; 00171 00172 if (n==1) return 0; 00173 00174 for(size_t ij=1;ij<n;ij++) { 00175 O2SCL_IX(off_diag,ij-1)=O2SCL_IX(off_diag,ij); 00176 } 00177 O2SCL_IX(off_diag,n-1)=0.0; 00178 00179 bool done=false; 00180 00181 l=1; 00182 j=0; 00183 00184 while (done==false && l<=((int)n)) { 00185 00186 // Look for small sub-diagonal element 00187 bool idone=false; 00188 for(m=l;m<((int)n) && idone==false;m++) { 00189 tst1=fabs(O2SCL_IX(diag,m-1))+fabs(O2SCL_IX(diag,m)); 00190 tst2=tst1+fabs(O2SCL_IX(off_diag,m-1)); 00191 if (tst2==tst1) { 00192 m--; 00193 idone=true; 00194 } 00195 } 00196 00197 p=O2SCL_IX(diag,l-1); 00198 00199 if (m!=l && j==((int)td_iter)) { 00200 00201 // Set error. No convergence after td_iter iterations 00202 td_lasteval=l; 00203 O2SCL_ERR_RET("No convergence in lanczos::eigen_tdiag()", 00204 o2scl::gsl_efailed); 00205 } 00206 00207 if (m!=l) { 00208 00209 j++; 00210 00211 // Form shift 00212 g=(O2SCL_IX(diag,l)-p)/(2.0*O2SCL_IX(off_diag,l-1)); 00213 r=gsl_hypot(g,1.0); 00214 00215 g=O2SCL_IX(diag,m-1)-p+O2SCL_IX(off_diag,l-1)/ 00216 (g+(g>=0.0 ? fabs(r) : -fabs(r))); 00217 s=1.0; 00218 c=1.0; 00219 p=0.0; 00220 mml=m-l; 00221 00222 for(int ii=1;ii<=mml;ii++) { 00223 00224 i=m-ii; 00225 f=s*O2SCL_IX(off_diag,i-1); 00226 b=c*O2SCL_IX(off_diag,i-1); 00227 r=gsl_hypot(f,g); 00228 O2SCL_IX(off_diag,i)=r; 00229 00230 if (r==0.0) { 00231 00232 // Recover from underflow 00233 O2SCL_IX(diag,i)-=p; 00234 O2SCL_IX(off_diag,m-1)=0.0; 00235 ii=mml+1; 00236 00237 } else { 00238 00239 s=f/r; 00240 c=g/r; 00241 g=O2SCL_IX(diag,i)-p; 00242 r=(O2SCL_IX(diag,i-1)-g)*s+2.0*c*b; 00243 p=s*r; 00244 O2SCL_IX(diag,i)=g+p; 00245 g=c*r-b; 00246 00247 } 00248 00249 } 00250 00251 O2SCL_IX(diag,l-1)-=p; 00252 O2SCL_IX(off_diag,l-1)=g; 00253 O2SCL_IX(off_diag,m-1)=0.0; 00254 00255 00256 } else { 00257 00258 // Order eigenvalues 00259 00260 if (l==1) { 00261 00262 i=1; 00263 O2SCL_IX(diag,i-1)=p; 00264 00265 } else { 00266 00267 bool skip=false; 00268 for(int ii=2;ii<=l;ii++) { 00269 i=l+2-ii; 00270 if (p>=O2SCL_IX(diag,i-2)) { 00271 ii=l+1; 00272 skip=true; 00273 } else { 00274 O2SCL_IX(diag,i-1)=O2SCL_IX(diag,i-2); 00275 } 00276 } 00277 00278 if (skip==false) i=1; 00279 O2SCL_IX(diag,i-1)=p; 00280 } 00281 00282 j=0; 00283 l++; 00284 } 00285 00286 } 00287 00288 return 0; 00289 } 00290 00291 #ifndef DOXYGEN_INTERNAL 00292 00293 protected: 00294 00295 /** 00296 \brief Naive matrix-vector product 00297 00298 It is assumed that memory is already allocated for \c prod. 00299 */ 00300 void product(size_t n, mat_t &a, vec_t &w, vec_t &prod) { 00301 size_t i, j; 00302 for(i=0;i<n;i++) { 00303 O2SCL_IX(prod,i)=0.0; 00304 for(j=0;j<n;j++) { 00305 O2SCL_IX(prod,i)+=O2SCL_IX2(a,i,j)*O2SCL_IX(w,j); 00306 } 00307 } 00308 return; 00309 } 00310 00311 #endif 00312 00313 }; 00314
Documentation generated with Doxygen and provided under the GNU Free Documentation License. See License Information for details.
Project hosting provided by
,
O2scl Sourceforge Project Page