STK++ 1.0

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