diff --git a/Eigen/src/Cholesky/LDLT.h b/Eigen/src/Cholesky/LDLT.h index 3f0e63b68..6a2d2994b 100644 --- a/Eigen/src/Cholesky/LDLT.h +++ b/Eigen/src/Cholesky/LDLT.h @@ -39,6 +39,8 @@ template struct LDLT_Traits; * \brief Robust Cholesky decomposition of a matrix with pivoting * * \param MatrixType the type of the matrix of which to compute the LDL^T Cholesky decomposition + * \param UpLo the triangular part that will be used for the decompositon: Lower (default) or Upper. + * The other triangular part won't be read. * * Perform a robust Cholesky decomposition of a positive semidefinite or negative semidefinite * matrix \f$ A \f$ such that \f$ A = P^TLDL^*P \f$, where P is a permutation matrix, L @@ -53,10 +55,6 @@ template struct LDLT_Traits; * * \sa MatrixBase::ldlt(), class LLT */ - /* THIS PART OF THE DOX IS CURRENTLY DISABLED BECAUSE INACCURATE BECAUSE OF BUG IN THE DECOMPOSITION CODE - * Note that during the decomposition, only the upper triangular part of A is considered. Therefore, - * the strict lower part does not have to store correct values. - */ template class LDLT { public: @@ -228,6 +226,17 @@ template class LDLT inline Index rows() const { return m_matrix.rows(); } inline Index cols() const { return m_matrix.cols(); } + /** \brief Reports whether previous computation was successful. + * + * \returns \c Success if computation was succesful, + * \c NumericalIssue if the matrix.appears to be negative. + */ + ComputationInfo info() const + { + eigen_assert(m_isInitialized && "LDLT is not initialized."); + return Success; + } + protected: /** \internal diff --git a/Eigen/src/Cholesky/LLT.h b/Eigen/src/Cholesky/LLT.h index 0c7093375..2140f3d5c 100644 --- a/Eigen/src/Cholesky/LLT.h +++ b/Eigen/src/Cholesky/LLT.h @@ -36,6 +36,8 @@ template struct LLT_Traits; * \brief Standard Cholesky decomposition (LL^T) of a matrix and associated features * * \param MatrixType the type of the matrix of which we are computing the LL^T Cholesky decomposition + * \param UpLo the triangular part that will be used for the decompositon: Lower (default) or Upper. + * The other triangular part won't be read. * * This class performs a LL^T Cholesky decomposition of a symmetric, positive definite * matrix A such that A = LL^* = U^*U, where L is lower triangular. @@ -182,7 +184,7 @@ template class LLT inline Index cols() const { return m_matrix.cols(); } template - void rankUpdate(const VectorType& vec); + LLT& rankUpdate(const VectorType& vec, const RealScalar& sigma = 1); protected: /** \internal @@ -200,11 +202,11 @@ template struct llt_inplace; template struct llt_inplace { + typedef typename NumTraits::Real RealScalar; template static typename MatrixType::Index unblocked(MatrixType& mat) { typedef typename MatrixType::Index Index; - typedef typename MatrixType::RealScalar RealScalar; eigen_assert(mat.rows()==mat.cols()); const Index size = mat.rows(); @@ -261,8 +263,9 @@ template struct llt_inplace } template - static void rankUpdate(MatrixType& mat, const VectorType& vec) + static typename MatrixType::Index rankUpdate(MatrixType& mat, const VectorType& vec, const RealScalar& sigma) { + typedef typename MatrixType::Index Index; typedef typename MatrixType::ColXpr ColXpr; typedef typename internal::remove_all::type ColXprCleaned; typedef typename ColXprCleaned::SegmentReturnType ColXprSegment; @@ -271,26 +274,67 @@ template struct llt_inplace int n = mat.cols(); eigen_assert(mat.rows()==n && vec.size()==n); - TempVectorType temp(vec); - for(int i=0; i0) { - JacobiRotation g; - g.makeGivens(mat(i,i), -temp(i), &mat(i,i)); + // This version is based on Givens rotations. + // It is faster than the other one below, but only works for updates, + // i.e., for sigma > 0 + temp = sqrt(sigma) * vec; - int rs = n-i-1; - if(rs>0) + for(int i=0; i g; + g.makeGivens(mat(i,i), -temp(i), &mat(i,i)); + + int rs = n-i-1; + if(rs>0) + { + ColXprSegment x(mat.col(i).tail(rs)); + TempVecSegment y(temp.tail(rs)); + apply_rotation_in_the_plane(x, y, g); + } } } + else + { + temp = vec; + RealScalar beta = 1; + for(int j=0; j struct llt_inplace { + typedef typename NumTraits::Real RealScalar; + template static EIGEN_STRONG_INLINE typename MatrixType::Index unblocked(MatrixType& mat) { @@ -304,10 +348,10 @@ template struct llt_inplace return llt_inplace::blocked(matt); } template - static void rankUpdate(MatrixType& mat, const VectorType& vec) + static typename MatrixType::Index rankUpdate(MatrixType& mat, const VectorType& vec, const RealScalar& sigma) { Transpose matt(mat); - return llt_inplace::rankUpdate(matt, vec.conjugate()); + return llt_inplace::rankUpdate(matt, vec.conjugate(), sigma); } }; @@ -343,7 +387,7 @@ template struct LLT_Traits template LLT& LLT::compute(const MatrixType& a) { - assert(a.rows()==a.cols()); + eigen_assert(a.rows()==a.cols()); const Index size = a.rows(); m_matrix.resize(size, size); m_matrix = a; @@ -355,18 +399,24 @@ LLT& LLT::compute(const MatrixType& a) return *this; } -/** Performs a rank one update of the current decomposition. +/** Performs a rank one update (or dowdate) of the current decomposition. * If A = LL^* before the rank one update, - * then after it we have LL^* = A + vv^* where \a v must be a vector + * then after it we have LL^* = A + sigma * v v^* where \a v must be a vector * of same dimension. - * */ template template -void LLT::rankUpdate(const VectorType& v) +LLT& LLT::rankUpdate(const VectorType& v, const RealScalar& sigma) { EIGEN_STATIC_ASSERT_VECTOR_ONLY(VectorType); - internal::llt_inplace::rankUpdate(m_matrix,v); + eigen_assert(v.size()==m_matrix.cols()); + eigen_assert(m_isInitialized); + if(internal::llt_inplace::rankUpdate(m_matrix,v,sigma)>=0) + m_info = NumericalIssue; + else + m_info = Success; + + return *this; } namespace internal { diff --git a/test/cholesky.cpp b/test/cholesky.cpp index d9806e5c3..1a1b2eeb5 100644 --- a/test/cholesky.cpp +++ b/test/cholesky.cpp @@ -41,6 +41,38 @@ static int nb_temporaries; VERIFY( (#XPR) && nb_temporaries==N ); \ } +template class CholType> void test_chol_update(const MatrixType& symm) +{ + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; + typedef Matrix VectorType; + + MatrixType symmLo = symm.template triangularView(); + MatrixType symmUp = symm.template triangularView(); + MatrixType symmCpy = symm; + + CholType chollo(symmLo); + CholType cholup(symmUp); + + for (int k=0; k<10; ++k) + { + VectorType vec = VectorType::Random(symm.rows()); + RealScalar sigma = internal::random(); + symmCpy += sigma * vec * vec.adjoint(); + + // we are doing some downdates, so it might be the case that the matrix is not SPD anymore + CholType chol(symmCpy); + if(chol.info()!=Success) + break; + + chollo.rankUpdate(vec, sigma); + VERIFY_IS_APPROX(symmCpy, chollo.reconstructedMatrix()); + + cholup.rankUpdate(vec, sigma); + VERIFY_IS_APPROX(symmCpy, cholup.reconstructedMatrix()); + } +} + template void cholesky(const MatrixType& m) { typedef typename MatrixType::Index Index; @@ -155,41 +187,9 @@ template void cholesky(const MatrixType& m) m2.noalias() -= symmLo.template selfadjointView().llt().solve(matB); VERIFY_IS_APPROX(m2, m1 - symmLo.template selfadjointView().llt().solve(matB)); - // LLT update/downdate - { - MatrixType symmLo = symm.template triangularView(); - MatrixType symmUp = symm.template triangularView(); - - VectorType vec = VectorType::Random(rows); - - MatrixType symmCpy = symm + vec * vec.adjoint(); - - LLT chollo(symmLo); - chollo.rankUpdate(vec); - VERIFY_IS_APPROX(symmCpy, chollo.reconstructedMatrix()); - - LLT cholup(symmUp); - cholup.rankUpdate(vec); - VERIFY_IS_APPROX(symmCpy, cholup.reconstructedMatrix()); - } - - // LDLT update/downdate - { - MatrixType symmLo = symm.template triangularView(); - MatrixType symmUp = symm.template triangularView(); - - VectorType vec = VectorType::Random(rows); - - MatrixType symmCpy = symm + vec * vec.adjoint(); - - LDLT chollo(symmLo); - chollo.rankUpdate(vec); - VERIFY_IS_APPROX(symmCpy, chollo.reconstructedMatrix()); - - LDLT cholup(symmUp); - cholup.rankUpdate(vec); - VERIFY_IS_APPROX(symmCpy, cholup.reconstructedMatrix()); - } + // update/downdate + CALL_SUBTEST(( test_chol_update(symm) )); + CALL_SUBTEST(( test_chol_update(symm) )); } template void cholesky_cplx(const MatrixType& m)