|
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: Implement the Svd Class. 00028 * Author: Serge Iovleff, serge.iovleff@stkpp.org 00029 * 00030 **/ 00031 00035 /* 00036 std:: << "U_ =\n"; 00037 std::cout << (U_) << "\n"; 00038 00039 std::cout << "F_ =\n"; 00040 std::cout << (F_) << "\n"; 00041 00042 std::cout << "D_ =\n"; 00043 std::cout << (D_) << "\n"; 00044 00045 std::cout << "V_ =\n"; 00046 std::cout << (V_) << "\n"; 00047 00048 Matrix D(ncolU(), nrowV(), 0.0); 00049 for (Integer i=1; i<=ncolD_; i++) {D(i,i)=D_[i];} 00050 for (Integer i=1; i< ncolD_; i++) {D(i,i+1)=F_[i];} 00051 Matrix Res1, Res2, Vt; 00052 00053 La2D::mult(Res2,(U_),D); 00054 La2D::transpose(Vt, V_); 00055 La2D::mult(Res1,Res2,Vt); 00056 std::cout << "U_ D=\n"; 00057 std::cout << Res2 << "\n"; 00058 00059 std::cout << "V' =\n"; 00060 std::cout << Vt << "\n"; 00061 00062 std::cout << "U_DV' =\n"; 00063 std::cout << Res1 << "\n"; 00064 */ 00065 00066 00067 #include "../include/STK_Svd.h" 00068 #include "../include/STK_Householder.h" 00069 #include "../include/STK_Givens.h" 00070 00071 /* Templated Expression handling classes. */ 00072 #include "../include/STK_TOperator.h" 00073 #include "../include/STK_TExpAlgebra.h" 00074 00075 namespace STK 00076 { 00077 00078 /* Constructors */ 00079 /* Default constructor */ 00080 Svd::Svd( Matrix const& A 00081 , bool ref 00082 , bool withU 00083 , bool withV 00084 ) 00085 : U_(A, ref) 00086 , withU_(withU) 00087 , withV_(withV) 00088 , ref_(ref) 00089 { 00090 // init containers 00091 init(); 00092 // compute svd 00093 compSvd(); 00094 } 00095 00096 /* Copy constructor */ 00097 Svd::Svd( const Svd &S) 00098 : U_(S.U_, S.ref_) 00099 , V_(S.V_) 00100 , D_(S.D_) 00101 , ncolV_(S.ncolV_) 00102 , ncolD_(S.ncolD_) 00103 , ncolU_(S.ncolU_) 00104 , nrowU_(S.nrowU_) 00105 , withU_(S.withU_) 00106 , withV_(S.withV_) 00107 , ref_(S.ref_) 00108 , norm_(S.norm_) 00109 , rank_(S.rank_) 00110 , error_(S.error_) 00111 { ; } 00112 00113 /* Operator = : overwrite the Svd with S. */ 00114 Svd& Svd::operator=(const Svd &S) 00115 { 00116 U_ = S.U_; 00117 V_ = S.V_; 00118 D_ = S.D_; 00119 ncolV_ = S.ncolV_; 00120 ncolD_ = S.ncolD_; 00121 ncolU_ = S.ncolU_; 00122 nrowU_ = S.nrowU_; 00123 withU_ = S.withU_; 00124 withV_ = S.withV_; 00125 ref_ = false; // no reference possible with operator = 00126 norm_ = S.norm_; 00127 rank_ = S.rank_; 00128 error_ = S.error_; 00129 00130 return *this; 00131 } 00132 00133 /* virtual destructor. */ 00134 Svd::~Svd() 00135 { ;} 00136 00137 00138 /* Private functions. */ 00139 /* initialization of the Svd. */ 00140 void Svd::init() 00141 { 00142 // U_ is just a copy of A, translate begin to 1 00143 U_.shift(1,1); // if U_ is a ref on A, this can generate an error 00144 00145 // If the container is empty, set default 00146 if (U_.empty()) 00147 { ncolV_ = 0; 00148 ncolD_ = 0; 00149 ncolU_ = U_.sizeHo(); 00150 nrowU_ = 0; 00151 return; 00152 } 00153 00154 // The container is not empty, thus we have to compute the dimension 00155 // and eventually to adjust the container (U_) 00156 ncolU_ = U_.sizeHo(); // Number of cols of (U_) 00157 nrowU_ = U_.sizeVe(); // Number of rows of (U_) 00158 ncolV_ = U_.sizeHo(); // Number of cols of V_ 00159 ncolD_ = min(nrowU_, ncolV_); // Maximal number of singular value 00160 error_ = false; // no problems... 00161 } 00162 00163 00164 /* Main method for the svd computation. */ 00165 void Svd::compSvd() 00166 { 00167 // if the container is empty, there is nothing to do 00168 if (U_.empty()) 00169 { rank_ = 0; 00170 norm_ = 0.0; 00171 return; 00172 } 00173 // Bidiagonalize (U_) 00174 norm_ = bidiag(U_, D_, F_); 00175 // We need to create V_ before rightEliminate 00176 if (withV_) { 00177 V_.resize(ncolV_); 00178 compV(); 00179 } 00180 // rightEliminate last element of F_ if any 00181 if (nrowU_ < ncolV_) 00182 { rightEliminate(D_, F_, nrowU_, V_, withV_, norm_);} 00183 // If (U_) is not needed, we can destroy the storage 00184 if (withU_) { compU();} 00185 // Diagonalize 00186 error_ = diag(D_, F_, U_, V_, withU_, withV_, norm_); 00187 // Compute the true inf norm 00188 norm_ = D_[1]; 00189 // Compute the rank 00190 rank_ = 0; 00191 for (Integer iter=1; iter<=ncolD_; iter++) 00192 if (norm_+D_[iter]!=norm_) { rank_++;} 00193 else break; 00194 // The sub diagonal is now zero 00195 F_.clear(); 00196 } 00197 00198 00199 /* New computation of the Svd. */ 00200 void Svd::newSvd( Matrix const& A 00201 , bool withU 00202 , bool withV 00203 ) 00204 { // clear old results 00205 clear(); 00206 00207 // create U_ 00208 U_ = A; // Copy A in U_ 00209 ref_ = false; // this is not a ref_ 00210 00211 withU_ = withU; // copy withU_ value 00212 withV_ = withV; // copy withV_ value 00213 00214 init(); // initialize (U_) and dimensions 00215 compSvd(); // compute the svd 00216 } 00217 00218 00219 /* clear (U_) */ 00220 void Svd::clearU() 00221 { U_.clear(); 00222 nrowU_ = 0; 00223 ncolU_ = 0; 00224 withU_ = false; 00225 } 00226 00227 00228 /* clear (U_) */ 00229 void Svd::clearV() 00230 { V_.clear(); withV_ = false; 00231 ncolV_ = 0; 00232 } 00233 00234 00235 /* clear Svd */ 00236 void Svd::clear() 00237 { clearU(); 00238 clearV(); 00239 D_.clear(); 00240 } 00241 00242 00243 /* Bidiagonalization of the matrix M. */ 00244 Real Svd::bidiag(const Matrix& M, Point& D, Vector& F) 00245 { 00246 // norm of the matrix M 00247 Real norm = 0.0; 00248 // compute the number of iteration 00249 Integer first_iter = M.firstCol(); 00250 Integer last_iter = M.firstCol() + min(M.sizeHo(), M.sizeVe()) -1; 00251 // Diagonal values 00252 D.resize(Range(first_iter, last_iter)); 00253 // Upper diagonal values 00254 F.resize(Range(first_iter-1, last_iter)); 00255 F.front() = 0.0; 00256 // Bidiagonalisation of M 00257 // loop on the cols and rows 00258 Range rowRange0(M.rangeVe()) 00259 , rowRange1(Range(M.firstRow()+1, M.lastRow())) 00260 , colRange1(Range(M.firstCol()+1, M.lastCol())); 00261 for ( Integer iter=first_iter ; iter<=last_iter 00262 ; iter++ 00263 , rowRange0.incFirst() 00264 , rowRange1.incFirst() 00265 , colRange1.incFirst() 00266 ) 00267 { 00268 // reference on the current column iter 00269 Vector X( M, rowRange0, iter); 00270 // Left Householder 00271 D[iter] = house(X); 00272 // apply Householder to next cols 00273 leftHouseholder(M[colRange1], X); 00274 // check if there is a row 00275 if ((iter < last_iter)||(M.sizeHo()>M.sizeVe())) 00276 { 00277 // ref on the current row iter 00278 Point P(M, colRange1, iter); 00279 // Right Householder 00280 F[iter] = house(P); 00281 // apply Householder to next rows 00282 rightHouseholder(M(rowRange1), P); 00283 } 00284 else 00285 F[iter] = 0.0; 00286 // Estimation of the norm of M 00287 norm = max(abs(D[iter])+abs(F[iter]), norm); 00288 } 00289 // return estimated norm 00290 return norm; 00291 } 00292 00293 00294 /* computation of V_ */ 00295 void Svd::compV() 00296 { // Construction of V_ 00297 // Number of right Householder rotations 00298 Integer niter = (ncolV_>nrowU_) ? (nrowU_) : (ncolV_-1); 00299 00300 // initialization of the remaining rows and cols of V_ to Identity 00301 for (Integer iter=niter+2; iter<=ncolV_; iter++) 00302 { 00303 Vector W(V_, V_.rangeHo(), iter); 00304 W = 0.0; 00305 W[iter] = 1.0; 00306 } 00307 00308 Range range1(niter+1, ncolV_), range2(niter+2, ncolV_); 00309 for ( Integer iter0=niter, iter1=niter+1, iter2=niter+2; iter0>=1 00310 ; iter0--, iter1--, iter2-- 00311 , range1.decFirst(), range2.decFirst() 00312 ) 00313 { 00314 // get beta and test 00315 Real beta = U_(iter0, iter1); 00316 if (beta) 00317 { 00318 // ref on the row iter1 of V_ 00319 Point Vrow1(V_, range1, iter1); 00320 // diagonal element 00321 Vrow1[iter1] = 1.0+beta; 00322 // ref on the column iter1 00323 Vector Vcol1(V_, range2, iter1); 00324 // get the Householder vector 00325 Vcol1 = Point(U_, range2, iter0); 00326 // Apply housholder to next cols 00327 for (Integer j=iter2; j<=ncolV_; j++) 00328 { 00329 Real aux; 00330 // ref on the column j 00331 Vector Vcolj( V_, range2, j); 00332 // update column j 00333 Vrow1[j] = (aux = dot(Vcol1, Vcolj) * beta); 00334 Vcolj += Vcol1 * aux; 00335 } 00336 // compute the Householder vector 00337 Vcol1 *= beta; 00338 } 00339 else // nothing to do 00340 { 00341 V_(range2, iter1) = 0.0; 00342 V_(iter1, iter1) = 1.0; 00343 V_(iter1, range2) = 0.0; 00344 } 00345 } 00346 // First column and rows 00347 V_(1,1) =1.0; 00348 V_(Range(2,ncolV_),1) =0.0; 00349 V_(1,Range(2,ncolV_)) =0.0; 00350 } 00351 00352 00353 /* computation of U_ */ 00354 void Svd::compU() 00355 { 00356 Integer niter = D_.size(); // Number of iterations 00357 Integer ncol = min(nrowU_, ncolU_); // number of non zero cols of U_ 00358 00359 // initialization of the remaining cols of U_ to 0.0 00360 // put 0 to unused cols 00361 U_[Range(ncol+1, ncolU_)] = 0.0; 00362 // Computation of U_ 00363 for (Integer iter=niter, iter1=niter+1; iter>=1; iter--, iter1--) 00364 { 00365 // ref of the column iter 00366 Vector X(U_, Range(iter1,nrowU_), iter); 00367 // ref of the row iter 00368 Point P(U_, Range(iter,ncolU_), iter); 00369 // Get beta and test 00370 Real beta = P[iter]; 00371 if (beta) 00372 { 00373 // update the column iter 00374 P[iter] = 1.0 + beta; 00375 // Updating the cols iter+1 to ncolU_ 00376 for (Integer j=iter1; j<=niter; j++) 00377 { // product of U_iter by U_j 00378 Real aux; 00379 Vector Y(U_, Range(iter1, nrowU_), j); // ref on the column j 00380 // U_j = aux = beta * X'Y 00381 P[j] = (aux = dot( X, Y) *beta); 00382 // U^j += aux * U^iter 00383 Y += X * aux; 00384 } 00385 // compute the vector v 00386 X *= beta; 00387 } 00388 else // U^iter = identity 00389 { 00390 P[iter] = 1.0; 00391 X = 0.0; 00392 } 00393 // update the column iter 00394 U_(Range(1,iter-1), iter) = 0.0; 00395 } 00396 } 00397 00398 00399 /* eliminate the element of the surdiagonal with right rotations */ 00400 void Svd::rightEliminate( Point& D 00401 , Vector& F 00402 , Integer const& nrow 00403 , MatrixSquare& V 00404 , bool withV 00405 , Real const& tol 00406 ) 00407 { 00408 // the element to eliminate 00409 Real z = F[nrow]; 00410 // if the element is not 0.0 00411 if (abs(z)+tol != tol) 00412 { 00413 // column of the element to eliminate 00414 Integer ncol1 = nrow+1; 00415 // begin the Givens rotations 00416 for (Integer k=nrow, k1=nrow-1; k>=1 ; k--, k1--) 00417 { 00418 // compute and apply Givens rotation to the rows (k, k+1) 00419 Real aux, sinus, cosinus; 00420 Real y = D[k]; 00421 D[k] = (aux = norm(y,z)); 00422 z = (sinus = -z/aux) * F[k1]; 00423 F[k1] *= (cosinus = y/aux); 00424 // Update V_ 00425 if (withV) 00426 rightGivens(V, ncol1, k, cosinus, sinus); 00427 // if 0.0 we can break now 00428 if (abs(z)+tol == tol) break; 00429 } 00430 } 00431 // the element is now 0 00432 F[nrow] = 0.0; // is 0.0 00433 } 00434 00435 00436 /* eliminate the element of the surdiagonal with left rotations */ 00437 void Svd::leftEliminate( Point& D 00438 , Vector& F 00439 , Integer const& nrow 00440 , Matrix& U 00441 , bool withU 00442 , Real const& tol 00443 ) 00444 { 00445 //the element to eliminate 00446 Real z = F[nrow]; 00447 // if the element is not 0.0 00448 if (abs(z)+tol != tol) 00449 { 00450 // begin the Givens rotations 00451 for (Integer k=nrow+1; k <=D.last(); k++) 00452 { 00453 // compute and apply Givens rotation to the rows (nrow, k) 00454 Real y = D[k]; 00455 Real aux, cosinus, sinus; 00456 D[k] = (aux = norm(y,z)); 00457 z = (sinus = -z/aux) * F[k]; 00458 F[k] *= (cosinus = y/aux); 00459 // Update U_ 00460 if (withU) 00461 rightGivens(U, nrow, k, cosinus, sinus); 00462 if (abs(z)+tol == tol) break; 00463 } 00464 } 00465 F[nrow] = 0.0; 00466 } 00467 00468 00469 /* diagonalization of the bidiag matrix */ 00470 bool Svd::diag( Point& D 00471 , Vector& F 00472 , Matrix& U 00473 , MatrixSquare& V 00474 , bool withU 00475 , bool withV 00476 , Real const& tol 00477 ) 00478 { 00479 // result of the diag process 00480 bool error = false; 00481 // Diagonalization of A : Reduction of la matrice bidiagonale 00482 for (Integer end=D.last(); end>=D.first(); --end) 00483 { // 30 iter max 00484 Integer iter; 00485 for (iter=1; iter<=30; iter++) 00486 { // if the last element of the surdiagonale is 0.0 00487 // stop the iterations 00488 Integer beg; 00489 if (abs(F[end-1])+tol == tol) { F[end-1] = 0.0; break;} 00490 // now F[end-1] !=0 00491 // if D[end] == 0, we can annulate F[end-1] 00492 // with rotations of the columns. 00493 if (abs(D[end])+tol == tol) 00494 { 00495 D[end] = 0.0; 00496 rightEliminate(D, F, end-1, V, withV, tol); 00497 break; // Last element of the surdiagonal is 0 00498 } 00499 // now D[end] != 0 and F[end-1] != 0 00500 // look for the greatest matrix such that all the elements 00501 // of the diagonal and surdiagonal are not zeros 00502 for (beg = end-1; beg>D.first(); --beg) 00503 { 00504 if ((abs(D[beg])+tol == tol)||(abs(F[beg])+tol == tol)) 00505 break; 00506 } 00507 // now F[beg-1]!=0 00508 // if D[beg] == 0 and F[beg] != 0, 00509 // we can eliminate the element F[beg] 00510 // with rotations of the rows 00511 if ((abs(D[beg])+tol == tol) && (abs(F[beg])+tol != tol)) 00512 { 00513 D[beg] = 0.0; 00514 leftEliminate(D, F, beg, U, withU, tol); 00515 } 00516 00517 // Si F[beg]==0, on augmente beg 00518 if (abs(F[beg])+tol == tol) { F[beg] = 0.0; beg++;} 00519 00520 // On peut commencer les rotations QR entre les lignes beg et end 00521 // Shift computation 00522 // easy shift : commented 00523 // Real aux = norm(D[end],F[end-1]); 00524 // Real y = (D[beg]+aux)*(D[beg]-aux); 00525 // Real z = D[beg]*F[beg]; 00526 // Wilkinson shift : look at the doc 00527 Real dd1 = D[end-1]; // d_1 00528 Real dd2 = D[end]; // d_2 00529 Real ff1 = F[end-2]; // f_1 00530 Real ff2 = F[end-1]; // f_2 00531 // g 00532 Real aux = ( (dd1+dd2)*(dd1-dd2) 00533 + (ff1-ff2)*(ff1+ff2))/(2*dd1*ff2); 00534 // A - mu 00535 Real d1 = D[beg]; 00536 Real f1 = F[beg]; 00537 Real y = (d1+dd2)*(d1-dd2) 00538 + ff2*(dd1/(aux+sign(aux,norm(1.0,aux)))- ff2); 00539 Real z = d1*f1; 00540 // chase no null element 00541 Integer k, k1; 00542 for (k=beg, k1 = beg+1; k<end; ++k, ++k1) 00543 { // Rotation colonnes (k,k+1) 00544 // Input : d1 contient D[k], 00545 // f1 contient F[k], 00546 // y contient F[k-1] 00547 // Output : d1 contient F[k], 00548 // d2 contient D[k+1], 00549 // y contient D[k] 00550 Real cosinus, sinus; 00551 Real d2 = D[k1]; 00552 F[k-1] = (aux = norm(y,z)); // F[k-1] 00553 // arbitrary rotation if y = z = 0.0 00554 if (aux) 00555 y = (cosinus = y/aux) * d1 - (sinus = -z/aux) * f1; // D[k] 00556 else 00557 y = cosinus * d1 - sinus * f1; // D[k] 00558 00559 z = -sinus * d2; // z 00560 d1 = sinus * d1 + cosinus * f1; // F[k] 00561 d2 *= cosinus; // D[k+1] 00562 // Update V 00563 if (withV) 00564 rightGivens(V, k1, k, cosinus, sinus); 00565 // avoid underflow 00566 // Rotation lignes (k,k+1) 00567 // Input : d1 contient F[k], 00568 // d2 contient D[k+1], 00569 // y contient D[k] 00570 // Output : d1 contient D[k+1], 00571 // f1 contient F[k+1], 00572 // y contient F[k] 00573 f1 = F[k1]; 00574 D[k] = (aux = norm(y,z)); // D[k] 00575 // arbitrary rotation if y = z = 0.0 00576 if (aux) 00577 y = (cosinus = y/aux) * d1 - (sinus = -z/aux) * d2; // F[k] 00578 else 00579 y = cosinus * d1 - sinus * d2; // F[k] 00580 00581 z = -sinus * f1; // z 00582 d1 = sinus *d1 + cosinus * d2; // D[k+1] 00583 f1 *= cosinus; // F[k+1] 00584 // Update U 00585 if (withU) 00586 rightGivens(U, k1, k, cosinus, sinus); 00587 } // end of the QR updating iteration 00588 D[end] = d1; 00589 F[end-1] = y; 00590 F[beg-1] = 0.0; // F[beg-1] is overwritten, we have to set 0.0 00591 } // iter 00592 // too many iterations 00593 if (iter >= 30) { error = true;} 00594 // positive singular value only 00595 if (D[end]< 0.0) 00596 { 00597 // change sign of the singular value 00598 D[end]= -D[end]; 00599 // change sign of the column end of V 00600 if (withV) V[end] = -V[end]; 00601 } 00602 00603 // We have to sort the singular value : we use a basic strategy 00604 Real z = D[end]; // current value 00605 for (Integer i=end+1; i<=D.last(); i++) 00606 { if (D[i]> z) // if the ith singular value is greater 00607 { D.swap(i-1, i); // swap the cols 00608 if (withU) U.swapCols(i-1, i); 00609 if (withV) V.swapCols(i-1, i); 00610 } 00611 else break; 00612 } // end sort 00613 } // boucle end 00614 return error; 00615 } 00616 00617 } // Namespace STK