blob: 0c101494ae528c23b5c1a4d02f0077c71a87b7c9 [file] [log] [blame]
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008-2015 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_SUPERLUSUPPORT_H
#define EIGEN_SUPERLUSUPPORT_H
// IWYU pragma: private
#include "./InternalHeaderCheck.h"
namespace Eigen {
#if defined(SUPERLU_MAJOR_VERSION) && (SUPERLU_MAJOR_VERSION >= 5)
#define DECL_GSSVX(PREFIX, FLOATTYPE, KEYTYPE) \
extern "C" { \
extern void PREFIX##gssvx(superlu_options_t *, SuperMatrix *, int *, int *, int *, char *, FLOATTYPE *, FLOATTYPE *, \
SuperMatrix *, SuperMatrix *, void *, int, SuperMatrix *, SuperMatrix *, FLOATTYPE *, \
FLOATTYPE *, FLOATTYPE *, FLOATTYPE *, GlobalLU_t *, mem_usage_t *, SuperLUStat_t *, \
int *); \
} \
inline float SuperLU_gssvx(superlu_options_t *options, SuperMatrix *A, int *perm_c, int *perm_r, int *etree, \
char *equed, FLOATTYPE *R, FLOATTYPE *C, SuperMatrix *L, SuperMatrix *U, void *work, \
int lwork, SuperMatrix *B, SuperMatrix *X, FLOATTYPE *recip_pivot_growth, \
FLOATTYPE *rcond, FLOATTYPE *ferr, FLOATTYPE *berr, SuperLUStat_t *stats, int *info, \
KEYTYPE) { \
mem_usage_t mem_usage; \
GlobalLU_t gLU; \
PREFIX##gssvx(options, A, perm_c, perm_r, etree, equed, R, C, L, U, work, lwork, B, X, recip_pivot_growth, rcond, \
ferr, berr, &gLU, &mem_usage, stats, info); \
return mem_usage.for_lu; /* bytes used by the factor storage */ \
}
#else // version < 5.0
#define DECL_GSSVX(PREFIX, FLOATTYPE, KEYTYPE) \
extern "C" { \
extern void PREFIX##gssvx(superlu_options_t *, SuperMatrix *, int *, int *, int *, char *, FLOATTYPE *, FLOATTYPE *, \
SuperMatrix *, SuperMatrix *, void *, int, SuperMatrix *, SuperMatrix *, FLOATTYPE *, \
FLOATTYPE *, FLOATTYPE *, FLOATTYPE *, mem_usage_t *, SuperLUStat_t *, int *); \
} \
inline float SuperLU_gssvx(superlu_options_t *options, SuperMatrix *A, int *perm_c, int *perm_r, int *etree, \
char *equed, FLOATTYPE *R, FLOATTYPE *C, SuperMatrix *L, SuperMatrix *U, void *work, \
int lwork, SuperMatrix *B, SuperMatrix *X, FLOATTYPE *recip_pivot_growth, \
FLOATTYPE *rcond, FLOATTYPE *ferr, FLOATTYPE *berr, SuperLUStat_t *stats, int *info, \
KEYTYPE) { \
mem_usage_t mem_usage; \
PREFIX##gssvx(options, A, perm_c, perm_r, etree, equed, R, C, L, U, work, lwork, B, X, recip_pivot_growth, rcond, \
ferr, berr, &mem_usage, stats, info); \
return mem_usage.for_lu; /* bytes used by the factor storage */ \
}
#endif
DECL_GSSVX(s, float, float)
DECL_GSSVX(c, float, std::complex<float>)
DECL_GSSVX(d, double, double)
DECL_GSSVX(z, double, std::complex<double>)
#ifdef MILU_ALPHA
#define EIGEN_SUPERLU_HAS_ILU
#endif
#ifdef EIGEN_SUPERLU_HAS_ILU
// similarly for the incomplete factorization using gsisx
#define DECL_GSISX(PREFIX, FLOATTYPE, KEYTYPE) \
extern "C" { \
extern void PREFIX##gsisx(superlu_options_t *, SuperMatrix *, int *, int *, int *, char *, FLOATTYPE *, FLOATTYPE *, \
SuperMatrix *, SuperMatrix *, void *, int, SuperMatrix *, SuperMatrix *, FLOATTYPE *, \
FLOATTYPE *, mem_usage_t *, SuperLUStat_t *, int *); \
} \
inline float SuperLU_gsisx(superlu_options_t *options, SuperMatrix *A, int *perm_c, int *perm_r, int *etree, \
char *equed, FLOATTYPE *R, FLOATTYPE *C, SuperMatrix *L, SuperMatrix *U, void *work, \
int lwork, SuperMatrix *B, SuperMatrix *X, FLOATTYPE *recip_pivot_growth, \
FLOATTYPE *rcond, SuperLUStat_t *stats, int *info, KEYTYPE) { \
mem_usage_t mem_usage; \
PREFIX##gsisx(options, A, perm_c, perm_r, etree, equed, R, C, L, U, work, lwork, B, X, recip_pivot_growth, rcond, \
&mem_usage, stats, info); \
return mem_usage.for_lu; /* bytes used by the factor storage */ \
}
DECL_GSISX(s, float, float)
DECL_GSISX(c, float, std::complex<float>)
DECL_GSISX(d, double, double)
DECL_GSISX(z, double, std::complex<double>)
#endif
template <typename MatrixType>
struct SluMatrixMapHelper;
/** \internal
*
* A wrapper class for SuperLU matrices. It supports only compressed sparse matrices
* and dense matrices. Supernodal and other fancy format are not supported by this wrapper.
*
* This wrapper class mainly aims to avoids the need of dynamic allocation of the storage structure.
*/
struct SluMatrix : SuperMatrix {
SluMatrix() { Store = &storage; }
SluMatrix(const SluMatrix &other) : SuperMatrix(other) {
Store = &storage;
storage = other.storage;
}
SluMatrix &operator=(const SluMatrix &other) {
SuperMatrix::operator=(static_cast<const SuperMatrix &>(other));
Store = &storage;
storage = other.storage;
return *this;
}
struct {
union {
int nnz;
int lda;
};
void *values;
int *innerInd;
int *outerInd;
} storage;
void setStorageType(Stype_t t) {
Stype = t;
if (t == SLU_NC || t == SLU_NR || t == SLU_DN)
Store = &storage;
else {
eigen_assert(false && "storage type not supported");
Store = 0;
}
}
template <typename Scalar>
void setScalarType() {
if (internal::is_same<Scalar, float>::value)
Dtype = SLU_S;
else if (internal::is_same<Scalar, double>::value)
Dtype = SLU_D;
else if (internal::is_same<Scalar, std::complex<float> >::value)
Dtype = SLU_C;
else if (internal::is_same<Scalar, std::complex<double> >::value)
Dtype = SLU_Z;
else {
eigen_assert(false && "Scalar type not supported by SuperLU");
}
}
template <typename MatrixType>
static SluMatrix Map(MatrixBase<MatrixType> &_mat) {
MatrixType &mat(_mat.derived());
eigen_assert(((MatrixType::Flags & RowMajorBit) != RowMajorBit) &&
"row-major dense matrices are not supported by SuperLU");
SluMatrix res;
res.setStorageType(SLU_DN);
res.setScalarType<typename MatrixType::Scalar>();
res.Mtype = SLU_GE;
res.nrow = internal::convert_index<int>(mat.rows());
res.ncol = internal::convert_index<int>(mat.cols());
res.storage.lda = internal::convert_index<int>(MatrixType::IsVectorAtCompileTime ? mat.size() : mat.outerStride());
res.storage.values = (void *)(mat.data());
return res;
}
template <typename MatrixType>
static SluMatrix Map(SparseMatrixBase<MatrixType> &a_mat) {
MatrixType &mat(a_mat.derived());
SluMatrix res;
if ((MatrixType::Flags & RowMajorBit) == RowMajorBit) {
res.setStorageType(SLU_NR);
res.nrow = internal::convert_index<int>(mat.cols());
res.ncol = internal::convert_index<int>(mat.rows());
} else {
res.setStorageType(SLU_NC);
res.nrow = internal::convert_index<int>(mat.rows());
res.ncol = internal::convert_index<int>(mat.cols());
}
res.Mtype = SLU_GE;
res.storage.nnz = internal::convert_index<int>(mat.nonZeros());
res.storage.values = mat.valuePtr();
res.storage.innerInd = mat.innerIndexPtr();
res.storage.outerInd = mat.outerIndexPtr();
res.setScalarType<typename MatrixType::Scalar>();
// FIXME the following is not very accurate
if (int(MatrixType::Flags) & int(Upper)) res.Mtype = SLU_TRU;
if (int(MatrixType::Flags) & int(Lower)) res.Mtype = SLU_TRL;
eigen_assert(((int(MatrixType::Flags) & int(SelfAdjoint)) == 0) &&
"SelfAdjoint matrix shape not supported by SuperLU");
return res;
}
};
template <typename Scalar, int Rows, int Cols, int Options, int MRows, int MCols>
struct SluMatrixMapHelper<Matrix<Scalar, Rows, Cols, Options, MRows, MCols> > {
typedef Matrix<Scalar, Rows, Cols, Options, MRows, MCols> MatrixType;
static void run(MatrixType &mat, SluMatrix &res) {
eigen_assert(((Options & RowMajor) != RowMajor) && "row-major dense matrices is not supported by SuperLU");
res.setStorageType(SLU_DN);
res.setScalarType<Scalar>();
res.Mtype = SLU_GE;
res.nrow = mat.rows();
res.ncol = mat.cols();
res.storage.lda = mat.outerStride();
res.storage.values = mat.data();
}
};
template <typename Derived>
struct SluMatrixMapHelper<SparseMatrixBase<Derived> > {
typedef Derived MatrixType;
static void run(MatrixType &mat, SluMatrix &res) {
if ((MatrixType::Flags & RowMajorBit) == RowMajorBit) {
res.setStorageType(SLU_NR);
res.nrow = mat.cols();
res.ncol = mat.rows();
} else {
res.setStorageType(SLU_NC);
res.nrow = mat.rows();
res.ncol = mat.cols();
}
res.Mtype = SLU_GE;
res.storage.nnz = mat.nonZeros();
res.storage.values = mat.valuePtr();
res.storage.innerInd = mat.innerIndexPtr();
res.storage.outerInd = mat.outerIndexPtr();
res.setScalarType<typename MatrixType::Scalar>();
// FIXME the following is not very accurate
if (MatrixType::Flags & Upper) res.Mtype = SLU_TRU;
if (MatrixType::Flags & Lower) res.Mtype = SLU_TRL;
eigen_assert(((MatrixType::Flags & SelfAdjoint) == 0) && "SelfAdjoint matrix shape not supported by SuperLU");
}
};
namespace internal {
template <typename MatrixType>
SluMatrix asSluMatrix(MatrixType &mat) {
return SluMatrix::Map(mat);
}
/** View a Super LU matrix as an Eigen expression */
template <typename Scalar, int Flags, typename Index>
Map<SparseMatrix<Scalar, Flags, Index> > map_superlu(SluMatrix &sluMat) {
eigen_assert(((Flags & RowMajor) == RowMajor && sluMat.Stype == SLU_NR) ||
((Flags & ColMajor) == ColMajor && sluMat.Stype == SLU_NC));
Index outerSize = (Flags & RowMajor) == RowMajor ? sluMat.ncol : sluMat.nrow;
return Map<SparseMatrix<Scalar, Flags, Index> >(sluMat.nrow, sluMat.ncol, sluMat.storage.outerInd[outerSize],
sluMat.storage.outerInd, sluMat.storage.innerInd,
reinterpret_cast<Scalar *>(sluMat.storage.values));
}
} // end namespace internal
/** \ingroup SuperLUSupport_Module
* \class SuperLUBase
* \brief The base class for the direct and incomplete LU factorization of SuperLU
*/
template <typename MatrixType_, typename Derived>
class SuperLUBase : public SparseSolverBase<Derived> {
protected:
typedef SparseSolverBase<Derived> Base;
using Base::derived;
using Base::m_isInitialized;
public:
typedef MatrixType_ MatrixType;
typedef typename MatrixType::Scalar Scalar;
typedef typename MatrixType::RealScalar RealScalar;
typedef typename MatrixType::StorageIndex StorageIndex;
typedef Matrix<Scalar, Dynamic, 1> Vector;
typedef Matrix<int, 1, MatrixType::ColsAtCompileTime> IntRowVectorType;
typedef Matrix<int, MatrixType::RowsAtCompileTime, 1> IntColVectorType;
typedef Map<PermutationMatrix<Dynamic, Dynamic, int> > PermutationMap;
typedef SparseMatrix<Scalar> LUMatrixType;
enum { ColsAtCompileTime = MatrixType::ColsAtCompileTime, MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime };
public:
SuperLUBase() {}
~SuperLUBase() { clearFactors(); }
inline Index rows() const { return m_matrix.rows(); }
inline Index cols() const { return m_matrix.cols(); }
/** \returns a reference to the Super LU option object to configure the Super LU algorithms. */
inline superlu_options_t &options() { return m_sluOptions; }
/** \brief Reports whether previous computation was successful.
*
* \returns \c Success if computation was successful,
* \c NumericalIssue if the matrix.appears to be negative.
*/
ComputationInfo info() const {
eigen_assert(m_isInitialized && "Decomposition is not initialized.");
return m_info;
}
/** Computes the sparse Cholesky decomposition of \a matrix */
void compute(const MatrixType &matrix) {
derived().analyzePattern(matrix);
derived().factorize(matrix);
}
/** Performs a symbolic decomposition on the sparcity of \a matrix.
*
* This function is particularly useful when solving for several problems having the same structure.
*
* \sa factorize()
*/
void analyzePattern(const MatrixType & /*matrix*/) {
m_isInitialized = true;
m_info = Success;
m_analysisIsOk = true;
m_factorizationIsOk = false;
}
template <typename Stream>
void dumpMemory(Stream & /*s*/) {}
protected:
void initFactorization(const MatrixType &a) {
set_default_options(&this->m_sluOptions);
const Index size = a.rows();
m_matrix = a;
m_sluA = internal::asSluMatrix(m_matrix);
clearFactors();
m_p.resize(size);
m_q.resize(size);
m_sluRscale.resize(size);
m_sluCscale.resize(size);
m_sluEtree.resize(size);
// set empty B and X
m_sluB.setStorageType(SLU_DN);
m_sluB.setScalarType<Scalar>();
m_sluB.Mtype = SLU_GE;
m_sluB.storage.values = 0;
m_sluB.nrow = 0;
m_sluB.ncol = 0;
m_sluB.storage.lda = internal::convert_index<int>(size);
m_sluX = m_sluB;
m_extractedDataAreDirty = true;
}
void init() {
m_info = InvalidInput;
m_isInitialized = false;
m_sluL.Store = 0;
m_sluU.Store = 0;
}
void extractData() const;
void clearFactors() {
if (m_sluL.Store) Destroy_SuperNode_Matrix(&m_sluL);
if (m_sluU.Store) Destroy_CompCol_Matrix(&m_sluU);
m_sluL.Store = 0;
m_sluU.Store = 0;
memset(&m_sluL, 0, sizeof m_sluL);
memset(&m_sluU, 0, sizeof m_sluU);
}
// cached data to reduce reallocation, etc.
mutable LUMatrixType m_l;
mutable LUMatrixType m_u;
mutable IntColVectorType m_p;
mutable IntRowVectorType m_q;
mutable LUMatrixType m_matrix; // copy of the factorized matrix
mutable SluMatrix m_sluA;
mutable SuperMatrix m_sluL, m_sluU;
mutable SluMatrix m_sluB, m_sluX;
mutable SuperLUStat_t m_sluStat;
mutable superlu_options_t m_sluOptions;
mutable std::vector<int> m_sluEtree;
mutable Matrix<RealScalar, Dynamic, 1> m_sluRscale, m_sluCscale;
mutable Matrix<RealScalar, Dynamic, 1> m_sluFerr, m_sluBerr;
mutable char m_sluEqued;
mutable ComputationInfo m_info;
int m_factorizationIsOk;
int m_analysisIsOk;
mutable bool m_extractedDataAreDirty;
private:
SuperLUBase(SuperLUBase &) {}
};
/** \ingroup SuperLUSupport_Module
* \class SuperLU
* \brief A sparse direct LU factorization and solver based on the SuperLU library
*
* This class allows to solve for A.X = B sparse linear problems via a direct LU factorization
* using the SuperLU library. The sparse matrix A must be squared and invertible. The vectors or matrices
* X and B can be either dense or sparse.
*
* \tparam MatrixType_ the type of the sparse matrix A, it must be a SparseMatrix<>
*
* \warning This class is only for the 4.x versions of SuperLU. The 3.x and 5.x versions are not supported.
*
* \implsparsesolverconcept
*
* \sa \ref TutorialSparseSolverConcept, class SparseLU
*/
template <typename MatrixType_>
class SuperLU : public SuperLUBase<MatrixType_, SuperLU<MatrixType_> > {
public:
typedef SuperLUBase<MatrixType_, SuperLU> Base;
typedef MatrixType_ MatrixType;
typedef typename Base::Scalar Scalar;
typedef typename Base::RealScalar RealScalar;
typedef typename Base::StorageIndex StorageIndex;
typedef typename Base::IntRowVectorType IntRowVectorType;
typedef typename Base::IntColVectorType IntColVectorType;
typedef typename Base::PermutationMap PermutationMap;
typedef typename Base::LUMatrixType LUMatrixType;
typedef TriangularView<LUMatrixType, Lower | UnitDiag> LMatrixType;
typedef TriangularView<LUMatrixType, Upper> UMatrixType;
public:
using Base::_solve_impl;
SuperLU() : Base() { init(); }
explicit SuperLU(const MatrixType &matrix) : Base() {
init();
Base::compute(matrix);
}
~SuperLU() {}
/** Performs a symbolic decomposition on the sparcity of \a matrix.
*
* This function is particularly useful when solving for several problems having the same structure.
*
* \sa factorize()
*/
void analyzePattern(const MatrixType &matrix) {
m_info = InvalidInput;
m_isInitialized = false;
Base::analyzePattern(matrix);
}
/** Performs a numeric decomposition of \a matrix
*
* The given matrix must has the same sparcity than the matrix on which the symbolic decomposition has been performed.
*
* \sa analyzePattern()
*/
void factorize(const MatrixType &matrix);
/** \internal */
template <typename Rhs, typename Dest>
void _solve_impl(const MatrixBase<Rhs> &b, MatrixBase<Dest> &dest) const;
inline const LMatrixType &matrixL() const {
if (m_extractedDataAreDirty) this->extractData();
return m_l;
}
inline const UMatrixType &matrixU() const {
if (m_extractedDataAreDirty) this->extractData();
return m_u;
}
inline const IntColVectorType &permutationP() const {
if (m_extractedDataAreDirty) this->extractData();
return m_p;
}
inline const IntRowVectorType &permutationQ() const {
if (m_extractedDataAreDirty) this->extractData();
return m_q;
}
Scalar determinant() const;
protected:
using Base::m_l;
using Base::m_matrix;
using Base::m_p;
using Base::m_q;
using Base::m_sluA;
using Base::m_sluB;
using Base::m_sluBerr;
using Base::m_sluCscale;
using Base::m_sluEqued;
using Base::m_sluEtree;
using Base::m_sluFerr;
using Base::m_sluL;
using Base::m_sluOptions;
using Base::m_sluRscale;
using Base::m_sluStat;
using Base::m_sluU;
using Base::m_sluX;
using Base::m_u;
using Base::m_analysisIsOk;
using Base::m_extractedDataAreDirty;
using Base::m_factorizationIsOk;
using Base::m_info;
using Base::m_isInitialized;
void init() {
Base::init();
set_default_options(&this->m_sluOptions);
m_sluOptions.PrintStat = NO;
m_sluOptions.ConditionNumber = NO;
m_sluOptions.Trans = NOTRANS;
m_sluOptions.ColPerm = COLAMD;
}
private:
SuperLU(SuperLU &) {}
};
template <typename MatrixType>
void SuperLU<MatrixType>::factorize(const MatrixType &a) {
eigen_assert(m_analysisIsOk && "You must first call analyzePattern()");
if (!m_analysisIsOk) {
m_info = InvalidInput;
return;
}
this->initFactorization(a);
m_sluOptions.ColPerm = COLAMD;
int info = 0;
RealScalar recip_pivot_growth, rcond;
RealScalar ferr, berr;
StatInit(&m_sluStat);
SuperLU_gssvx(&m_sluOptions, &m_sluA, m_q.data(), m_p.data(), &m_sluEtree[0], &m_sluEqued, &m_sluRscale[0],
&m_sluCscale[0], &m_sluL, &m_sluU, NULL, 0, &m_sluB, &m_sluX, &recip_pivot_growth, &rcond, &ferr, &berr,
&m_sluStat, &info, Scalar());
StatFree(&m_sluStat);
m_extractedDataAreDirty = true;
// FIXME how to better check for errors ???
m_info = info == 0 ? Success : NumericalIssue;
m_factorizationIsOk = true;
}
template <typename MatrixType>
template <typename Rhs, typename Dest>
void SuperLU<MatrixType>::_solve_impl(const MatrixBase<Rhs> &b, MatrixBase<Dest> &x) const {
eigen_assert(m_factorizationIsOk &&
"The decomposition is not in a valid state for solving, you must first call either compute() or "
"analyzePattern()/factorize()");
const Index rhsCols = b.cols();
eigen_assert(m_matrix.rows() == b.rows());
m_sluOptions.Trans = NOTRANS;
m_sluOptions.Fact = FACTORED;
m_sluOptions.IterRefine = NOREFINE;
m_sluFerr.resize(rhsCols);
m_sluBerr.resize(rhsCols);
Ref<const Matrix<typename Rhs::Scalar, Dynamic, Dynamic, ColMajor> > b_ref(b);
Ref<const Matrix<typename Dest::Scalar, Dynamic, Dynamic, ColMajor> > x_ref(x);
m_sluB = SluMatrix::Map(b_ref.const_cast_derived());
m_sluX = SluMatrix::Map(x_ref.const_cast_derived());
typename Rhs::PlainObject b_cpy;
if (m_sluEqued != 'N') {
b_cpy = b;
m_sluB = SluMatrix::Map(b_cpy.const_cast_derived());
}
StatInit(&m_sluStat);
int info = 0;
RealScalar recip_pivot_growth, rcond;
SuperLU_gssvx(&m_sluOptions, &m_sluA, m_q.data(), m_p.data(), &m_sluEtree[0], &m_sluEqued, &m_sluRscale[0],
&m_sluCscale[0], &m_sluL, &m_sluU, NULL, 0, &m_sluB, &m_sluX, &recip_pivot_growth, &rcond,
&m_sluFerr[0], &m_sluBerr[0], &m_sluStat, &info, Scalar());
StatFree(&m_sluStat);
if (x.derived().data() != x_ref.data()) x = x_ref;
m_info = info == 0 ? Success : NumericalIssue;
}
// the code of this extractData() function has been adapted from the SuperLU's Matlab support code,
//
// Copyright (c) 1994 by Xerox Corporation. All rights reserved.
//
// THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY
// EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
//
template <typename MatrixType, typename Derived>
void SuperLUBase<MatrixType, Derived>::extractData() const {
eigen_assert(m_factorizationIsOk &&
"The decomposition is not in a valid state for extracting factors, you must first call either compute() "
"or analyzePattern()/factorize()");
if (m_extractedDataAreDirty) {
int upper;
int fsupc, istart, nsupr;
int lastl = 0, lastu = 0;
SCformat *Lstore = static_cast<SCformat *>(m_sluL.Store);
NCformat *Ustore = static_cast<NCformat *>(m_sluU.Store);
Scalar *SNptr;
const Index size = m_matrix.rows();
m_l.resize(size, size);
m_l.resizeNonZeros(Lstore->nnz);
m_u.resize(size, size);
m_u.resizeNonZeros(Ustore->nnz);
int *Lcol = m_l.outerIndexPtr();
int *Lrow = m_l.innerIndexPtr();
Scalar *Lval = m_l.valuePtr();
int *Ucol = m_u.outerIndexPtr();
int *Urow = m_u.innerIndexPtr();
Scalar *Uval = m_u.valuePtr();
Ucol[0] = 0;
Ucol[0] = 0;
/* for each supernode */
for (int k = 0; k <= Lstore->nsuper; ++k) {
fsupc = L_FST_SUPC(k);
istart = L_SUB_START(fsupc);
nsupr = L_SUB_START(fsupc + 1) - istart;
upper = 1;
/* for each column in the supernode */
for (int j = fsupc; j < L_FST_SUPC(k + 1); ++j) {
SNptr = &((Scalar *)Lstore->nzval)[L_NZ_START(j)];
/* Extract U */
for (int i = U_NZ_START(j); i < U_NZ_START(j + 1); ++i) {
Uval[lastu] = ((Scalar *)Ustore->nzval)[i];
/* Matlab doesn't like explicit zero. */
if (Uval[lastu] != 0.0) Urow[lastu++] = U_SUB(i);
}
for (int i = 0; i < upper; ++i) {
/* upper triangle in the supernode */
Uval[lastu] = SNptr[i];
/* Matlab doesn't like explicit zero. */
if (Uval[lastu] != 0.0) Urow[lastu++] = L_SUB(istart + i);
}
Ucol[j + 1] = lastu;
/* Extract L */
Lval[lastl] = 1.0; /* unit diagonal */
Lrow[lastl++] = L_SUB(istart + upper - 1);
for (int i = upper; i < nsupr; ++i) {
Lval[lastl] = SNptr[i];
/* Matlab doesn't like explicit zero. */
if (Lval[lastl] != 0.0) Lrow[lastl++] = L_SUB(istart + i);
}
Lcol[j + 1] = lastl;
++upper;
} /* for j ... */
} /* for k ... */
// squeeze the matrices :
m_l.resizeNonZeros(lastl);
m_u.resizeNonZeros(lastu);
m_extractedDataAreDirty = false;
}
}
template <typename MatrixType>
typename SuperLU<MatrixType>::Scalar SuperLU<MatrixType>::determinant() const {
eigen_assert(m_factorizationIsOk &&
"The decomposition is not in a valid state for computing the determinant, you must first call either "
"compute() or analyzePattern()/factorize()");
if (m_extractedDataAreDirty) this->extractData();
Scalar det = Scalar(1);
for (int j = 0; j < m_u.cols(); ++j) {
if (m_u.outerIndexPtr()[j + 1] - m_u.outerIndexPtr()[j] > 0) {
int lastId = m_u.outerIndexPtr()[j + 1] - 1;
eigen_assert(m_u.innerIndexPtr()[lastId] <= j);
if (m_u.innerIndexPtr()[lastId] == j) det *= m_u.valuePtr()[lastId];
}
}
if (PermutationMap(m_p.data(), m_p.size()).determinant() * PermutationMap(m_q.data(), m_q.size()).determinant() < 0)
det = -det;
if (m_sluEqued != 'N')
return det / m_sluRscale.prod() / m_sluCscale.prod();
else
return det;
}
#ifdef EIGEN_PARSED_BY_DOXYGEN
#define EIGEN_SUPERLU_HAS_ILU
#endif
#ifdef EIGEN_SUPERLU_HAS_ILU
/** \ingroup SuperLUSupport_Module
* \class SuperILU
* \brief A sparse direct \b incomplete LU factorization and solver based on the SuperLU library
*
* This class allows to solve for an approximate solution of A.X = B sparse linear problems via an incomplete LU
* factorization using the SuperLU library. This class is aimed to be used as a preconditioner of the iterative linear
* solvers.
*
* \warning This class is only for the 4.x versions of SuperLU. The 3.x and 5.x versions are not supported.
*
* \tparam MatrixType_ the type of the sparse matrix A, it must be a SparseMatrix<>
*
* \implsparsesolverconcept
*
* \sa \ref TutorialSparseSolverConcept, class IncompleteLUT, class ConjugateGradient, class BiCGSTAB
*/
template <typename MatrixType_>
class SuperILU : public SuperLUBase<MatrixType_, SuperILU<MatrixType_> > {
public:
typedef SuperLUBase<MatrixType_, SuperILU> Base;
typedef MatrixType_ MatrixType;
typedef typename Base::Scalar Scalar;
typedef typename Base::RealScalar RealScalar;
public:
using Base::_solve_impl;
SuperILU() : Base() { init(); }
SuperILU(const MatrixType &matrix) : Base() {
init();
Base::compute(matrix);
}
~SuperILU() {}
/** Performs a symbolic decomposition on the sparcity of \a matrix.
*
* This function is particularly useful when solving for several problems having the same structure.
*
* \sa factorize()
*/
void analyzePattern(const MatrixType &matrix) { Base::analyzePattern(matrix); }
/** Performs a numeric decomposition of \a matrix
*
* The given matrix must has the same sparcity than the matrix on which the symbolic decomposition has been performed.
*
* \sa analyzePattern()
*/
void factorize(const MatrixType &matrix);
#ifndef EIGEN_PARSED_BY_DOXYGEN
/** \internal */
template <typename Rhs, typename Dest>
void _solve_impl(const MatrixBase<Rhs> &b, MatrixBase<Dest> &dest) const;
#endif // EIGEN_PARSED_BY_DOXYGEN
protected:
using Base::m_l;
using Base::m_matrix;
using Base::m_p;
using Base::m_q;
using Base::m_sluA;
using Base::m_sluB;
using Base::m_sluBerr;
using Base::m_sluCscale;
using Base::m_sluEqued;
using Base::m_sluEtree;
using Base::m_sluFerr;
using Base::m_sluL;
using Base::m_sluOptions;
using Base::m_sluRscale;
using Base::m_sluStat;
using Base::m_sluU;
using Base::m_sluX;
using Base::m_u;
using Base::m_analysisIsOk;
using Base::m_extractedDataAreDirty;
using Base::m_factorizationIsOk;
using Base::m_info;
using Base::m_isInitialized;
void init() {
Base::init();
ilu_set_default_options(&m_sluOptions);
m_sluOptions.PrintStat = NO;
m_sluOptions.ConditionNumber = NO;
m_sluOptions.Trans = NOTRANS;
m_sluOptions.ColPerm = MMD_AT_PLUS_A;
// no attempt to preserve column sum
m_sluOptions.ILU_MILU = SILU;
// only basic ILU(k) support -- no direct control over memory consumption
// better to use ILU_DropRule = DROP_BASIC | DROP_AREA
// and set ILU_FillFactor to max memory growth
m_sluOptions.ILU_DropRule = DROP_BASIC;
m_sluOptions.ILU_DropTol = NumTraits<Scalar>::dummy_precision() * 10;
}
private:
SuperILU(SuperILU &) {}
};
template <typename MatrixType>
void SuperILU<MatrixType>::factorize(const MatrixType &a) {
eigen_assert(m_analysisIsOk && "You must first call analyzePattern()");
if (!m_analysisIsOk) {
m_info = InvalidInput;
return;
}
this->initFactorization(a);
int info = 0;
RealScalar recip_pivot_growth, rcond;
StatInit(&m_sluStat);
SuperLU_gsisx(&m_sluOptions, &m_sluA, m_q.data(), m_p.data(), &m_sluEtree[0], &m_sluEqued, &m_sluRscale[0],
&m_sluCscale[0], &m_sluL, &m_sluU, NULL, 0, &m_sluB, &m_sluX, &recip_pivot_growth, &rcond, &m_sluStat,
&info, Scalar());
StatFree(&m_sluStat);
// FIXME how to better check for errors ???
m_info = info == 0 ? Success : NumericalIssue;
m_factorizationIsOk = true;
}
#ifndef EIGEN_PARSED_BY_DOXYGEN
template <typename MatrixType>
template <typename Rhs, typename Dest>
void SuperILU<MatrixType>::_solve_impl(const MatrixBase<Rhs> &b, MatrixBase<Dest> &x) const {
eigen_assert(m_factorizationIsOk &&
"The decomposition is not in a valid state for solving, you must first call either compute() or "
"analyzePattern()/factorize()");
const int rhsCols = b.cols();
eigen_assert(m_matrix.rows() == b.rows());
m_sluOptions.Trans = NOTRANS;
m_sluOptions.Fact = FACTORED;
m_sluOptions.IterRefine = NOREFINE;
m_sluFerr.resize(rhsCols);
m_sluBerr.resize(rhsCols);
Ref<const Matrix<typename Rhs::Scalar, Dynamic, Dynamic, ColMajor> > b_ref(b);
Ref<const Matrix<typename Dest::Scalar, Dynamic, Dynamic, ColMajor> > x_ref(x);
m_sluB = SluMatrix::Map(b_ref.const_cast_derived());
m_sluX = SluMatrix::Map(x_ref.const_cast_derived());
typename Rhs::PlainObject b_cpy;
if (m_sluEqued != 'N') {
b_cpy = b;
m_sluB = SluMatrix::Map(b_cpy.const_cast_derived());
}
int info = 0;
RealScalar recip_pivot_growth, rcond;
StatInit(&m_sluStat);
SuperLU_gsisx(&m_sluOptions, &m_sluA, m_q.data(), m_p.data(), &m_sluEtree[0], &m_sluEqued, &m_sluRscale[0],
&m_sluCscale[0], &m_sluL, &m_sluU, NULL, 0, &m_sluB, &m_sluX, &recip_pivot_growth, &rcond, &m_sluStat,
&info, Scalar());
StatFree(&m_sluStat);
if (x.derived().data() != x_ref.data()) x = x_ref;
m_info = info == 0 ? Success : NumericalIssue;
}
#endif
#endif
} // end namespace Eigen
#endif // EIGEN_SUPERLUSUPPORT_H