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 IndexType> 44 struct pardiso_run_selector 45 { runpardiso_run_selector46 static IndexType run( _MKL_DSS_HANDLE_t pt, IndexType maxfct, IndexType mnum, IndexType type, IndexType phase, IndexType n, void *a, 47 IndexType *ia, IndexType *ja, IndexType *perm, IndexType nrhs, IndexType *iparm, IndexType msglvl, void *b, void *x) 48 { 49 IndexType 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 IndexType; 58 static IndexType run( _MKL_DSS_HANDLE_t pt, IndexType maxfct, IndexType mnum, IndexType type, IndexType phase, IndexType n, void *a, 59 IndexType *ia, IndexType *ja, IndexType *perm, IndexType nrhs, IndexType *iparm, IndexType msglvl, void *b, void *x) 60 { 61 IndexType 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::StorageIndex StorageIndex; 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::StorageIndex StorageIndex; 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::StorageIndex StorageIndex; 94 }; 95 96 } // end namespace internal 97 98 template<class Derived> 99 class PardisoImpl : public SparseSolverBase<Derived> 100 { 101 protected: 102 typedef SparseSolverBase<Derived> Base; 103 using Base::derived; 104 using Base::m_isInitialized; 105 106 typedef internal::pardiso_traits<Derived> Traits; 107 public: 108 using Base::_solve_impl; 109 110 typedef typename Traits::MatrixType MatrixType; 111 typedef typename Traits::Scalar Scalar; 112 typedef typename Traits::RealScalar RealScalar; 113 typedef typename Traits::StorageIndex StorageIndex; 114 typedef SparseMatrix<Scalar,RowMajor,StorageIndex> SparseMatrixType; 115 typedef Matrix<Scalar,Dynamic,1> VectorType; 116 typedef Matrix<StorageIndex, 1, MatrixType::ColsAtCompileTime> IntRowVectorType; 117 typedef Matrix<StorageIndex, MatrixType::RowsAtCompileTime, 1> IntColVectorType; 118 typedef Array<StorageIndex,64,1,DontAlign> ParameterType; 119 enum { 120 ScalarIsComplex = NumTraits<Scalar>::IsComplex, 121 ColsAtCompileTime = Dynamic, 122 MaxColsAtCompileTime = Dynamic 123 }; 124 125 PardisoImpl() 126 : m_analysisIsOk(false), m_factorizationIsOk(false) 127 { 128 eigen_assert((sizeof(StorageIndex) >= sizeof(_INTEGER_t) && sizeof(StorageIndex) <= 8) && "Non-supported index type"); 129 m_iparm.setZero(); 130 m_msglvl = 0; // No output 131 m_isInitialized = false; 132 } 133 134 ~PardisoImpl() 135 { 136 pardisoRelease(); 137 } 138 139 inline Index cols() const { return m_size; } 140 inline Index rows() const { return m_size; } 141 142 /** \brief Reports whether previous computation was successful. 143 * 144 * \returns \c Success if computation was successful, 145 * \c NumericalIssue if the matrix appears to be negative. 146 */ 147 ComputationInfo info() const 148 { 149 eigen_assert(m_isInitialized && "Decomposition is not initialized."); 150 return m_info; 151 } 152 153 /** \warning for advanced usage only. 154 * \returns a reference to the parameter array controlling PARDISO. 155 * See the PARDISO manual to know how to use it. */ 156 ParameterType& pardisoParameterArray() 157 { 158 return m_iparm; 159 } 160 161 /** Performs a symbolic decomposition on the sparcity of \a matrix. 162 * 163 * This function is particularly useful when solving for several problems having the same structure. 164 * 165 * \sa factorize() 166 */ 167 Derived& analyzePattern(const MatrixType& matrix); 168 169 /** Performs a numeric decomposition of \a matrix 170 * 171 * The given matrix must has the same sparcity than the matrix on which the symbolic decomposition has been performed. 172 * 173 * \sa analyzePattern() 174 */ 175 Derived& factorize(const MatrixType& matrix); 176 177 Derived& compute(const MatrixType& matrix); 178 179 template<typename Rhs,typename Dest> 180 void _solve_impl(const MatrixBase<Rhs> &b, MatrixBase<Dest> &dest) const; 181 182 protected: 183 void pardisoRelease() 184 { 185 if(m_isInitialized) // Factorization ran at least once 186 { 187 internal::pardiso_run_selector<StorageIndex>::run(m_pt, 1, 1, m_type, -1, internal::convert_index<StorageIndex>(m_size),0, 0, 0, m_perm.data(), 0, 188 m_iparm.data(), m_msglvl, NULL, NULL); 189 m_isInitialized = false; 190 } 191 } 192 193 void pardisoInit(int type) 194 { 195 m_type = type; 196 bool symmetric = std::abs(m_type) < 10; 197 m_iparm[0] = 1; // No solver default 198 m_iparm[1] = 2; // use Metis for the ordering 199 m_iparm[2] = 0; // Reserved. Set to zero. (??Numbers of processors, value of OMP_NUM_THREADS??) 200 m_iparm[3] = 0; // No iterative-direct algorithm 201 m_iparm[4] = 0; // No user fill-in reducing permutation 202 m_iparm[5] = 0; // Write solution into x, b is left unchanged 203 m_iparm[6] = 0; // Not in use 204 m_iparm[7] = 2; // Max numbers of iterative refinement steps 205 m_iparm[8] = 0; // Not in use 206 m_iparm[9] = 13; // Perturb the pivot elements with 1E-13 207 m_iparm[10] = symmetric ? 0 : 1; // Use nonsymmetric permutation and scaling MPS 208 m_iparm[11] = 0; // Not in use 209 m_iparm[12] = symmetric ? 0 : 1; // Maximum weighted matching algorithm is switched-off (default for symmetric). 210 // Try m_iparm[12] = 1 in case of inappropriate accuracy 211 m_iparm[13] = 0; // Output: Number of perturbed pivots 212 m_iparm[14] = 0; // Not in use 213 m_iparm[15] = 0; // Not in use 214 m_iparm[16] = 0; // Not in use 215 m_iparm[17] = -1; // Output: Number of nonzeros in the factor LU 216 m_iparm[18] = -1; // Output: Mflops for LU factorization 217 m_iparm[19] = 0; // Output: Numbers of CG Iterations 218 219 m_iparm[20] = 0; // 1x1 pivoting 220 m_iparm[26] = 0; // No matrix checker 221 m_iparm[27] = (sizeof(RealScalar) == 4) ? 1 : 0; 222 m_iparm[34] = 1; // C indexing 223 m_iparm[36] = 0; // CSR 224 m_iparm[59] = 0; // 0 - In-Core ; 1 - Automatic switch between In-Core and Out-of-Core modes ; 2 - Out-of-Core 225 226 memset(m_pt, 0, sizeof(m_pt)); 227 } 228 229 protected: 230 // cached data to reduce reallocation, etc. 231 232 void manageErrorCode(Index error) const 233 { 234 switch(error) 235 { 236 case 0: 237 m_info = Success; 238 break; 239 case -4: 240 case -7: 241 m_info = NumericalIssue; 242 break; 243 default: 244 m_info = InvalidInput; 245 } 246 } 247 248 mutable SparseMatrixType m_matrix; 249 mutable ComputationInfo m_info; 250 bool m_analysisIsOk, m_factorizationIsOk; 251 StorageIndex m_type, m_msglvl; 252 mutable void *m_pt[64]; 253 mutable ParameterType m_iparm; 254 mutable IntColVectorType m_perm; 255 Index m_size; 256 257 }; 258 259 template<class Derived> 260 Derived& PardisoImpl<Derived>::compute(const MatrixType& a) 261 { 262 m_size = a.rows(); 263 eigen_assert(a.rows() == a.cols()); 264 265 pardisoRelease(); 266 m_perm.setZero(m_size); 267 derived().getMatrix(a); 268 269 Index error; 270 error = internal::pardiso_run_selector<StorageIndex>::run(m_pt, 1, 1, m_type, 12, internal::convert_index<StorageIndex>(m_size), 271 m_matrix.valuePtr(), m_matrix.outerIndexPtr(), m_matrix.innerIndexPtr(), 272 m_perm.data(), 0, m_iparm.data(), m_msglvl, NULL, NULL); 273 manageErrorCode(error); 274 m_analysisIsOk = true; 275 m_factorizationIsOk = true; 276 m_isInitialized = true; 277 return derived(); 278 } 279 280 template<class Derived> 281 Derived& PardisoImpl<Derived>::analyzePattern(const MatrixType& a) 282 { 283 m_size = a.rows(); 284 eigen_assert(m_size == a.cols()); 285 286 pardisoRelease(); 287 m_perm.setZero(m_size); 288 derived().getMatrix(a); 289 290 Index error; 291 error = internal::pardiso_run_selector<StorageIndex>::run(m_pt, 1, 1, m_type, 11, internal::convert_index<StorageIndex>(m_size), 292 m_matrix.valuePtr(), m_matrix.outerIndexPtr(), m_matrix.innerIndexPtr(), 293 m_perm.data(), 0, m_iparm.data(), m_msglvl, NULL, NULL); 294 295 manageErrorCode(error); 296 m_analysisIsOk = true; 297 m_factorizationIsOk = false; 298 m_isInitialized = true; 299 return derived(); 300 } 301 302 template<class Derived> 303 Derived& PardisoImpl<Derived>::factorize(const MatrixType& a) 304 { 305 eigen_assert(m_analysisIsOk && "You must first call analyzePattern()"); 306 eigen_assert(m_size == a.rows() && m_size == a.cols()); 307 308 derived().getMatrix(a); 309 310 Index error; 311 error = internal::pardiso_run_selector<StorageIndex>::run(m_pt, 1, 1, m_type, 22, internal::convert_index<StorageIndex>(m_size), 312 m_matrix.valuePtr(), m_matrix.outerIndexPtr(), m_matrix.innerIndexPtr(), 313 m_perm.data(), 0, m_iparm.data(), m_msglvl, NULL, NULL); 314 315 manageErrorCode(error); 316 m_factorizationIsOk = true; 317 return derived(); 318 } 319 320 template<class Derived> 321 template<typename BDerived,typename XDerived> 322 void PardisoImpl<Derived>::_solve_impl(const MatrixBase<BDerived> &b, MatrixBase<XDerived>& x) const 323 { 324 if(m_iparm[0] == 0) // Factorization was not computed 325 { 326 m_info = InvalidInput; 327 return; 328 } 329 330 //Index n = m_matrix.rows(); 331 Index nrhs = Index(b.cols()); 332 eigen_assert(m_size==b.rows()); 333 eigen_assert(((MatrixBase<BDerived>::Flags & RowMajorBit) == 0 || nrhs == 1) && "Row-major right hand sides are not supported"); 334 eigen_assert(((MatrixBase<XDerived>::Flags & RowMajorBit) == 0 || nrhs == 1) && "Row-major matrices of unknowns are not supported"); 335 eigen_assert(((nrhs == 1) || b.outerStride() == b.rows())); 336 337 338 // switch (transposed) { 339 // case SvNoTrans : m_iparm[11] = 0 ; break; 340 // case SvTranspose : m_iparm[11] = 2 ; break; 341 // case SvAdjoint : m_iparm[11] = 1 ; break; 342 // default: 343 // //std::cerr << "Eigen: transposition option \"" << transposed << "\" not supported by the PARDISO backend\n"; 344 // m_iparm[11] = 0; 345 // } 346 347 Scalar* rhs_ptr = const_cast<Scalar*>(b.derived().data()); 348 Matrix<Scalar,Dynamic,Dynamic,ColMajor> tmp; 349 350 // Pardiso cannot solve in-place 351 if(rhs_ptr == x.derived().data()) 352 { 353 tmp = b; 354 rhs_ptr = tmp.data(); 355 } 356 357 Index error; 358 error = internal::pardiso_run_selector<StorageIndex>::run(m_pt, 1, 1, m_type, 33, internal::convert_index<StorageIndex>(m_size), 359 m_matrix.valuePtr(), m_matrix.outerIndexPtr(), m_matrix.innerIndexPtr(), 360 m_perm.data(), internal::convert_index<StorageIndex>(nrhs), m_iparm.data(), m_msglvl, 361 rhs_ptr, x.derived().data()); 362 363 manageErrorCode(error); 364 } 365 366 367 /** \ingroup PardisoSupport_Module 368 * \class PardisoLU 369 * \brief A sparse direct LU factorization and solver based on the PARDISO library 370 * 371 * This class allows to solve for A.X = B sparse linear problems via a direct LU factorization 372 * using the Intel MKL PARDISO library. The sparse matrix A must be squared and invertible. 373 * The vectors or matrices X and B can be either dense or sparse. 374 * 375 * By default, it runs in in-core mode. To enable PARDISO's out-of-core feature, set: 376 * \code solver.pardisoParameterArray()[59] = 1; \endcode 377 * 378 * \tparam _MatrixType the type of the sparse matrix A, it must be a SparseMatrix<> 379 * 380 * \implsparsesolverconcept 381 * 382 * \sa \ref TutorialSparseSolverConcept, class SparseLU 383 */ 384 template<typename MatrixType> 385 class PardisoLU : public PardisoImpl< PardisoLU<MatrixType> > 386 { 387 protected: 388 typedef PardisoImpl<PardisoLU> Base; 389 using Base::pardisoInit; 390 using Base::m_matrix; 391 friend class PardisoImpl< PardisoLU<MatrixType> >; 392 393 public: 394 395 typedef typename Base::Scalar Scalar; 396 typedef typename Base::RealScalar RealScalar; 397 398 using Base::compute; 399 using Base::solve; 400 401 PardisoLU() 402 : Base() 403 { 404 pardisoInit(Base::ScalarIsComplex ? 13 : 11); 405 } 406 407 explicit PardisoLU(const MatrixType& matrix) 408 : Base() 409 { 410 pardisoInit(Base::ScalarIsComplex ? 13 : 11); 411 compute(matrix); 412 } 413 protected: 414 void getMatrix(const MatrixType& matrix) 415 { 416 m_matrix = matrix; 417 m_matrix.makeCompressed(); 418 } 419 }; 420 421 /** \ingroup PardisoSupport_Module 422 * \class PardisoLLT 423 * \brief A sparse direct Cholesky (LLT) factorization and solver based on the PARDISO library 424 * 425 * This class allows to solve for A.X = B sparse linear problems via a LL^T Cholesky factorization 426 * using the Intel MKL PARDISO library. The sparse matrix A must be selfajoint and positive definite. 427 * The vectors or matrices X and B can be either dense or sparse. 428 * 429 * By default, it runs in in-core mode. To enable PARDISO's out-of-core feature, set: 430 * \code solver.pardisoParameterArray()[59] = 1; \endcode 431 * 432 * \tparam MatrixType the type of the sparse matrix A, it must be a SparseMatrix<> 433 * \tparam UpLo can be any bitwise combination of Upper, Lower. The default is Upper, meaning only the upper triangular part has to be used. 434 * Upper|Lower can be used to tell both triangular parts can be used as input. 435 * 436 * \implsparsesolverconcept 437 * 438 * \sa \ref TutorialSparseSolverConcept, class SimplicialLLT 439 */ 440 template<typename MatrixType, int _UpLo> 441 class PardisoLLT : public PardisoImpl< PardisoLLT<MatrixType,_UpLo> > 442 { 443 protected: 444 typedef PardisoImpl< PardisoLLT<MatrixType,_UpLo> > Base; 445 using Base::pardisoInit; 446 using Base::m_matrix; 447 friend class PardisoImpl< PardisoLLT<MatrixType,_UpLo> >; 448 449 public: 450 451 typedef typename Base::Scalar Scalar; 452 typedef typename Base::RealScalar RealScalar; 453 typedef typename Base::StorageIndex StorageIndex; 454 enum { UpLo = _UpLo }; 455 using Base::compute; 456 457 PardisoLLT() 458 : Base() 459 { 460 pardisoInit(Base::ScalarIsComplex ? 4 : 2); 461 } 462 463 explicit PardisoLLT(const MatrixType& matrix) 464 : Base() 465 { 466 pardisoInit(Base::ScalarIsComplex ? 4 : 2); 467 compute(matrix); 468 } 469 470 protected: 471 472 void getMatrix(const MatrixType& matrix) 473 { 474 // PARDISO supports only upper, row-major matrices 475 PermutationMatrix<Dynamic,Dynamic,StorageIndex> p_null; 476 m_matrix.resize(matrix.rows(), matrix.cols()); 477 m_matrix.template selfadjointView<Upper>() = matrix.template selfadjointView<UpLo>().twistedBy(p_null); 478 m_matrix.makeCompressed(); 479 } 480 }; 481 482 /** \ingroup PardisoSupport_Module 483 * \class PardisoLDLT 484 * \brief A sparse direct Cholesky (LDLT) factorization and solver based on the PARDISO library 485 * 486 * This class allows to solve for A.X = B sparse linear problems via a LDL^T Cholesky factorization 487 * using the Intel MKL PARDISO library. The sparse matrix A is assumed to be selfajoint and positive definite. 488 * For complex matrices, A can also be symmetric only, see the \a Options template parameter. 489 * The vectors or matrices X and B can be either dense or sparse. 490 * 491 * By default, it runs in in-core mode. To enable PARDISO's out-of-core feature, set: 492 * \code solver.pardisoParameterArray()[59] = 1; \endcode 493 * 494 * \tparam MatrixType the type of the sparse matrix A, it must be a SparseMatrix<> 495 * \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. 496 * Symmetric can be used for symmetric, non-selfadjoint complex matrices, the default being to assume a selfadjoint matrix. 497 * Upper|Lower can be used to tell both triangular parts can be used as input. 498 * 499 * \implsparsesolverconcept 500 * 501 * \sa \ref TutorialSparseSolverConcept, class SimplicialLDLT 502 */ 503 template<typename MatrixType, int Options> 504 class PardisoLDLT : public PardisoImpl< PardisoLDLT<MatrixType,Options> > 505 { 506 protected: 507 typedef PardisoImpl< PardisoLDLT<MatrixType,Options> > Base; 508 using Base::pardisoInit; 509 using Base::m_matrix; 510 friend class PardisoImpl< PardisoLDLT<MatrixType,Options> >; 511 512 public: 513 514 typedef typename Base::Scalar Scalar; 515 typedef typename Base::RealScalar RealScalar; 516 typedef typename Base::StorageIndex StorageIndex; 517 using Base::compute; 518 enum { UpLo = Options&(Upper|Lower) }; 519 520 PardisoLDLT() 521 : Base() 522 { 523 pardisoInit(Base::ScalarIsComplex ? ( bool(Options&Symmetric) ? 6 : -4 ) : -2); 524 } 525 526 explicit PardisoLDLT(const MatrixType& matrix) 527 : Base() 528 { 529 pardisoInit(Base::ScalarIsComplex ? ( bool(Options&Symmetric) ? 6 : -4 ) : -2); 530 compute(matrix); 531 } 532 533 void getMatrix(const MatrixType& matrix) 534 { 535 // PARDISO supports only upper, row-major matrices 536 PermutationMatrix<Dynamic,Dynamic,StorageIndex> p_null; 537 m_matrix.resize(matrix.rows(), matrix.cols()); 538 m_matrix.template selfadjointView<Upper>() = matrix.template selfadjointView<UpLo>().twistedBy(p_null); 539 m_matrix.makeCompressed(); 540 } 541 }; 542 543 } // end namespace Eigen 544 545 #endif // EIGEN_PARDISOSUPPORT_H 546