Main Page   Class Hierarchy   Compound List   File List   Compound Members  

matrixMat.cpp

00001 /*=============================================================================
00002         File: matrixMat.cpp
00003      Purpose:
00004     Revision: $Id: matrixMat.cpp,v 1.2 2002/05/13 21:07:45 philosophil Exp $
00005   Created by: Philippe Lavoie          (22 Oct, 1997)
00006  Modified by: 
00007 
00008  Copyright notice:
00009           Copyright (C) 1996-1997 Philippe Lavoie
00010  
00011           This library is free software; you can redistribute it and/or
00012           modify it under the terms of the GNU Library General Public
00013           License as published by the Free Software Foundation; either
00014           version 2 of the License, or (at your option) any later version.
00015  
00016           This library is distributed in the hope that it will be useful,
00017           but WITHOUT ANY WARRANTY; without even the implied warranty of
00018           MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019           Library General Public License for more details.
00020  
00021           You should have received a copy of the GNU Library General Public
00022           License along with this library; if not, write to the Free
00023           Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00024 =============================================================================*/
00025 
00026 #ifndef matrixMat_SOURCES
00027 #define matrixMat_SOURCES
00028 
00029 #include "matrixMat.h"
00030 
00031 #include <float.h>
00032 
00035 namespace PLib {
00036 
00048 template <class T>
00049 LUMatrix<T>& LUMatrix<T>::operator=(const LUMatrix<T>& a){
00050   resize(a.rows(),a.cols()) ;
00051   for(int i=0;i<rows();++i)
00052     for(int j=0;j<cols();++j)
00053       elem(i,j) = a(i,j) ;
00054   pivot_ = a.pivot_ ;
00055   return *this ;
00056 }
00057 
00069 template <class T>
00070 LUMatrix<T>& LUMatrix<T>::decompose(const Matrix<T>& a)
00071 {
00072   int i, j, k, l, kp1, nm1, n;
00073   T t, q;
00074   double den, ten;
00075   
00076   n = a.rows();
00077 
00078   resize(n,n) ;
00079 
00080   if(a.rows()!=a.cols()){
00081 #ifdef USE_EXCEPTION
00082     throw WrongSize2D(a.rows(),a.cols(),0,0) ;
00083 #else
00084     Error err("LUMatrix::decompose");
00085     err << "The a matrix is not squared!" ;
00086     err.fatal() ;
00087 #endif
00088   }
00089 
00090   //    lu = a;  must do it by copying or LUFACT will be recursively called !
00091   for(i=0;i<n;++i)
00092     for(j=0;j<n;++j)
00093       elem(i,j) = a(i,j) ;
00094 
00095   errval = 0;
00096   nm1 = n - 1;
00097   for (k = 0; k < n; k++)
00098     pivot_[k] = k ;
00099 
00100   sign = 1 ;
00101   
00102   if (nm1 >= 1) /* non-trivial problem */
00103     {
00104       for (k = 0; k < nm1; k++)
00105         {
00106           kp1 = k + 1;
00107           // partial pivoting ROW exchanges
00108           // -- search over column 
00109 #ifdef COMPLEX_COMMENT
00110           ten = absolute(real(a(k,k)))+
00111             absolute(imag(a(k,k)));
00112 #else
00113           ten = absolute(a(k,k));
00114 #endif
00115           l = k;
00116           for (i = kp1; i < n; i++)
00117             {
00118 #ifdef COMPLEX_COMMENT
00119               den = absolute(real(a(i,k)))+
00120                 absolute(imag(a(i,k)));
00121 #else
00122               den = absolute(a(i,k));
00123 #endif
00124               if ( den > ten )
00125                 {
00126                   ten = den;
00127                   l = i;
00128                 }
00129             }
00130           pivot_[k] = l;
00131 
00132           if ( elem(l,k) != 0.0 )
00133             {                   // nonsingular pivot found 
00134               if (l != k ){     // interchange needed 
00135                 for (i = k; i < n; i++)
00136                   {
00137                     t = elem(l,i) ;
00138                     elem(l,i) = elem(k,i) ;
00139                     elem(k,i) = t ; 
00140                   }
00141                 sign = -sign ;
00142               }
00143               q =  elem(k,k);   /* scale row */
00144               for (i = kp1; i < n; i++)
00145                 {
00146                   t = - elem(i,k)/q;
00147                   elem(i,k) = t;
00148                   for (j = kp1; j < n; j++)
00149                     elem(i,j) += t * elem(k,j);
00150                 }
00151             }
00152           else          /* pivot singular */
00153             errval = k;
00154         }
00155       
00156     }
00157   
00158   pivot_[nm1] = nm1;
00159   if (elem(nm1,nm1) == 0.0)
00160     errval = nm1;  
00161   return *this;
00162 }
00163 
00164 /*
00165 // a type cast from an LUMatrix directly to a Matrix
00166 template <class T>
00167 Matrix<T> LUMatrix::operator Matrix()   
00168 {
00169   int i, j, r = rows(), c = cols();
00170 
00171   Matrix mat( r, c );
00172         
00173   el_type *ptr,*mptr;
00174   for (i = 0; i < r; i++)
00175     {
00176       mptr = mat.vm[i];
00177       ptr = vm[i];
00178       for (j = 0; j < c; j++)
00179         *mptr++ = *ptr++;
00180     }
00181   return mat;
00182         
00183 }
00184 
00185 */
00186 
00197 template <class T>
00198 T LUMatrix<T>::determinant(){
00199   T det = elem(0,0) ;
00200   for(int i=1;i<rows();++i)
00201     det *= elem(i,i) ;
00202   return det * (T)sign ;
00203 }
00204 
00205 /*
00206 // routine copied from Numerical Recipes in C
00207 // IT DOESN"T WORK
00208 template <class T>
00209 void LUMatrix<T>::backSub(const Matrix<T>& B, Matrix<T>& X){
00210   int i,ii,ip,j,k ;
00211   T sum ;
00212   int n = rows() ;
00213   // one column at a time
00214   //X.resize(n,B.cols()) ;
00215   X = B ;
00216   for(j=0;j<B.cols();++j){
00217     ii = 0 ;
00218     for(i=0;i<n;++i){ // doing back substitution
00219       ip=pivot[i] ;
00220       sum=X(ip,j) ;
00221       X(ip,j) = X(i,j) ;
00222       if(ii)
00223         for(k=ii;k<i;++k)
00224           sum -= elem(i,k)*X(k,j);
00225       else
00226         if(sum)
00227           ii=i ;
00228       X(i,j) = sum ;
00229     }  
00230     for(i=n-1;i>0;--i){ // doing forward substitution
00231       sum=X(i,j) ;
00232       for(k=i+1;k<n;++k)
00233         sum -= elem(i,k)*X(k,j) ;
00234       X(i,j) = sum/elem(i,i) ;
00235     }
00236   }
00237 }
00238 */
00239 
00250 template <class T>
00251 void LUMatrix<T>::inverseIn(Matrix<T>& inv) 
00252 {
00253   T ten;
00254   int i, j, k, l, kb, kp1, nm1, n, coln;
00255 
00256   if ( rows() != cols() )
00257     {
00258 #ifdef USE_EXCEPTION
00259     throw WrongSize2D(rows(),cols(),0,0) ;
00260 #else
00261       Error error("invm");
00262       error << "matrix inverse, not square: " << rows() << " by " << cols() << endl;
00263       error.fatal();
00264 #endif
00265     }
00266 
00267   n = coln = rows();
00268 
00269 
00270   inv = *this ;
00271   
00272   nm1 = n - 1;
00273 
00274   // inverse U 
00275   for (k = 0; k < n; k++)
00276     {
00277       inv(k,k) = ten = 1.0 / inv(k,k) ;
00278       ten = -ten;
00279       for (i = 0; i < k; i++)
00280         inv(i,k) *= ten;
00281       kp1 = k + 1;
00282       if (nm1 >= kp1)
00283         {
00284           for (j = kp1; j < n; j++)
00285             {
00286               ten = inv(k,j) ;
00287               inv(k,j) = 0.0 ;
00288               for (i = 0; i < kp1; i++)
00289                 inv(i,j) += ten * inv(i,k) ;          
00290             }
00291         }
00292     }
00293 
00294   // INV(U) * INV(L)   
00295   if (nm1 >= 1)
00296     {
00297       Vector<T> work(n) ;
00298       //error.memory( work.memory() );
00299 
00300       for (kb = 0; kb < nm1; kb++)
00301         {
00302           k = nm1 - kb - 1;
00303           kp1 = k + 1;
00304           for (i = kp1; i < n; i++)
00305             {
00306               work[i] = inv(i,k) ;
00307               inv(i,k) = 0.0;
00308             }
00309           for (j = kp1; j < n; j++)
00310             {
00311               ten = work[j];
00312               for (i = 0; i < n; i++)
00313                 inv(i,k) += ten * inv(i,j) ;
00314             }
00315           l = pivot[k];
00316           if (l != k)
00317             for (i = 0; i < n; i++)
00318               {
00319                 ten = inv(i,k) ;
00320                 inv(i,k) = inv(i,l);
00321                 inv(i,l) = ten;
00322               }
00323         }
00324       
00325     } 
00326 }
00327 
00338 template <class T>
00339 Matrix<T> LUMatrix<T>::inverse() 
00340 {
00341   if ( rows() != cols() )
00342     {
00343 #ifdef USE_EXCEPTION
00344       throw WrongSize2D(rows(),cols(),0,0) ;
00345 #else
00346       Error error("invm");
00347       error << "matrix inverse, not square: " << rows() << " by " << cols() << endl;
00348       error.fatal();
00349 #endif
00350     }
00351 
00352   Matrix<T> inverse ;
00353   inverseIn(inverse) ;
00354 
00355   return inverse;
00356 }
00357 
00358 
00359 template <class T>
00360 inline T sqr(T a){
00361   return a*a ;
00362 }
00363 
00364 /*
00365  *------------------------------------------------------------------------
00366  *                              Bidiagonalization
00367  */
00368 
00369  /*
00370  *                      Left Householder Transformations
00371  *
00372  * Zero out an entire subdiagonal of the i-th column of A and compute the
00373  * modified A[i,i] by multiplication (UP' * A) with a matrix UP'
00374  *   (1)  UP' = E - UPi * UPi' / beta
00375  *
00376  * where a column-vector UPi is as follows
00377  *   (2)  UPi = [ (i-1) zeros, A[i,i] + Norm, vector A[i+1:M,i] ]
00378  * where beta = UPi' * A[,i] and Norm is the norm of a vector A[i:M,i]
00379  * (sub-diag part of the i-th column of A). Note we assign the Norm the
00380  * same sign as that of A[i,i].
00381  * By construction, (1) does not affect the first (i-1) rows of A. Since
00382  * A[*,1:i-1] is bidiagonal (the result of the i-1 previous steps of
00383  * the bidiag algorithm), transform (1) doesn't affect these i-1 columns
00384  * either as one can easily verify.
00385  * The i-th column of A is transformed as
00386  *   (3)  UP' * A[*,i] = A[*,i] - UPi
00387  * (since UPi'*A[*,i]/beta = 1 by construction of UPi and beta)
00388  * This means effectively zeroing out A[i+1:M,i] (the entire subdiagonal
00389  * of the i-th column of A) and replacing A[i,i] with the -Norm. Thus
00390  * modified A[i,i] is returned by the present function.
00391  * The other (i+1:N) columns of A are transformed as
00392  *    (4)  UP' * A[,j] = A[,j] - UPi * ( UPi' * A[,j] / beta )
00393  * Note, due to (2), only elements of rows i+1:M actually  participate
00394  * in above transforms; the upper i-1 rows of A are not affected.
00395  * As was mentioned earlier,
00396  * (5)  beta = UPi' * A[,i] = (A[i,i] + Norm)*A[i,i] + A[i+1:M,i]*A[i+1:M,i]
00397  *      = ||A[i:M,i]||^2 + Norm*A[i,i] = Norm^2 + Norm*A[i,i]
00398  * (note the sign of the Norm is the same as A[i,i])
00399  * For extra precision, vector UPi (and so is Norm and beta) are scaled,
00400  * which would not affect (4) as easy to see.
00401  *
00402  * To satisfy the definition
00403  *   (6)  .SIG = U' A V
00404  * the result of consecutive transformations (1) over matrix A is accumulated
00405  * in matrix U' (which is initialized to be a unit matrix). At each step,
00406  * U' is left-multiplied by UP' = UP (UP is symmetric by construction,
00407  * see (1)). That is, U is right-multiplied by UP, that is, rows of U are
00408  * transformed similarly to columns of A, see eq. (4). We also keep in mind
00409  * that multiplication by UP at the i-th step does not affect the first i-1
00410  * columns of U.
00411  * Note that the vector UPi doesn't have to be allocated explicitly: its
00412  * first i-1 components are zeros (which we can always imply in computations),
00413  * and the rest of the components (but the UPi[i]) are the same as those
00414  * of A[i:M,i], the subdiagonal of A[,i]. This column, A[,i] is affected only
00415  * trivially as explained above, that is, we don't need to carry this
00416  * transformation explicitly (only A[i,i] is going to be non-trivially
00417  * affected, that is, replaced by -Norm, but we will use sig[i] to store
00418  * the result).
00419  *
00420  */
00421 
00422 template <class T> 
00423 double SVDMatrix<T>::left_householder(Matrix<T>& A, const int i)
00424  {
00425    int j,k;
00426    T scale = 0;                 // Compute the scaling factor
00427    for(k=i; k<M; k++)
00428      scale += absolute(A(k,i));
00429    if( scale == 0 )                     // If A[,i] is a null vector, no
00430      return 0;                          // transform is required
00431    double Norm_sqr = 0;                 // Scale UPi (that is, A[,i])
00432    for(k=i; k<M; k++)                   // and compute its norm, Norm^2
00433      Norm_sqr += sqr(A(k,i) /= scale);
00434    double new_Aii = sqrt(Norm_sqr);     // new_Aii = -Norm, Norm has the
00435    if( A(i,i) > 0 ) new_Aii = -new_Aii; // same sign as Aii (that is, UPi[i])
00436    const float beta = - A(i,i)*new_Aii + Norm_sqr;
00437    A(i,i) -= new_Aii;                   // UPi[i] = A[i,i] - (-Norm)
00438    
00439    for(j=i+1; j<N; j++)         // Transform i+1:N columns of A
00440    {
00441      T factor = 0;
00442      for(k=i; k<M; k++)
00443        factor += A(k,i) * A(k,j);       
00444      factor /= beta;
00445      for(k=i; k<M; k++)
00446        A(k,j) -= A(k,i) * factor;
00447    }
00448 
00449    for(j=0; j<M; j++)                   // Accumulate the transform in U
00450    {
00451      T factor = 0;
00452      for(k=i; k<M; k++)
00453        factor += A(k,i) * U_(j,k);      // Compute  U[j,] * UPi
00454      factor /= beta;
00455      for(k=i; k<M; k++)
00456         U_(j,k) -= A(k,i) * factor;
00457    }
00458    return new_Aii * scale;              // Scale new Aii back (our new Sig[i])
00459  }
00460  
00461 /*
00462  *                      Right Householder Transformations
00463  *
00464  * Zero out i+2:N elements of a row A[i,] of matrix A by right
00465  * multiplication (A * VP) with a matrix VP
00466  *   (1)  VP = E - VPi * VPi' / beta
00467  *
00468  * where a vector-column .VPi is as follows
00469  *   (2)  VPi = [ i zeros, A[i,i+1] + Norm, vector A[i,i+2:N] ]
00470  * where beta = A[i,] * VPi and Norm is the norm of a vector A[i,i+1:N]
00471  * (right-diag part of the i-th row of A). Note we assign the Norm the
00472  * same sign as that of A[i,i+1].
00473  * By construction, (1) does not affect the first i columns of A. Since
00474  * A[1:i-1,] is bidiagonal (the result of the previous steps of
00475  * the bidiag algorithm), transform (1) doesn't affect these i-1 rows
00476  * either as one can easily verify.
00477  * The i-th row of A is transformed as
00478  *  (3)  A[i,*] * VP = A[i,*] - VPi'
00479  * (since A[i,*]*VPi/beta = 1 by construction of VPi and beta)
00480  * This means effectively zeroing out A[i,i+2:N] (the entire right super-
00481  * diagonal of the i-th row of A, but ONE superdiag element) and replacing
00482  * A[i,i+1] with - Norm. Thus modified A[i,i+1] is returned as the result of
00483  * the present function.
00484  * The other (i+1:M) rows of A are transformed as
00485  *    (4)  A[j,] * VP = A[j,] - VPi' * ( A[j,] * VPi / beta )
00486  * Note, due to (2), only elements of columns i+1:N actually  participate
00487  * in above transforms; the left i columns of A are not affected.
00488  * As was mentioned earlier,
00489  * (5)  beta = A[i,] * VPi = (A[i,i+1] + Norm)*A[i,i+1]
00490  *                         + A[i,i+2:N]*A[i,i+2:N]
00491  *      = ||A[i,i+1:N]||^2 + Norm*A[i,i+1] = Norm^2 + Norm*A[i,i+1]
00492  * (note the sign of the Norm is the same as A[i,i+1])
00493  * For extra precision, vector VPi (and so is Norm and beta) are scaled,
00494  * which would not affect (4) as easy to see.
00495  *
00496  * The result of consecutive transformations (1) over matrix A is accumulated
00497  * in matrix V (which is initialized to be a unit matrix). At each step,
00498  * V is right-multiplied by VP. That is, rows of V are transformed similarly
00499  * to rows of A, see eq. (4). We also keep in mind that multiplication by
00500  * VP at the i-th step does not affect the first i rows of V.
00501  * Note that vector VPi doesn't have to be allocated explicitly: its
00502  * first i components are zeros (which we can always imply in computations),
00503  * and the rest of the components (but the VPi[i+1]) are the same as those
00504  * of A[i,i+1:N], the superdiagonal of A[i,]. This row, A[i,] is affected
00505  * only trivially as explained above, that is, we don't need to carry this
00506  * transformation explicitly (only A[i,i+1] is going to be non-trivially
00507  * affected, that is, replaced by -Norm, but we will use super_diag[i+1] to
00508  * store the result).
00509  *
00510  */
00511  
00512 template <class T>
00513 double SVDMatrix<T>::right_householder(Matrix<T>& A, const int i)
00514 {
00515    int j,k;
00516    T scale = 0;                 // Compute the scaling factor
00517    for(k=i+1; k<N; k++)
00518      scale += absolute(A(i,k));
00519    if( scale == 0 )                     // If A[i,] is a null vector, no
00520      return 0;                          // transform is required
00521 
00522    double Norm_sqr = 0;                 // Scale VPi (that is, A[i,])
00523    for(k=i+1; k<N; k++)         // and compute its norm, Norm^2
00524      Norm_sqr += sqr(A(i,k) /= scale);
00525    double new_Aii1 = sqrt(Norm_sqr);    // new_Aii1 = -Norm, Norm has the
00526    if( A(i,i+1) > 0 )                   // same sign as
00527      new_Aii1 = -new_Aii1;              // Aii1 (that is, VPi[i+1])
00528    const float beta = - A(i,i+1)*new_Aii1 + Norm_sqr;
00529    A(i,i+1) -= new_Aii1;                // VPi[i+1] = A[i,i+1] - (-Norm)
00530    
00531    for(j=i+1; j<M; j++)         // Transform i+1:M rows of A
00532    {
00533      T factor = 0;
00534      for(k=i+1; k<N; k++)
00535        factor += A(i,k) * A(j,k);       // Compute A[j,] * VPi
00536      factor /= beta;
00537      for(k=i+1; k<N; k++)
00538        A(j,k) -= A(i,k) * factor;
00539    }
00540 
00541    for(j=0; j<N; j++)                   // Accumulate the transform in V
00542    {
00543      T factor = 0;
00544      for(k=i+1; k<N; k++)
00545        factor += A(i,k) * V_(j,k);      // Compute  V[j,] * VPi
00546      factor /= beta;
00547      for(k=i+1; k<N; k++)
00548        V_(j,k) -= A(i,k) * factor;
00549    }
00550    return new_Aii1 * scale;             // Scale new Aii1 back
00551 }
00552 
00553 /*
00554  *------------------------------------------------------------------------
00555  *                        Bidiagonalization
00556  * This nethod turns matrix A into a bidiagonal one. Its N diagonal elements
00557  * are stored in a vector sig, while N-1 superdiagonal elements are stored
00558  * in a vector super_diag(2:N) (with super_diag(1) being always 0).
00559  * Matrices U and V store the record of orthogonal Householder
00560  * reflections that were used to convert A to this form. The method
00561  * returns the norm of the resulting bidiagonal matrix, that is, the
00562  * maximal column sum.
00563  */
00564 
00565 template <class T>
00566 double SVDMatrix<T>::bidiagonalize(Vector<T>& super_diag, const Matrix<T>& _A)
00567 {
00568   double norm_acc = 0;
00569   super_diag[0] = 0;                    // No superdiag elem above A(1,1)
00570   Matrix<T> A = _A;                     // A being transformed
00571   for(int i=0; i<N; i++)
00572   {
00573     const T& diagi = sig_[i] = left_householder(A,i);
00574     if( i < N-1 )
00575       super_diag[i+1] = right_householder(A,i);
00576     double t2 = absolute(diagi)+absolute(super_diag[i]) ;
00577     norm_acc = maximum(norm_acc,t2);
00578   }
00579   return norm_acc;
00580 }
00581 
00582 /*
00583  *------------------------------------------------------------------------
00584  *              QR-diagonalization of a bidiagonal matrix
00585  *
00586  * After bidiagonalization we get a bidiagonal matrix J:
00587  *    (1)  J = U' * A * V
00588  * The present method turns J into a matrix JJ by applying a set of
00589  * orthogonal transforms
00590  *    (2)  JJ = S' * J * T
00591  * Orthogonal matrices S and T are chosen so that JJ were also a
00592  * bidiagonal matrix, but with superdiag elements smaller than those of J.
00593  * We repeat (2) until non-diag elements of JJ become smaller than EPS
00594  * and can be disregarded.
00595  * Matrices S and T are constructed as
00596  *    (3)  S = S1 * S2 * S3 ... Sn, and similarly T
00597  * where Sk and Tk are matrices of simple rotations
00598  *    (4)  Sk[i,j] = i==j ? 1 : 0 for all i>k or i<k-1
00599  *         Sk[k-1,k-1] = cos(Phk),  Sk[k-1,k] = -sin(Phk),
00600  *         SK[k,k-1] = sin(Phk),    Sk[k,k] = cos(Phk), k=2..N
00601  * Matrix Tk is constructed similarly, only with angle Thk rather than Phk.
00602  *
00603  * Thus left multiplication of J by SK' can be spelled out as
00604  *    (5)  (Sk' * J)[i,j] = J[i,j] when i>k or i<k-1,
00605  *                  [k-1,j] = cos(Phk)*J[k-1,j] + sin(Phk)*J[k,j]
00606  *                  [k,j] =  -sin(Phk)*J[k-1,j] + cos(Phk)*J[k,j]
00607  * That is, k-1 and k rows of J are replaced by their linear combinations;
00608  * the rest of J is unaffected. Right multiplication of J by Tk similarly
00609  * changes only k-1 and k columns of J.
00610  * Matrix T2 is chosen the way that T2'J'JT2 were a QR-transform with a
00611  * shift. Note that multiplying J by T2 gives rise to a J[2,1] element of
00612  * the product J (which is below the main diagonal). Angle Ph2 is then
00613  * chosen so that multiplication by S2' (which combines 1 and 2 rows of J)
00614  * gets rid of that elemnent. But this will create a [1,3] non-zero element.
00615  * T3 is made to make it disappear, but this leads to [3,2], etc.
00616  * In the end, Sn removes a [N,N-1] element of J and matrix S'JT becomes
00617  * bidiagonal again. However, because of a special choice
00618  * of T2 (QR-algorithm), its non-diag elements are smaller than those of J.
00619  *
00620  * All this process in more detail is described in
00621  *    J.H. Wilkinson, C. Reinsch. Linear algebra - Springer-Verlag, 1971
00622  *
00623  * If during transforms (1), JJ[N-1,N] turns 0, then JJ[N,N] is a singular
00624  * number (possibly with a wrong (that is, negative) sign). This is a
00625  * consequence of Frantsis' Theorem, see the book above. In that case, we can
00626  * eliminate the N-th row and column of JJ and carry out further transforms
00627  * with a smaller matrix. If any other superdiag element of JJ turns 0,
00628  * then JJ effectively falls into two independent matrices. We will process
00629  * them independently (the bottom one first).
00630  *
00631  * Since matrix J is a bidiagonal, it can be stored efficiently. As a matter
00632  * of fact, its N diagonal elements are in array Sig, and superdiag elements
00633  * are stored in array super_diag.
00634  */
00635  
00636                                 // Carry out U * S with a rotation matrix
00637                                 // S (which combines i-th and j-th columns
00638                                 // of U, i>j)
00639 template <class T>
00640 void SVDMatrix<T>::rotate(Matrix<T>& Mu, const int i, const int j,
00641                    const double cos_ph, const double sin_ph)
00642 {
00643   for(int l=0; l<Mu.rows(); l++)
00644   {
00645     T& Uil = Mu(l,i); T& Ujl = Mu(l,j);
00646     const T Ujl_was = Ujl;
00647     Ujl =  cos_ph*Ujl_was + sin_ph*Uil;
00648     Uil = -sin_ph*Ujl_was + cos_ph*Uil;
00649   }
00650 }
00651 
00652 /*
00653  * A diagonal element J[l-1,l-1] turns out 0 at the k-th step of the
00654  * algorithm. That means that one of the original matrix' singular numbers
00655  * shall be zero. In that case, we multiply J by specially selected
00656  * matrices S' of simple rotations to eliminate a superdiag element J[l-1,l].
00657  * After that, matrix J falls into two pieces, which can be dealt with
00658  * in a regular way (the bottom piece first).
00659  * 
00660  * These special S transformations are accumulated into matrix U: since J
00661  * is left-multiplied by S', U would be right-multiplied by S. Transform
00662  * formulas for doing these rotations are similar to (5) above. See the
00663  * book cited above for more details.
00664  */
00665 template <class T>
00666 void SVDMatrix<T>::rip_through(Vector<T>& super_diag, const int k, const int l, const double eps)
00667 {
00668   double cos_ph = 0, sin_ph = 1;        // Accumulate cos,sin of Ph
00669                                 // The first step of the loop below
00670                                 // (when i==l) would eliminate J[l-1,l],
00671                                 // which is stored in super_diag(l)
00672                                 // However, it gives rise to J[l-1,l+1]
00673                                 // and J[l,l+2]
00674                                 // The following steps eliminate these
00675                                 // until they fall below
00676                                 // significance
00677   for(int i=l; i<=k; i++)
00678   {
00679     const double f = sin_ph * super_diag[i];
00680     super_diag[i] *= cos_ph;
00681     if( absolute(f) <= eps )
00682       break;                    // Current J[l-1,l] became unsignificant
00683     cos_ph = sig[i]; sin_ph = -f;       // unnormalized sin/cos
00684     const double norm = (sig_[i] = hypot(cos_ph,sin_ph)); // sqrt(sin^2+cos^2)
00685     cos_ph /= norm, sin_ph /= norm;     // Normalize sin/cos
00686     rotate(U_,i,l-1,cos_ph,sin_ph);
00687   }
00688 }
00689 
00690                         // We're about to proceed doing QR-transforms
00691                         // on a (bidiag) matrix J[1:k,1:k]. It may happen
00692                         // though that the matrix splits (or can be
00693                         // split) into two independent pieces. This function
00694                         // checks for splitting and returns the lowerbound
00695                         // index l of the bottom piece, J[l:k,l:k]
00696 template <class T>
00697 int SVDMatrix<T>::get_submatrix_to_work_on(Vector<T>& super_diag, const int k, const double eps)
00698 {
00699   for(register int l=k; l>0; l--)
00700     if( absolute(super_diag[l]) <= eps )
00701       return l;                         // The breaking point: zero J[l-1,l]
00702     else if( absolute(sig[l-1]) <= eps )        // Diagonal J[l,l] turns out 0
00703     {                                   // meaning J[l-1,l] _can_ be made
00704       rip_through(super_diag,k,l,eps);  // zero after some rotations
00705       return l;
00706     }
00707   return 0;                     // Deal with J[1:k,1:k] as a whole
00708 }
00709 
00710                 // Diagonalize root module
00711 template <class T>
00712 void SVDMatrix<T>::diagonalize(Vector<T>& super_diag, const double eps)
00713 {
00714   for(register int k=N-1; k>=0; k--)    // QR-iterate upon J[l:k,l:k]
00715   {
00716     int l;
00717     //    while(l=get_submatrix_to_work_on(super_diag,k,eps),
00718     //    absolute(super_diag[k]) > eps)        // until superdiag J[k-1,k] becomes 0
00719     // Phil
00720     while(absolute(super_diag[k]) > eps)// until superdiag J[k-1,k] becomes 0
00721     {
00722       l=get_submatrix_to_work_on(super_diag,k,eps) ;
00723       double shift;                     // Compute a QR-shift from a bottom
00724       {                                 // corner minor of J[l:k,l:k] order 2
00725         T Jk2k1 = super_diag[k-1],      // J[k-2,k-1]
00726              Jk1k  = super_diag[k],
00727              Jk1k1 = sig[k-1],          // J[k-1,k-1]
00728              Jkk   = sig[k],
00729              Jll   = sig[l];            // J[l,l]
00730         shift = (Jk1k1-Jkk)*(Jk1k1+Jkk) + (Jk2k1-Jk1k)*(Jk2k1+Jk1k);
00731         shift /= 2*Jk1k*Jk1k1;
00732         shift += (shift < 0 ? -1 : 1) * sqrt(shift*shift+1);
00733         shift = ( (Jll-Jkk)*(Jll+Jkk) + Jk1k*(Jk1k1/shift-Jk1k) )/Jll;
00734       }
00735                                 // Carry on multiplications by T2, S2, T3...
00736       double cos_th = 1, sin_th = 1;
00737       T Ji1i1 = sig[l]; // J[i-1,i-1] at i=l+1...k
00738       for(register int i=l+1; i<=k; i++)
00739       {
00740         T Ji1i = super_diag[i], Jii = sig[i];  // J[i-1,i] and J[i,i]
00741         sin_th *= Ji1i; Ji1i *= cos_th; cos_th = shift;
00742         double norm_f = (super_diag[i-1] = hypot(cos_th,sin_th));
00743         cos_th /= norm_f, sin_th /= norm_f;
00744                                         // Rotate J[i-1:i,i-1:i] by Ti
00745         shift = cos_th*Ji1i1 + sin_th*Ji1i;     // new J[i-1,i-1]
00746         Ji1i = -sin_th*Ji1i1 + cos_th*Ji1i;     // J[i-1,i] after rotation
00747         const double Jii1 = Jii*sin_th;         // Emerged J[i,i-1]
00748         Jii *= cos_th;                          // new J[i,i]
00749         rotate(V_,i,i-1,cos_th,sin_th); // Accumulate T rotations in V
00750         
00751         double cos_ph = shift, sin_ph = Jii1;// Make Si to get rid of J[i,i-1]
00752         sig_[i-1] = (norm_f = hypot(cos_ph,sin_ph));    // New J[i-1,i-1]
00753         if( norm_f == 0 )               // If norm =0, rotation angle
00754           cos_ph = cos_th, sin_ph = sin_th; // can be anything now
00755         else
00756           cos_ph /= norm_f, sin_ph /= norm_f;
00757                                         // Rotate J[i-1:i,i-1:i] by Si
00758         shift = cos_ph * Ji1i + sin_ph*Jii;     // New J[i-1,i]
00759         Ji1i1 = -sin_ph*Ji1i + cos_ph*Jii;      // New Jii, would carry over
00760                                                 // as J[i-1,i-1] for next i
00761         rotate(U_,i,i-1,cos_ph,sin_ph);  // Accumulate S rotations in U
00762                                         // Jii1 disappears, sin_th would
00763         cos_th = cos_ph, sin_th = sin_ph; // carry over a (scaled) J[i-1,i+1]
00764                                         // to eliminate on the next i, cos_th
00765                                         // would carry over a scaled J[i,i+1]
00766       }
00767       super_diag[l] = 0;                // Supposed to be eliminated by now
00768       super_diag[k] = shift;
00769       sig_[k] = Ji1i1;
00770     }           // --- end-of-QR-iterations
00771     if( sig[k] < 0 )            // Correct the sign of the sing number
00772     {
00773       sig_[k] = -sig_[k];
00774       for(register int j=0; j<V_.rows(); j++)
00775         V_(j,k) = -V_(j,k);
00776     }
00777   }
00778 } 
00779 
00795 template <class T>
00796 SVDMatrix<T>::SVDMatrix(const Matrix<T>& A)
00797    : U(U_),V(V_),sig(sig_)
00798 {
00799   decompose(A) ;
00800 }
00801 
00816 template <class T>
00817 int SVDMatrix<T>::decompose(const Matrix<T>& A){
00818   M = A.rows() ;
00819   N = A.cols() ;
00820 
00821   if( M < N ){
00822 #ifdef USE_EXCEPTION
00823     throw WrongSize2D(A.rows(),A.cols(),0,0) ;
00824 #else
00825     Error err("SVD") ;
00826     err << "Matrix A should have at least as many rows() as it has columns";
00827     err.warning() ;
00828 #endif
00829     return 0 ;
00830   }
00831   
00832   U_.resize(M,M) ;
00833   V_.resize(N,N) ;
00834   sig_.resize(N) ;
00835 
00836   U_.diag(1) ;
00837   V_.diag(1) ;
00838   
00839   Vector<T> super_diag(N);
00840   const double bidiag_norm = bidiagonalize(super_diag,A);
00841   const double eps = FLT_EPSILON * bidiag_norm; // Significance threshold
00842   diagonalize(super_diag,eps);
00843 
00844   return 1 ;
00845 }
00846 
00847 
00861 template <class T>
00862 void SVDMatrix<T>::minMax(T& min_sig, T& max_sig) const
00863 {
00864   max_sig = min_sig = sig[0];
00865   for(register int i=0; i<sig.n(); ++i)
00866     if( sig[i] > max_sig )
00867       max_sig = sig[i];
00868     else if( sig[i] < min_sig )
00869       min_sig = sig[i];
00870 }
00871 
00885 template <class T>
00886 double SVDMatrix<T>::q_cond_number(void) const
00887 {
00888   T mins,maxs ;
00889   minMax(mins,maxs) ;
00890   return (maxs/mins);
00891 }
00892 
00906 template <class T>
00907 void SVDMatrix<T>::inverseIn(Matrix<T>& A, double tau) {
00908   Matrix<T> S ;
00909 
00910   T min_sig, max_sig ;
00911   minMax(min_sig,max_sig) ;
00912 
00913   if(tau==0)
00914     tau = V.rows()*max_sig*FLT_EPSILON ;
00915 
00916   if(min_sig<tau){
00917 #ifdef USE_EXCEPTION
00918     throw MatrixErr() ;
00919 #else
00920     Error err("SVDMatrix::inverseIn") ;
00921     err << "\nSVD found some singular value null coefficients.\n" ;
00922     err.warning() ;
00923 #endif
00924   }
00925 
00926   S.resize(V.cols(),U.rows());
00927   S.reset(0) ;
00928   for(int i=0;i<sig.n();++i)
00929     S(i,i) = (T)1/sig[i] ;
00930 
00931   //A = V*S*transpose(U) ; 
00932   A = U.transpose() ;
00933   A = (const Matrix<T>&)S*(const Matrix<T>&)A ; // transpose(U) ; 
00934   A = (const Matrix<T>&)V*(const Matrix<T>&)A ;
00935 }
00936 
00937 
00950 template <class T>
00951 Matrix<T> SVDMatrix<T>::inverse(double tau) {
00952   Matrix<T> A ;
00953 
00954   inverseIn(A,tau) ;
00955   return A ;
00956 }
00957 
00976 template <class T>
00977 int SVDMatrix<T>::solve(const Matrix<T>&B, Matrix<T>&X, double tau) {
00978   T min_sig, max_sig ;
00979   minMax(min_sig,max_sig) ;
00980 
00981   if(B.rows() != U.rows()){
00982 #ifdef USE_EXCEPTION
00983     throw WrongSize2D(B.rows(),B.cols(),U.rows(),U.cols()) ;
00984 #else
00985     Error err("SVDMatrix::solve");
00986     err << "The matrix B doesn't have a proper size for this SVD matrix." ;
00987     err.fatal() ;
00988     return 0 ;
00989 #endif
00990   }
00991 
00992   X.resize(V.rows(),B.cols()) ;
00993 
00994   if(tau==0)
00995     tau = V.rows()*max_sig*FLT_EPSILON ;
00996   int stable = 1 ;
00997 
00998   Vector<T> S(sig.n()) ;
00999 
01000   // doing one column from B at a time 
01001   int i,j,k ;
01002   for(j=0;j<B.cols();++j){
01003     for(i=0;i<V.cols();++i){
01004       T s = 0 ;
01005       if(sig[i]>tau){
01006         for(k=0;k<U.cols();++k)
01007           s += U(k,i)*B(k,j);
01008         s /= sig[i] ;
01009       }
01010       else
01011         stable = 0 ;
01012       S[i] = s ;
01013     }
01014     for(i=0;i<V.rows();++i){
01015       X(i,j) = 0 ;
01016       for(k=0;k<V.rows();++k)
01017         X(i,j) += V(i,k)*S[k] ;
01018     }
01019   }
01020   return stable ;
01021 }
01022 
01040 template <class T> 
01041 int solve(const Matrix<T>& A, 
01042            const Matrix<T>& B, 
01043            Matrix<T>& X){
01044   if(A.rows()==A.cols()){ 
01045     // use LU decomposition to solve the problem
01046     LUMatrix<T> lu(A) ;
01047     X = lu.inverse()*B ;
01048   }
01049   else{
01050     SVDMatrix<T> svd(A) ;
01051     return svd.solve(B,X) ;
01052   }
01053   return 1;
01054 }
01055 
01072 template <class T>
01073 Matrix<T> inverse(const Matrix<T>&A){
01074   Matrix<T> inv ;
01075   if(A.rows()==A.cols()){
01076     LUMatrix<T> lu(A) ;
01077     lu.inverseIn(inv) ;
01078   }
01079   else{
01080     SVDMatrix<T> svd(A) ;
01081     svd.inverseIn(inv) ;
01082   }
01083   return inv ;
01084 }
01085 
01086 }
01087 
01088 
01089 #endif

Generated on Tue Jun 24 13:26:56 2003 for NURBS++ by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002