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
1.3.9.1