00001 /* 00002 ------------------------------------------------------------------- 00003 00004 Copyright (C) 2006, 2007, 2008, 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 */ 00040 template<class vec_t, class mat_t, class alloc_vec_t, class alloc_t> 00041 class lanczos { 00042 00043 public: 00044 00045 lanczos() { 00046 td_iter=30; 00047 td_lasteval=0; 00048 } 00049 00050 /** \brief Number of iterations for finding the eigenvalues of the 00051 tridiagonal matrix (default 30) 00052 */ 00053 size_t td_iter; 00054 00055 /** \brief The index for the last eigenvalue not determined if 00056 tridiagonalization fails 00057 */ 00058 size_t td_lasteval; 00059 00060 /** \brief Approximate the largest eigenvalues of a symmetric 00061 matrix \c mat using the Lanczos method 00062 00063 Given a square matrix \c mat with size \c size, this function 00064 applies \c n_iter iterations of the Lanczos algorithm to 00065 produce \c n_iter approximate eigenvalues stored in \c 00066 eigen. As a by-product, this function also partially 00067 tridiagonalizes the matrix placing the result in \c diag and 00068 \c off_diag. Before calling this function, space must have 00069 already been allocated for \c eigen, \c diag, and \c 00070 off_diag. All three of these arrays must have at least enough 00071 space for \c n_iter elements. 00072 00073 Choosing /c n_iter = \c size will produce all of the exact 00074 eigenvalues and the corresponding tridiagonal matrix, but this 00075 may be slower than diagonalizing the matrix directly. 00076 */ 00077 int eigenvalues(size_t size, mat_t &mat, size_t n_iter, 00078 vec_t &eigen, vec_t &diag, vec_t &off_diag) { 00079 double t; 00080 bool cont=true; 00081 size_t i, j, k; 00082 00083 alloc_vec_t v; 00084 alloc_vec_t w; 00085 alloc_vec_t b3; 00086 alloc_vec_t prod; 00087 alloc_t ao; 00088 ao.allocate(v,size); 00089 ao.allocate(w,size); 00090 ao.allocate(b3,size); 00091 ao.allocate(prod,size); 00092 00093 // Pick a unit vector 00094 O2SCL_IX(w,0)=1.0; 00095 for(i=1;i<size;i++) O2SCL_IX(w,i)=0.0; 00096 00097 for(i=0;i<size;i++) O2SCL_IX(v,i)=0; 00098 j=0; 00099 while (cont) { 00100 if (j!=0) { 00101 for(i=0;i<size;i++) { 00102 t=O2SCL_IX(w,i); 00103 O2SCL_IX(w,i)=O2SCL_IX(v,i)/O2SCL_IX(off_diag,j-1); 00104 O2SCL_IX(v,i)=-O2SCL_IX(off_diag,j-1)*t; 00105 } 00106 } 00107 product(size,mat,w,prod); 00108 for(k=0;k<size;k++) O2SCL_IX(v,k)+=O2SCL_IX(prod,k); 00109 O2SCL_IX(diag,j)=0.0; 00110 O2SCL_IX(off_diag,j)=0.0; 00111 00112 for(k=0;k<size;k++) O2SCL_IX(diag,j)+=O2SCL_IX(w,k)*O2SCL_IX(v,k); 00113 for(k=0;k<size;k++) O2SCL_IX(v,k)-=O2SCL_IX(diag,j)*O2SCL_IX(w,k); 00114 for(k=0;k<size;k++) O2SCL_IX(off_diag,j)+=O2SCL_IX(v,k)*O2SCL_IX(v,k); 00115 O2SCL_IX(off_diag,j)=sqrt(O2SCL_IX(off_diag,j)); 00116 j++; 00117 00118 if (j>=n_iter || O2SCL_IX(off_diag,j-1)==0.0) cont=false; 00119 00120 if (j>0) { 00121 for(k=0;k<size-1;k++) { 00122 O2SCL_IX(b3,k+1)=O2SCL_IX(off_diag,k); 00123 O2SCL_IX(eigen,k)=O2SCL_IX(diag,k); 00124 } 00125 O2SCL_IX(eigen,size-1)=O2SCL_IX(diag,size-1); 00126 if (eigen_tdiag(j,eigen,b3)!=0) { 00127 00128 ao.free(v); 00129 ao.free(w); 00130 ao.free(b3); 00131 ao.free(prod); 00132 00133 add_err_ret("Call to eigen_tdiag() in eigenvalues() failed.", 00134 o2scl::gsl_efailed); 00135 } 00136 } 00137 } 00138 00139 ao.free(v); 00140 ao.free(w); 00141 ao.free(b3); 00142 ao.free(prod); 00143 00144 return 0; 00145 } 00146 00147 /** 00148 \brief In-place diagonalization of a tri-diagonal matrix 00149 00150 On input, the vectors \c diag and \c off_diag should both be 00151 vectors of size \c n. The diagonal entries stored in \c diag, 00152 and the \f$ n-1 \f$ off-diagonal entries should be stored in 00153 \c off_diag, starting with \c off_diag[1]. The value in \c 00154 off_diag[0] is unused. The vector \c off_diag is destroyed by 00155 the computation. 00156 00157 This uses an implict QL method from the EISPACK routine \c 00158 imtql1. The value of \c ierr from the original Fortran routine 00159 is stored in \ref td_lasteval. 00160 00161 */ 00162 int eigen_tdiag(size_t n, vec_t &diag, vec_t &off_diag) { 00163 00164 // 'i' is set to zero here because Cygwin complained 00165 // about uninit'ed variables. This is probably ok, 00166 // but it would be nice to double check that there 00167 // isn't a problem with setting i=0 here. 00168 int i=0,j,l,m,mml; 00169 double b,c,f,g,p,r,s,tst1,tst2; 00170 00171 if (n==1) return 0; 00172 00173 for(size_t ij=1;ij<n;ij++) { 00174 O2SCL_IX(off_diag,ij-1)=O2SCL_IX(off_diag,ij); 00175 } 00176 O2SCL_IX(off_diag,n-1)=0.0; 00177 00178 bool done=false; 00179 00180 l=1; 00181 j=0; 00182 00183 while (done==false && l<=((int)n)) { 00184 00185 // Look for small sub-diagonal element 00186 bool idone=false; 00187 for(m=l;m<((int)n) && idone==false;m++) { 00188 tst1=fabs(O2SCL_IX(diag,m-1))+fabs(O2SCL_IX(diag,m)); 00189 tst2=tst1+fabs(O2SCL_IX(off_diag,m-1)); 00190 if (tst2==tst1) { 00191 m--; 00192 idone=true; 00193 } 00194 } 00195 00196 p=O2SCL_IX(diag,l-1); 00197 00198 if (m!=l && j==((int)td_iter)) { 00199 00200 // Set error. No convergence after td_iter iterations 00201 td_lasteval=l; 00202 set_err_ret("No convergence in lanczos::eigen_tdiag()", 00203 o2scl::gsl_efailed); 00204 } 00205 00206 if (m!=l) { 00207 00208 j++; 00209 00210 // Form shift 00211 g=(O2SCL_IX(diag,l)-p)/(2.0*O2SCL_IX(off_diag,l-1)); 00212 r=gsl_hypot(g,1.0); 00213 00214 g=O2SCL_IX(diag,m-1)-p+O2SCL_IX(off_diag,l-1)/ 00215 (g+(g>=0.0 ? fabs(r) : -fabs(r))); 00216 s=1.0; 00217 c=1.0; 00218 p=0.0; 00219 mml=m-l; 00220 00221 for(int ii=1;ii<=mml;ii++) { 00222 00223 i=m-ii; 00224 f=s*O2SCL_IX(off_diag,i-1); 00225 b=c*O2SCL_IX(off_diag,i-1); 00226 r=gsl_hypot(f,g); 00227 O2SCL_IX(off_diag,i)=r; 00228 00229 if (r==0.0) { 00230 00231 // Recover from underflow 00232 O2SCL_IX(diag,i)-=p; 00233 O2SCL_IX(off_diag,m-1)=0.0; 00234 ii=mml+1; 00235 00236 } else { 00237 00238 s=f/r; 00239 c=g/r; 00240 g=O2SCL_IX(diag,i)-p; 00241 r=(O2SCL_IX(diag,i-1)-g)*s+2.0*c*b; 00242 p=s*r; 00243 O2SCL_IX(diag,i)=g+p; 00244 g=c*r-b; 00245 00246 } 00247 00248 } 00249 00250 O2SCL_IX(diag,l-1)-=p; 00251 O2SCL_IX(off_diag,l-1)=g; 00252 O2SCL_IX(off_diag,m-1)=0.0; 00253 00254 00255 } else { 00256 00257 // Order eigenvalues 00258 00259 if (l==1) { 00260 00261 i=1; 00262 O2SCL_IX(diag,i-1)=p; 00263 00264 } else { 00265 00266 bool skip=false; 00267 for(int ii=2;ii<=l;ii++) { 00268 i=l+2-ii; 00269 if (p>=O2SCL_IX(diag,i-2)) { 00270 ii=l+1; 00271 skip=true; 00272 } else { 00273 O2SCL_IX(diag,i-1)=O2SCL_IX(diag,i-2); 00274 } 00275 } 00276 00277 if (skip==false) i=1; 00278 O2SCL_IX(diag,i-1)=p; 00279 } 00280 00281 j=0; 00282 l++; 00283 } 00284 00285 } 00286 00287 return 0; 00288 } 00289 00290 #ifndef DOXYGEN_INTERNAL 00291 00292 protected: 00293 00294 /** 00295 \brief Naive matrix-vector product 00296 00297 It is assumed that memory is already allocated for \c prod. 00298 */ 00299 void product(size_t n, mat_t &a, vec_t &w, vec_t &prod) { 00300 size_t i, j; 00301 for(i=0;i<n;i++) { 00302 O2SCL_IX(prod,i)=0.0; 00303 for(j=0;j<n;j++) { 00304 O2SCL_IX(prod,i)+=O2SCL_IX2(a,i,j)*O2SCL_IX(w,j); 00305 } 00306 } 00307 return; 00308 } 00309 00310 #endif 00311 00312 }; 00313
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