00001 // This is core/vnl/algo/vnl_ldl_cholesky.h 00002 #ifndef vnl_ldl_cholesky_h_ 00003 #define vnl_ldl_cholesky_h_ 00004 #ifdef VCL_NEEDS_PRAGMA_INTERFACE 00005 #pragma interface 00006 #endif 00007 //: 00008 // \file 00009 // \brief Updateable Cholesky decomposition: A=LDL' 00010 // \author Tim Cootes 00011 // \date 29 Mar 2006 00012 00013 #include <vnl/vnl_vector.h> 00014 #include <vnl/vnl_matrix.h> 00015 00016 //: Updateable Cholesky decomposition: A=LDL' 00017 // A class to hold the Cholesky decomposition of a positive definite 00018 // symmetric matrix, using the form A=LDL', where L is lower triangular 00019 // with ones on the leading diagonal, and D is a diagonal matrix. 00020 // This differs from vnl_cholesky, which decomposes as A=LL', without 00021 // a constraint on the diagonal. The advantage of the LDL' form is that 00022 // it can be efficiently updated. Thus given the decomposition of A, 00023 // we can compute the decomposition of (A+v.v') in O(n^2) time. 00024 // 00025 // This can be used to solve linear systems, compute determinants and inverses. 00026 // 00027 // To check that the decomposition can be used safely for solving a linear 00028 // equation it is wise to construct with mode==estimate_condition and 00029 // check that rcond()>sqrt(machine precision). If this is not the case 00030 // it might be a good idea to use vnl_svd instead. 00031 class vnl_ldl_cholesky 00032 { 00033 public: 00034 //: Modes of computation. See constructor for details. 00035 enum Operation { 00036 quiet, 00037 verbose, 00038 estimate_condition 00039 }; 00040 00041 //: Make cholesky decomposition of M optionally computing the reciprocal condition number. 00042 vnl_ldl_cholesky(vnl_matrix<double> const& M, Operation mode = verbose); 00043 ~vnl_ldl_cholesky() {} 00044 00045 //: Solve LS problem M x = b 00046 vnl_vector<double> solve(vnl_vector<double> const& b) const; 00047 00048 //: Solve LS problem M x = b 00049 void solve(vnl_vector<double> const& b, vnl_vector<double>* x) const; 00050 00051 //: Solve equation of form Lx=y (in-place) 00052 // x is overwritten with solution 00053 void solve_lx(vnl_vector<double>& y); 00054 00055 //: Compute determinant 00056 double determinant() const; 00057 00058 //: Compute rank-1 update, ie the decomposition of (M+v.v') 00059 // If the initial state is the decomposition of M, then 00060 // L and D are updated so that on exit LDL'=M+v.v' 00061 void rank1_update(const vnl_vector<double>& v); 00062 00063 //: Multi-rank update, ie the decomposition of (M+W.W') 00064 // If the initial state is the decomposition of M, then 00065 // L and D are updated so that on exit LDL'=M+W.W' 00066 void update(const vnl_matrix<double>& W); 00067 00068 //: Compute inverse. Not efficient. 00069 // Note that you rarely need the inverse - backsubstitution 00070 // is faster and less prone to rounding errors. 00071 vnl_matrix<double> inverse() const; 00072 00073 //: Return lower-triangular factor. 00074 const vnl_matrix<double>& lower_triangle() const { return L_; } 00075 00076 //: Return upper-triangular factor. 00077 vnl_matrix<double> upper_triangle() const { return L_.transpose(); } 00078 00079 //: Return elements of diagonal matrix D in LDL' 00080 const vnl_vector<double>& diagonal() const { return d_; } 00081 00082 //: Efficient computation of x' * inv(M) * x 00083 // Useful when M is a covariance matrix! 00084 double xt_m_inv_x(const vnl_vector<double>& x) const; 00085 00086 //: Efficient computation of x' * M * x 00087 // Twice as fast as explicitly computing x' * M * x 00088 double xt_m_x(const vnl_vector<double>& x) const; 00089 00090 //: A Success/failure flag 00091 int rank_deficiency() const { return num_dims_rank_def_; } 00092 00093 //: Return reciprocal condition number (smallest/largest singular values). 00094 // As long as rcond()>sqrt(precision) the decomposition can be used for 00095 // solving equations safely. 00096 // Not calculated unless Operation mode at construction was estimate_condition. 00097 double rcond() const { return rcond_; } 00098 00099 //: Return computed nullvector. 00100 // Not calculated unless Operation mode at construction was estimate_condition. 00101 vnl_vector<double> & nullvector() { return nullvector_; } 00102 vnl_vector<double> const& nullvector() const { return nullvector_; } 00103 00104 protected: 00105 // Data Members-------------------------------------------------------------- 00106 00107 //: Lower triangular matrix 00108 vnl_matrix<double> L_; 00109 00110 //: Elements of diagonal matrix 00111 vnl_vector<double> d_; 00112 00113 //: 1/(condition number) 00114 double rcond_; 00115 long num_dims_rank_def_; 00116 vnl_vector<double> nullvector_; 00117 00118 private: 00119 //: Copy constructor - privatised to avoid it being used 00120 vnl_ldl_cholesky(vnl_ldl_cholesky const & that); 00121 //: Assignment operator - privatised to avoid it being used 00122 vnl_ldl_cholesky& operator=(vnl_ldl_cholesky const & that); 00123 00124 //: Solve Mx=b, overwriting input vector with the solution. 00125 // x points to beginning of an n-element vector containing b 00126 // On exit, x[i] filled with solution vector. 00127 void inplace_solve(double* x) const; 00128 }; 00129 00130 #endif // vnl_ldl_cholesky_h_