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 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 98 template<typename _Scalar, int _Options, typename _Index> 99 const 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. */ 107 template<typename _Scalar, int _Options, typename _Index, unsigned int UpLo> 108 cholmod_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. */ 120 template<typename Derived> 121 cholmod_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. */ 141 template<typename Scalar, int Flags, typename Index> 142 MappedSparseMatrix<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 149 enum 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 */ 159 template<typename _MatrixType, int _UpLo, typename Derived> 160 class 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 */ 383 template<typename _MatrixType, int _UpLo = Lower> 384 class 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 */ 430 template<typename _MatrixType, int _UpLo = Lower> 431 class 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 */ 475 template<typename _MatrixType, int _UpLo = Lower> 476 class 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 */ 522 template<typename _MatrixType, int _UpLo = Lower> 523 class 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 575 namespace internal { 576 577 template<typename _MatrixType, int _UpLo, typename Derived, typename Rhs> 578 struct 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 590 template<typename _MatrixType, int _UpLo, typename Derived, typename Rhs> 591 struct 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