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
35namespace Eigen {
36
37template<typename _MatrixType> class PardisoLU;
38template<typename _MatrixType, int Options=Upper> class PardisoLLT;
39template<typename _MatrixType, int Options=Upper> class PardisoLDLT;
40
41namespace internal
42{
43  template<typename Index>
44  struct pardiso_run_selector
45  {
46    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
98template<class Derived>
99class 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 = 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
284template<class Derived>
285Derived& 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
307template<class Derived>
308Derived& 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
330template<class Derived>
331Derived& 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
348template<class Base>
349template<typename BDerived,typename XDerived>
350bool 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  */
404template<typename MatrixType>
405class 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  */
456template<typename MatrixType, int _UpLo>
457class 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  */
517template<typename MatrixType, int Options>
518class 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
560namespace internal {
561
562template<typename _Derived, typename Rhs>
563struct 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
575template<typename Derived, typename Rhs>
576struct 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