|
STK++ 1.0
|
00001 /*--------------------------------------------------------------------*/ 00002 /* Copyright (C) 2004-2007 Serge Iovleff 00003 00004 This program is free software; you can redistribute it and/or modify 00005 it under the terms of the GNU Lesser General Public License as 00006 published by the Free Software Foundation; either version 2 of the 00007 License, or (at your option) any later version. 00008 00009 This program is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 GNU Lesser General Public License for more details. 00013 00014 You should have received a copy of the GNU Lesser General Public 00015 License along with this program; if not, write to the 00016 Free Software Foundation, Inc., 00017 59 Temple Place, 00018 Suite 330, 00019 Boston, MA 02111-1307 00020 USA 00021 00022 Contact : Serge.Iovleff@stkpp.org 00023 */ 00024 00025 /* 00026 * Project: Algebra 00027 * Purpose: Implementation of the EigenValue class 00028 * Author: Serge Iovleff, serge.iovleff@stkpp.org 00029 * 00030 **/ 00031 00036 #include "../include/STK_Vector.h" 00037 #include "../include/STK_Householder.h" 00038 #include "../include/STK_Givens.h" 00039 #include "../include/STK_LinAlgebra1D.h" 00040 #include "../include/STK_TExpAlgebra.h" 00041 00042 #include "../include/STK_Eigenvalues.h" 00043 00044 namespace STK 00045 { 00046 /* Constructors. 00047 * A is the matrix to decompose. 00048 **/ 00049 Eigenvalues::Eigenvalues( MatrixSquare const& A, const bool &ref) 00050 : P_(A, ref) 00051 , D_(A.rangeVe()) 00052 , first_(A.firstCol()) 00053 , last_(A.lastCol()) 00054 , norm_(0) 00055 , rank_(0) 00056 , error_(false) 00057 { 00058 if (A.empty()) return; 00059 // diagonalize 00060 diagsym(); 00061 } 00062 00063 /* Copy constructor */ 00064 Eigenvalues::Eigenvalues( const Eigenvalues &S) 00065 : P_(S.P_) 00066 , D_(S.D_) 00067 , first_(S.first_) 00068 , last_(S.last_) 00069 , norm_(S.norm_) 00070 , rank_(S.rank_) 00071 , error_(S.error_) 00072 { ;} 00073 00074 /* Operator = : overwrite the Eigenvalues with S. 00075 **/ 00076 Eigenvalues& Eigenvalues::operator=(const Eigenvalues &S) 00077 { 00078 P_ = S.P_; // Matrix P 00079 D_ = S.D_; // Singular values 00080 first_ = S.first_; // first value 00081 last_ = S.last_; // last value 00082 norm_ = S.norm_; // rank of the matrix 00083 rank_ = S.rank_; // rank of the matrix 00084 error_ = S.error_; // Everything OK during computation ? 00085 00086 return *this; 00087 } 00088 00089 /* destructor */ 00090 Eigenvalues::~Eigenvalues() 00091 { ;} 00092 00093 /* 00094 * Compute the generalized inverse of the diagonalized matrix. 00095 * The result is allocated dynamically and is not liberated by this 00096 * class. 00097 * @return a pointer on the generalized inverse. 00098 */ 00099 MatrixSquare* Eigenvalues::ginv() 00100 { 00101 // create pseudo inverse matrix 00102 MatrixSquare* res = new MatrixSquare(P_.range()); 00103 // compute tolerance 00104 Real tol = Arithmetic<Real>::epsilon() * norm_; 00105 // compute PDP' 00106 for (Integer i = first_; i<=last_; i++) 00107 { 00108 for (Integer j = first_; j<=last_; j++) 00109 { 00110 Real sum = 0.0; 00111 for (Integer k = first_; k<=last_; k++) 00112 { 00113 if (abs(D_[k]) > tol) 00114 sum += (P_(i, k) * P_(j, k)) / D_[k]; 00115 } 00116 (*res)(j,i) = sum; 00117 } 00118 } 00119 return res; 00120 } 00121 00122 /* 00123 * Compute the generalized inverse of the diagonalized matrix P_ and put 00124 * the result in res. 00125 * @param res the generalized inverse of the matrix P_. 00126 */ 00127 void Eigenvalues::ginv(MatrixSquare& res) 00128 { 00129 // create pseudo inverse matrix 00130 res.resize(P_.range()); 00131 // compute tolerance 00132 Real tol = Arithmetic<Real>::epsilon() * norm_; 00133 // compute PDP' 00134 for (Integer i = first_; i<=last_; i++) 00135 { 00136 for (Integer j = first_; j<=last_; j++) 00137 { 00138 Real sum = 0.0; 00139 for (Integer k = first_; k<=last_; k++) 00140 { 00141 if (abs(D_[k]) > tol) 00142 sum += (P_(i, k) * P_(j, k)) / D_[k]; 00143 } 00144 res(j,i) = sum; 00145 } 00146 } 00147 } 00148 00149 00150 /* Main methods. */ 00151 /* Compute diagonalization of the symmetric matrix */ 00152 void Eigenvalues::diagsym() 00153 { 00154 // tridiagonalize P_ 00155 tridiag(); 00156 // compute P_ 00157 compHouse(); 00158 // Diagonalize 00159 diag(); 00160 } 00161 00162 /* tridiagonalisation of the symetric matrix P_. Only the lower 00163 * part of P_ used. P_ is overwritten with the Householder vectors. 00164 * D_ contains the diagonal. F_ contains the subdiagonal. 00165 **/ 00166 void Eigenvalues::tridiag() 00167 { 00168 // Upper diagonal values 00169 F_.resize(Range(first_-1, last_)); 00170 F_.front() = 0.0; F_.back() =0.0; 00171 // initial range of the Householder vectors 00172 Range range1(Range(first_+1, last_)); 00173 // Bidiagonalisation of P_ 00174 // loop on the cols and rows 00175 for ( Integer iter=first_, iter1=first_+1, iter2=first_+2 00176 ; iter<last_ 00177 ; iter++, iter1++, iter2++, range1.incFirst() 00178 ) 00179 { 00180 // ref on the current column iter in the range iter1:last_ 00181 Vector v(P_, range1, iter); 00182 // Compute Householder Vector and get subdiagonal element 00183 F_[iter] = house(v); 00184 // Save diagonal element 00185 D_[iter] = P_(iter,iter); 00186 // get beta 00187 Real beta = v.front(); 00188 if (beta) 00189 { 00190 // ref on the current column iter1 in the range iter1:last_ 00191 Vector M1(P_, range1, iter1); 00192 // aux1 will contain <v,p> 00193 Real aux1 = 0.0; 00194 // apply left and right Householder to P_ 00195 // compute D(iter1:last_) = p and aux1 = <p,v> 00196 // Computation of p_iter1 = beta * P_ v using the lower part of P_ 00197 Real aux = M1[iter1] /* *1.0 */; 00198 for (Integer j=iter2; j<=last_; j++) { aux += M1[j]*v[j];} 00199 // save p_iter1 in the unusued part of D_ and compute p'v 00200 aux1 += (D_[iter1] = beta*aux) /* *1.0 */; 00201 // other cols 00202 for (Integer i=iter2; i<=last_; i++) 00203 { 00204 // Computation of p_i = beta * P_ v using the lower part of P_ 00205 aux = M1[i] /* *1.0 */; 00206 for (Integer j=iter2; j<=i; j++) { aux += P_(i,j)*v[j];} 00207 for (Integer j=i+1; j<=last_; j++) { aux += P_(j,i)*v[j];} 00208 // save p_i in the unusued part of D_ and compute p'v 00209 aux1 += (D_[i] = beta*aux) * v[i]; 00210 } 00211 // update diagonal element M_ii+= 2 v_i * q_i = 2* q_i (i=iter1) 00212 // aux = q_iter1 and aux1 = beta*<p,v>/2 (we don't save aux in D_) 00213 aux = (D_[iter1] + (aux1 *= (beta/2.0))); 00214 M1[iter1] += 2.0*aux; 00215 // update lower part: all rows 00216 // compute q_i and update the lower part of P_ 00217 for (Integer i=iter2; i<=last_; i++) 00218 { 00219 // get v_i 00220 Real v_i = v[i]; 00221 // get q_i and save it in D_i=q_i = p_i + <p,v> * beta * v_i/2 00222 Real q_i = (D_[i] += aux1 * v_i); 00223 // Compute P_ + u q' + q u', 00224 // update the row i, first_ element 00225 M1[i] += q_i /* *1.0 */+ v_i * aux; 00226 // update the row i: all cols under the diagonal 00227 for (Integer j=iter2; j<=i; j++) 00228 P_(i,j) += v[j] * q_i + v_i * D_[j]; 00229 } 00230 } 00231 } 00232 // Last col: F[last_] = 0.0; 00233 D_[last_] = P_(last_,last_); 00234 } 00235 00236 // Compute P_ from the Householder rotations 00237 void Eigenvalues::compHouse() 00238 { 00239 // compute P_ 00240 // iter0 is the column of the Householder vector 00241 // iter is the current column to compute 00242 for ( Integer iter0=last_-1, iter=last_, iter1=last_+1 00243 ; iter0>=first_ 00244 ; iter0--, iter--, iter1--) 00245 { 00246 // reference on the Householder vector 00247 Vector v(P_, Range(iter, last_), iter0); 00248 // Get Beta 00249 Real beta = v[iter]; 00250 if (beta) 00251 { 00252 // Compute Col iter -> P_ e_{iter}= e_{iter}+ beta v 00253 P_(iter, iter) = 1.0 + beta /* *1.0 */; 00254 for (Integer i=iter1; i<=last_; i++) 00255 { P_(i, iter) = beta * v[i];} 00256 00257 // Update the other Cols 00258 for (Integer i=iter1; i<=last_; i++) 00259 { 00260 // compute beta*<v, P_^i> 00261 Real aux = 0.0; 00262 for (Integer j=iter1; j<=last_; j++) 00263 { aux += P_(j, i) * v[j]; } 00264 aux *= beta; 00265 // first_ row (iter) 00266 P_(iter, i) = aux; 00267 // other rows 00268 for (Integer j=iter1; j<=last_; j++) 00269 { P_(j, i) += aux * v[j];} 00270 } 00271 } 00272 else // beta = 0, nothing to do 00273 { P_(iter,iter)=1.0; 00274 for (Integer j=iter1; j<=last_; j++) 00275 { P_(iter, j) =0.0; P_(j, iter) = 0.0;} 00276 } 00277 } 00278 // first_ row and first_ col 00279 P_(first_, first_) = 1.0; 00280 for (Integer j=first_+1; j<=last_; j++) 00281 { P_(first_, j) = 0.0; P_(j, first_) = 0.0;} 00282 } 00283 00284 // diagonalize D_ and F_ 00285 void Eigenvalues::diag() 00286 { 00287 // Diagonalisation of P_ 00288 for (Integer iend=last_; iend>=first_+1; iend--) 00289 { 00290 Integer iter; 00291 for (iter=0; iter<30; iter++) // 30 iterations max 00292 { 00293 // check cv for the last element 00294 Real sum = abs(D_[iend]) + abs(D_[iend-1]); 00295 // if the first element of the small subdiagonal 00296 // is 0. we stop the QR iterations and increment iend 00297 if (abs(F_[iend-1])+sum == sum) 00298 { F_[iend-1] = 0.0 ; break;} 00299 // look for a single small subdiagonal element to split the matrix 00300 Integer ibeg = iend-1; 00301 while (ibeg>first_) 00302 { 00303 ibeg--; 00304 // if a subdiagonal is zero, we get a sub matrix unreduced 00305 sum = abs(D_[ibeg])+abs(D_[ibeg+1]); 00306 if (abs(F_[ibeg])+sum == sum) { F_[ibeg] = 0.; ibeg++; break;} 00307 }; 00308 // QR rotations between the rows/cols ibeg et iend 00309 // Computation of the Wilkinson's shift 00310 Real aux = (D_[iend-1] - D_[iend])/(2.0 * F_[iend-1]); 00311 // initialisation of the matrix of rotation 00312 // y is the current F_[k-1], 00313 Real y = D_[ibeg]-D_[iend] + F_[iend-1]/sign(aux, norm(aux,1.0)); 00314 // z is the element to annulate 00315 Real z = F_[ibeg]; 00316 // Fk is the temporary F_[k] 00317 Real Fk = z; 00318 // temporary DeltaD(k) 00319 Real DeltaDk = 0.0; 00320 // Index of the columns 00321 Integer k,k1; 00322 // Givens rotation to restore tridiaonal form 00323 for (k=ibeg, k1=ibeg+1; k<iend; k++, k1++) 00324 { 00325 // underflow ? we have a tridiagonal form exit now 00326 if (z == 0.0) { F_[k]=Fk; break;} 00327 // Rotation columns (k,k+1) 00328 F_[k-1] = (aux = norm(y,z)); // compute final F_[k-1] 00329 // compute cosinus and sinus 00330 Real cosinus = y/aux, sinus = -z/aux; 00331 Real Dk = D_[k] + DeltaDk; // compute current D[k] 00332 Real aux1 = 2.0 * cosinus * Fk + sinus * (Dk - D_[k1]); 00333 // compute DeltaD(k+1) 00334 D_[k] = Dk - (DeltaDk = sinus * aux1); // compute D_[k] 00335 y = cosinus*aux1 - Fk; // temporary F_[k] 00336 Fk = cosinus * F_[k1]; // temporary F_[k+1] 00337 z = -sinus * F_[k1]; // temporary z 00338 // update P_ 00339 rightGivens(P_, k1, k, cosinus, sinus); 00340 } 00341 // k=iend if z!=0 and k<iend if z==0 00342 D_[k] += DeltaDk ; F_[k-1] = y; 00343 // restore F[ibeg-1] 00344 F_[ibeg-1] = 0.; 00345 } // iter 00346 if (iter == 30) { error_ = true;} 00347 // We have to sort the eigenvalues : we use a basic strategy 00348 Real z = D_[iend]; // current value 00349 for (Integer i=iend+1; i<=D_.last(); i++) 00350 { if (D_[i] > z) // if the ith eigenvalue is greater 00351 { D_.swap(i-1, i); // swap the cols 00352 P_.swapCols(i-1, i); 00353 } 00354 else break; 00355 } // end sort 00356 } // iend 00357 // sort first value 00358 Real z = D_[first_]; // current value 00359 for (Integer i=first_+1; i<=D_.last(); i++) 00360 { if (D_[i] > z) // if the ith eigenvalue is greater 00361 { 00362 D_.swap(i-1, i); // swap the cols 00363 P_.swapCols(i-1, i); 00364 } 00365 else break; 00366 } // end sort 00367 00368 // compute norm_ 00369 norm_ = D_[first_]; 00370 // compute tolerance 00371 Real tol = Arithmetic<Real>::epsilon() * norm_; 00372 // compute rank_ 00373 for (Integer i = first_; i<= last_; i++) 00374 { 00375 if (abs(D_[i])> tol ) rank_++; 00376 } 00377 } 00378 00379 // Compute P_ from the Householder rotations 00380 void Eigenvalues::compHouse(MatrixSquare &P) 00381 { 00382 // compute the number of iteration 00383 const Integer first = P.firstRow(); 00384 const Integer last = P.lastRow(); 00385 // compute P 00386 // iter0 is the column of the Householder vector 00387 // iter is the current column to compute 00388 for ( Integer iter0=last-1, iter=last, iter1=last+1 00389 ; iter0>=first 00390 ; iter0--, iter--, iter1--) 00391 { 00392 // reference on the Householder vector 00393 Vector v(P, Range(iter, last), iter0); 00394 // Get Beta 00395 Real beta = v[iter]; 00396 if (beta) 00397 { 00398 // Compute Col iter -> P e_{iter}= e_{iter}+ beta v 00399 P(iter, iter) = 1.0 + beta /* *1.0 */; 00400 for (Integer i=iter1; i<=last; i++) 00401 { P(i, iter) = beta * v[i];} 00402 00403 // Update the other Cols 00404 for (Integer i=iter1; i<=last; i++) 00405 { 00406 // compute beta*<v, P^i> 00407 Real aux = 0.0; 00408 for (Integer j=iter1; j<=last; j++) 00409 { aux += P(j, i) * v[j]; } 00410 aux *= beta; 00411 // first row (iter) 00412 P(iter, i) = aux; 00413 // other rows 00414 for (Integer j=iter1; j<=last; j++) 00415 { P(j, i) += aux * v[j];} 00416 } 00417 } 00418 else // beta = 0, nothing to do 00419 { P(iter,iter)=1.0; 00420 for (Integer j=iter1; j<=last; j++) 00421 { P(iter, j) =0.0; P(j, iter) = 0.0;} 00422 } 00423 } 00424 // first row and first col 00425 P(first, first) = 1.0; 00426 for (Integer j=first+1; j<=last; j++) 00427 { P(first, j) = 0.0; P(j, first) = 0.0;} 00428 } 00429 00430 00431 /* tridiagonalisation of the symetric matrix P. Only the lower 00432 * part of P used. P is overwritten with the Householder vectors. 00433 * D contains the diagonal. F contains the subdiagonal. 00434 **/ 00435 Real Eigenvalues::tridiag( MatrixSquare &P, Point &D, Point& F) 00436 { 00437 // norm of the matrix P 00438 Real norm = 0.0; 00439 // compute the number of iteration 00440 Integer first = P.firstRow(); 00441 Integer last = P.lastRow(); 00442 // Diagonal values 00443 D.resize(P.rangeVe()); 00444 // Upper diagonal values 00445 F.resize(Range(first-1, last)); 00446 F.front() = 0.0; F.back() =0.0; 00447 // initial range of the Householder vectors 00448 Range range1(Range(first+1, last)); 00449 // Bidiagonalisation of P 00450 // loop on the cols and rows 00451 for ( Integer iter=first, iter1=first+1, iter2=first+2 00452 ; iter<last 00453 ; iter++, iter1++, iter2++, range1.incFirst() 00454 ) 00455 { 00456 // ref on the current column iter in the range iter1:last 00457 Vector v(P, range1, iter); 00458 // Compute Householder Vector and get subdiagonal element 00459 F[iter] = house(v); 00460 // Save diagonal element 00461 D[iter] = P(iter,iter); 00462 // get beta 00463 Real beta = v.front(); 00464 if (beta) 00465 { 00466 // ref on the current column iter1 in the range iter1:last 00467 Vector P1(P, range1, iter1); 00468 // aux1 will contain <v,p> 00469 Real aux1 = 0.0; 00470 // apply left and right Householder to P 00471 // compute D(iter1:last) = p and aux1 = <p,v> 00472 // Computation of p_iter1 = beta * M v using the lower part of M 00473 Real aux = P1[iter1] /* *1.0 */; 00474 for (Integer j=iter2; j<=last; j++) { aux += P1[j]*v[j];} 00475 // save p_iter1 in the unusued part of D_ and compute p'v 00476 aux1 += (D[iter1] = beta*aux) /* *1.0 */; 00477 // other cols 00478 for (Integer i=iter2; i<=last; i++) 00479 { 00480 // Computation of p_i = beta * M v using the lower part of M 00481 aux = P1[i] /* *1.0 */; 00482 for (Integer j=iter2; j<=i; j++) { aux += P(i,j)*v[j];} 00483 for (Integer j=i+1; j<=last; j++) { aux += P(j,i)*v[j];} 00484 // save p_i in the unusued part of D_ and compute p'v 00485 aux1 += (D[i] = beta*aux) * v[i]; 00486 } 00487 // update diagonal element M_ii+= 2 v_i * q_i = 2* q_i (i=iter1) 00488 // aux = q_iter1 and aux1 = beta*<p,v>/2 (we don't save aux in D_) 00489 aux = (D[iter1] + (aux1 *= (beta/2.0))); 00490 P1[iter1] += 2.0*aux; 00491 // update lower part: all rows 00492 // compute q_i and update the lower part of M 00493 for (Integer i=iter2; i<=last; i++) 00494 { 00495 // get v_i 00496 Real v_i = v[i]; 00497 // get q_i and save it in D_i=q_i = p_i + <p,v> * beta * v_i/2 00498 Real q_i = (D[i] += aux1 * v_i); 00499 // Compute M + u q' + q u', 00500 // update the row i, first element 00501 P1[i] += q_i /* *1.0 */+ v_i * aux; 00502 // update the row i: all cols under the diagonal 00503 for (Integer j=iter2; j<=i; j++) 00504 P(i,j) += v[j] * q_i + v_i * D[j]; 00505 } 00506 } 00507 // Estimation of the norm 00508 norm = max(abs(D[iter])+abs(F[iter]), norm); 00509 } 00510 // Last col: F[last] = 0.0; 00511 D[last] = P(last,last); 00512 // return norm estimated 00513 return sqrt(norm); 00514 } 00515 00516 // diagonalize D_ and F_ 00517 bool Eigenvalues::diag( MatrixSquare &P, Point &D, Point &F 00518 , const bool &withP) 00519 { 00520 // error detected in the iterations 00521 bool error =false; 00522 // dimensions 00523 Integer first = D.first(), last =D.last(); 00524 // Diagonalisation of P_ 00525 for (Integer iend=last; iend>=first+1; iend--) 00526 { 00527 Integer iter; 00528 for (iter=0; iter<30; iter++) // 30 iterations max 00529 { 00530 // check Cv fo the last element 00531 Real sum = abs(D[iend]) + abs(D[iend-1]); 00532 // if the first element of the small subdiagonal 00533 // is 0. we stop the QR iterations and increment iend 00534 if (abs(F[iend-1])+sum == sum) 00535 { F[iend-1] = 0.0 ; break;} 00536 // look for a single small subdiagonal element to split the matrix 00537 Integer ibeg = iend-1; 00538 while (ibeg>first) 00539 { 00540 ibeg--; 00541 // if a subdiagonal is zero, we get a sub matrix unreduced 00542 sum = abs(D[ibeg])+abs(D[ibeg+1]); 00543 if (abs(F[ibeg])+sum == sum) { F[ibeg] = 0.; ibeg++; break;} 00544 }; 00545 // QR rotations between the rows/cols ibeg et iend 00546 // Computation of the Wilkinson's shift 00547 Real aux = (D[iend-1] - D[iend])/(2.0 * F[iend-1]); 00548 // initialisation of the matrix of rotation 00549 // y is the current F[k-1], 00550 Real y = D[ibeg]-D[iend] + F[iend-1]/sign(aux, norm(aux,1.0)); 00551 // z is the element to annulate 00552 Real z = F[ibeg]; 00553 // Fk is the temporary F[k] 00554 Real Fk = z; 00555 // temporary DeltaD(k) 00556 Real DeltaDk = 0.0; 00557 // Index of the columns 00558 Integer k,k1; 00559 // Givens rotation to restore tridiaonal form 00560 for (k=ibeg, k1=ibeg+1; k<iend; k++, k1++) 00561 { 00562 // underflow ? we have a tridiagonal form exit now 00563 if (z == 0.0) { F[k]=Fk; break;} 00564 // Rotation columns (k,k+1) 00565 F[k-1] = (aux = norm(y,z)); // compute final F[k-1] 00566 // compute cosinus and sinus 00567 Real cosinus = y/aux, sinus = -z/aux; 00568 Real Dk = D[k] + DeltaDk; // compute current D[k] 00569 Real aux1 = 2.0 * cosinus * Fk + sinus * (Dk - D[k1]); 00570 // compute DeltaD(k+1) 00571 D[k] = Dk - (DeltaDk = sinus * aux1); // compute D[k] 00572 y = cosinus*aux1 - Fk; // temporary F[k] 00573 Fk = cosinus * F[k1]; // temporary F[k+1] 00574 z = -sinus * F[k1]; // temporary z 00575 // update P_ 00576 if (withP) rightGivens(P, k1, k, cosinus, sinus); 00577 } 00578 // k=iend if z!=0 and k<iend if z==0 00579 D[k] += DeltaDk ; F[k-1] = y; 00580 // restore F[ibeg-1] 00581 F[ibeg-1] = 0.; 00582 } // iter 00583 if (iter == 30) { error = true;} 00584 } // iend 00585 return error; 00586 } 00587 00588 } // Namespace STK