// This file is part of Eigen, a lightweight C++ template library // for linear algebra. // // Copyright (C) 2008 Gael Guennebaud // Copyright (C) 2009 Keir Mierle // Copyright (C) 2009 Benoit Jacob // // Eigen is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 3 of the License, or (at your option) any later version. // // Alternatively, you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation; either version 2 of // the License, or (at your option) any later version. // // Eigen is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS // FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License or the // GNU General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License and a copy of the GNU General Public License along with // Eigen. If not, see . #ifndef EIGEN_LDLT_H #define EIGEN_LDLT_H template struct LDLT_Traits; /** \ingroup cholesky_Module * * \class LDLT * * \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 * * 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 * is lower triangular with a unit diagonal and D is a diagonal matrix. * * The decomposition uses pivoting to ensure stability, so that L will have * zeros in the bottom right rank(A) - n submatrix. Avoiding the square root * on D also stabilizes the computation. * * Remember that Cholesky decompositions are not rank-revealing. Also, do not use a Cholesky * decomposition to determine whether a system of equations has a solution. * * \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: typedef _MatrixType MatrixType; enum { RowsAtCompileTime = MatrixType::RowsAtCompileTime, ColsAtCompileTime = MatrixType::ColsAtCompileTime, Options = MatrixType::Options, MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime, MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime, UpLo = _UpLo }; typedef typename MatrixType::Scalar Scalar; typedef typename NumTraits::Real RealScalar; typedef typename MatrixType::Index Index; typedef typename ei_plain_col_type::type IntColVectorType; typedef Matrix TmpMatrixType; typedef LDLT_Traits Traits; /** \brief Default Constructor. * * The default constructor is useful in cases in which the user intends to * perform decompositions via LDLT::compute(const MatrixType&). */ LDLT() : m_matrix(), m_p(), m_transpositions(), m_isInitialized(false) {} /** \brief Default Constructor with memory preallocation * * Like the default constructor but with preallocation of the internal data * according to the specified problem \a size. * \sa LDLT() */ LDLT(Index size) : m_matrix(size, size), m_p(size), m_transpositions(size), m_temporary(size), m_isInitialized(false) {} LDLT(const MatrixType& matrix) : m_matrix(matrix.rows(), matrix.cols()), m_p(matrix.rows()), m_transpositions(matrix.rows()), m_temporary(matrix.rows()), m_isInitialized(false) { compute(matrix); } /** \returns a view of the upper triangular matrix U */ inline typename Traits::MatrixU matrixU() const { ei_assert(m_isInitialized && "LDLT is not initialized."); return Traits::getU(m_matrix); } /** \returns a view of the lower triangular matrix L */ inline typename Traits::MatrixL matrixL() const { ei_assert(m_isInitialized && "LDLT is not initialized."); return Traits::getL(m_matrix); } /** \returns a vector of integers, whose size is the number of rows of the matrix being decomposed, * representing the P permutation i.e. the permutation of the rows. For its precise meaning, * see the examples given in the documentation of class FullPivLU. */ inline const IntColVectorType& permutationP() const { ei_assert(m_isInitialized && "LDLT is not initialized."); return m_p; } /** \returns the coefficients of the diagonal matrix D */ inline Diagonal vectorD(void) const { ei_assert(m_isInitialized && "LDLT is not initialized."); return m_matrix.diagonal(); } /** \returns true if the matrix is positive (semidefinite) */ inline bool isPositive(void) const { ei_assert(m_isInitialized && "LDLT is not initialized."); return m_sign == 1; } /** \returns true if the matrix is negative (semidefinite) */ inline bool isNegative(void) const { ei_assert(m_isInitialized && "LDLT is not initialized."); return m_sign == -1; } /** \returns a solution x of \f$ A x = b \f$ using the current decomposition of A. * * \note_about_checking_solutions * * \sa solveInPlace(), MatrixBase::ldlt() */ template inline const ei_solve_retval solve(const MatrixBase& b) const { ei_assert(m_isInitialized && "LDLT is not initialized."); ei_assert(m_matrix.rows()==b.rows() && "LDLT::solve(): invalid number of rows of the right hand side matrix b"); return ei_solve_retval(*this, b.derived()); } template bool solveInPlace(MatrixBase &bAndX) const; LDLT& compute(const MatrixType& matrix); /** \returns the internal LDLT decomposition matrix * * TODO: document the storage layout */ inline const MatrixType& matrixLDLT() const { ei_assert(m_isInitialized && "LDLT is not initialized."); return m_matrix; } MatrixType reconstructedMatrix() const; inline Index rows() const { return m_matrix.rows(); } inline Index cols() const { return m_matrix.cols(); } protected: /** \internal * Used to compute and store the Cholesky decomposition A = L D L^* = U^* D U. * The strict upper part is used during the decomposition, the strict lower * part correspond to the coefficients of L (its diagonal is equal to 1 and * is not stored), and the diagonal entries correspond to D. */ MatrixType m_matrix; IntColVectorType m_p; IntColVectorType m_transpositions; // FIXME do we really need to store permanently the transpositions? TmpMatrixType m_temporary; int m_sign; bool m_isInitialized; }; template struct ei_ldlt_inplace; template<> struct ei_ldlt_inplace { template static bool unblocked(MatrixType& mat, Transpositions& transpositions, Workspace& temp, int* sign=0) { typedef typename MatrixType::Scalar Scalar; typedef typename MatrixType::RealScalar RealScalar; typedef typename MatrixType::Index Index; ei_assert(mat.rows()==mat.cols()); const Index size = mat.rows(); if (size <= 1) { transpositions.setZero(); if(sign) *sign = ei_real(mat.coeff(0,0))>0 ? 1:-1; return true; } RealScalar cutoff = 0, biggest_in_corner; for (Index k = 0; k < size; ++k) { // Find largest diagonal element Index index_of_biggest_in_corner; biggest_in_corner = mat.diagonal().tail(size-k).cwiseAbs().maxCoeff(&index_of_biggest_in_corner); index_of_biggest_in_corner += k; if(k == 0) { // The biggest overall is the point of reference to which further diagonals // are compared; if any diagonal is negligible compared // to the largest overall, the algorithm bails. cutoff = ei_abs(NumTraits::epsilon() * biggest_in_corner); if(sign) *sign = ei_real(mat.diagonal().coeff(index_of_biggest_in_corner)) > 0 ? 1 : -1; } // Finish early if the matrix is not full rank. if(biggest_in_corner < cutoff) { for(Index i = k; i < size; i++) transpositions.coeffRef(i) = i; break; } transpositions.coeffRef(k) = index_of_biggest_in_corner; if(k != index_of_biggest_in_corner) { mat.row(k).swap(mat.row(index_of_biggest_in_corner)); mat.col(k).swap(mat.col(index_of_biggest_in_corner)); } Index rs = size - k - 1; Block A21(mat,k+1,k,rs,1); Block A10(mat,k,0,1,k); Block A20(mat,k+1,0,rs,k); if(k>0) { temp.head(k) = mat.diagonal().head(k).asDiagonal() * A10.adjoint(); mat.coeffRef(k,k) -= (A10 * temp.head(k)).value(); if(rs>0) A21.noalias() -= A20 * temp.head(k); } if((rs>0) && (ei_abs(mat.coeffRef(k,k)) > cutoff)) A21 /= mat.coeffRef(k,k); } return true; } }; template<> struct ei_ldlt_inplace { template static EIGEN_STRONG_INLINE bool unblocked(MatrixType& mat, Transpositions& transpositions, Workspace& temp, int* sign=0) { Transpose matt(mat); return ei_ldlt_inplace::unblocked(matt, transpositions, temp, sign); } }; template struct LDLT_Traits { typedef TriangularView MatrixL; typedef TriangularView MatrixU; inline static MatrixL getL(const MatrixType& m) { return m; } inline static MatrixU getU(const MatrixType& m) { return m.adjoint(); } }; template struct LDLT_Traits { typedef TriangularView MatrixL; typedef TriangularView MatrixU; inline static MatrixL getL(const MatrixType& m) { return m.adjoint(); } inline static MatrixU getU(const MatrixType& m) { return m; } }; /** Compute / recompute the LDLT decomposition A = L D L^* = U^* D U of \a matrix */ template LDLT& LDLT::compute(const MatrixType& a) { ei_assert(a.rows()==a.cols()); const Index size = a.rows(); m_matrix = a; m_p.resize(size); m_transpositions.resize(size); m_isInitialized = false; m_temporary.resize(size); ei_ldlt_inplace::unblocked(m_matrix, m_transpositions, m_temporary, &m_sign); if(size==0) m_p.setZero(); // Reverse applied swaps to get P matrix. for(Index k = 0; k < size; ++k) m_p.coeffRef(k) = k; for(Index k = size-1; k >= 0; --k) { std::swap(m_p.coeffRef(k), m_p.coeffRef(m_transpositions.coeff(k))); } m_isInitialized = true; return *this; } template struct ei_solve_retval, Rhs> : ei_solve_retval_base, Rhs> { typedef LDLT<_MatrixType,_UpLo> LDLTType; EIGEN_MAKE_SOLVE_HELPERS(LDLTType,Rhs) template void evalTo(Dest& dst) const { dst = rhs(); dec().solveInPlace(dst); } }; /** This is the \em in-place version of solve(). * * \param bAndX represents both the right-hand side matrix b and result x. * * \returns true always! If you need to check for existence of solutions, use another decomposition like LU, QR, or SVD. * * This version avoids a copy when the right hand side matrix b is not * needed anymore. * * \sa LDLT::solve(), MatrixBase::ldlt() */ template template bool LDLT::solveInPlace(MatrixBase &bAndX) const { ei_assert(m_isInitialized && "LDLT is not initialized."); const Index size = m_matrix.rows(); ei_assert(size == bAndX.rows()); // z = P b for(Index i = 0; i < size; ++i) bAndX.row(m_transpositions.coeff(i)).swap(bAndX.row(i)); // y = L^-1 z //matrixL().solveInPlace(bAndX); matrixL().solveInPlace(bAndX); // w = D^-1 y bAndX = m_matrix.diagonal().asDiagonal().inverse() * bAndX; // u = L^-T w matrixU().solveInPlace(bAndX); // x = P^T u for (Index i = size-1; i >= 0; --i) bAndX.row(m_transpositions.coeff(i)).swap(bAndX.row(i)); return true; } /** \returns the matrix represented by the decomposition, * i.e., it returns the product: P^T L D L^* P. * This function is provided for debug purpose. */ template MatrixType LDLT::reconstructedMatrix() const { ei_assert(m_isInitialized && "LDLT is not initialized."); const Index size = m_matrix.rows(); MatrixType res(size,size); res.setIdentity(); // PI for(Index i = 0; i < size; ++i) res.row(m_transpositions.coeff(i)).swap(res.row(i)); // L^* P res = matrixU() * res; // D(L^*P) res = vectorD().asDiagonal() * res; // L(DL^*P) res = matrixL() * res; // P^T (LDL^*P) for (Index i = size-1; i >= 0; --i) res.row(m_transpositions.coeff(i)).swap(res.row(i)); return res; } /** \cholesky_module * \returns the Cholesky decomposition with full pivoting without square root of \c *this */ template inline const LDLT::PlainObject, UpLo> SelfAdjointView::ldlt() const { return LDLT(m_matrix); } /** \cholesky_module * \returns the Cholesky decomposition with full pivoting without square root of \c *this */ template inline const LDLT::PlainObject> MatrixBase::ldlt() const { return LDLT(derived()); } #endif // EIGEN_LDLT_H