// This file is part of Eigen, a lightweight C++ template library // for linear algebra. // // Copyright (C) 2008-2009 Gael Guennebaud // // 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_CHOLMODSUPPORT_LEGACY_H #define EIGEN_CHOLMODSUPPORT_LEGACY_H namespace internal { template void cholmod_configure_matrix_legacy(CholmodType& mat) { if (internal::is_same::value) { mat.xtype = CHOLMOD_REAL; mat.dtype = CHOLMOD_SINGLE; } else if (internal::is_same::value) { mat.xtype = CHOLMOD_REAL; mat.dtype = CHOLMOD_DOUBLE; } else if (internal::is_same >::value) { mat.xtype = CHOLMOD_COMPLEX; mat.dtype = CHOLMOD_SINGLE; } else if (internal::is_same >::value) { mat.xtype = CHOLMOD_COMPLEX; mat.dtype = CHOLMOD_DOUBLE; } else { eigen_assert(false && "Scalar type not supported by CHOLMOD"); } } template cholmod_sparse cholmod_map_eigen_to_sparse(_MatrixType& mat) { typedef typename _MatrixType::Scalar Scalar; cholmod_sparse res; res.nzmax = mat.nonZeros(); res.nrow = mat.rows();; res.ncol = mat.cols(); res.p = mat._outerIndexPtr(); res.i = mat._innerIndexPtr(); res.x = mat._valuePtr(); res.xtype = CHOLMOD_REAL; res.itype = CHOLMOD_INT; res.sorted = 1; res.packed = 1; res.dtype = 0; res.stype = -1; internal::cholmod_configure_matrix_legacy(res); if (_MatrixType::Flags & SelfAdjoint) { if (_MatrixType::Flags & Upper) res.stype = 1; else if (_MatrixType::Flags & Lower) res.stype = -1; else res.stype = 0; } else res.stype = -1; // by default we consider the lower part return res; } template cholmod_dense cholmod_map_eigen_to_dense(MatrixBase& mat) { EIGEN_STATIC_ASSERT((internal::traits::Flags&RowMajorBit)==0,THIS_METHOD_IS_ONLY_FOR_COLUMN_MAJOR_MATRICES); typedef typename Derived::Scalar Scalar; cholmod_dense res; res.nrow = mat.rows(); res.ncol = mat.cols(); res.nzmax = res.nrow * res.ncol; res.d = Derived::IsVectorAtCompileTime ? mat.derived().size() : mat.derived().outerStride(); res.x = mat.derived().data(); res.z = 0; internal::cholmod_configure_matrix_legacy(res); return res; } template MappedSparseMatrix map_cholmod_sparse_to_eigen(cholmod_sparse& cm) { return MappedSparseMatrix (cm.nrow, cm.ncol, reinterpret_cast(cm.p)[cm.ncol], reinterpret_cast(cm.p), reinterpret_cast(cm.i),reinterpret_cast(cm.x) ); } } // namespace internal template class SparseLLT<_MatrixType, Cholmod> : public SparseLLT<_MatrixType> { protected: typedef SparseLLT<_MatrixType> Base; typedef typename Base::Scalar Scalar; typedef typename Base::RealScalar RealScalar; typedef typename Base::CholMatrixType CholMatrixType; using Base::MatrixLIsDirty; using Base::SupernodalFactorIsDirty; using Base::m_flags; using Base::m_matrix; using Base::m_status; public: typedef _MatrixType MatrixType; typedef typename MatrixType::Index Index; SparseLLT(int flags = 0) : Base(flags), m_cholmodFactor(0) { cholmod_start(&m_cholmod); } SparseLLT(const MatrixType& matrix, int flags = 0) : Base(flags), m_cholmodFactor(0) { cholmod_start(&m_cholmod); compute(matrix); } ~SparseLLT() { if (m_cholmodFactor) cholmod_free_factor(&m_cholmodFactor, &m_cholmod); cholmod_finish(&m_cholmod); } inline const CholMatrixType& matrixL() const; template bool solveInPlace(MatrixBase &b) const; template inline const internal::solve_retval, Rhs> solve(const MatrixBase& b) const { eigen_assert(true && "SparseLLT is not initialized."); return internal::solve_retval, Rhs>(*this, b.derived()); } void compute(const MatrixType& matrix); inline Index cols() const { return m_matrix.cols(); } inline Index rows() const { return m_matrix.rows(); } inline const cholmod_factor* cholmodFactor() const { return m_cholmodFactor; } inline cholmod_common* cholmodCommon() const { return &m_cholmod; } bool succeeded() const; protected: mutable cholmod_common m_cholmod; cholmod_factor* m_cholmodFactor; }; namespace internal { template struct solve_retval, Rhs> : solve_retval_base, Rhs> { typedef SparseLLT<_MatrixType, Cholmod> SpLLTDecType; EIGEN_MAKE_SOLVE_HELPERS(SpLLTDecType,Rhs) template void evalTo(Dest& dst) const { //Index size = dec().cholmodFactor()->n; eigen_assert((Index)dec().cholmodFactor()->n==rhs().rows()); cholmod_factor* cholmodFactor = const_cast(dec().cholmodFactor()); cholmod_common* cholmodCommon = const_cast(dec().cholmodCommon()); // this uses Eigen's triangular sparse solver // if (m_status & MatrixLIsDirty) // matrixL(); // Base::solveInPlace(b); // as long as our own triangular sparse solver is not fully optimal, // let's use CHOLMOD's one: cholmod_dense cdb = internal::cholmod_map_eigen_to_dense(rhs().const_cast_derived()); cholmod_dense* x = cholmod_solve(CHOLMOD_A, cholmodFactor, &cdb, cholmodCommon); dst = Matrix::Map(reinterpret_cast(x->x), rhs().rows()); cholmod_free_dense(&x, cholmodCommon); } }; } // namespace internal template void SparseLLT<_MatrixType,Cholmod>::compute(const _MatrixType& a) { if (m_cholmodFactor) { cholmod_free_factor(&m_cholmodFactor, &m_cholmod); m_cholmodFactor = 0; } cholmod_sparse A = internal::cholmod_map_eigen_to_sparse(const_cast<_MatrixType&>(a)); // m_cholmod.supernodal = CHOLMOD_AUTO; // TODO // if (m_flags&IncompleteFactorization) // { // m_cholmod.nmethods = 1; // m_cholmod.method[0].ordering = CHOLMOD_NATURAL; // m_cholmod.postorder = 0; // } // else // { // m_cholmod.nmethods = 1; // m_cholmod.method[0].ordering = CHOLMOD_NATURAL; // m_cholmod.postorder = 0; // } // m_cholmod.final_ll = 1; m_cholmodFactor = cholmod_analyze(&A, &m_cholmod); cholmod_factorize(&A, m_cholmodFactor, &m_cholmod); m_status = (m_status & ~SupernodalFactorIsDirty) | MatrixLIsDirty; } // TODO template bool SparseLLT<_MatrixType,Cholmod>::succeeded() const { return true; } template inline const typename SparseLLT<_MatrixType,Cholmod>::CholMatrixType& SparseLLT<_MatrixType,Cholmod>::matrixL() const { if (m_status & MatrixLIsDirty) { eigen_assert(!(m_status & SupernodalFactorIsDirty)); cholmod_sparse* cmRes = cholmod_factor_to_sparse(m_cholmodFactor, &m_cholmod); const_cast(m_matrix) = internal::map_cholmod_sparse_to_eigen(*cmRes); free(cmRes); m_status = (m_status & ~MatrixLIsDirty); } return m_matrix; } template template bool SparseLLT<_MatrixType,Cholmod>::solveInPlace(MatrixBase &b) const { //Index size = m_cholmodFactor->n; eigen_assert((Index)m_cholmodFactor->n==b.rows()); // this uses Eigen's triangular sparse solver // if (m_status & MatrixLIsDirty) // matrixL(); // Base::solveInPlace(b); // as long as our own triangular sparse solver is not fully optimal, // let's use CHOLMOD's one: cholmod_dense cdb = internal::cholmod_map_eigen_to_dense(b); cholmod_dense* x = cholmod_solve(CHOLMOD_A, m_cholmodFactor, &cdb, &m_cholmod); eigen_assert(x && "Eigen: cholmod_solve failed."); b = Matrix::Map(reinterpret_cast(x->x),b.rows()); cholmod_free_dense(&x, &m_cholmod); return true; } template class SparseLDLT<_MatrixType,Cholmod> : public SparseLDLT<_MatrixType> { protected: typedef SparseLDLT<_MatrixType> Base; typedef typename Base::Scalar Scalar; typedef typename Base::RealScalar RealScalar; using Base::MatrixLIsDirty; using Base::SupernodalFactorIsDirty; using Base::m_flags; using Base::m_matrix; using Base::m_status; public: typedef _MatrixType MatrixType; typedef typename MatrixType::Index Index; SparseLDLT(int flags = 0) : Base(flags), m_cholmodFactor(0) { cholmod_start(&m_cholmod); } SparseLDLT(const _MatrixType& matrix, int flags = 0) : Base(flags), m_cholmodFactor(0) { cholmod_start(&m_cholmod); compute(matrix); } ~SparseLDLT() { if (m_cholmodFactor) cholmod_free_factor(&m_cholmodFactor, &m_cholmod); cholmod_finish(&m_cholmod); } inline const typename Base::CholMatrixType& matrixL(void) const; template void solveInPlace(MatrixBase &b) const; template inline const internal::solve_retval, Rhs> solve(const MatrixBase& b) const { eigen_assert(true && "SparseLDLT is not initialized."); return internal::solve_retval, Rhs>(*this, b.derived()); } void compute(const _MatrixType& matrix); inline Index cols() const { return m_matrix.cols(); } inline Index rows() const { return m_matrix.rows(); } inline const cholmod_factor* cholmodFactor() const { return m_cholmodFactor; } inline cholmod_common* cholmodCommon() const { return &m_cholmod; } bool succeeded() const; protected: mutable cholmod_common m_cholmod; cholmod_factor* m_cholmodFactor; }; namespace internal { template struct solve_retval, Rhs> : solve_retval_base, Rhs> { typedef SparseLDLT<_MatrixType, Cholmod> SpLDLTDecType; EIGEN_MAKE_SOLVE_HELPERS(SpLDLTDecType,Rhs) template void evalTo(Dest& dst) const { //Index size = dec().cholmodFactor()->n; eigen_assert((Index)dec().cholmodFactor()->n==rhs().rows()); cholmod_factor* cholmodFactor = const_cast(dec().cholmodFactor()); cholmod_common* cholmodCommon = const_cast(dec().cholmodCommon()); // this uses Eigen's triangular sparse solver // if (m_status & MatrixLIsDirty) // matrixL(); // Base::solveInPlace(b); // as long as our own triangular sparse solver is not fully optimal, // let's use CHOLMOD's one: cholmod_dense cdb = internal::cholmod_map_eigen_to_dense(rhs().const_cast_derived()); cholmod_dense* x = cholmod_solve(CHOLMOD_LDLt, cholmodFactor, &cdb, cholmodCommon); dst = Matrix::Map(reinterpret_cast(x->x), rhs().rows()); cholmod_free_dense(&x, cholmodCommon); } }; } // namespace internal template void SparseLDLT<_MatrixType,Cholmod>::compute(const _MatrixType& a) { if (m_cholmodFactor) { cholmod_free_factor(&m_cholmodFactor, &m_cholmod); m_cholmodFactor = 0; } cholmod_sparse A = internal::cholmod_map_eigen_to_sparse(const_cast<_MatrixType&>(a)); //m_cholmod.supernodal = CHOLMOD_AUTO; m_cholmod.supernodal = CHOLMOD_SIMPLICIAL; //m_cholmod.supernodal = CHOLMOD_SUPERNODAL; // TODO if (m_flags & IncompleteFactorization) { m_cholmod.nmethods = 1; //m_cholmod.method[0].ordering = CHOLMOD_NATURAL; m_cholmod.method[0].ordering = CHOLMOD_COLAMD; m_cholmod.postorder = 1; } else { m_cholmod.nmethods = 1; m_cholmod.method[0].ordering = CHOLMOD_NATURAL; m_cholmod.postorder = 0; } m_cholmod.final_ll = 0; m_cholmodFactor = cholmod_analyze(&A, &m_cholmod); cholmod_factorize(&A, m_cholmodFactor, &m_cholmod); m_status = (m_status & ~SupernodalFactorIsDirty) | MatrixLIsDirty; } // TODO template bool SparseLDLT<_MatrixType,Cholmod>::succeeded() const { return true; } template inline const typename SparseLDLT<_MatrixType>::CholMatrixType& SparseLDLT<_MatrixType,Cholmod>::matrixL() const { if (m_status & MatrixLIsDirty) { eigen_assert(!(m_status & SupernodalFactorIsDirty)); cholmod_sparse* cmRes = cholmod_factor_to_sparse(m_cholmodFactor, &m_cholmod); const_cast(m_matrix) = MappedSparseMatrix(*cmRes); free(cmRes); m_status = (m_status & ~MatrixLIsDirty); } return m_matrix; } template template void SparseLDLT<_MatrixType,Cholmod>::solveInPlace(MatrixBase &b) const { //Index size = m_cholmodFactor->n; eigen_assert((Index)m_cholmodFactor->n == b.rows()); // this uses Eigen's triangular sparse solver // if (m_status & MatrixLIsDirty) // matrixL(); // Base::solveInPlace(b); // as long as our own triangular sparse solver is not fully optimal, // let's use CHOLMOD's one: cholmod_dense cdb = internal::cholmod_map_eigen_to_dense(b); cholmod_dense* x = cholmod_solve(CHOLMOD_A, m_cholmodFactor, &cdb, &m_cholmod); b = Matrix::Map(reinterpret_cast(x->x),b.rows()); cholmod_free_dense(&x, &m_cholmod); } #endif // EIGEN_CHOLMODSUPPORT_LEGACY_H