1 /* 2 Copyright (c) 2011, Intel Corporation. All rights reserved. 3 4 Redistribution and use in source and binary forms, with or without modification, 5 are permitted provided that the following conditions are met: 6 7 * Redistributions of source code must retain the above copyright notice, this 8 list of conditions and the following disclaimer. 9 * Redistributions in binary form must reproduce the above copyright notice, 10 this list of conditions and the following disclaimer in the documentation 11 and/or other materials provided with the distribution. 12 * Neither the name of Intel Corporation nor the names of its contributors may 13 be used to endorse or promote products derived from this software without 14 specific prior written permission. 15 16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 23 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 27 ******************************************************************************** 28 * Content : Eigen bindings to Intel(R) MKL PARDISO 29 ******************************************************************************** 30 */ 31 32 #ifndef EIGEN_PARDISOSUPPORT_H 33 #define EIGEN_PARDISOSUPPORT_H 34 35 namespace Eigen { 36 37 template<typename _MatrixType> class PardisoLU; 38 template<typename _MatrixType, int Options=Upper> class PardisoLLT; 39 template<typename _MatrixType, int Options=Upper> class PardisoLDLT; 40 41 namespace internal 42 { 43 template<typename Index> 44 struct pardiso_run_selector 45 { runpardiso_run_selector46 static Index run( _MKL_DSS_HANDLE_t pt, Index maxfct, Index mnum, Index type, Index phase, Index n, void *a, 47 Index *ia, Index *ja, Index *perm, Index nrhs, Index *iparm, Index msglvl, void *b, void *x) 48 { 49 Index error = 0; 50 ::pardiso(pt, &maxfct, &mnum, &type, &phase, &n, a, ia, ja, perm, &nrhs, iparm, &msglvl, b, x, &error); 51 return error; 52 } 53 }; 54 template<> 55 struct pardiso_run_selector<long long int> 56 { 57 typedef long long int Index; 58 static Index run( _MKL_DSS_HANDLE_t pt, Index maxfct, Index mnum, Index type, Index phase, Index n, void *a, 59 Index *ia, Index *ja, Index *perm, Index nrhs, Index *iparm, Index msglvl, void *b, void *x) 60 { 61 Index error = 0; 62 ::pardiso_64(pt, &maxfct, &mnum, &type, &phase, &n, a, ia, ja, perm, &nrhs, iparm, &msglvl, b, x, &error); 63 return error; 64 } 65 }; 66 67 template<class Pardiso> struct pardiso_traits; 68 69 template<typename _MatrixType> 70 struct pardiso_traits< PardisoLU<_MatrixType> > 71 { 72 typedef _MatrixType MatrixType; 73 typedef typename _MatrixType::Scalar Scalar; 74 typedef typename _MatrixType::RealScalar RealScalar; 75 typedef typename _MatrixType::Index Index; 76 }; 77 78 template<typename _MatrixType, int Options> 79 struct pardiso_traits< PardisoLLT<_MatrixType, Options> > 80 { 81 typedef _MatrixType MatrixType; 82 typedef typename _MatrixType::Scalar Scalar; 83 typedef typename _MatrixType::RealScalar RealScalar; 84 typedef typename _MatrixType::Index Index; 85 }; 86 87 template<typename _MatrixType, int Options> 88 struct pardiso_traits< PardisoLDLT<_MatrixType, Options> > 89 { 90 typedef _MatrixType MatrixType; 91 typedef typename _MatrixType::Scalar Scalar; 92 typedef typename _MatrixType::RealScalar RealScalar; 93 typedef typename _MatrixType::Index Index; 94 }; 95 96 } 97 98 template<class Derived> 99 class PardisoImpl 100 { 101 typedef internal::pardiso_traits<Derived> Traits; 102 public: 103 typedef typename Traits::MatrixType MatrixType; 104 typedef typename Traits::Scalar Scalar; 105 typedef typename Traits::RealScalar RealScalar; 106 typedef typename Traits::Index Index; 107 typedef SparseMatrix<Scalar,RowMajor,Index> SparseMatrixType; 108 typedef Matrix<Scalar,Dynamic,1> VectorType; 109 typedef Matrix<Index, 1, MatrixType::ColsAtCompileTime> IntRowVectorType; 110 typedef Matrix<Index, MatrixType::RowsAtCompileTime, 1> IntColVectorType; 111 typedef Array<Index,64,1,DontAlign> ParameterType; 112 enum { 113 ScalarIsComplex = NumTraits<Scalar>::IsComplex 114 }; 115 116 PardisoImpl() 117 { 118 eigen_assert((sizeof(Index) >= sizeof(_INTEGER_t) && sizeof(Index) <= 8) && "Non-supported index type"); 119 m_iparm.setZero(); 120 m_msglvl = 0; // No output 121 m_initialized = false; 122 } 123 124 ~PardisoImpl() 125 { 126 pardisoRelease(); 127 } 128 129 inline Index cols() const { return m_size; } 130 inline Index rows() const { return m_size; } 131 132 /** \brief Reports whether previous computation was successful. 133 * 134 * \returns \c Success if computation was succesful, 135 * \c NumericalIssue if the matrix appears to be negative. 136 */ 137 ComputationInfo info() const 138 { 139 eigen_assert(m_initialized && "Decomposition is not initialized."); 140 return m_info; 141 } 142 143 /** \warning for advanced usage only. 144 * \returns a reference to the parameter array controlling PARDISO. 145 * See the PARDISO manual to know how to use it. */ 146 ParameterType& pardisoParameterArray() 147 { 148 return m_iparm; 149 } 150 151 /** Performs a symbolic decomposition on the sparcity of \a matrix. 152 * 153 * This function is particularly useful when solving for several problems having the same structure. 154 * 155 * \sa factorize() 156 */ 157 Derived& analyzePattern(const MatrixType& matrix); 158 159 /** Performs a numeric decomposition of \a matrix 160 * 161 * The given matrix must has the same sparcity than the matrix on which the symbolic decomposition has been performed. 162 * 163 * \sa analyzePattern() 164 */ 165 Derived& factorize(const MatrixType& matrix); 166 167 Derived& compute(const MatrixType& matrix); 168 169 /** \returns the solution x of \f$ A x = b \f$ using the current decomposition of A. 170 * 171 * \sa compute() 172 */ 173 template<typename Rhs> 174 inline const internal::solve_retval<PardisoImpl, Rhs> 175 solve(const MatrixBase<Rhs>& b) const 176 { 177 eigen_assert(m_initialized && "Pardiso solver is not initialized."); 178 eigen_assert(rows()==b.rows() 179 && "PardisoImpl::solve(): invalid number of rows of the right hand side matrix b"); 180 return internal::solve_retval<PardisoImpl, Rhs>(*this, b.derived()); 181 } 182 183 /** \returns the solution x of \f$ A x = b \f$ using the current decomposition of A. 184 * 185 * \sa compute() 186 */ 187 template<typename Rhs> 188 inline const internal::sparse_solve_retval<PardisoImpl, Rhs> 189 solve(const SparseMatrixBase<Rhs>& b) const 190 { 191 eigen_assert(m_initialized && "Pardiso solver is not initialized."); 192 eigen_assert(rows()==b.rows() 193 && "PardisoImpl::solve(): invalid number of rows of the right hand side matrix b"); 194 return internal::sparse_solve_retval<PardisoImpl, Rhs>(*this, b.derived()); 195 } 196 197 Derived& derived() 198 { 199 return *static_cast<Derived*>(this); 200 } 201 const Derived& derived() const 202 { 203 return *static_cast<const Derived*>(this); 204 } 205 206 template<typename BDerived, typename XDerived> 207 bool _solve(const MatrixBase<BDerived> &b, MatrixBase<XDerived>& x) const; 208 209 protected: 210 void pardisoRelease() 211 { 212 if(m_initialized) // Factorization ran at least once 213 { 214 internal::pardiso_run_selector<Index>::run(m_pt, 1, 1, m_type, -1, m_size, 0, 0, 0, m_perm.data(), 0, 215 m_iparm.data(), m_msglvl, 0, 0); 216 } 217 } 218 219 void pardisoInit(int type) 220 { 221 m_type = type; 222 bool symmetric = std::abs(m_type) < 10; 223 m_iparm[0] = 1; // No solver default 224 m_iparm[1] = 3; // use Metis for the ordering 225 m_iparm[2] = 1; // Numbers of processors, value of OMP_NUM_THREADS 226 m_iparm[3] = 0; // No iterative-direct algorithm 227 m_iparm[4] = 0; // No user fill-in reducing permutation 228 m_iparm[5] = 0; // Write solution into x 229 m_iparm[6] = 0; // Not in use 230 m_iparm[7] = 2; // Max numbers of iterative refinement steps 231 m_iparm[8] = 0; // Not in use 232 m_iparm[9] = 13; // Perturb the pivot elements with 1E-13 233 m_iparm[10] = symmetric ? 0 : 1; // Use nonsymmetric permutation and scaling MPS 234 m_iparm[11] = 0; // Not in use 235 m_iparm[12] = symmetric ? 0 : 1; // Maximum weighted matching algorithm is switched-off (default for symmetric). 236 // Try m_iparm[12] = 1 in case of inappropriate accuracy 237 m_iparm[13] = 0; // Output: Number of perturbed pivots 238 m_iparm[14] = 0; // Not in use 239 m_iparm[15] = 0; // Not in use 240 m_iparm[16] = 0; // Not in use 241 m_iparm[17] = -1; // Output: Number of nonzeros in the factor LU 242 m_iparm[18] = -1; // Output: Mflops for LU factorization 243 m_iparm[19] = 0; // Output: Numbers of CG Iterations 244 245 m_iparm[20] = 0; // 1x1 pivoting 246 m_iparm[26] = 0; // No matrix checker 247 m_iparm[27] = (sizeof(RealScalar) == 4) ? 1 : 0; 248 m_iparm[34] = 1; // C indexing 249 m_iparm[59] = 1; // Automatic switch between In-Core and Out-of-Core modes 250 } 251 252 protected: 253 // cached data to reduce reallocation, etc. 254 255 void manageErrorCode(Index error) 256 { 257 switch(error) 258 { 259 case 0: 260 m_info = Success; 261 break; 262 case -4: 263 case -7: 264 m_info = NumericalIssue; 265 break; 266 default: 267 m_info = InvalidInput; 268 } 269 } 270 271 mutable SparseMatrixType m_matrix; 272 ComputationInfo m_info; 273 bool m_initialized, m_analysisIsOk, m_factorizationIsOk; 274 Index m_type, m_msglvl; 275 mutable void *m_pt[64]; 276 mutable ParameterType m_iparm; 277 mutable IntColVectorType m_perm; 278 Index m_size; 279 280 private: 281 PardisoImpl(PardisoImpl &) {} 282 }; 283 284 template<class Derived> 285 Derived& PardisoImpl<Derived>::compute(const MatrixType& a) 286 { 287 m_size = a.rows(); 288 eigen_assert(a.rows() == a.cols()); 289 290 pardisoRelease(); 291 memset(m_pt, 0, sizeof(m_pt)); 292 m_perm.setZero(m_size); 293 derived().getMatrix(a); 294 295 Index error; 296 error = internal::pardiso_run_selector<Index>::run(m_pt, 1, 1, m_type, 12, m_size, 297 m_matrix.valuePtr(), m_matrix.outerIndexPtr(), m_matrix.innerIndexPtr(), 298 m_perm.data(), 0, m_iparm.data(), m_msglvl, NULL, NULL); 299 300 manageErrorCode(error); 301 m_analysisIsOk = true; 302 m_factorizationIsOk = true; 303 m_initialized = true; 304 return derived(); 305 } 306 307 template<class Derived> 308 Derived& PardisoImpl<Derived>::analyzePattern(const MatrixType& a) 309 { 310 m_size = a.rows(); 311 eigen_assert(m_size == a.cols()); 312 313 pardisoRelease(); 314 memset(m_pt, 0, sizeof(m_pt)); 315 m_perm.setZero(m_size); 316 derived().getMatrix(a); 317 318 Index error; 319 error = internal::pardiso_run_selector<Index>::run(m_pt, 1, 1, m_type, 11, m_size, 320 m_matrix.valuePtr(), m_matrix.outerIndexPtr(), m_matrix.innerIndexPtr(), 321 m_perm.data(), 0, m_iparm.data(), m_msglvl, NULL, NULL); 322 323 manageErrorCode(error); 324 m_analysisIsOk = true; 325 m_factorizationIsOk = false; 326 m_initialized = true; 327 return derived(); 328 } 329 330 template<class Derived> 331 Derived& PardisoImpl<Derived>::factorize(const MatrixType& a) 332 { 333 eigen_assert(m_analysisIsOk && "You must first call analyzePattern()"); 334 eigen_assert(m_size == a.rows() && m_size == a.cols()); 335 336 derived().getMatrix(a); 337 338 Index error; 339 error = internal::pardiso_run_selector<Index>::run(m_pt, 1, 1, m_type, 22, m_size, 340 m_matrix.valuePtr(), m_matrix.outerIndexPtr(), m_matrix.innerIndexPtr(), 341 m_perm.data(), 0, m_iparm.data(), m_msglvl, NULL, NULL); 342 343 manageErrorCode(error); 344 m_factorizationIsOk = true; 345 return derived(); 346 } 347 348 template<class Base> 349 template<typename BDerived,typename XDerived> 350 bool PardisoImpl<Base>::_solve(const MatrixBase<BDerived> &b, MatrixBase<XDerived>& x) const 351 { 352 if(m_iparm[0] == 0) // Factorization was not computed 353 return false; 354 355 //Index n = m_matrix.rows(); 356 Index nrhs = Index(b.cols()); 357 eigen_assert(m_size==b.rows()); 358 eigen_assert(((MatrixBase<BDerived>::Flags & RowMajorBit) == 0 || nrhs == 1) && "Row-major right hand sides are not supported"); 359 eigen_assert(((MatrixBase<XDerived>::Flags & RowMajorBit) == 0 || nrhs == 1) && "Row-major matrices of unknowns are not supported"); 360 eigen_assert(((nrhs == 1) || b.outerStride() == b.rows())); 361 362 363 // switch (transposed) { 364 // case SvNoTrans : m_iparm[11] = 0 ; break; 365 // case SvTranspose : m_iparm[11] = 2 ; break; 366 // case SvAdjoint : m_iparm[11] = 1 ; break; 367 // default: 368 // //std::cerr << "Eigen: transposition option \"" << transposed << "\" not supported by the PARDISO backend\n"; 369 // m_iparm[11] = 0; 370 // } 371 372 Scalar* rhs_ptr = const_cast<Scalar*>(b.derived().data()); 373 Matrix<Scalar,Dynamic,Dynamic,ColMajor> tmp; 374 375 // Pardiso cannot solve in-place 376 if(rhs_ptr == x.derived().data()) 377 { 378 tmp = b; 379 rhs_ptr = tmp.data(); 380 } 381 382 Index error; 383 error = internal::pardiso_run_selector<Index>::run(m_pt, 1, 1, m_type, 33, m_size, 384 m_matrix.valuePtr(), m_matrix.outerIndexPtr(), m_matrix.innerIndexPtr(), 385 m_perm.data(), nrhs, m_iparm.data(), m_msglvl, 386 rhs_ptr, x.derived().data()); 387 388 return error==0; 389 } 390 391 392 /** \ingroup PardisoSupport_Module 393 * \class PardisoLU 394 * \brief A sparse direct LU factorization and solver based on the PARDISO library 395 * 396 * This class allows to solve for A.X = B sparse linear problems via a direct LU factorization 397 * using the Intel MKL PARDISO library. The sparse matrix A must be squared and invertible. 398 * The vectors or matrices X and B can be either dense or sparse. 399 * 400 * \tparam _MatrixType the type of the sparse matrix A, it must be a SparseMatrix<> 401 * 402 * \sa \ref TutorialSparseDirectSolvers 403 */ 404 template<typename MatrixType> 405 class PardisoLU : public PardisoImpl< PardisoLU<MatrixType> > 406 { 407 protected: 408 typedef PardisoImpl< PardisoLU<MatrixType> > Base; 409 typedef typename Base::Scalar Scalar; 410 typedef typename Base::RealScalar RealScalar; 411 using Base::pardisoInit; 412 using Base::m_matrix; 413 friend class PardisoImpl< PardisoLU<MatrixType> >; 414 415 public: 416 417 using Base::compute; 418 using Base::solve; 419 420 PardisoLU() 421 : Base() 422 { 423 pardisoInit(Base::ScalarIsComplex ? 13 : 11); 424 } 425 426 PardisoLU(const MatrixType& matrix) 427 : Base() 428 { 429 pardisoInit(Base::ScalarIsComplex ? 13 : 11); 430 compute(matrix); 431 } 432 protected: 433 void getMatrix(const MatrixType& matrix) 434 { 435 m_matrix = matrix; 436 } 437 438 private: 439 PardisoLU(PardisoLU& ) {} 440 }; 441 442 /** \ingroup PardisoSupport_Module 443 * \class PardisoLLT 444 * \brief A sparse direct Cholesky (LLT) factorization and solver based on the PARDISO library 445 * 446 * This class allows to solve for A.X = B sparse linear problems via a LL^T Cholesky factorization 447 * using the Intel MKL PARDISO library. The sparse matrix A must be selfajoint and positive definite. 448 * The vectors or matrices X and B can be either dense or sparse. 449 * 450 * \tparam MatrixType the type of the sparse matrix A, it must be a SparseMatrix<> 451 * \tparam UpLo can be any bitwise combination of Upper, Lower. The default is Upper, meaning only the upper triangular part has to be used. 452 * Upper|Lower can be used to tell both triangular parts can be used as input. 453 * 454 * \sa \ref TutorialSparseDirectSolvers 455 */ 456 template<typename MatrixType, int _UpLo> 457 class PardisoLLT : public PardisoImpl< PardisoLLT<MatrixType,_UpLo> > 458 { 459 protected: 460 typedef PardisoImpl< PardisoLLT<MatrixType,_UpLo> > Base; 461 typedef typename Base::Scalar Scalar; 462 typedef typename Base::Index Index; 463 typedef typename Base::RealScalar RealScalar; 464 using Base::pardisoInit; 465 using Base::m_matrix; 466 friend class PardisoImpl< PardisoLLT<MatrixType,_UpLo> >; 467 468 public: 469 470 enum { UpLo = _UpLo }; 471 using Base::compute; 472 using Base::solve; 473 474 PardisoLLT() 475 : Base() 476 { 477 pardisoInit(Base::ScalarIsComplex ? 4 : 2); 478 } 479 480 PardisoLLT(const MatrixType& matrix) 481 : Base() 482 { 483 pardisoInit(Base::ScalarIsComplex ? 4 : 2); 484 compute(matrix); 485 } 486 487 protected: 488 489 void getMatrix(const MatrixType& matrix) 490 { 491 // PARDISO supports only upper, row-major matrices 492 PermutationMatrix<Dynamic,Dynamic,Index> p_null; 493 m_matrix.resize(matrix.rows(), matrix.cols()); 494 m_matrix.template selfadjointView<Upper>() = matrix.template selfadjointView<UpLo>().twistedBy(p_null); 495 } 496 497 private: 498 PardisoLLT(PardisoLLT& ) {} 499 }; 500 501 /** \ingroup PardisoSupport_Module 502 * \class PardisoLDLT 503 * \brief A sparse direct Cholesky (LDLT) factorization and solver based on the PARDISO library 504 * 505 * This class allows to solve for A.X = B sparse linear problems via a LDL^T Cholesky factorization 506 * using the Intel MKL PARDISO library. The sparse matrix A is assumed to be selfajoint and positive definite. 507 * For complex matrices, A can also be symmetric only, see the \a Options template parameter. 508 * The vectors or matrices X and B can be either dense or sparse. 509 * 510 * \tparam MatrixType the type of the sparse matrix A, it must be a SparseMatrix<> 511 * \tparam Options can be any bitwise combination of Upper, Lower, and Symmetric. The default is Upper, meaning only the upper triangular part has to be used. 512 * Symmetric can be used for symmetric, non-selfadjoint complex matrices, the default being to assume a selfadjoint matrix. 513 * Upper|Lower can be used to tell both triangular parts can be used as input. 514 * 515 * \sa \ref TutorialSparseDirectSolvers 516 */ 517 template<typename MatrixType, int Options> 518 class PardisoLDLT : public PardisoImpl< PardisoLDLT<MatrixType,Options> > 519 { 520 protected: 521 typedef PardisoImpl< PardisoLDLT<MatrixType,Options> > Base; 522 typedef typename Base::Scalar Scalar; 523 typedef typename Base::Index Index; 524 typedef typename Base::RealScalar RealScalar; 525 using Base::pardisoInit; 526 using Base::m_matrix; 527 friend class PardisoImpl< PardisoLDLT<MatrixType,Options> >; 528 529 public: 530 531 using Base::compute; 532 using Base::solve; 533 enum { UpLo = Options&(Upper|Lower) }; 534 535 PardisoLDLT() 536 : Base() 537 { 538 pardisoInit(Base::ScalarIsComplex ? ( bool(Options&Symmetric) ? 6 : -4 ) : -2); 539 } 540 541 PardisoLDLT(const MatrixType& matrix) 542 : Base() 543 { 544 pardisoInit(Base::ScalarIsComplex ? ( bool(Options&Symmetric) ? 6 : -4 ) : -2); 545 compute(matrix); 546 } 547 548 void getMatrix(const MatrixType& matrix) 549 { 550 // PARDISO supports only upper, row-major matrices 551 PermutationMatrix<Dynamic,Dynamic,Index> p_null; 552 m_matrix.resize(matrix.rows(), matrix.cols()); 553 m_matrix.template selfadjointView<Upper>() = matrix.template selfadjointView<UpLo>().twistedBy(p_null); 554 } 555 556 private: 557 PardisoLDLT(PardisoLDLT& ) {} 558 }; 559 560 namespace internal { 561 562 template<typename _Derived, typename Rhs> 563 struct solve_retval<PardisoImpl<_Derived>, Rhs> 564 : solve_retval_base<PardisoImpl<_Derived>, Rhs> 565 { 566 typedef PardisoImpl<_Derived> Dec; 567 EIGEN_MAKE_SOLVE_HELPERS(Dec,Rhs) 568 569 template<typename Dest> void evalTo(Dest& dst) const 570 { 571 dec()._solve(rhs(),dst); 572 } 573 }; 574 575 template<typename Derived, typename Rhs> 576 struct sparse_solve_retval<PardisoImpl<Derived>, Rhs> 577 : sparse_solve_retval_base<PardisoImpl<Derived>, Rhs> 578 { 579 typedef PardisoImpl<Derived> Dec; 580 EIGEN_MAKE_SPARSE_SOLVE_HELPERS(Dec,Rhs) 581 582 template<typename Dest> void evalTo(Dest& dst) const 583 { 584 this->defaultEvalTo(dst); 585 } 586 }; 587 588 } // end namespace internal 589 590 } // end namespace Eigen 591 592 #endif // EIGEN_PARDISOSUPPORT_H 593