Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

eigenvalues.h

00001 /***************************************************************************
00002                           eigenvalues.h  -  description
00003                              -------------------
00004     begin                : Tue May 16 2000
00005     copyright            : (C) 2000 by Michael Peeters
00006     email                : Michael.Peeters@vub.ac.be
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 #ifndef EIGENVALUES_H
00019 #define EIGENVALUES_H
00020 
00021 #include "numerictypes.h"
00022 #include "numerictraits.h"
00023 #include "invariant.h"
00024 #include "utility.h"
00025 #include <stdexcept>
00026 
00027 namespace MODEL
00028 {
00029 
00033   template<integer dims, typename nelem=number, class NT = NumericTraits<nelem,dims> >
00034   class Eigenvalues
00035   {
00036   public:
00037         
00038         typedef typename NT::number     numT;
00039         typedef typename NT::vect       vect;
00040         typedef typename NT::matrix matrix;
00041         typedef typename NT::vf         vf;
00042 
00043         typedef typename NumericTraits<complex,dims>::vect      EVect;
00044         
00046         Eigenvalues(const matrix& ma) : m(ma)
00047         {calculate();}
00048         
00050         const   vect&   real(void) {return wr;} 
00051 
00053         const   vect&   imag(void) {return wi;} 
00054                         
00055   private:
00059         const matrix&   balance(void);
00060 
00063         const matrix&   hessenberg(void);
00064                 
00066         void    calculate(void);
00067 
00069         NO_COPY(Eigenvalues);
00070         
00071   private:
00072         matrix  m;
00073         
00074         vect    wr;                     // real part
00075         vect    wi;                     // imaginary part
00076         
00077         static const number radix=2.;
00078   };
00079 
00082   template <integer dims, typename nelem, class NT >
00083   void
00084   Eigenvalues<dims,nelem,NT>::calculate(void)
00085   {
00086         // Make sure we have balanced it and reduced to upper hessenberg
00087         balance();
00088         hessenberg();
00089         
00090         numT    anorm(abs(m[0][0]));
00091         
00092         for (integer i=0;i<dims;i++)            // compute matrix norm
00093           for (integer j=max(i-1,0);j<dims;j++)   // Because we start from upper Hessenberg !
00094                 anorm += abs(m[i][j]);
00095         
00096         integer nn(dims-1);
00097         numT    t(0.);                  // Gets changed only by an exceptional shift.
00098         
00099         while (nn >= 0)         // Search next eigenvalue
00100           {
00101                 integer its(0);         // iterations
00102                 integer l(0);
00103                 do
00104                   {
00105                         for (l=nn;l>=1;l--)     // look for single small subdiagonal
00106                           {
00107                                 numT s=abs(m[l-1][l-1])+abs(m[l][l]);
00108                                 if (s == 0.0) s=anorm;
00109                                 if ( ( abs(m[l][l-1]) + s ) == s) break;
00110                           }
00111                         numT x(m[nn][nn]),y(0.),z(0.);
00112                         if (l == nn)                    // One root found
00113                           {
00114                                 wr[nn]=x+t;             // Compensate for shift
00115                                 wi[nn]=0.0;
00116                                 nn--;
00117                           }
00118                         else
00119                           {
00120                                 y = m[nn-1][nn-1];
00121                                 numT w(m[nn][nn-1]*m[nn-1][nn]);
00122                                 if (l == (nn-1))
00123                                   {  // Two roots found
00124                                         numT    p = 0.5*(y-x);
00125                                         numT    q=p*p+w;
00126                                         z=sqrt(abs(q));
00127                                         x += t;                 // Compensate for shift
00128                                         if (q >= 0.0)
00129                                           {     // a real pair
00130                                                 z= p + ((p>=0.)?abs(z):-abs(z));
00131                                                 wr[nn-1]=wr[nn]=x+z;
00132                                                 if (z) wr[nn]=x-w/z;
00133                                                 wi[nn-1]=wi[nn]=0.0;
00134                                           }
00135                                         else
00136                                           {     // a complex pair
00137                                                 wr[nn-1]=wr[nn]=x+p;
00138                                                 wi[nn-1]= -(wi[nn]=z);
00139                                           }
00140                                         nn -= 2;
00141                                   }
00142                                 else
00143                                   {     // no roots found, continue
00144                                         numT p(0.),q(0.),r(0.),s(0.);
00145                         
00146                                         if (its == 30) throw std::logic_error("Too many iterations in eigenvalue loop");
00147                                         if (its == 10 || its == 20)
00148                                           {   // do a exceptional shift
00149                                                 t += x;
00150                                                 for (integer i=0;i<=nn;i++) m[i][i] -= x;
00151                                                 s = abs(m[nn][nn-1]) + abs(m[nn-1][nn-2]);
00152                                                 y=x=0.75*s;
00153                                                 w = -0.4375*s*s;
00154                                           }
00155                                         
00156                                         ++its;
00157                                         
00158                                         integer mm(0);
00159                                         
00160                                         for (mm=(nn-2);mm>=l;mm--)
00161                                           {     // do shift and look for 2 consequtive small elements
00162                                                 z=m[mm][mm];
00163                                                 r=x-z;
00164                                                 s=y-z;
00165                                                 
00166                                                 p=(r*s-w)/m[mm+1][mm]+m[mm][mm+1];
00167                                                 q=m[mm+1][mm+1]-z-r-s;
00168                                                 
00169                                                 r=m[mm+2][mm+1];
00170                                                 s=abs(p)+abs(q)+abs(r);
00171                                                 
00172                                                 p /= s;
00173                                                 q /= s;
00174                                                 r /= s;
00175                                                 
00176                                                 if (mm == l) break;
00177                                                 numT u= abs(m[mm][mm-1])*(abs(q)+abs(r));
00178                                                 numT v= abs(p)*(abs(m[mm-1][mm-1])+abs(z)+abs(m[mm+1][mm+1]));
00179                                                 if ((u+v) == v) break;
00180                                           }
00181                                         
00182                                         for (integer i=mm+2;i<=nn;i++)
00183                                           {
00184                                                 m[i][i-2]=0.0;
00185                                                 if (i != (mm+2)) m[i][i-3]=0.0;
00186                                           }
00187                                         
00188                                         for (integer k=mm;k<=nn-1;k++)
00189                                           {     // Double QR step
00190                                                 if (k != mm)
00191                                                   {
00192                                                         p=m[k][k-1];            // Householder vector setup
00193                                                         q=m[k+1][k-1];
00194                                                         r=0.;
00195                                                         if (k != (nn-1)) r=m[k+2][k-1];
00196                                                         x=abs(p)+abs(q)+abs(r);
00197                                                         if (x != 0.0)
00198                                                           {
00199                                                                 p /= x;                         // Scale to prevent overflow
00200                                                                 q /= x;
00201                                                                 r /= x;
00202                                                           }
00203                                                   }
00204                                                 s=sqrt(p*p+q*q+r*r);
00205                                                 s=((p>=0.)?s:-s);
00206                                                 if (s != 0.0)
00207                                                   {
00208                                                         if (k == mm)
00209                                                           {
00210                                                                 if (l != mm) m[k][k-1] = -m[k][k-1];
00211                                                           }
00212                                                         else m[k][k-1] = -s*x;
00213                                                         p += s;
00214                                                         
00215                                                         x= p/s;
00216                                                         y= q/s;
00217                                                         z= r/s;
00218                                                         
00219                                                         q /= p;
00220                                                         r /= p;
00221                                                         
00222                                                         for (integer j=k;j<=nn;j++)
00223                                                           {
00224                                                                 p=m[k][j]+q*m[k+1][j];
00225                                                                 if (k != (nn-1))
00226                                                                   {
00227                                                                         p += r*m[k+2][j];
00228                                                                         m[k+2][j] -= p*z;
00229                                                                   }
00230                                                                 m[k+1][j] -= p*y;
00231                                                                 m[k][j] -= p*x;
00232                                                           }
00233                                                         integer mmin = nn<k+3 ? nn : k+3;
00234                                                         for (integer i=l;i<=mmin;i++)
00235                                                           {
00236                                                                 p=x*m[i][k]+y*m[i][k+1];
00237                                                                 if (k != (nn-1))
00238                                                                   {
00239                                                                         p += z*m[i][k+2];
00240                                                                         m[i][k+2] -= p*r;
00241                                                                   }
00242                                                                 m[i][k+1] -= p*q;
00243                                                                 m[i][k] -= p;
00244                                                           }
00245                                                   }
00246                                           }
00247                                   }
00248                           }
00249                   } while (l < nn);
00250           }
00251   }
00252 
00253   template <integer dims, typename nelem, class NT >
00254   const Eigenvalues<dims,nelem,NT>::matrix&
00255   Eigenvalues<dims,nelem,NT>::balance(void)
00256   {
00257         integer last(0);        
00258         numT sqrdx(radix*radix);
00259         
00260         while (last == 0)
00261           {
00262                 last=1;
00263                 for (integer i=0;i<dims;i++)
00264                   {
00265                         // Calculate row and column norms.
00266                         numT r(0.),c(0.),g(0.);
00267                         for (integer j=0;j<dims;j++)
00268                           if (j != i)
00269                                 {
00270                                   c += abs(m[j][i]);
00271                                   r += abs(m[i][j]);
00272                                 }
00273                                 
00274                         if ((c!=0.) && (r!=0.))
00275                           {
00276                                 // If both are nonzero,
00277                                 numT f(1.);
00278                                 numT s(c+r);
00279                                 
00280                                 g=r/radix;
00281                                 while (c<g)
00282                                   {
00283                                         // nd the integer power of the machine radix that comes closest to balancing the matrix.
00284                                         f *= radix;
00285                                         c *= sqrdx;
00286                                   }
00287                                 
00288                                 g=r*radix;
00289                                 while (c>g)
00290                                   {
00291                                         f /= radix;
00292                                         c /= sqrdx;
00293                                   }
00294                                 if (((c+r)/f) < (0.95*s))
00295                                   {
00296                                         last=0;
00297                                         g=1.0/f;
00298                                         // numT a=m[0][0];
00299                                         for (integer k=0;k<dims;k++) m[i][k] *= g; // Apply similarity transforma- tion.
00300                                         // numT b=m[0][0];
00301                                         for (integer k=0;k<dims;k++) m[k][i] *= f;
00302                                   }
00303                           }
00304                   }
00305           }     
00306         return m;
00307   }
00308 
00310   template<typename T>
00311   void swap(T& a,T& b)
00312   {
00313         T       temp(a);
00314         a=b;
00315         b=temp;
00316   }
00317 
00318   template <integer dims, typename nelem, class NT >
00319   const Eigenvalues<dims,nelem,NT>::matrix&
00320   Eigenvalues<dims,nelem,NT>::hessenberg(void)
00321   {
00322         integer i(0);
00323         for (integer rp1=1;rp1<(dims-1);rp1++)
00324           {                                     //rp1 is r + 1in NRC.
00325                 numT x(0.);
00326                 i=rp1;
00327                 
00328                 for (integer j=rp1;j<dims;j++)
00329                   {  // Find the pivot.
00330                         if (abs(m[j][rp1-1]) > abs(x))
00331                           {
00332                                 x=m[j][rp1-1];
00333                                 i=j;
00334                           }
00335                   }
00336                 if (i != rp1)
00337                   { // Interchange rows and columns.
00338                         for (integer j=rp1-1;j<dims;j++) swap(m[i][j],m[rp1][j]);
00339                         for (integer j=0;j<dims;j++) swap(m[j][i],m[j][rp1]);
00340                   }
00341                 if (x!=0.)
00342                   {
00343                         //Carry out the elimination.
00344                         for (integer k=rp1+1;k<dims;k++)
00345                           {
00346                                 numT    y(0.);
00347                                 if ((y=m[k][rp1-1]) != 0.0)
00348                                   {
00349                                         y /= x;
00350                                         m[k][rp1-1]=y;
00351                                         for (integer j=rp1;j<dims;j++) m[k][j] -= y*m[rp1][j];
00352                                         for (integer j=0;j<dims;j++) m[j][rp1] += y*m[j][k];
00353                                   }
00354                           }
00355                   }
00356           }             
00357         return m;
00358   }
00359 
00360 } // end namespace
00361 #endif
00362 
00363 /*********************************************************************
00364 $Id: eigenvalues.h,v 1.1 2001/05/22 10:54:55 mpeeters Exp $
00365 **********************************************************************
00366 
00367 $Log: eigenvalues.h,v $
00368 Revision 1.1  2001/05/22 10:54:55  mpeeters
00369 Moved sources and headers for libModel to model/ subdirectory, in an attempt to rationalize the source tree. This should make things "netter".
00370 
00371 Revision 1.2  2000/09/15 10:26:31  mpeeters
00372 Cleaned out header and added CVS tails to files, removed superfuous
00373 @author comments, inserted dates
00374 
00375 *********************************************************************/

To get the sources or tarballs, please go to SourceForge or you can use the CVS repository.

More Info? Michael Peeters. Also, check our research website: www.alna.vub.ac.be

Last update: June 2002.


Looking for Open Source? Check out SourceForge Logo !