SRIMatrix.hpp

Go to the documentation of this file.
00001 #pragma ident "$Id: SRIMatrix.hpp 2293 2010-02-12 18:14:16Z btolman $"
00002 
00003 //============================================================================
00004 //
00005 //  This file is part of GPSTk, the GPS Toolkit.
00006 //
00007 //  The GPSTk is free software; you can redistribute it and/or modify
00008 //  it under the terms of the GNU Lesser General Public License as published
00009 //  by the Free Software Foundation; either version 2.1 of the License, or
00010 //  any later version.
00011 //
00012 //  The GPSTk is distributed in the hope that it will be useful,
00013 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 //  GNU Lesser General Public License for more details.
00016 //
00017 //  You should have received a copy of the GNU Lesser General Public
00018 //  License along with GPSTk; if not, write to the Free Software Foundation,
00019 //  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020 //  
00021 //  Copyright 2004, The University of Texas at Austin
00022 //
00023 //============================================================================
00024 
00025 //============================================================================
00026 //
00027 //This software developed by Applied Research Laboratories at the University of
00028 //Texas at Austin, under contract to an agency or agencies within the U.S. 
00029 //Department of Defense. The U.S. Government retains all rights to use,
00030 //duplicate, distribute, disclose, or release this software. 
00031 //
00032 //Pursuant to DoD Directive 523024 
00033 //
00034 // DISTRIBUTION STATEMENT A: This software has been approved for public 
00035 //                           release, distribution is unlimited.
00036 //
00037 //=============================================================================
00038 
00045 //------------------------------------------------------------------------------------
00046 #ifndef SQUAREROOTINFORMATION_MATRICIES_INCLUDE
00047 #define SQUAREROOTINFORMATION_MATRICIES_INCLUDE
00048 
00049 //------------------------------------------------------------------------------------
00050 // system includes
00051 // GPSTk
00052 #include "Vector.hpp"
00053 #include "Matrix.hpp"
00054 #include "StringUtils.hpp"
00055 // geomatics
00056 
00057 namespace gpstk
00058 {
00059 
00060    //---------------------------------------------------------------------------------
00061    // This routine uses the Householder algorithm to update the SRI
00062    // state and covariance.
00063    // Input:
00064    //    R  a priori SRI matrix (upper triangular, dimension N)
00065    //    Z  a priori SRI data vector (length N)
00066    //    A  concatentation of H and D : A = H || D, where
00067    //    H  Measurement partials, an M by N matrix.
00068    //    D  Data vector, of length M
00069    //       H and D may have row dimension > M; then pass M:
00070    //    M  (optional) Row dimension of H and D
00071    // Output:
00072    //    Updated R and Z.  H is trashed, but the data vector D
00073    //    contains the residuals of fit (D - A*state).
00074    // Return values:
00075    //    SrifMU returns void, but throws exceptions if the input matrices
00076    // or vectors have incompatible dimensions.
00077    // 
00078    // Measurment noise associated with H and D must be white
00079    // with unit covariance.  If necessary, the data can be 'whitened'
00080    // before calling this routine in order to satisfy this requirement.
00081    // This is done as follows.  Compute the lower triangular square root 
00082    // of the covariance matrix, L, and replace H with inverse(L)*H and
00083    // D with inverse(L)*D.
00084    // 
00085    //    The Householder transformation is simply an orthogonal
00086    // transformation designed to make the elements below the diagonal
00087    // zero.  It works by explicitly performing the transformation, one
00088    // column at a time, without actually constructing the transformation
00089    // matrix. The matrix is transformed as follows
00090    //   [  A(m,n) ] => [ sum       a       ]
00091    //   [         ] => [  0    A'(m-1,n-1) ]
00092    // after which the same transformation is applied to A' matrix, until A'
00093    // has only one row or column. The transformation that zeros the diagonal
00094    // below the (k,k) element also replaces the (k,k) element and modifies
00095    // the matrix elements for columns >= k and rows >=k, but does not affect
00096    // the matrix for columns < k or rows < k.
00097    //    Column k (=0..min(m,n)-1) of the input matrix A(m,n) can be zeroed
00098    // below the diagonal (columns < k have already been so zeroed) as follows:
00099    //    let y be the vector equal to column k at the diagonal and below,
00100    //       ( so y(j)==A(k+j,k), y(0)==A(k,k), y.size = m-k )
00101    //    let sum = -sign(y(0))*|y|,
00102    //    define vector u by u(0) = y(0)-sum, u(j)=y(j) for j>0 (j=1..m-k)
00103    //    and finally define b = 1/(sum*u(0)).
00104    // Redefine column k with A(k,k)=sum and A(k+j,k)=0, j=1..m, and
00105    // then for each column j > k, (j=k+1..n)
00106    //    compute g = b*sum[u(i)*A(k+i,j)], i=0..m-k-1,
00107    //    replace A(k+i,j) with A(k+i,j)+g*u(i), for i=0..m-k-1
00108    // Most algorithms don't handle special cases:
00109    // 1. If column k is already zero below the diagonal, but A(k,k)!=0, then
00110    // y=[A(k,k),0,0,...0], sum=-A(k,k), u(0)=2A(k,k), u=[2A(k,k),0,0,...0]
00111    // and b = -1/(2*A(k,k)^2). Then, zeroing column k only changes the sign
00112    // of A(k,k), and for the other columns j>k, g = -A(k,j)/A(k,k) and only
00113    // row k is changed.
00114    // 2. If column k is already zero below the diagonal, AND A(k,k) is zero,
00115    // then y=0,sum=0,u=0 and b is infinite...the transformation is undefined.
00116    // However this column should be skipped (Biermann Appendix VII.B).
00117    //
00118    // Ref: Bierman, G.J. "Factorization Methods for Discrete Sequential
00119    //      Estimation," Academic Press, 1977.
00120    
00124    template <class T>
00125    void SrifMU(Matrix<T>& R, Vector<T>& Z, Matrix<T>& A, unsigned int M=0)
00126       throw(MatrixException)
00127    {
00128       if(A.cols() <= 1 || A.cols() != R.cols()+1 || Z.size() < R.rows()) {
00129          if(A.cols() > 1 && R.rows() == 0 && Z.size() == 0) {
00130             // create R and Z
00131             R = Matrix<double>(A.cols()-1,A.cols()-1,0.0);
00132             Z = Vector<double>(A.cols()-1,0.0);
00133          }
00134          else {
00135             MatrixException me("Invalid input dimensions:\n  R has dimension "
00136                + StringUtils::asString<int>(R.rows()) + "x"
00137                + StringUtils::asString<int>(R.cols()) + ",\n  Z has length "
00138                + StringUtils::asString<int>(Z.size()) + ",\n  and A has dimension "
00139                + StringUtils::asString<int>(A.rows()) + "x"
00140                + StringUtils::asString<int>(A.cols()));
00141             GPSTK_THROW(me);
00142          }
00143       }
00144    
00145       const T EPS=-T(1.e-200);
00146       unsigned int m=M, n=R.rows();
00147       if(m==0 || m > A.rows()) m=A.rows();
00148       unsigned int np1=n+1;         // if np1 = n, state vector Z is not updated
00149       unsigned int i,j,k;
00150       T dum, delta, beta;
00151    
00152       for(j=0; j<n; j++) {          // loop over columns
00153          T sum = T(0);
00154          for(i=0; i<m; i++)
00155             sum += A(i,j)*A(i,j);   // sum squares of elements in this column below d
00156          if(sum <= T(0)) continue;
00157    
00158          dum = R(j,j);
00159          sum += dum * dum;          // add diagonal element
00160          sum = (dum > T(0) ? -T(1) : T(1)) * ::sqrt(sum);
00161          delta = dum - sum;
00162          R(j,j) = sum;
00163    
00164          if(j+1 > np1) break;
00165    
00166          beta = sum*delta;          // beta must be negative
00167          if(beta > EPS) continue;
00168          beta = T(1)/beta;
00169    
00170          for(k=j+1; k<np1; k++) {   // columns to right of diagonal
00171             sum = delta * (k==n ? Z(j) : R(j,k));
00172             for(i=0; i<m; i++)
00173                sum += A(i,j) * A(i,k);
00174             if(sum == T(0)) continue;
00175    
00176             sum *= beta;
00177             if(k==n) Z(j) += sum*delta;
00178             else   R(j,k) += sum*delta;
00179    
00180             for(i=0; i<m; i++)
00181                A(i,k) += sum * A(i,j);
00182          }
00183       }
00184    }  // end SrifMU
00185     
00186 
00187    //---------------------------------------------------------------------------------
00188    // This is simply SrifMU(R,Z,A) with H and D passed in rather
00189    // than concatenated into a single Matrix A = H || D.
00190    
00207    template <class T>
00208    void SrifMU(Matrix<T>& R,
00209                Vector<T>& Z,
00210                const Matrix<T>& H,
00211                Vector<T>& D,
00212                unsigned int M=0)
00213       throw(MatrixException)
00214    {
00215       Matrix<double> A;
00216       try { A = H || D; }
00217       catch(MatrixException& me) { GPSTK_RETHROW(me); }
00218    
00219       SrifMU(R,Z,A,M);
00220    
00221          // copy residuals out of A into D
00222       D = Vector<double>(A.colCopy(A.cols()-1));
00223    }
00224    
00225 
00226    //---------------------------------------------------------------------------------
00227    // Compute Cholesky decomposition of symmetric positive definite matrix using Crout
00228    // algorithm. A = L*L^T where A and L are (nxn) and L is lower triangular reads:
00229    // [ A00 A01 A02 ... A0n ] = [ L00  0   0  0 ...  0 ][ L00 L10 L20 ... L0n ]
00230    // [ A10 A11 A12 ... A1n ] = [ L10 L11  0  0 ...  0 ][  0  L11 L21 ... L1n ]
00231    // [ A20 A21 A22 ... A2n ] = [ L20 L21 L22 0 ...  0 ][  0   0  L22 ... L2n ]
00232    //           ...                      ...                  ...
00233    // [ An0 An1 An2 ... Ann ] = [ Ln0 Ln1 Ln2 0 ... Lnn][  0   0   0  ... Lnn ]
00234    //   but multiplying out gives
00235    //          A              = [ L00^2
00236    //                           [ L00*L10  L11^2+L10^2
00237    //                           [ L00*L20  L11*L21+L10*L20 L22^2+L21^2+L20^2
00238    //                                 ...
00239    //    Aii = Lii^2 + sum(k=0,i-1) Lik^2
00240    //    Aij = Lij*Ljj + sum(k=0,j-1) Lik*Ljk
00241    // These can be inverted by looping over columns, and filling L from diagonal down.
00242    
00249    template <class T>
00250    Matrix<T> lowerCholesky(const Matrix<T>& A) throw(MatrixException)
00251    {
00252       if(A.rows() != A.cols() || A.rows() == 0) {
00253          MatrixException me("Invalid input dimensions: "
00254                + StringUtils::asString<int>(A.rows()) + "x"
00255                + StringUtils::asString<int>(A.cols()));
00256          GPSTK_THROW(me);
00257       }
00258    
00259       const unsigned int n=A.rows();
00260       unsigned int i,j,k;
00261       Matrix<T> L(n,n,0.0);
00262    
00263       for(j=0; j<n; j++) {             // loop over cols
00264          T d(A(j,j));
00265          for(k=0; k<j; k++) d -= L(j,k)*L(j,k);
00266          if(d <= 0.0) {
00267             MatrixException e("Non-positive eigenvalue; "
00268                "Cholesky requires positive-definite input");
00269             GPSTK_THROW(e);
00270          }
00271          L(j,j) = ::sqrt(d);
00272          for(i=j+1; i<n; i++) {        // loop over rows below diagonal
00273             d = A(i,j);
00274             for(k=0; k<j; k++) d -= L(i,k)*L(j,k);
00275             L(i,j) = d/L(j,j);
00276          }
00277       }
00278    
00279       return L;
00280    }
00281 
00282 
00283    //---------------------------------------------------------------------------------
00293    template <class T>
00294    Matrix<T> upperCholesky(const Matrix<T>& A) throw(MatrixException)
00295       { return transpose(lowerCholesky(A)); }
00296 
00297 
00298    //---------------------------------------------------------------------------------
00305    template <class T>
00306    Matrix<T> inverseCholesky(const Matrix<T>& A) throw(MatrixException)
00307    {
00308       try {
00309          Matrix<T> L(lowerCholesky(A));
00310          Matrix<T> Uinv(inverseUT(transpose(L)));
00311          Matrix<T> Ainv(UTtimesTranspose(Uinv));
00312          return Ainv;
00313       }
00314       catch(MatrixException& me) {
00315          me.addText("Called by inverseCholesky()");
00316          GPSTK_RETHROW(me);
00317       }
00318    }
00319 
00320 
00321    //---------------------------------------------------------------------------------
00322    // Invert the upper triangular matrix stored in the square matrix UT, using a very
00323    // efficient algorithm. Throw MatrixException if the matrix is singular.
00324    // If the pointers are defined, on exit (but not if an exception is thrown),
00325    // they return the smallest and largest eigenvalues of the matrix.
00326    
00335    template <class T>
00336    Matrix<T> inverseUT(const Matrix<T>& UT, T *ptrSmall=NULL, T *ptrBig=NULL)
00337       throw(MatrixException)
00338    {
00339       if(UT.rows() != UT.cols() || UT.rows() == 0) {
00340          MatrixException me("Invalid input dimensions: "
00341                + StringUtils::asString<int>(UT.rows()) + "x"
00342                + StringUtils::asString<int>(UT.cols()));
00343          GPSTK_THROW(me);
00344       }
00345    
00346       unsigned int i,j,k,n=UT.rows();
00347       T big(0),small(0),sum,dum;
00348       Matrix<T> Inv(UT);
00349    
00350          // start at the last row,col
00351       dum = UT(n-1,n-1);
00352       if(dum == T(0)) {
00353          SingularMatrixException e("Singular matrix");
00354          GPSTK_THROW(e);
00355       }
00356    
00357       big = small = fabs(dum);
00358       Inv(n-1,n-1) = T(1)/dum;
00359       if(n == 1) return Inv;                 // 1x1 matrix
00360       for(i=0; i<n-1; i++) Inv(n-1,i)=0;
00361    
00362          // now move to rows i = n-2 to 0
00363       for(i=n-2; i>=0; i--) {
00364          if(UT(i,i) == T(0))
00365             throw SingularMatrixException("Singular matrix");
00366    
00367          if(fabs(UT(i,i)) > big) big = fabs(UT(i,i));
00368          if(fabs(UT(i,i)) < small) small = fabs(UT(i,i));
00369          dum = T(1)/UT(i,i);
00370          Inv(i,i) = dum;                        // diagonal element first
00371    
00372             // now do off-diagonal elements (i,i+1) to (i,n-1)
00373          for(j=i+1; j<n; j++) {
00374             sum = T(0);
00375             for(k=i+1; k<=j; k++)
00376                sum += Inv(k,j) * UT(i,k);
00377             Inv(i,j) = - sum * dum;
00378          }
00379          for(j=0; j<i; j++) Inv(i,j)=0;
00380    
00381          if(i==0) break;         // NB i is unsigned, hence 0-1 = 4294967295!
00382       }
00383    
00384       if(ptrSmall) *ptrSmall=small;
00385       if(ptrBig) *ptrBig=big;
00386    
00387       return Inv;
00388    }
00389    
00390 
00391    //---------------------------------------------------------------------------------
00392    // Given an upper triangular matrix UT, compute the symmetric matrix
00393    // UT * transpose(UT) using a very efficient algorithm.
00398    template <class T>
00399    Matrix<T> UTtimesTranspose(const Matrix<T>& UT)
00400       throw(MatrixException)
00401    {
00402       const unsigned int n=UT.rows();
00403       if(n == 0 || UT.cols() != n) {
00404          MatrixException me("Invalid input dimensions: "
00405                + StringUtils::asString<int>(UT.rows()) + "x"
00406                + StringUtils::asString<int>(UT.cols()));
00407          GPSTK_THROW(me);
00408       }
00409    
00410       unsigned int i,j,k;
00411       T sum;
00412       Matrix<T> S(n,n);
00413    
00414       for(i=0; i<n-1; i++) {        // loop over rows of UT, except the last
00415          sum = T(0);                // diagonal element (i,i)
00416          for(j=i; j<n; j++)
00417             sum += UT(i,j)*UT(i,j);
00418          S(i,i) = sum;
00419          for(j=i+1; j<n; j++) {     // loop over columns to right of (i,i)
00420             sum = T(0);
00421             for(k=j; k<n; k++)
00422                sum += UT(i,k) * UT(j,k);
00423             S(i,j) = S(j,i) = sum;
00424          }
00425       }
00426       S(n-1,n-1) = UT(n-1,n-1)*UT(n-1,n-1);   // the last diagonal element
00427    
00428       return S;
00429    }
00430    
00431    
00432 } // end namespace gpstk
00433    
00434 #endif // SQUAREROOTINFORMATION_MATRICIES_INCLUDE

Generated on Tue May 22 03:31:02 2012 for GPS ToolKit Software Library by  doxygen 1.3.9.1