Home | History | Annotate | Download | only in CholmodSupport
      1 // This file is part of Eigen, a lightweight C++ template library
      2 // for linear algebra.
      3 //
      4 // Copyright (C) 2008-2010 Gael Guennebaud <gael.guennebaud (at) inria.fr>
      5 //
      6 // This Source Code Form is subject to the terms of the Mozilla
      7 // Public License v. 2.0. If a copy of the MPL was not distributed
      8 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
      9 
     10 #ifndef EIGEN_CHOLMODSUPPORT_H
     11 #define EIGEN_CHOLMODSUPPORT_H
     12 
     13 namespace Eigen {
     14 
     15 namespace internal {
     16 
     17 template<typename Scalar, typename CholmodType>
     18 void cholmod_configure_matrix(CholmodType& mat)
     19 {
     20   if (internal::is_same<Scalar,float>::value)
     21   {
     22     mat.xtype = CHOLMOD_REAL;
     23     mat.dtype = CHOLMOD_SINGLE;
     24   }
     25   else if (internal::is_same<Scalar,double>::value)
     26   {
     27     mat.xtype = CHOLMOD_REAL;
     28     mat.dtype = CHOLMOD_DOUBLE;
     29   }
     30   else if (internal::is_same<Scalar,std::complex<float> >::value)
     31   {
     32     mat.xtype = CHOLMOD_COMPLEX;
     33     mat.dtype = CHOLMOD_SINGLE;
     34   }
     35   else if (internal::is_same<Scalar,std::complex<double> >::value)
     36   {
     37     mat.xtype = CHOLMOD_COMPLEX;
     38     mat.dtype = CHOLMOD_DOUBLE;
     39   }
     40   else
     41   {
     42     eigen_assert(false && "Scalar type not supported by CHOLMOD");
     43   }
     44 }
     45 
     46 } // namespace internal
     47 
     48 /** Wraps the Eigen sparse matrix \a mat into a Cholmod sparse matrix object.
     49   * Note that the data are shared.
     50   */
     51 template<typename _Scalar, int _Options, typename _Index>
     52 cholmod_sparse viewAsCholmod(SparseMatrix<_Scalar,_Options,_Index>& mat)
     53 {
     54   typedef SparseMatrix<_Scalar,_Options,_Index> MatrixType;
     55   cholmod_sparse res;
     56   res.nzmax   = mat.nonZeros();
     57   res.nrow    = mat.rows();;
     58   res.ncol    = mat.cols();
     59   res.p       = mat.outerIndexPtr();
     60   res.i       = mat.innerIndexPtr();
     61   res.x       = mat.valuePtr();
     62   res.sorted  = 1;
     63   if(mat.isCompressed())
     64   {
     65     res.packed  = 1;
     66   }
     67   else
     68   {
     69     res.packed  = 0;
     70     res.nz = mat.innerNonZeroPtr();
     71   }
     72 
     73   res.dtype   = 0;
     74   res.stype   = -1;
     75 
     76   if (internal::is_same<_Index,int>::value)
     77   {
     78     res.itype = CHOLMOD_INT;
     79   }
     80   else
     81   {
     82     eigen_assert(false && "Index type different than int is not supported yet");
     83   }
     84 
     85   // setup res.xtype
     86   internal::cholmod_configure_matrix<_Scalar>(res);
     87 
     88   res.stype = 0;
     89 
     90   return res;
     91 }
     92 
     93 template<typename _Scalar, int _Options, typename _Index>
     94 const cholmod_sparse viewAsCholmod(const SparseMatrix<_Scalar,_Options,_Index>& mat)
     95 {
     96   cholmod_sparse res = viewAsCholmod(mat.const_cast_derived());
     97   return res;
     98 }
     99 
    100 /** Returns a view of the Eigen sparse matrix \a mat as Cholmod sparse matrix.
    101   * The data are not copied but shared. */
    102 template<typename _Scalar, int _Options, typename _Index, unsigned int UpLo>
    103 cholmod_sparse viewAsCholmod(const SparseSelfAdjointView<SparseMatrix<_Scalar,_Options,_Index>, UpLo>& mat)
    104 {
    105   cholmod_sparse res = viewAsCholmod(mat.matrix().const_cast_derived());
    106 
    107   if(UpLo==Upper) res.stype =  1;
    108   if(UpLo==Lower) res.stype = -1;
    109 
    110   return res;
    111 }
    112 
    113 /** Returns a view of the Eigen \b dense matrix \a mat as Cholmod dense matrix.
    114   * The data are not copied but shared. */
    115 template<typename Derived>
    116 cholmod_dense viewAsCholmod(MatrixBase<Derived>& mat)
    117 {
    118   EIGEN_STATIC_ASSERT((internal::traits<Derived>::Flags&RowMajorBit)==0,THIS_METHOD_IS_ONLY_FOR_COLUMN_MAJOR_MATRICES);
    119   typedef typename Derived::Scalar Scalar;
    120 
    121   cholmod_dense res;
    122   res.nrow   = mat.rows();
    123   res.ncol   = mat.cols();
    124   res.nzmax  = res.nrow * res.ncol;
    125   res.d      = Derived::IsVectorAtCompileTime ? mat.derived().size() : mat.derived().outerStride();
    126   res.x      = mat.derived().data();
    127   res.z      = 0;
    128 
    129   internal::cholmod_configure_matrix<Scalar>(res);
    130 
    131   return res;
    132 }
    133 
    134 /** Returns a view of the Cholmod sparse matrix \a cm as an Eigen sparse matrix.
    135   * The data are not copied but shared. */
    136 template<typename Scalar, int Flags, typename Index>
    137 MappedSparseMatrix<Scalar,Flags,Index> viewAsEigen(cholmod_sparse& cm)
    138 {
    139   return MappedSparseMatrix<Scalar,Flags,Index>
    140          (cm.nrow, cm.ncol, reinterpret_cast<Index*>(cm.p)[cm.ncol],
    141           reinterpret_cast<Index*>(cm.p), reinterpret_cast<Index*>(cm.i),reinterpret_cast<Scalar*>(cm.x) );
    142 }
    143 
    144 enum CholmodMode {
    145   CholmodAuto, CholmodSimplicialLLt, CholmodSupernodalLLt, CholmodLDLt
    146 };
    147 
    148 
    149 /** \ingroup CholmodSupport_Module
    150   * \class CholmodBase
    151   * \brief The base class for the direct Cholesky factorization of Cholmod
    152   * \sa class CholmodSupernodalLLT, class CholmodSimplicialLDLT, class CholmodSimplicialLLT
    153   */
    154 template<typename _MatrixType, int _UpLo, typename Derived>
    155 class CholmodBase : internal::noncopyable
    156 {
    157   public:
    158     typedef _MatrixType MatrixType;
    159     enum { UpLo = _UpLo };
    160     typedef typename MatrixType::Scalar Scalar;
    161     typedef typename MatrixType::RealScalar RealScalar;
    162     typedef MatrixType CholMatrixType;
    163     typedef typename MatrixType::Index Index;
    164 
    165   public:
    166 
    167     CholmodBase()
    168       : m_cholmodFactor(0), m_info(Success), m_isInitialized(false)
    169     {
    170       cholmod_start(&m_cholmod);
    171     }
    172 
    173     CholmodBase(const MatrixType& matrix)
    174       : m_cholmodFactor(0), m_info(Success), m_isInitialized(false)
    175     {
    176       cholmod_start(&m_cholmod);
    177       compute(matrix);
    178     }
    179 
    180     ~CholmodBase()
    181     {
    182       if(m_cholmodFactor)
    183         cholmod_free_factor(&m_cholmodFactor, &m_cholmod);
    184       cholmod_finish(&m_cholmod);
    185     }
    186 
    187     inline Index cols() const { return m_cholmodFactor->n; }
    188     inline Index rows() const { return m_cholmodFactor->n; }
    189 
    190     Derived& derived() { return *static_cast<Derived*>(this); }
    191     const Derived& derived() const { return *static_cast<const Derived*>(this); }
    192 
    193     /** \brief Reports whether previous computation was successful.
    194       *
    195       * \returns \c Success if computation was succesful,
    196       *          \c NumericalIssue if the matrix.appears to be negative.
    197       */
    198     ComputationInfo info() const
    199     {
    200       eigen_assert(m_isInitialized && "Decomposition is not initialized.");
    201       return m_info;
    202     }
    203 
    204     /** Computes the sparse Cholesky decomposition of \a matrix */
    205     Derived& compute(const MatrixType& matrix)
    206     {
    207       analyzePattern(matrix);
    208       factorize(matrix);
    209       return derived();
    210     }
    211 
    212     /** \returns the solution x of \f$ A x = b \f$ using the current decomposition of A.
    213       *
    214       * \sa compute()
    215       */
    216     template<typename Rhs>
    217     inline const internal::solve_retval<CholmodBase, Rhs>
    218     solve(const MatrixBase<Rhs>& b) const
    219     {
    220       eigen_assert(m_isInitialized && "LLT is not initialized.");
    221       eigen_assert(rows()==b.rows()
    222                 && "CholmodDecomposition::solve(): invalid number of rows of the right hand side matrix b");
    223       return internal::solve_retval<CholmodBase, Rhs>(*this, b.derived());
    224     }
    225 
    226     /** \returns the solution x of \f$ A x = b \f$ using the current decomposition of A.
    227       *
    228       * \sa compute()
    229       */
    230     template<typename Rhs>
    231     inline const internal::sparse_solve_retval<CholmodBase, Rhs>
    232     solve(const SparseMatrixBase<Rhs>& b) const
    233     {
    234       eigen_assert(m_isInitialized && "LLT is not initialized.");
    235       eigen_assert(rows()==b.rows()
    236                 && "CholmodDecomposition::solve(): invalid number of rows of the right hand side matrix b");
    237       return internal::sparse_solve_retval<CholmodBase, Rhs>(*this, b.derived());
    238     }
    239 
    240     /** Performs a symbolic decomposition on the sparcity of \a matrix.
    241       *
    242       * This function is particularly useful when solving for several problems having the same structure.
    243       *
    244       * \sa factorize()
    245       */
    246     void analyzePattern(const MatrixType& matrix)
    247     {
    248       if(m_cholmodFactor)
    249       {
    250         cholmod_free_factor(&m_cholmodFactor, &m_cholmod);
    251         m_cholmodFactor = 0;
    252       }
    253       cholmod_sparse A = viewAsCholmod(matrix.template selfadjointView<UpLo>());
    254       m_cholmodFactor = cholmod_analyze(&A, &m_cholmod);
    255 
    256       this->m_isInitialized = true;
    257       this->m_info = Success;
    258       m_analysisIsOk = true;
    259       m_factorizationIsOk = false;
    260     }
    261 
    262     /** Performs a numeric decomposition of \a matrix
    263       *
    264       * The given matrix must has the same sparcity than the matrix on which the symbolic decomposition has been performed.
    265       *
    266       * \sa analyzePattern()
    267       */
    268     void factorize(const MatrixType& matrix)
    269     {
    270       eigen_assert(m_analysisIsOk && "You must first call analyzePattern()");
    271       cholmod_sparse A = viewAsCholmod(matrix.template selfadjointView<UpLo>());
    272       cholmod_factorize(&A, m_cholmodFactor, &m_cholmod);
    273 
    274       this->m_info = Success;
    275       m_factorizationIsOk = true;
    276     }
    277 
    278     /** Returns a reference to the Cholmod's configuration structure to get a full control over the performed operations.
    279      *  See the Cholmod user guide for details. */
    280     cholmod_common& cholmod() { return m_cholmod; }
    281 
    282     #ifndef EIGEN_PARSED_BY_DOXYGEN
    283     /** \internal */
    284     template<typename Rhs,typename Dest>
    285     void _solve(const MatrixBase<Rhs> &b, MatrixBase<Dest> &dest) const
    286     {
    287       eigen_assert(m_factorizationIsOk && "The decomposition is not in a valid state for solving, you must first call either compute() or symbolic()/numeric()");
    288       const Index size = m_cholmodFactor->n;
    289       eigen_assert(size==b.rows());
    290 
    291       // note: cd stands for Cholmod Dense
    292       cholmod_dense b_cd = viewAsCholmod(b.const_cast_derived());
    293       cholmod_dense* x_cd = cholmod_solve(CHOLMOD_A, m_cholmodFactor, &b_cd, &m_cholmod);
    294       if(!x_cd)
    295       {
    296         this->m_info = NumericalIssue;
    297       }
    298       // TODO optimize this copy by swapping when possible (be carreful with alignment, etc.)
    299       dest = Matrix<Scalar,Dest::RowsAtCompileTime,Dest::ColsAtCompileTime>::Map(reinterpret_cast<Scalar*>(x_cd->x),b.rows(),b.cols());
    300       cholmod_free_dense(&x_cd, &m_cholmod);
    301     }
    302 
    303     /** \internal */
    304     template<typename RhsScalar, int RhsOptions, typename RhsIndex, typename DestScalar, int DestOptions, typename DestIndex>
    305     void _solve(const SparseMatrix<RhsScalar,RhsOptions,RhsIndex> &b, SparseMatrix<DestScalar,DestOptions,DestIndex> &dest) const
    306     {
    307       eigen_assert(m_factorizationIsOk && "The decomposition is not in a valid state for solving, you must first call either compute() or symbolic()/numeric()");
    308       const Index size = m_cholmodFactor->n;
    309       eigen_assert(size==b.rows());
    310 
    311       // note: cs stands for Cholmod Sparse
    312       cholmod_sparse b_cs = viewAsCholmod(b);
    313       cholmod_sparse* x_cs = cholmod_spsolve(CHOLMOD_A, m_cholmodFactor, &b_cs, &m_cholmod);
    314       if(!x_cs)
    315       {
    316         this->m_info = NumericalIssue;
    317       }
    318       // TODO optimize this copy by swapping when possible (be carreful with alignment, etc.)
    319       dest = viewAsEigen<DestScalar,DestOptions,DestIndex>(*x_cs);
    320       cholmod_free_sparse(&x_cs, &m_cholmod);
    321     }
    322     #endif // EIGEN_PARSED_BY_DOXYGEN
    323 
    324     template<typename Stream>
    325     void dumpMemory(Stream& s)
    326     {}
    327 
    328   protected:
    329     mutable cholmod_common m_cholmod;
    330     cholmod_factor* m_cholmodFactor;
    331     mutable ComputationInfo m_info;
    332     bool m_isInitialized;
    333     int m_factorizationIsOk;
    334     int m_analysisIsOk;
    335 };
    336 
    337 /** \ingroup CholmodSupport_Module
    338   * \class CholmodSimplicialLLT
    339   * \brief A simplicial direct Cholesky (LLT) factorization and solver based on Cholmod
    340   *
    341   * This class allows to solve for A.X = B sparse linear problems via a simplicial LL^T Cholesky factorization
    342   * using the Cholmod library.
    343   * This simplicial variant is equivalent to Eigen's built-in SimplicialLLT class. Thefore, it has little practical interest.
    344   * The sparse matrix A must be selfajoint and positive definite. The vectors or matrices
    345   * X and B can be either dense or sparse.
    346   *
    347   * \tparam _MatrixType the type of the sparse matrix A, it must be a SparseMatrix<>
    348   * \tparam _UpLo the triangular part that will be used for the computations. It can be Lower
    349   *               or Upper. Default is Lower.
    350   *
    351   * This class supports all kind of SparseMatrix<>: row or column major; upper, lower, or both; compressed or non compressed.
    352   *
    353   * \sa \ref TutorialSparseDirectSolvers, class CholmodSupernodalLLT, class SimplicialLLT
    354   */
    355 template<typename _MatrixType, int _UpLo = Lower>
    356 class CholmodSimplicialLLT : public CholmodBase<_MatrixType, _UpLo, CholmodSimplicialLLT<_MatrixType, _UpLo> >
    357 {
    358     typedef CholmodBase<_MatrixType, _UpLo, CholmodSimplicialLLT> Base;
    359     using Base::m_cholmod;
    360 
    361   public:
    362 
    363     typedef _MatrixType MatrixType;
    364 
    365     CholmodSimplicialLLT() : Base() { init(); }
    366 
    367     CholmodSimplicialLLT(const MatrixType& matrix) : Base()
    368     {
    369       init();
    370       compute(matrix);
    371     }
    372 
    373     ~CholmodSimplicialLLT() {}
    374   protected:
    375     void init()
    376     {
    377       m_cholmod.final_asis = 0;
    378       m_cholmod.supernodal = CHOLMOD_SIMPLICIAL;
    379       m_cholmod.final_ll = 1;
    380     }
    381 };
    382 
    383 
    384 /** \ingroup CholmodSupport_Module
    385   * \class CholmodSimplicialLDLT
    386   * \brief A simplicial direct Cholesky (LDLT) factorization and solver based on Cholmod
    387   *
    388   * This class allows to solve for A.X = B sparse linear problems via a simplicial LDL^T Cholesky factorization
    389   * using the Cholmod library.
    390   * This simplicial variant is equivalent to Eigen's built-in SimplicialLDLT class. Thefore, it has little practical interest.
    391   * The sparse matrix A must be selfajoint and positive definite. The vectors or matrices
    392   * X and B can be either dense or sparse.
    393   *
    394   * \tparam _MatrixType the type of the sparse matrix A, it must be a SparseMatrix<>
    395   * \tparam _UpLo the triangular part that will be used for the computations. It can be Lower
    396   *               or Upper. Default is Lower.
    397   *
    398   * This class supports all kind of SparseMatrix<>: row or column major; upper, lower, or both; compressed or non compressed.
    399   *
    400   * \sa \ref TutorialSparseDirectSolvers, class CholmodSupernodalLLT, class SimplicialLDLT
    401   */
    402 template<typename _MatrixType, int _UpLo = Lower>
    403 class CholmodSimplicialLDLT : public CholmodBase<_MatrixType, _UpLo, CholmodSimplicialLDLT<_MatrixType, _UpLo> >
    404 {
    405     typedef CholmodBase<_MatrixType, _UpLo, CholmodSimplicialLDLT> Base;
    406     using Base::m_cholmod;
    407 
    408   public:
    409 
    410     typedef _MatrixType MatrixType;
    411 
    412     CholmodSimplicialLDLT() : Base() { init(); }
    413 
    414     CholmodSimplicialLDLT(const MatrixType& matrix) : Base()
    415     {
    416       init();
    417       compute(matrix);
    418     }
    419 
    420     ~CholmodSimplicialLDLT() {}
    421   protected:
    422     void init()
    423     {
    424       m_cholmod.final_asis = 1;
    425       m_cholmod.supernodal = CHOLMOD_SIMPLICIAL;
    426     }
    427 };
    428 
    429 /** \ingroup CholmodSupport_Module
    430   * \class CholmodSupernodalLLT
    431   * \brief A supernodal Cholesky (LLT) factorization and solver based on Cholmod
    432   *
    433   * This class allows to solve for A.X = B sparse linear problems via a supernodal LL^T Cholesky factorization
    434   * using the Cholmod library.
    435   * This supernodal variant performs best on dense enough problems, e.g., 3D FEM, or very high order 2D FEM.
    436   * The sparse matrix A must be selfajoint and positive definite. The vectors or matrices
    437   * X and B can be either dense or sparse.
    438   *
    439   * \tparam _MatrixType the type of the sparse matrix A, it must be a SparseMatrix<>
    440   * \tparam _UpLo the triangular part that will be used for the computations. It can be Lower
    441   *               or Upper. Default is Lower.
    442   *
    443   * This class supports all kind of SparseMatrix<>: row or column major; upper, lower, or both; compressed or non compressed.
    444   *
    445   * \sa \ref TutorialSparseDirectSolvers
    446   */
    447 template<typename _MatrixType, int _UpLo = Lower>
    448 class CholmodSupernodalLLT : public CholmodBase<_MatrixType, _UpLo, CholmodSupernodalLLT<_MatrixType, _UpLo> >
    449 {
    450     typedef CholmodBase<_MatrixType, _UpLo, CholmodSupernodalLLT> Base;
    451     using Base::m_cholmod;
    452 
    453   public:
    454 
    455     typedef _MatrixType MatrixType;
    456 
    457     CholmodSupernodalLLT() : Base() { init(); }
    458 
    459     CholmodSupernodalLLT(const MatrixType& matrix) : Base()
    460     {
    461       init();
    462       compute(matrix);
    463     }
    464 
    465     ~CholmodSupernodalLLT() {}
    466   protected:
    467     void init()
    468     {
    469       m_cholmod.final_asis = 1;
    470       m_cholmod.supernodal = CHOLMOD_SUPERNODAL;
    471     }
    472 };
    473 
    474 /** \ingroup CholmodSupport_Module
    475   * \class CholmodDecomposition
    476   * \brief A general Cholesky factorization and solver based on Cholmod
    477   *
    478   * This class allows to solve for A.X = B sparse linear problems via a LL^T or LDL^T Cholesky factorization
    479   * using the Cholmod library. The sparse matrix A must be selfajoint and positive definite. The vectors or matrices
    480   * X and B can be either dense or sparse.
    481   *
    482   * This variant permits to change the underlying Cholesky method at runtime.
    483   * On the other hand, it does not provide access to the result of the factorization.
    484   * The default is to let Cholmod automatically choose between a simplicial and supernodal factorization.
    485   *
    486   * \tparam _MatrixType the type of the sparse matrix A, it must be a SparseMatrix<>
    487   * \tparam _UpLo the triangular part that will be used for the computations. It can be Lower
    488   *               or Upper. Default is Lower.
    489   *
    490   * This class supports all kind of SparseMatrix<>: row or column major; upper, lower, or both; compressed or non compressed.
    491   *
    492   * \sa \ref TutorialSparseDirectSolvers
    493   */
    494 template<typename _MatrixType, int _UpLo = Lower>
    495 class CholmodDecomposition : public CholmodBase<_MatrixType, _UpLo, CholmodDecomposition<_MatrixType, _UpLo> >
    496 {
    497     typedef CholmodBase<_MatrixType, _UpLo, CholmodDecomposition> Base;
    498     using Base::m_cholmod;
    499 
    500   public:
    501 
    502     typedef _MatrixType MatrixType;
    503 
    504     CholmodDecomposition() : Base() { init(); }
    505 
    506     CholmodDecomposition(const MatrixType& matrix) : Base()
    507     {
    508       init();
    509       compute(matrix);
    510     }
    511 
    512     ~CholmodDecomposition() {}
    513 
    514     void setMode(CholmodMode mode)
    515     {
    516       switch(mode)
    517       {
    518         case CholmodAuto:
    519           m_cholmod.final_asis = 1;
    520           m_cholmod.supernodal = CHOLMOD_AUTO;
    521           break;
    522         case CholmodSimplicialLLt:
    523           m_cholmod.final_asis = 0;
    524           m_cholmod.supernodal = CHOLMOD_SIMPLICIAL;
    525           m_cholmod.final_ll = 1;
    526           break;
    527         case CholmodSupernodalLLt:
    528           m_cholmod.final_asis = 1;
    529           m_cholmod.supernodal = CHOLMOD_SUPERNODAL;
    530           break;
    531         case CholmodLDLt:
    532           m_cholmod.final_asis = 1;
    533           m_cholmod.supernodal = CHOLMOD_SIMPLICIAL;
    534           break;
    535         default:
    536           break;
    537       }
    538     }
    539   protected:
    540     void init()
    541     {
    542       m_cholmod.final_asis = 1;
    543       m_cholmod.supernodal = CHOLMOD_AUTO;
    544     }
    545 };
    546 
    547 namespace internal {
    548 
    549 template<typename _MatrixType, int _UpLo, typename Derived, typename Rhs>
    550 struct solve_retval<CholmodBase<_MatrixType,_UpLo,Derived>, Rhs>
    551   : solve_retval_base<CholmodBase<_MatrixType,_UpLo,Derived>, Rhs>
    552 {
    553   typedef CholmodBase<_MatrixType,_UpLo,Derived> Dec;
    554   EIGEN_MAKE_SOLVE_HELPERS(Dec,Rhs)
    555 
    556   template<typename Dest> void evalTo(Dest& dst) const
    557   {
    558     dec()._solve(rhs(),dst);
    559   }
    560 };
    561 
    562 template<typename _MatrixType, int _UpLo, typename Derived, typename Rhs>
    563 struct sparse_solve_retval<CholmodBase<_MatrixType,_UpLo,Derived>, Rhs>
    564   : sparse_solve_retval_base<CholmodBase<_MatrixType,_UpLo,Derived>, Rhs>
    565 {
    566   typedef CholmodBase<_MatrixType,_UpLo,Derived> Dec;
    567   EIGEN_MAKE_SPARSE_SOLVE_HELPERS(Dec,Rhs)
    568 
    569   template<typename Dest> void evalTo(Dest& dst) const
    570   {
    571     dec()._solve(rhs(),dst);
    572   }
    573 };
    574 
    575 } // end namespace internal
    576 
    577 } // end namespace Eigen
    578 
    579 #endif // EIGEN_CHOLMODSUPPORT_H
    580