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