|
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 "../../Arrays/include/STK_Vector.h" 00037 00038 #include "../include/STK_Householder.h" 00039 #include "../include/STK_Givens.h" 00040 #include "../include/STK_LinAlgebra1D.h" 00041 #include "../include/STK_TExpAlgebra.h" 00042 00043 #include "../include/STK_EigenvaluesSymmetric.h" 00044 00045 #define MAXITER 100 00046 00047 namespace STK 00048 { 00049 /* Constructors. 00050 * A is the matrix to decompose. 00051 **/ 00052 EigenvaluesSymmetric::EigenvaluesSymmetric( MatrixSquare const* p_data) 00053 : IRunnerPtrMatrix(p_data) 00054 , P_(p_data->range()) 00055 , D_(p_data->range()) 00056 , first_(p_data->first()) 00057 , last_(p_data->last()) 00058 , norm_(0) 00059 , rank_(0) 00060 , det_(0) 00061 { } 00062 00063 /* Copy constructor */ 00064 EigenvaluesSymmetric::EigenvaluesSymmetric( EigenvaluesSymmetric const& S) 00065 : IRunnerPtrMatrix(S) 00066 , P_(S.P_) 00067 , D_(S.D_) 00068 , first_(S.first_) 00069 , last_(S.last_) 00070 , norm_(S.norm_) 00071 , rank_(S.rank_) 00072 , det_(S.det_) 00073 { ;} 00074 00075 /* Operator = : overwrite the EigenvaluesSymmetric with S. 00076 **/ 00077 EigenvaluesSymmetric& EigenvaluesSymmetric::operator=( EigenvaluesSymmetric const& S) 00078 { 00079 P_ = S.P_; // Matrix P 00080 D_ = S.D_; // Singular values 00081 first_ = S.first_; // first value 00082 last_ = S.last_; // last value 00083 norm_ = S.norm_; // norm of the matrix 00084 rank_ = S.rank_; // rank of the matrix 00085 det_ = S.det_; // determinant of the matrix 00086 error_ = S.error_; // Everything OK during computation ? 00087 00088 return *this; 00089 } 00090 00091 /* destructor */ 00092 EigenvaluesSymmetric::~EigenvaluesSymmetric() 00093 { ;} 00094 00095 /* 00096 * Compute the generalized inverse of the diagonalized matrix. 00097 * The result is allocated dynamically and is not liberated by this 00098 * class. 00099 * @return a pointer on the generalized inverse. 00100 */ 00101 MatrixSquare* EigenvaluesSymmetric::ginv() 00102 { 00103 // create pseudo inverse matrix 00104 MatrixSquare* res = new MatrixSquare(P_.range()); 00105 // compute tolerance 00106 Real tol = Arithmetic<Real>::epsilon() * norm_; 00107 // compute PDP' 00108 for (Integer i = first_; i<=last_; i++) 00109 { 00110 for (Integer j = first_; j<=last_; j++) 00111 { 00112 Real sum = 0.0; 00113 for (Integer k = first_; k<=last_; k++) 00114 { 00115 if (abs(D_[k]) > tol) 00116 sum += (P_(i, k) * P_(j, k)) / D_[k]; 00117 } 00118 (*res)(j,i) = sum; 00119 } 00120 } 00121 return res; 00122 } 00123 00124 /* 00125 * Compute the generalized inverse of the diagonalized matrix P_ and put 00126 * the result in res. 00127 * @param res the generalized inverse of the matrix P_. 00128 */ 00129 void EigenvaluesSymmetric::ginv(MatrixSquare& res) 00130 { 00131 // create pseudo inverse matrix 00132 res.resize(P_.range()); 00133 // compute tolerance 00134 Real tol = Arithmetic<Real>::epsilon() * norm_; 00135 // compute PDP' 00136 for (Integer i = first_; i<=last_; i++) 00137 { 00138 for (Integer j = first_; j<=last_; j++) 00139 { 00140 Real sum = 0.0; 00141 for (Integer k = first_; k<=last_; k++) 00142 { 00143 if (abs(D_[k]) > tol) 00144 sum += (P_(i, k) * P_(j, k)) / D_[k]; 00145 } 00146 res(j,i) = sum; 00147 } 00148 } 00149 } 00150 00151 00152 /* Main methods. */ 00153 /* Compute diagonalization of the symmetric matrix */ 00154 bool EigenvaluesSymmetric::run() 00155 { 00156 try 00157 { 00158 // copy data 00159 P_ = p_data_->asLeaf(); 00160 D_.resize(p_data_->rangeHo()); 00161 first_ = p_data_->firstCol(); 00162 last_ = p_data_->lastCol(); 00163 norm_ = 0.; 00164 rank_ = 0; 00165 det_ = 0.; 00166 // tridiagonalize P_ 00167 tridiagonalize(); 00168 // compute P_ 00169 compHouse(); 00170 // Diagonalize 00171 diagonalize(); 00172 // compute rank, norm and determinant 00173 compEstimates(); 00174 } 00175 catch (Exception e) 00176 { 00177 error_ = e.error(); 00178 return false; 00179 } 00180 return true; 00181 } 00182 00183 /* Main methods. */ 00184 /* Compute diagonalization of the symmetric matrix */ 00185 bool EigenvaluesSymmetric::run(Vector const& weights) 00186 { 00187 // copy data 00188 if (p_data_->rangeVe() != weights.range()) 00189 { throw runtime_error("In EigenvaluesSymmetric::run(weights) " 00190 "p_data_->rangeVe() != weights.range().\n"); 00191 } 00192 00193 try 00194 { 00195 // copy data 00196 // copy data 00197 P_ = p_data_->asLeaf(); 00198 D_.resize(P_.range()); 00199 first_ = P_.first(); 00200 last_ = P_.last(); 00201 norm_ = 0.; 00202 rank_ = 0; 00203 det_ = 0.; 00204 // weight rows and columns of the container 00205 for (Integer i= first_; i <= last_; ++i) 00206 { 00207 Real aux = Real(sqrt(weights[i])); 00208 P_(i) *= aux; 00209 P_[i] *= aux; 00210 } 00211 00212 // tridiagonalize P_ 00213 tridiagonalize(); 00214 // compute P_ 00215 compHouse(); 00216 // Diagonalize 00217 diagonalize(); 00218 // compute rank, norm and determinant 00219 compEstimates(); 00220 00221 // Unweighed rows and columns of the rotation 00222 for (Integer i= first_; i <= last_; ++i) 00223 { 00224 Real aux = Real(sqrt(weights[i])); 00225 if (aux) 00226 { 00227 P_(i) /= aux; 00228 P_[i] /= aux; 00229 } 00230 } 00231 } 00232 catch (Exception e) 00233 { 00234 error_ = e.error(); 00235 return false; 00236 } 00237 return true; 00238 } 00239 00240 /* tridiagonalisation of the symetric matrix P_. Only the lower 00241 * part of P_ used. P_ is overwritten with the Householder vectors. 00242 * D_ contains the diagonal. F_ contains the subdiagonal. 00243 **/ 00244 void EigenvaluesSymmetric::tridiagonalize() 00245 { 00246 // Upper diagonal values 00247 F_.resize(Range(first_-1, last_)); 00248 F_.front() = 0.0; F_.back() =0.0; 00249 // initial range of the Householder vectors 00250 Range range1(Range(first_+1, last_)); 00251 // Bidiagonalisation of P_ 00252 // loop on the cols and rows 00253 for ( Integer iter=first_, iter1=first_+1, iter2=first_+2 00254 ; iter<last_ 00255 ; iter++, iter1++, iter2++, range1.incFirst() 00256 ) 00257 { 00258 // ref on the current column iter in the range iter1:last_ 00259 Vector v(P_, range1, iter); 00260 // Compute Householder Vector and get subdiagonal element 00261 F_[iter] = house(v); 00262 // Save diagonal element 00263 D_[iter] = P_(iter,iter); 00264 // get beta 00265 Real beta = v.front(); 00266 if (beta) 00267 { 00268 // ref on the current column iter1 in the range iter1:last_ 00269 Vector M1(P_, range1, iter1); 00270 // aux1 will contain <v,p> 00271 Real aux1 = 0.0; 00272 // apply left and right Householder to P_ 00273 // compute D(iter1:last_) = p and aux1 = <p,v> 00274 // Computation of p_iter1 = beta * P_ v using the lower part of P_ 00275 Real aux = M1[iter1] /* *1.0 */; 00276 for (Integer j=iter2; j<=last_; j++) { aux += M1[j]*v[j];} 00277 // save p_iter1 in the unusued part of D_ and compute p'v 00278 aux1 += (D_[iter1] = beta*aux) /* *1.0 */; 00279 // other cols 00280 for (Integer i=iter2; i<=last_; i++) 00281 { 00282 // Computation of p_i = beta * P_ v using the lower part of P_ 00283 aux = M1[i] /* *1.0 */; 00284 for (Integer j=iter2; j<=i; j++) { aux += P_(i,j)*v[j];} 00285 for (Integer j=i+1; j<=last_; j++) { aux += P_(j,i)*v[j];} 00286 // save p_i in the unusued part of D_ and compute p'v 00287 aux1 += (D_[i] = beta*aux) * v[i]; 00288 } 00289 // update diagonal element M_ii+= 2 v_i * q_i = 2* q_i (i=iter1) 00290 // aux = q_iter1 and aux1 = beta*<p,v>/2 (we don't save aux in D_) 00291 aux = (D_[iter1] + (aux1 *= (beta/2.0))); 00292 M1[iter1] += 2.0*aux; 00293 // update lower part: all rows 00294 // compute q_i and update the lower part of P_ 00295 for (Integer i=iter2; i<=last_; i++) 00296 { 00297 // get v_i 00298 Real v_i = v[i]; 00299 // get q_i and save it in D_i=q_i = p_i + <p,v> * beta * v_i/2 00300 Real q_i = (D_[i] += aux1 * v_i); 00301 // Compute P_ + u q' + q u', 00302 // update the row i, first_ element 00303 M1[i] += q_i /* *1.0 */+ v_i * aux; 00304 // update the row i: all cols under the diagonal 00305 for (Integer j=iter2; j<=i; j++) 00306 P_(i,j) += v[j] * q_i + v_i * D_[j]; 00307 } 00308 } 00309 } 00310 // Last col: F[last_] = 0.0; 00311 D_[last_] = P_(last_,last_); 00312 } 00313 00314 // Compute P_ from the Householder rotations 00315 void EigenvaluesSymmetric::compHouse() 00316 { 00317 // compute P_ 00318 // iter0 is the column of the Householder vector 00319 // iter is the current column to compute 00320 for ( Integer iter0=last_-1, iter=last_, iter1=last_+1 00321 ; iter0>=first_ 00322 ; iter0--, iter--, iter1--) 00323 { 00324 // reference on the Householder vector 00325 Vector v(P_, Range(iter, last_), iter0); 00326 // Get Beta 00327 Real beta = v[iter]; 00328 if (beta) 00329 { 00330 // Compute Col iter -> P_ e_{iter}= e_{iter}+ beta v 00331 P_(iter, iter) = 1.0 + beta /* *1.0 */; 00332 for (Integer i=iter1; i<=last_; i++) 00333 { P_(i, iter) = beta * v[i];} 00334 00335 // Update the other Cols 00336 for (Integer i=iter1; i<=last_; i++) 00337 { 00338 // compute beta*<v, P_^i> 00339 Real aux = 0.0; 00340 for (Integer j=iter1; j<=last_; j++) 00341 { aux += P_(j, i) * v[j]; } 00342 aux *= beta; 00343 // first_ row (iter) 00344 P_(iter, i) = aux; 00345 // other rows 00346 for (Integer j=iter1; j<=last_; j++) 00347 { P_(j, i) += aux * v[j];} 00348 } 00349 } 00350 else // beta = 0, nothing to do 00351 { P_(iter,iter)=1.0; 00352 for (Integer j=iter1; j<=last_; j++) 00353 { P_(iter, j) =0.0; P_(j, iter) = 0.0;} 00354 } 00355 } 00356 // first_ row and first_ col 00357 P_(first_, first_) = 1.0; 00358 for (Integer j=first_+1; j<=last_; j++) 00359 { P_(first_, j) = 0.0; P_(j, first_) = 0.0;} 00360 } 00361 00362 // diagonalize D_ and F_ 00363 void EigenvaluesSymmetric::diagonalize() 00364 { 00365 // Diagonalisation of P_ 00366 for (Integer iend=last_; iend>=first_+1; iend--) 00367 { 00368 Integer iter; 00369 for (iter=0; iter<MAXITER; iter++) // 30 iterations max 00370 { 00371 // check cv for the last element 00372 Real sum = abs(D_[iend]) + abs(D_[iend-1]); 00373 // if the first element of the small subdiagonal 00374 // is 0. we stop the QR iterations and increment iend 00375 if (abs(F_[iend-1])+sum == sum) 00376 { F_[iend-1] = 0.0 ; break;} 00377 // look for a single small subdiagonal element to split the matrix 00378 Integer ibeg = iend-1; 00379 while (ibeg>first_) 00380 { 00381 ibeg--; 00382 // if a subdiagonal is zero, we get a sub matrix unreduced 00383 sum = abs(D_[ibeg])+abs(D_[ibeg+1]); 00384 if (abs(F_[ibeg])+sum == sum) { F_[ibeg] = 0.; ibeg++; break;} 00385 }; 00386 // QR rotations between the rows/cols ibeg et iend 00387 // Computation of the Wilkinson's shift 00388 Real aux = (D_[iend-1] - D_[iend])/(2.0 * F_[iend-1]); 00389 // initialisation of the matrix of rotation 00390 // y is the current F_[k-1], 00391 Real y = D_[ibeg]-D_[iend] + F_[iend-1]/sign(aux, STK::norm(aux,1.0)); 00392 // z is the element to annulate 00393 Real z = F_[ibeg]; 00394 // Fk is the temporary F_[k] 00395 Real Fk = z; 00396 // temporary DeltaD(k) 00397 Real DeltaDk = 0.0; 00398 // Index of the columns 00399 Integer k,k1; 00400 // Givens rotation to restore tridiaonal form 00401 for (k=ibeg, k1=ibeg+1; k<iend; k++, k1++) 00402 { 00403 // underflow ? we have a tridiagonal form exit now 00404 if (z == 0.0) { F_[k]=Fk; break;} 00405 // Rotation columns (k,k+1) 00406 F_[k-1] = (aux = STK::norm(y,z)); // compute final F_[k-1] 00407 // compute cosinus and sinus 00408 Real cosinus = y/aux, sinus = -z/aux; 00409 Real Dk = D_[k] + DeltaDk; // compute current D[k] 00410 Real aux1 = 2.0 * cosinus * Fk + sinus * (Dk - D_[k1]); 00411 // compute DeltaD(k+1) 00412 D_[k] = Dk - (DeltaDk = sinus * aux1); // compute D_[k] 00413 y = cosinus*aux1 - Fk; // temporary F_[k] 00414 Fk = cosinus * F_[k1]; // temporary F_[k+1] 00415 z = -sinus * F_[k1]; // temporary z 00416 // update P_ 00417 rightGivens(P_, k1, k, cosinus, sinus); 00418 } 00419 // k=iend if z!=0 and k<iend if z==0 00420 D_[k] += DeltaDk ; F_[k-1] = y; 00421 // restore F[ibeg-1] 00422 F_[ibeg-1] = 0.; 00423 } // iter 00424 if (iter == MAXITER) { error_ = true;} 00425 // We have to sort the eigenvalues : we use a basic strategy 00426 Real z = D_[iend]; // current value 00427 for (Integer i=iend+1; i<=D_.last(); i++) 00428 { if (D_[i] > z) // if the ith eigenvalue is greater 00429 { D_.swap(i-1, i); // swap the cols 00430 P_.swapCols(i-1, i); 00431 } 00432 else break; 00433 } // end sort 00434 } // iend 00435 // sort first value 00436 Real z = D_[first_]; // current value 00437 for (Integer i=first_+1; i<=D_.last(); i++) 00438 { if (D_[i] > z) // if the ith eigenvalue is greater 00439 { 00440 D_.swap(i-1, i); // swap the cols 00441 P_.swapCols(i-1, i); 00442 } 00443 else break; 00444 } // end sort 00445 } 00446 00447 /* compute rank, norm and determinant. */ 00448 void EigenvaluesSymmetric::compEstimates() 00449 { 00450 // compute 2-norm_ 00451 norm_ = D_[first_]; 00452 // trivial case 00453 if (norm_ < Arithmetic<Real>::epsilon()) 00454 { 00455 rank_ = 0; 00456 det_ = 1; 00457 // compute determinant 00458 for (Integer i = first_; i<= last_; i++) 00459 { det_ *= D_[i];} 00460 return; 00461 } 00462 // compute tolerance 00463 Real tol = Arithmetic<Real>::epsilon() * norm_; 00464 det_ = 1; 00465 // compute rank_and determinant 00466 for (Integer i = first_; i<= last_; i++) 00467 { 00468 det_ *= D_[i]; 00469 if (abs(D_[i])> tol ) rank_++; 00470 } 00471 } 00472 00473 } // Namespace STK