// This file is part of Eigen, a lightweight C++ template library // for linear algebra. // // Copyright (C) 2008-2009 Gael Guennebaud // // Eigen is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 3 of the License, or (at your option) any later version. // // Alternatively, you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation; either version 2 of // the License, or (at your option) any later version. // // Eigen is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS // FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License or the // GNU General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License and a copy of the GNU General Public License along with // Eigen. If not, see . #ifndef EIGEN_SUPERLUSUPPORT_H #define EIGEN_SUPERLUSUPPORT_H // declaration of gssvx taken from GMM++ #define DECL_GSSVX(NAMESPACE,FNAME,FLOATTYPE,KEYTYPE) \ 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) { \ using namespace NAMESPACE; \ mem_usage_t mem_usage; \ NAMESPACE::FNAME(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 */ \ } DECL_GSSVX(SuperLU_S,sgssvx,float,float) DECL_GSSVX(SuperLU_C,cgssvx,float,std::complex) DECL_GSSVX(SuperLU_D,dgssvx,double,double) DECL_GSSVX(SuperLU_Z,zgssvx,double,std::complex) #ifdef MILU_ALPHA #define EIGEN_SUPERLU_HAS_ILU #endif #ifdef EIGEN_SUPERLU_HAS_ILU // similarly for the incomplete factorization using gsisx #define DECL_GSISX(NAMESPACE,FNAME,FLOATTYPE,KEYTYPE) \ 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) { \ using namespace NAMESPACE; \ mem_usage_t mem_usage; \ NAMESPACE::FNAME(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(SuperLU_S,sgsisx,float,float) DECL_GSISX(SuperLU_C,cgsisx,float,std::complex) DECL_GSISX(SuperLU_D,dgsisx,double,double) DECL_GSISX(SuperLU_Z,zgsisx,double,std::complex) #endif template 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(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 { ei_assert(false && "storage type not supported"); Store = 0; } } template void setScalarType() { if (ei_is_same_type::ret) Dtype = SLU_S; else if (ei_is_same_type::ret) Dtype = SLU_D; else if (ei_is_same_type >::ret) Dtype = SLU_C; else if (ei_is_same_type >::ret) Dtype = SLU_Z; else { ei_assert(false && "Scalar type not supported by SuperLU"); } } template static SluMatrix Map(Matrix& mat) { typedef Matrix MatrixType; ei_assert( ((Options&RowMajor)!=RowMajor) && "row-major dense matrices is not supported by SuperLU"); SluMatrix res; res.setStorageType(SLU_DN); res.setScalarType(); res.Mtype = SLU_GE; res.nrow = mat.rows(); res.ncol = mat.cols(); res.storage.lda = MatrixType::IsVectorAtCompileTime ? mat.size() : mat.outerStride(); res.storage.values = mat.data(); return res; } template static SluMatrix Map(SparseMatrixBase& 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.derived()._valuePtr(); res.storage.innerInd = mat.derived()._innerIndexPtr(); res.storage.outerInd = mat.derived()._outerIndexPtr(); res.setScalarType(); // FIXME the following is not very accurate if (MatrixType::Flags & Upper) res.Mtype = SLU_TRU; if (MatrixType::Flags & Lower) res.Mtype = SLU_TRL; if (MatrixType::Flags & SelfAdjoint) ei_assert(false && "SelfAdjoint matrix shape not supported by SuperLU"); return res; } }; template struct SluMatrixMapHelper > { typedef Matrix MatrixType; static void run(MatrixType& mat, SluMatrix& res) { ei_assert( ((Options&RowMajor)!=RowMajor) && "row-major dense matrices is not supported by SuperLU"); res.setStorageType(SLU_DN); res.setScalarType(); res.Mtype = SLU_GE; res.nrow = mat.rows(); res.ncol = mat.cols(); res.storage.lda = mat.outerStride(); res.storage.values = mat.data(); } }; template struct SluMatrixMapHelper > { 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(); // FIXME the following is not very accurate if (MatrixType::Flags & Upper) res.Mtype = SLU_TRU; if (MatrixType::Flags & Lower) res.Mtype = SLU_TRL; if (MatrixType::Flags & SelfAdjoint) ei_assert(false && "SelfAdjoint matrix shape not supported by SuperLU"); } }; template SluMatrix SparseMatrixBase::asSluMatrix() { return SluMatrix::Map(derived()); } /** View a Super LU matrix as an Eigen expression */ template MappedSparseMatrix::MappedSparseMatrix(SluMatrix& sluMat) { if ((Flags&RowMajorBit)==RowMajorBit) { assert(sluMat.Stype == SLU_NR); m_innerSize = sluMat.ncol; m_outerSize = sluMat.nrow; } else { assert(sluMat.Stype == SLU_NC); m_innerSize = sluMat.nrow; m_outerSize = sluMat.ncol; } m_outerIndex = sluMat.storage.outerInd; m_innerIndices = sluMat.storage.innerInd; m_values = reinterpret_cast(sluMat.storage.values); m_nnz = sluMat.storage.outerInd[m_outerSize]; } template class SparseLU : public SparseLU { protected: typedef SparseLU Base; typedef typename Base::Scalar Scalar; typedef typename Base::RealScalar RealScalar; typedef Matrix Vector; typedef Matrix IntRowVectorType; typedef Matrix IntColVectorType; typedef SparseMatrix LMatrixType; typedef SparseMatrix UMatrixType; using Base::m_flags; using Base::m_status; public: SparseLU(int flags = NaturalOrdering) : Base(flags) { } SparseLU(const MatrixType& matrix, int flags = NaturalOrdering) : Base(flags) { compute(matrix); } ~SparseLU() { Destroy_SuperNode_Matrix(&m_sluL); Destroy_CompCol_Matrix(&m_sluU); } inline const LMatrixType& matrixL() const { if (m_extractedDataAreDirty) extractData(); return m_l; } inline const UMatrixType& matrixU() const { if (m_extractedDataAreDirty) extractData(); return m_u; } inline const IntColVectorType& permutationP() const { if (m_extractedDataAreDirty) extractData(); return m_p; } inline const IntRowVectorType& permutationQ() const { if (m_extractedDataAreDirty) extractData(); return m_q; } Scalar determinant() const; template bool solve(const MatrixBase &b, MatrixBase* x, const int transposed = SvNoTrans) const; void compute(const MatrixType& matrix); protected: void extractData() const; protected: // cached data to reduce reallocation, etc. mutable LMatrixType m_l; mutable UMatrixType m_u; mutable IntColVectorType m_p; mutable IntRowVectorType m_q; mutable SparseMatrix m_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 m_sluEtree; mutable std::vector m_sluRscale, m_sluCscale; mutable std::vector m_sluFerr, m_sluBerr; mutable char m_sluEqued; mutable bool m_extractedDataAreDirty; }; template void SparseLU::compute(const MatrixType& a) { const int size = a.rows(); m_matrix = a; set_default_options(&m_sluOptions); m_sluOptions.ColPerm = NATURAL; m_sluOptions.PrintStat = NO; m_sluOptions.ConditionNumber = NO; m_sluOptions.Trans = NOTRANS; // m_sluOptions.Equil = NO; switch (Base::orderingMethod()) { case NaturalOrdering : m_sluOptions.ColPerm = NATURAL; break; case MinimumDegree_AT_PLUS_A : m_sluOptions.ColPerm = MMD_AT_PLUS_A; break; case MinimumDegree_ATA : m_sluOptions.ColPerm = MMD_ATA; break; case ColApproxMinimumDegree : m_sluOptions.ColPerm = COLAMD; break; default: //std::cerr << "Eigen: ordering method \"" << Base::orderingMethod() << "\" not supported by the SuperLU backend\n"; m_sluOptions.ColPerm = NATURAL; }; m_sluA = m_matrix.asSluMatrix(); memset(&m_sluL,0,sizeof m_sluL); memset(&m_sluU,0,sizeof m_sluU); //m_sluEqued = 'B'; int info = 0; m_p.resize(size); m_q.resize(size); m_sluRscale.resize(size); m_sluCscale.resize(size); m_sluEtree.resize(size); RealScalar recip_pivot_gross, rcond; RealScalar ferr, berr; // set empty B and X m_sluB.setStorageType(SLU_DN); m_sluB.setScalarType(); m_sluB.Mtype = SLU_GE; m_sluB.storage.values = 0; m_sluB.nrow = m_sluB.ncol = 0; m_sluB.storage.lda = size; m_sluX = m_sluB; StatInit(&m_sluStat); if (m_flags&IncompleteFactorization) { #ifdef EIGEN_SUPERLU_HAS_ILU ilu_set_default_options(&m_sluOptions); // 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 = Base::m_precision; 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_gross, &rcond, &m_sluStat, &info, Scalar()); #else //std::cerr << "Incomplete factorization is only available in SuperLU v4\n"; Base::m_succeeded = false; return; #endif } else { 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_gross, &rcond, &ferr, &berr, &m_sluStat, &info, Scalar()); } StatFree(&m_sluStat); m_extractedDataAreDirty = true; // FIXME how to better check for errors ??? Base::m_succeeded = (info == 0); } template template bool SparseLU::solve(const MatrixBase &b, MatrixBase *x, const int transposed) const { const int size = m_matrix.rows(); const int rhsCols = b.cols(); ei_assert(size==b.rows()); switch (transposed) { case SvNoTrans : m_sluOptions.Trans = NOTRANS; break; case SvTranspose : m_sluOptions.Trans = TRANS; break; case SvAdjoint : m_sluOptions.Trans = CONJ; break; default: //std::cerr << "Eigen: transposition option \"" << transposed << "\" not supported by the SuperLU backend\n"; m_sluOptions.Trans = NOTRANS; } m_sluOptions.Fact = FACTORED; m_sluOptions.IterRefine = NOREFINE; m_sluFerr.resize(rhsCols); m_sluBerr.resize(rhsCols); m_sluB = SluMatrix::Map(b.const_cast_derived()); m_sluX = SluMatrix::Map(x->derived()); StatInit(&m_sluStat); int info = 0; RealScalar recip_pivot_gross, rcond; if (m_flags&IncompleteFactorization) { #ifdef EIGEN_SUPERLU_HAS_ILU 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_gross, &rcond, &m_sluStat, &info, Scalar()); #else //std::cerr << "Incomplete factorization is only available in SuperLU v4\n"; return false; #endif } else { 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_gross, &rcond, &m_sluFerr[0], &m_sluBerr[0], &m_sluStat, &info, Scalar()); } StatFree(&m_sluStat); // reset to previous state m_sluOptions.Trans = NOTRANS; return info==0; } // // 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 void SparseLU::extractData() const { if (m_extractedDataAreDirty) { int upper; int fsupc, istart, nsupr; int lastl = 0, lastu = 0; SCformat *Lstore = static_cast(m_sluL.Store); NCformat *Ustore = static_cast(m_sluU.Store); Scalar *SNptr; const int 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 SparseLU::Scalar SparseLU::determinant() const { if (m_extractedDataAreDirty) extractData(); // TODO this code could be moved to the default/base backend // FIXME perhaps we have to take into account the scale factors m_sluRscale and m_sluCscale ??? Scalar det = Scalar(1); for (int j=0; j 0) { int lastId = m_u._outerIndexPtr()[j+1]-1; ei_assert(m_u._innerIndexPtr()[lastId]<=j); if (m_u._innerIndexPtr()[lastId]==j) { det *= m_u._valuePtr()[lastId]; } } // std::cout << m_sluRscale[j] << " " << m_sluCscale[j] << " "; } return det; } #endif // EIGEN_SUPERLUSUPPORT_H