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