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