STK++ 1.0
STK_Qr.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 Qr Class.
00028  * Author:   Serge Iovleff, serge.iovleff@stkpp.org
00029  *
00030  **/
00031 
00036 #include "../include/STK_Qr.h"
00037 
00038 #include "../include/STK_Householder.h"
00039 #include "../include/STK_Givens.h"
00040 
00041 /* Templated Expression handling classes.                             */
00042 #include "../include/STK_TOperator.h"
00043 #include "../include/STK_TExpAlgebra.h"
00044 
00045 namespace STK
00046 {
00047 
00048 /* Constructor */
00049 Qr::Qr( const Matrix &A, bool ref)
00050       : Q_(A, ref)      // Creating Q
00051       , R_()            // Creating R
00052 { run();}
00053 
00054 /* Computing the QR decomposition of the matrix Q_.                   */
00055 void Qr::run()
00056 {
00057   if (Q_.empty())     // if the container is empty
00058   {
00059     ncolr_ =0;
00060     ncolq_ =0;
00061     nrowq_ =0;
00062     compq_  = true;  // Q_ is computed
00063 
00064     return;
00065   }
00066 
00067   Q_.shift(1,1);      // translate the beg to 1
00068   ncolr_ = Q_.sizeHo(); // Number of cols of R
00069   ncolq_ = Q_.sizeHo(); // Number of column of Q
00070   nrowq_ = Q_.sizeVe(); // Number of rows of Q
00071 
00072   compq_  = false;    // Q_ is not computed
00073 
00074   // compute QR decomposition
00075   qr();
00076 }
00077 
00078 
00079 /* Computation of the QR decomposition                                */
00080 void Qr::qr()
00081 {
00082   Integer niter = min(nrowq_,ncolr_);   // number of iterations
00083   R_.resize(nrowq_, ncolr_);
00084   R_ = 0.0;                   // initialize to 0.0
00085   
00086 
00087   /* Main loop.                                                       */
00088   for (Integer iter=1, iter1=2; iter<=niter; iter++, iter1++)
00089   { 
00090     // A ref on the row of the matrix R_
00091     Point  Rrow0(R_, Range(iter, ncolr_), iter);
00092     // A ref on the row of the matrix R_
00093     Point  Qrow0(Q_, Range(iter, ncolq_), iter);
00094     // A ref on the column iter of the matrix Q_ : will contain the
00095     // Householder vector
00096     Vector u(Q_, Range(iter, nrowq_), iter);
00097     // compute the Householder vector of the current col
00098     Rrow0[iter] = house(u);
00099     // get beta
00100     Real beta = u.front();
00101     if (beta)
00102     {
00103       // ref on the essential part of the Householder vector
00104       Vector v(u, Range(iter1, nrowq_));
00105       // Apply Householder to next cols
00106       for (Integer j=iter1; j<=ncolr_; j++)
00107       {
00108         // Auxiliary data
00109         Real aux;
00110         // a ref on the jth column of Q_
00111         Vector Z(Q_, Range(iter1, nrowq_), j);
00112         // save the  current row of R_
00113         Rrow0[j] = Qrow0[j] + (aux = ( Qrow0[j] + dot(v, Z)) * beta);
00114         // update the next cols of Q_ 
00115         Z += v * aux;
00116       }
00117     }
00118     else
00119     {
00120       // just copy the row iter in R_
00121       for (Integer j=iter1; j<=ncolr_; j++)
00122       {
00123         Rrow0[j] = Qrow0[j];
00124       }
00125     }
00126   }
00127 }
00128 
00129 
00130 /* Computation of Q. */
00131 void Qr::compQ()
00132 {
00133   // if Q_ is computed yet
00134   if (compq_) return;
00135   // number of non zero cols of Q_  
00136   Integer ncol  = min(nrowq_, ncolq_);
00137   // Q_ is square
00138   if (nrowq_ < ncolq_)
00139   {
00140      Q_.popBackCols(ncolq_-nrowq_);
00141   }
00142   else
00143   { 
00144     Q_.pushBackCols(nrowq_-ncolq_);
00145     // initialization of the remaining cols of Q_ to 0.0
00146     Q_[Range(ncol+1,nrowq_)] = 0.0;
00147   }
00148   // the number of col_ is equal to the number of row
00149   ncolq_ = nrowq_;
00150   // Computation of Q_.
00151   // compute added col
00152   for (Integer iter=ncolq_; iter> ncol; --iter)
00153   { Q_(iter, iter) = 1.0;}
00154   // compute other cols
00155   for (Integer iter=ncol, iter1=ncol+1; iter>=1; iter--, iter1--)
00156   {
00157     // Get beta and test
00158     Real beta = Q_(iter,iter);
00159     if (beta)
00160     {
00161       // ref of the row iter
00162       Point P(Q_, Range(iter,ncolq_), iter);
00163       // ref of the column iter
00164       Vector X(Q_, Range(iter1,nrowq_), iter);
00165       // Update the cols from iter+1 to ncol
00166       for (Integer j=iter1; j<=ncolq_; j++)
00167       { Real aux;
00168         Vector Y(Q_, Range(iter1,nrowq_), j); // ref on the column j
00169         // Q_(iter, j) = beta * X'Y
00170         P[j] = (aux = dot( X, Y) * beta);
00171         // Q^j += aux * Q^iter
00172         Y += X * aux;
00173       }
00174       P[iter] = 1.0 + beta;
00175       // Q^iter *= beta
00176       X *= beta;
00177     }
00178     else // Q^iter = identity
00179     {
00180       Q_(iter, iter) = 1.0;
00181       Q_(Range(iter1,nrowq_), iter) = 0.0;
00182     }
00183     // update the column iter
00184     Q_(Range(1,iter-1), iter) = 0.0;
00185   }
00186   // Q_ is now computed
00187   compq_ = true;
00188 }
00189 
00190 
00191 /* Destructeur de la classe Qr                                        */
00192 Qr::~Qr() { ;}
00193 
00194 /* clear Q_ and R_.                                                   */    
00195 void Qr::clear()
00196 {
00197   Q_.clear();
00198   R_.clear();
00199 }
00200 
00201 
00202 /* Operator = : overwrite the Qr with S.                              */
00203 Qr& Qr::operator=(const Qr& S)
00204 {
00205   ncolr_ = S.ncolr_;       //< Number of cols of R actually computed
00206   ncolq_ = S.ncolq_;       //< Number of cols used by Q
00207   nrowq_ = S.nrowq_;       //< Number of rows used by Q
00208     
00209   compq_ = S.compq_;       //< Is Q computed ?
00210 
00211   Q_ = S.Q_;               //< Matrix V
00212   R_ = S.R_;               //< Singular values
00213 
00214   return *this;
00215 }
00216 
00217 /* Delete the jth column and update the QR decomposition : default
00218  * is the last col
00219  **/    
00220 void Qr::popBackCols(Integer const& n)
00221 {
00222   // delete n cols
00223   R_.popBackCols(n);
00224   ncolr_ -= n;
00225 }
00226 
00227 void Qr::eraseCol(Integer const& pos)
00228 {
00229 #ifdef STK_BOUNDS_CHECK
00230   if (pos < R_.firstCol())
00231   { throw out_of_range("Qr::eraseCol(pos) "
00232                        "pos < R_.firstCol()");
00233   }
00234   if (R_.lastCol() < pos)
00235   { throw out_of_range("Qr::eraseCol(pos) "
00236                        "R_.lastCol() < pos");
00237   }
00238 #endif
00239   // if Q_ is not computed yet
00240   if (!compq_) compQ();
00241   // compute the number of iteration for updating to zeroed
00242   Integer niter = R_.firstCol()-1+min(R_.sizeVe(), R_.sizeHo());
00243   // Zeroed the unwanted elements (z)
00244   for (Integer iter = pos+1; iter<=niter; iter++)
00245   {
00246     Real sinus, cosinus;
00247     // compute the Givens rotation
00248     R_(iter-1, iter) = compGivens( R_(iter-1, iter)
00249                                  , R_(iter, iter)
00250                                  , cosinus
00251                                  , sinus
00252                                  );
00253     R_(iter, iter)   = 0.0;
00254     // if necessary update R_ and Q_
00255     if (sinus)
00256     {
00257       // create a reference on the sub-Matrix
00258       MatrixUpperTriangular Rsub(R_[Range(iter+1, R_.lastCol())], true);
00259       // Update the next rows (iter1:ncolr_) of R_
00260       leftGivens (Rsub, iter-1, iter, cosinus, sinus);
00261       // Update the cols of Q_
00262       rightGivens(Q_, iter-1, iter, cosinus, sinus);
00263     }
00264   }
00265   // erase the column pos
00266   R_.eraseCols(pos);
00267   ncolr_--;
00268   
00269   // update the range of the remaining cols of the container
00270   R_.update(Range(pos, min(R_.lastRow(), R_.lastCol())));
00271 }
00272 
00273 
00274 /* Adding the last column and update the QR decomposition.               */    
00275 void Qr::pushBackCol(Vector const& T)
00276 {
00277   // check conditions
00278   if (T.range() != Q_.rangeVe())
00279   { throw runtime_error("Qr::pushBackCol(T) "
00280                         "T.range() != Q_.rangeVe()");
00281   }
00282   // if Q_ is not computed yet
00283   if (!compq_) compQ();
00284   // Adding a column to R
00285   R_.pushBackCols();
00286   ncolr_++;
00287   // Create an auxiliary container
00288   Vector Rncolr(Q_.rangeVe());
00289   // Multipliate T by Q'and put the result in Rncolr
00290   for (Integer j=Q_.firstCol(); j<=Q_.lastCol(); j++)
00291     Rncolr[j] = dot(Q_[j], T);
00292   // update Q_
00293   for (Integer iter = ncolq_-1, iter1 = ncolq_; iter>=ncolr_; iter--, iter1--)
00294   { 
00295     Real sinus, cosinus, y = Rncolr[iter], z = Rncolr[iter1] ;
00296     // compute the Givens rotition
00297     Rncolr[iter]  = compGivens(y, z, cosinus, sinus);
00298     // apply Givens rotation if necessary
00299     if (sinus)
00300     {
00301       // Update the cols of Q_
00302       rightGivens(Q_, iter, iter1, cosinus, sinus);
00303     }
00304   }
00305   // update R_
00306   R_[ncolr_] = Rncolr[R_.compRangeVe(ncolr_)];
00307 }
00308 
00309 
00310 /* Adding the jth column and update the QR decomposition.               */
00311 void Qr::insertCol(Vector const& T, Integer const& pos)
00312 {
00313 #ifdef STK_BOUNDS_CHECK
00314   if (pos < 1)
00315   { throw out_of_range("Qr::insertCol(T, pos) "
00316                             "j < 1");
00317   }
00318   if (ncolr_ < pos)
00319   { throw out_of_range("Qr::insertCol(T, pos) "
00320                             "ncolr_ < pos");
00321   }
00322 #endif
00323 #ifdef STK_DEBUG
00324   if (T.range() != Q_.rangeVe())
00325   { throw runtime_error("Qr::insertCol(T, pos) "
00326                              "T.range() != Q_.rangeVe()");
00327   }
00328 #endif
00329   // if Q_ is not computed yet
00330   if (!compq_) compQ();
00331   // Adding a column to R
00332   R_.insertCols(pos);
00333   ncolr_++;
00334   // update the range of the remaining cols of R_
00335   R_.update(Range(pos+1, min(R_.lastRow(), R_.lastCol())));
00336   for (Integer i=pos+1; i<= min(R_.lastRow(), R_.lastCol()); ++i)
00337     R_(i,i) = 0.0;
00338   // A ref on the last column of R_
00339   Vector Rpos(Q_.rangeVe());
00340   // Multipliate T by Q'
00341   // we cannot use mult as we are using ncolq_ columns.
00342   for (Integer j=Q_.firstCol(); j<=Q_.lastCol(); j++)
00343     Rpos[j] = dot(Q_[j],T);
00344   // Zeroed the unwanted elements
00345   for (Integer iter= ncolq_-1, iter1= ncolq_; iter>=pos; iter--, iter1--)
00346   { 
00347     Real sinus, cosinus, y = Rpos[iter], z = Rpos[iter1] ;
00348     // compute the Givens rotation
00349     Rpos[iter]  = compGivens(y, z, cosinus, sinus);
00350     // apply Givens rotation if necessary
00351     if (sinus)
00352     {
00353       // create a reference on the sub-Matrix
00354       MatrixUpperTriangular Rsub(R_[Range(iter1, R_.lastCol())], true);
00355       // Update the next rows (iter1:ncolr_) of R_
00356       leftGivens( Rsub, iter, iter1, cosinus, sinus);
00357       // Update the cols of Q_
00358       rightGivens(Q_, iter, iter1, cosinus, sinus);
00359     }
00360   }
00361   // update R_
00362   R_[pos] = Rpos[R_.compRangeVe(pos)];
00363   R_.update(pos);
00364 }
00365 
00366 } // Namespace STK
00367