TDecompBase.cxx

Go to the documentation of this file.
00001 // @(#)root/matrix:$Id: TDecompBase.cxx 20882 2007-11-19 11:31:26Z rdm $
00002 // Authors: Fons Rademakers, Eddy Offermann  Dec 2003
00003 
00004 /*************************************************************************
00005  * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
00006  * All rights reserved.                                                  *
00007  *                                                                       *
00008  * For the licensing terms see $ROOTSYS/LICENSE.                         *
00009  * For the list of contributors see $ROOTSYS/README/CREDITS.             *
00010  *************************************************************************/
00011 
00012 ///////////////////////////////////////////////////////////////////////////
00013 //                                                                       //
00014 // Decomposition Base class                                              //
00015 //                                                                       //
00016 // This class forms the base for all the decompositions methods in the   //
00017 // linear algebra package .                                              //
00018 // It or its derived classes have installed the methods to solve         //
00019 // equations,invert matrices and calculate determinants while monitoring //
00020 // the accuracy.                                                         //
00021 //                                                                       //
00022 // Each derived class has always the following methods available:        //
00023 //                                                                       //
00024 // Condition() :                                                         //
00025 //   In an iterative scheme the condition number for matrix inversion is //
00026 //   calculated . This number is of interest for estimating the accuracy //
00027 //   of x in the equation Ax=b                                           //
00028 //   For example:                                                        //
00029 //     A is a (10x10) Hilbert matrix which looks deceivingly innocent    //
00030 //     and simple, A(i,j) = 1/(i+j+1)                                    //
00031 //     b(i) = Sum_j A(i,j), so a sum of a row in A                       //
00032 //                                                                       //
00033 //     the solution is x(i) = 1. i=0,.,9                                 //
00034 //                                                                       //
00035 //   However,                                                            //
00036 //     TMatrixD m....; TVectorD b.....                                   //
00037 //     TDecompLU lu(m); lu.SetTol(1.0e-12); lu.Solve(b); b.Print()       //
00038 //   gives,                                                              //
00039 //                                                                       //
00040 //   {1.000,1.000,1.000,1.000,0.998,1.000,0.993,1.001,0.996,1.000}       //
00041 //                                                                       //
00042 //   Looking at the condition number, this is in line with expected the  //
00043 //   accuracy . The condition number is 3.957e+12 . As a simple rule of  //
00044 //   thumb, a condition number of 1.0e+n means that you lose up to n     //
00045 //   digits of accuracy in a solution . Since doubles are stored with 15 //
00046 //   digits, we can expect the accuracy to be as small as 3 digits .     //
00047 //                                                                       //
00048 // Det(Double_t &d1,Double_t &d2)                                        //
00049 //   The determinant is d1*TMath::Power(2.,d2)                           //
00050 //   Expressing the determinant this way makes under/over-flow very      //
00051 //   unlikely .                                                          //
00052 //                                                                       //
00053 // Decompose()                                                           //
00054 //   Here the actually decomposition is performed . One can change the   //
00055 //   matrix A after the decomposition constructor has been called        //
00056 //   without effecting the decomposition result                          //
00057 //                                                                       //
00058 // Solve(TVectorD &b)                                                    //
00059 //  Solve A x = b . x is supplied through the argument and replaced with //
00060 //  the solution .                                                       //
00061 //                                                                       //
00062 // TransSolve(TVectorD &b)                                               //
00063 //  Solve A^T x = b . x is supplied through the argument and replaced    //
00064 //  with the solution .                                                  //
00065 //                                                                       //
00066 // MultiSolve(TMatrixD    &B)                                            //
00067 //  Solve A X = B . where X and are now matrices . X is supplied through //
00068 //  the argument and replaced with the solution .                        //
00069 //                                                                       //
00070 // Invert(TMatrixD &inv)                                                 //
00071 //  This is of course just a call to MultiSolve with as input argument   //
00072 //  the unit matrix . Note that for a matrix a(m,n) with m > n  a        //
00073 //  pseudo-inverse is calculated .                                       //
00074 //                                                                       //
00075 // Tolerances and Scaling                                                //
00076 // ----------------------                                                //
00077 // The tolerance parameter (which is a member of this base class) plays  //
00078 // a crucial role in all operations of the decomposition classes . It    //
00079 // gives the user a powerful tool to monitor and steer the operations    //
00080 // Its default value is sqrt(epsilon) where 1+epsilon = 1                //
00081 //                                                                       //
00082 // If you do not want to be bothered by the following considerations,    //
00083 // like in most other linear algebra packages, just set the tolerance    //
00084 // with SetTol to an arbitrary small number .                            //
00085 //                                                                       //
00086 // The tolerance number is used by each decomposition method to decide   //
00087 // whether the matrix is near singular, except of course SVD which can   //
00088 // handle singular matrices .                                            //
00089 // For each decomposition this will be checked in a different way; in LU //
00090 // the matrix is considered singular when, at some point in the          //
00091 // decomposition, a diagonal element < fTol . Therefore, we had to set in//
00092 // the example above of the (10x10) Hilbert, which is near singular, the //
00093 // tolerance on 10e-12 . (The fact that we have to set the tolerance <   //
00094 // sqrt(epsilon) is a clear indication that we are losing precision .)   //
00095 //                                                                       //
00096 // If the matrix is flagged as being singular, operations with the       //
00097 // decomposition will fail and will return matrices/vectors that are     //
00098 // invalid .                                                             //
00099 //                                                                       //
00100 // The observant reader will notice that by scaling the complete matrix  //
00101 // by some small number the decomposition will detect a singular matrix .//
00102 // In this case the user will have to reduce the tolerance number by this//
00103 // factor . (For CPU time saving we decided not to make this an automatic//
00104 // procedure) .                                                          //
00105 //                                                                       //
00106 // Code for this could look as follows:                                  //
00107 // const Double_t max_abs = Abs(a).Max();                                //
00108 // const Double_t scale = TMath::Min(max_abs,1.);                        //
00109 // a.SetTol(a.GetTol()*scale);                                           //
00110 //                                                                       //
00111 // For usage examples see $ROOTSYS/test/stressLinear.cxx                 //
00112 ///////////////////////////////////////////////////////////////////////////
00113 
00114 #include "TDecompBase.h"
00115 #include "TMath.h"
00116 #include "TError.h"
00117 
00118 ClassImp(TDecompBase)
00119 
00120 //______________________________________________________________________________
00121 TDecompBase::TDecompBase()
00122 {
00123 // Default constructor
00124 
00125    fTol       = std::numeric_limits<double>::epsilon();
00126    fDet1      = 0;
00127    fDet2      = 0;
00128    fCondition = -1.0;
00129    fRowLwb    = 0;
00130    fColLwb    = 0;
00131 }
00132 
00133 //______________________________________________________________________________
00134 TDecompBase::TDecompBase(const TDecompBase &another) : TObject(another)
00135 {
00136 // Copy constructor
00137 
00138    *this = another;
00139 }
00140 
00141 //______________________________________________________________________________
00142 Int_t TDecompBase::Hager(Double_t &est,Int_t iter)
00143 {
00144 
00145 // Estimates lower bound for norm1 of inverse of A. Returns norm
00146 // estimate in est.  iter sets the maximum number of iterations to be used.
00147 // The return value indicates the number of iterations remaining on exit from
00148 // loop, hence if this is non-zero the processed "converged".
00149 // This routine uses Hager's Convex Optimisation Algorithm.
00150 // See Applied Numerical Linear Algebra, p139 & SIAM J Sci Stat Comp 1984 pp 311-16
00151 
00152    est = -1.0;
00153 
00154    const TMatrixDBase &m = GetDecompMatrix();
00155    if (!m.IsValid())
00156       return iter;
00157 
00158    const Int_t n = m.GetNrows();
00159 
00160    TVectorD b(n); TVectorD y(n); TVectorD z(n);
00161    b = Double_t(1.0/n);
00162    Double_t inv_norm1 = 0.0;
00163    Bool_t stop = kFALSE;
00164    do {
00165       y = b;
00166       if (!Solve(y))
00167          return iter;
00168       const Double_t ynorm1 = y.Norm1();
00169       if ( ynorm1 <= inv_norm1 ) {
00170          stop = kTRUE;
00171       } else {
00172          inv_norm1 = ynorm1;
00173          Int_t i;
00174          for (i = 0; i < n; i++)
00175             z(i) = ( y(i) >= 0.0 ? 1.0 : -1.0 );
00176          if (!TransSolve(z))
00177             return iter;
00178          Int_t imax = 0;
00179          Double_t maxz = TMath::Abs(z(0));
00180          for (i = 1; i < n; i++) {
00181             const Double_t absz = TMath::Abs(z(i));
00182             if ( absz > maxz ) {
00183                maxz = absz;
00184                imax = i;
00185             }
00186          }
00187          stop = (maxz <= b*z);
00188          if (!stop) {
00189             b = 0.0;
00190             b(imax) = 1.0;
00191          }
00192       }
00193       iter--;
00194    } while (!stop && iter);
00195    est = inv_norm1;
00196 
00197    return iter;
00198 }
00199 
00200 //______________________________________________________________________________
00201 void TDecompBase::DiagProd(const TVectorD &diag,Double_t tol,Double_t &d1,Double_t &d2)
00202 {
00203 
00204 // Returns product of matrix diagonal elements in d1 and d2. d1 is a mantissa and d2
00205 // an exponent for powers of 2. If matrix is in diagonal or triangular-matrix form this
00206 // will be the determinant.
00207 // Based on Bowler, Martin, Peters and Wilkinson in HACLA
00208 
00209    const Double_t zero      = 0.0;
00210    const Double_t one       = 1.0;
00211    const Double_t four      = 4.0;
00212    const Double_t sixteen   = 16.0;
00213    const Double_t sixteenth = 0.0625;
00214 
00215    const Int_t n = diag.GetNrows();
00216 
00217    Double_t t1 = 1.0;
00218    Double_t t2 = 0.0;
00219    for (Int_t i = 0; (i < n) && (t1 != zero); i++) {
00220       if (TMath::Abs(diag(i)) > tol) {
00221          t1 *= (Double_t) diag(i);
00222          while (TMath::Abs(t1) > one) {
00223             t1 *= sixteenth;
00224             t2 += four;
00225          }
00226          while (TMath::Abs(t1) < sixteenth) {
00227             t1 *= sixteen;
00228             t2 -= four;
00229          }
00230       } else {
00231          t1 = zero;
00232          t2 = zero;
00233       }
00234    }
00235    d1 = t1;
00236    d2 = t2;
00237 
00238    return;
00239 }
00240 
00241 //______________________________________________________________________________
00242 Double_t TDecompBase::Condition()
00243 {
00244 // Matrix condition number
00245 
00246    if ( !TestBit(kCondition) ) {
00247       fCondition = -1;
00248       if (TestBit(kSingular))
00249          return fCondition;
00250       if ( !TestBit(kDecomposed) ) {
00251          if (!Decompose())
00252             return fCondition;
00253       }
00254       Double_t invNorm;
00255       if (Hager(invNorm))
00256          fCondition *= invNorm;
00257       else // no convergence in Hager
00258          Error("Condition()","Hager procedure did NOT converge");
00259       SetBit(kCondition);
00260    }
00261    return fCondition;
00262 }
00263 
00264 //______________________________________________________________________________
00265 Bool_t TDecompBase::MultiSolve(TMatrixD &B)
00266 {
00267 // Solve set of equations with RHS in columns of B
00268 
00269    const TMatrixDBase &m = GetDecompMatrix();
00270    R__ASSERT(m.IsValid() && B.IsValid());
00271 
00272    const Int_t colLwb = B.GetColLwb();
00273    const Int_t colUpb = B.GetColUpb();
00274    Bool_t status = kTRUE;
00275    for (Int_t icol = colLwb; icol <= colUpb && status; icol++) {
00276       TMatrixDColumn b(B,icol);
00277       status &= Solve(b);
00278    }
00279 
00280    return status;
00281 }
00282 
00283 //______________________________________________________________________________
00284 void TDecompBase::Det(Double_t &d1,Double_t &d2)
00285 {
00286 // Matrix determinant det = d1*TMath::Power(2.,d2)
00287 
00288    if ( !TestBit(kDetermined) ) {
00289       if ( !TestBit(kDecomposed) )
00290          Decompose();
00291       if (TestBit(kSingular) ) {
00292          fDet1 = 0.0;
00293          fDet2 = 0.0;
00294       } else {
00295          const TMatrixDBase &m = GetDecompMatrix();
00296          R__ASSERT(m.IsValid());
00297          TVectorD diagv(m.GetNrows());
00298          for (Int_t i = 0; i < diagv.GetNrows(); i++)
00299             diagv(i) = m(i,i);
00300          DiagProd(diagv,fTol,fDet1,fDet2);
00301       }
00302       SetBit(kDetermined);
00303    }
00304    d1 = fDet1;
00305    d2 = fDet2;
00306 }
00307 
00308 //______________________________________________________________________________
00309 void TDecompBase::Print(Option_t * /*opt*/) const
00310 {
00311 // Print class members
00312 
00313    printf("fTol       = %.4e\n",fTol);
00314    printf("fDet1      = %.4e\n",fDet1);
00315    printf("fDet2      = %.4e\n",fDet2);
00316    printf("fCondition = %.4e\n",fCondition);
00317    printf("fRowLwb    = %d\n",fRowLwb);
00318    printf("fColLwb    = %d\n",fColLwb);
00319 }
00320 
00321 //______________________________________________________________________________
00322 TDecompBase &TDecompBase::operator=(const TDecompBase &source)
00323 {
00324 // Assignment operator
00325 
00326    if (this != &source) {
00327       TObject::operator=(source);
00328       fTol       = source.fTol;
00329       fDet1      = source.fDet1;
00330       fDet2      = source.fDet2;
00331       fCondition = source.fCondition;
00332       fRowLwb    = source.fRowLwb;
00333       fColLwb    = source.fColLwb;
00334    }
00335    return *this;
00336 }
00337 
00338 //______________________________________________________________________________
00339 Bool_t DefHouseHolder(const TVectorD &vc,Int_t lp,Int_t l,Double_t &up,Double_t &beta,
00340                       Double_t tol)
00341 {
00342 // Define a Householder-transformation through the parameters up and b .
00343 
00344    const Int_t n = vc.GetNrows();
00345    const Double_t * const vp = vc.GetMatrixArray();
00346 
00347    Double_t c = TMath::Abs(vp[lp]);
00348    Int_t i;
00349    for (i = l; i < n; i++)
00350       c = TMath::Max(TMath::Abs(vp[i]),c);
00351 
00352    up   = 0.0;
00353    beta = 0.0;
00354    if (c <= tol) {
00355 //     Warning("DefHouseHolder","max vector=%.4e < %.4e",c,tol);
00356       return kFALSE;
00357    }
00358 
00359    Double_t sd = vp[lp]/c; sd *= sd;
00360    for (i = l; i < n; i++) {
00361       const Double_t tmp = vp[i]/c;
00362       sd += tmp*tmp;
00363    }
00364 
00365    Double_t vpprim = c*TMath::Sqrt(sd);
00366    if (vp[lp] > 0.) vpprim = -vpprim;
00367    up = vp[lp]-vpprim;
00368    beta = 1./(vpprim*up);
00369 
00370    return kTRUE;
00371 }
00372 
00373 //______________________________________________________________________________
00374 void ApplyHouseHolder(const TVectorD &vc,Double_t up,Double_t beta,
00375                       Int_t lp,Int_t l,TMatrixDRow &cr)
00376 {
00377 // Apply Householder-transformation.
00378 
00379    const Int_t nv = vc.GetNrows();
00380    const Int_t nc = (cr.GetMatrix())->GetNcols();
00381 
00382    if (nv > nc) {
00383       Error("ApplyHouseHolder(const TVectorD &,..,TMatrixDRow &)","matrix row too short");
00384       return;
00385    }
00386 
00387    const Int_t inc_c = cr.GetInc();
00388    const Double_t * const vp = vc.GetMatrixArray();
00389          Double_t *       cp = cr.GetPtr();
00390 
00391    Double_t s = cp[lp*inc_c]*up;
00392    Int_t i;
00393    for (i = l; i < nv; i++)
00394       s += cp[i*inc_c]*vp[i];
00395 
00396    s = s*beta;
00397    cp[lp*inc_c] += s*up;
00398    for (i = l; i < nv; i++)
00399       cp[i*inc_c] += s*vp[i];
00400 }
00401 
00402 //______________________________________________________________________________
00403 void ApplyHouseHolder(const TVectorD &vc,Double_t up,Double_t beta,
00404                       Int_t lp,Int_t l,TMatrixDColumn &cc)
00405 {
00406 // Apply Householder-transformation.
00407 
00408    const Int_t nv = vc.GetNrows();
00409    const Int_t nc = (cc.GetMatrix())->GetNrows();
00410 
00411    if (nv > nc) {
00412       Error("ApplyHouseHolder(const TVectorD &,..,TMatrixDRow &)","matrix column too short");
00413       return;
00414    }
00415 
00416    const Int_t inc_c = cc.GetInc();
00417    const Double_t * const vp = vc.GetMatrixArray();
00418          Double_t *       cp = cc.GetPtr();
00419 
00420    Double_t s = cp[lp*inc_c]*up;
00421    Int_t i;
00422    for (i = l; i < nv; i++)
00423       s += cp[i*inc_c]*vp[i];
00424 
00425    s = s*beta;
00426    cp[lp*inc_c] += s*up;
00427    for (i = l; i < nv; i++)
00428       cp[i*inc_c] += s*vp[i];
00429 }
00430 
00431 //______________________________________________________________________________
00432 void ApplyHouseHolder(const TVectorD &vc,Double_t up,Double_t beta,
00433                       Int_t lp,Int_t l,TVectorD &cv)
00434 {
00435 //  Apply Householder-transformation.
00436 
00437    const Int_t nv = vc.GetNrows();
00438    const Int_t nc = cv.GetNrows();
00439 
00440    if (nv > nc) {
00441       Error("ApplyHouseHolder(const TVectorD &,..,TVectorD &)","vector too short");
00442       return;
00443    }
00444 
00445    const Double_t * const vp = vc.GetMatrixArray();
00446          Double_t *       cp = cv.GetMatrixArray();
00447 
00448    Double_t s = cp[lp]*up;
00449    Int_t i;
00450    for (i = l; i < nv; i++)
00451       s += cp[i]*vp[i];
00452 
00453    s = s*beta;
00454    cp[lp] += s*up;
00455    for (i = l; i < nv; i++)
00456       cp[i] += s*vp[i];
00457 }
00458 
00459 //______________________________________________________________________________
00460 void DefGivens(Double_t v1,Double_t v2,Double_t &c,Double_t &s)
00461 {
00462 // Defines a Givens-rotation by calculating 2 rotation parameters c and s.
00463 // The rotation is defined with the vector components v1 and v2.
00464 
00465    const Double_t a1 = TMath::Abs(v1);
00466    const Double_t a2 = TMath::Abs(v2);
00467    if (a1 > a2) {
00468       const Double_t w = v2/v1;
00469       const Double_t q = TMath::Hypot(1.,w);
00470       c = 1./q;
00471       if (v1 < 0.) c = -c;
00472       s = c*w;
00473    } else {
00474       if (v2 != 0) {
00475          const Double_t w = v1/v2;
00476          const Double_t q = TMath::Hypot(1.,w);
00477          s = 1./q;
00478          if (v2 < 0.) s = -s;
00479          c = s*w;
00480       } else {
00481          c = 1.;
00482          s = 0.;
00483       }
00484    }
00485 }
00486 
00487 //______________________________________________________________________________
00488 void DefAplGivens(Double_t &v1,Double_t &v2,Double_t &c,Double_t &s)
00489 {
00490 // Define and apply a Givens-rotation by calculating 2 rotation
00491 // parameters c and s. The rotation is defined with and applied to the vector
00492 // components v1 and v2.
00493 
00494    const Double_t a1 = TMath::Abs(v1);
00495    const Double_t a2 = TMath::Abs(v2);
00496    if (a1 > a2) {
00497       const Double_t w = v2/v1;
00498       const Double_t q = TMath::Hypot(1.,w);
00499       c = 1./q;
00500       if (v1 < 0.) c = -c;
00501       s  = c*w;
00502       v1 = a1*q;
00503       v2 = 0.;
00504    } else {
00505       if (v2 != 0) {
00506          const Double_t w = v1/v2;
00507          const Double_t q = TMath::Hypot(1.,w);
00508          s = 1./q;
00509          if (v2 < 0.) s = -s;
00510          c  = s*w;
00511          v1 = a2*q;
00512          v2 = 0.;
00513       } else {
00514          c = 1.;
00515          s = 0.;
00516       }
00517    }
00518 }
00519 
00520 //______________________________________________________________________________
00521 void ApplyGivens(Double_t &z1,Double_t &z2,Double_t c,Double_t s)
00522 {
00523 // Apply a Givens transformation as defined by c and s to the vector compenents
00524 // v1 and v2 .
00525 
00526    const Double_t w = z1*c+z2*s;
00527    z2 = -z1*s+z2*c;
00528    z1 = w;
00529 }

Generated on Tue Jul 5 14:36:26 2011 for ROOT_528-00b_version by  doxygen 1.5.1