|
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 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