STK++ 1.0
STK_Svd.cpp
Go to the documentation of this file.
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