| // This file is part of Eigen, a lightweight C++ template library |
| // for linear algebra. |
| // |
| // Copyright (C) 2012 Désiré Nuentsa-Wakam <desire.nuentsa_wakam@inria.fr> |
| // Copyright (C) 2012 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_SPARSELU_SUPERNODAL_MATRIX_H |
| #define EIGEN_SPARSELU_SUPERNODAL_MATRIX_H |
| |
| // IWYU pragma: private |
| #include "./InternalHeaderCheck.h" |
| |
| namespace Eigen { |
| namespace internal { |
| |
| /** \ingroup SparseLU_Module |
| * \brief a class to manipulate the L supernodal factor from the SparseLU factorization |
| * |
| * This class contain the data to easily store |
| * and manipulate the supernodes during the factorization and solution phase of Sparse LU. |
| * Only the lower triangular matrix has supernodes. |
| * |
| * NOTE : This class corresponds to the SCformat structure in SuperLU |
| * |
| */ |
| /* TODO |
| * InnerIterator as for sparsematrix |
| * SuperInnerIterator to iterate through all supernodes |
| * Function for triangular solve |
| */ |
| template <typename Scalar_, typename StorageIndex_> |
| class MappedSuperNodalMatrix { |
| public: |
| typedef Scalar_ Scalar; |
| typedef StorageIndex_ StorageIndex; |
| typedef Matrix<StorageIndex, Dynamic, 1> IndexVector; |
| typedef Matrix<Scalar, Dynamic, 1> ScalarVector; |
| |
| public: |
| MappedSuperNodalMatrix() {} |
| MappedSuperNodalMatrix(Index m, Index n, ScalarVector& nzval, IndexVector& nzval_colptr, IndexVector& rowind, |
| IndexVector& rowind_colptr, IndexVector& col_to_sup, IndexVector& sup_to_col) { |
| setInfos(m, n, nzval, nzval_colptr, rowind, rowind_colptr, col_to_sup, sup_to_col); |
| } |
| |
| ~MappedSuperNodalMatrix() {} |
| /** |
| * Set appropriate pointers for the lower triangular supernodal matrix |
| * These infos are available at the end of the numerical factorization |
| * FIXME This class will be modified such that it can be use in the course |
| * of the factorization. |
| */ |
| void setInfos(Index m, Index n, ScalarVector& nzval, IndexVector& nzval_colptr, IndexVector& rowind, |
| IndexVector& rowind_colptr, IndexVector& col_to_sup, IndexVector& sup_to_col) { |
| m_row = m; |
| m_col = n; |
| m_nzval = nzval.data(); |
| m_nzval_colptr = nzval_colptr.data(); |
| m_rowind = rowind.data(); |
| m_rowind_colptr = rowind_colptr.data(); |
| m_nsuper = col_to_sup(n); |
| m_col_to_sup = col_to_sup.data(); |
| m_sup_to_col = sup_to_col.data(); |
| } |
| |
| /** |
| * Number of rows |
| */ |
| Index rows() const { return m_row; } |
| |
| /** |
| * Number of columns |
| */ |
| Index cols() const { return m_col; } |
| |
| /** |
| * Return the array of nonzero values packed by column |
| * |
| * The size is nnz |
| */ |
| Scalar* valuePtr() { return m_nzval; } |
| |
| const Scalar* valuePtr() const { return m_nzval; } |
| /** |
| * Return the pointers to the beginning of each column in \ref valuePtr() |
| */ |
| StorageIndex* colIndexPtr() { return m_nzval_colptr; } |
| |
| const StorageIndex* colIndexPtr() const { return m_nzval_colptr; } |
| |
| /** |
| * Return the array of compressed row indices of all supernodes |
| */ |
| StorageIndex* rowIndex() { return m_rowind; } |
| |
| const StorageIndex* rowIndex() const { return m_rowind; } |
| |
| /** |
| * Return the location in \em rowvaluePtr() which starts each column |
| */ |
| StorageIndex* rowIndexPtr() { return m_rowind_colptr; } |
| |
| const StorageIndex* rowIndexPtr() const { return m_rowind_colptr; } |
| |
| /** |
| * Return the array of column-to-supernode mapping |
| */ |
| StorageIndex* colToSup() { return m_col_to_sup; } |
| |
| const StorageIndex* colToSup() const { return m_col_to_sup; } |
| /** |
| * Return the array of supernode-to-column mapping |
| */ |
| StorageIndex* supToCol() { return m_sup_to_col; } |
| |
| const StorageIndex* supToCol() const { return m_sup_to_col; } |
| |
| /** |
| * Return the number of supernodes |
| */ |
| Index nsuper() const { return m_nsuper; } |
| |
| class InnerIterator; |
| template <typename Dest> |
| void solveInPlace(MatrixBase<Dest>& X) const; |
| template <bool Conjugate, typename Dest> |
| void solveTransposedInPlace(MatrixBase<Dest>& X) const; |
| |
| protected: |
| Index m_row; // Number of rows |
| Index m_col; // Number of columns |
| Index m_nsuper; // Number of supernodes |
| Scalar* m_nzval; // array of nonzero values packed by column |
| StorageIndex* m_nzval_colptr; // nzval_colptr[j] Stores the location in nzval[] which starts column j |
| StorageIndex* m_rowind; // Array of compressed row indices of rectangular supernodes |
| StorageIndex* m_rowind_colptr; // rowind_colptr[j] stores the location in rowind[] which starts column j |
| StorageIndex* m_col_to_sup; // col_to_sup[j] is the supernode number to which column j belongs |
| StorageIndex* m_sup_to_col; // sup_to_col[s] points to the starting column of the s-th supernode |
| |
| private: |
| }; |
| |
| /** |
| * \brief InnerIterator class to iterate over nonzero values of the current column in the supernodal matrix L |
| * |
| */ |
| template <typename Scalar, typename StorageIndex> |
| class MappedSuperNodalMatrix<Scalar, StorageIndex>::InnerIterator { |
| public: |
| InnerIterator(const MappedSuperNodalMatrix& mat, Index outer) |
| : m_matrix(mat), |
| m_outer(outer), |
| m_supno(mat.colToSup()[outer]), |
| m_idval(mat.colIndexPtr()[outer]), |
| m_startidval(m_idval), |
| m_endidval(mat.colIndexPtr()[outer + 1]), |
| m_idrow(mat.rowIndexPtr()[mat.supToCol()[mat.colToSup()[outer]]]), |
| m_endidrow(mat.rowIndexPtr()[mat.supToCol()[mat.colToSup()[outer]] + 1]) {} |
| inline InnerIterator& operator++() { |
| m_idval++; |
| m_idrow++; |
| return *this; |
| } |
| inline Scalar value() const { return m_matrix.valuePtr()[m_idval]; } |
| |
| inline Scalar& valueRef() { return const_cast<Scalar&>(m_matrix.valuePtr()[m_idval]); } |
| |
| inline Index index() const { return m_matrix.rowIndex()[m_idrow]; } |
| inline Index row() const { return index(); } |
| inline Index col() const { return m_outer; } |
| |
| inline Index supIndex() const { return m_supno; } |
| |
| inline operator bool() const { |
| return ((m_idval < m_endidval) && (m_idval >= m_startidval) && (m_idrow < m_endidrow)); |
| } |
| |
| protected: |
| const MappedSuperNodalMatrix& m_matrix; // Supernodal lower triangular matrix |
| const Index m_outer; // Current column |
| const Index m_supno; // Current SuperNode number |
| Index m_idval; // Index to browse the values in the current column |
| const Index m_startidval; // Start of the column value |
| const Index m_endidval; // End of the column value |
| Index m_idrow; // Index to browse the row indices |
| Index m_endidrow; // End index of row indices of the current column |
| }; |
| |
| /** |
| * \brief Solve with the supernode triangular matrix |
| * |
| */ |
| template <typename Scalar, typename Index_> |
| template <typename Dest> |
| void MappedSuperNodalMatrix<Scalar, Index_>::solveInPlace(MatrixBase<Dest>& X) const { |
| /* Explicit type conversion as the Index type of MatrixBase<Dest> may be wider than Index */ |
| // eigen_assert(X.rows() <= NumTraits<Index>::highest()); |
| // eigen_assert(X.cols() <= NumTraits<Index>::highest()); |
| Index n = int(X.rows()); |
| Index nrhs = Index(X.cols()); |
| const Scalar* Lval = valuePtr(); // Nonzero values |
| Matrix<Scalar, Dynamic, Dest::ColsAtCompileTime, ColMajor> work(n, nrhs); // working vector |
| work.setZero(); |
| for (Index k = 0; k <= nsuper(); k++) { |
| Index fsupc = supToCol()[k]; // First column of the current supernode |
| Index istart = rowIndexPtr()[fsupc]; // Pointer index to the subscript of the current column |
| Index nsupr = rowIndexPtr()[fsupc + 1] - istart; // Number of rows in the current supernode |
| Index nsupc = supToCol()[k + 1] - fsupc; // Number of columns in the current supernode |
| Index nrow = nsupr - nsupc; // Number of rows in the non-diagonal part of the supernode |
| Index irow; // Current index row |
| |
| if (nsupc == 1) { |
| for (Index j = 0; j < nrhs; j++) { |
| InnerIterator it(*this, fsupc); |
| ++it; // Skip the diagonal element |
| for (; it; ++it) { |
| irow = it.row(); |
| X(irow, j) -= X(fsupc, j) * it.value(); |
| } |
| } |
| } else { |
| // The supernode has more than one column |
| Index luptr = colIndexPtr()[fsupc]; |
| Index lda = colIndexPtr()[fsupc + 1] - luptr; |
| |
| // Triangular solve |
| Map<const Matrix<Scalar, Dynamic, Dynamic, ColMajor>, 0, OuterStride<> > A(&(Lval[luptr]), nsupc, nsupc, |
| OuterStride<>(lda)); |
| typename Dest::RowsBlockXpr U = X.derived().middleRows(fsupc, nsupc); |
| U = A.template triangularView<UnitLower>().solve(U); |
| // Matrix-vector product |
| new (&A) Map<const Matrix<Scalar, Dynamic, Dynamic, ColMajor>, 0, OuterStride<> >(&(Lval[luptr + nsupc]), nrow, |
| nsupc, OuterStride<>(lda)); |
| work.topRows(nrow).noalias() = A * U; |
| |
| // Begin Scatter |
| for (Index j = 0; j < nrhs; j++) { |
| Index iptr = istart + nsupc; |
| for (Index i = 0; i < nrow; i++) { |
| irow = rowIndex()[iptr]; |
| X(irow, j) -= work(i, j); // Scatter operation |
| work(i, j) = Scalar(0); |
| iptr++; |
| } |
| } |
| } |
| } |
| } |
| |
| template <typename Scalar, typename Index_> |
| template <bool Conjugate, typename Dest> |
| void MappedSuperNodalMatrix<Scalar, Index_>::solveTransposedInPlace(MatrixBase<Dest>& X) const { |
| using numext::conj; |
| Index n = int(X.rows()); |
| Index nrhs = Index(X.cols()); |
| const Scalar* Lval = valuePtr(); // Nonzero values |
| Matrix<Scalar, Dynamic, Dest::ColsAtCompileTime, ColMajor> work(n, nrhs); // working vector |
| work.setZero(); |
| for (Index k = nsuper(); k >= 0; k--) { |
| Index fsupc = supToCol()[k]; // First column of the current supernode |
| Index istart = rowIndexPtr()[fsupc]; // Pointer index to the subscript of the current column |
| Index nsupr = rowIndexPtr()[fsupc + 1] - istart; // Number of rows in the current supernode |
| Index nsupc = supToCol()[k + 1] - fsupc; // Number of columns in the current supernode |
| Index nrow = nsupr - nsupc; // Number of rows in the non-diagonal part of the supernode |
| Index irow; // Current index row |
| |
| if (nsupc == 1) { |
| for (Index j = 0; j < nrhs; j++) { |
| InnerIterator it(*this, fsupc); |
| ++it; // Skip the diagonal element |
| for (; it; ++it) { |
| irow = it.row(); |
| X(fsupc, j) -= X(irow, j) * (Conjugate ? conj(it.value()) : it.value()); |
| } |
| } |
| } else { |
| // The supernode has more than one column |
| Index luptr = colIndexPtr()[fsupc]; |
| Index lda = colIndexPtr()[fsupc + 1] - luptr; |
| |
| // Begin Gather |
| for (Index j = 0; j < nrhs; j++) { |
| Index iptr = istart + nsupc; |
| for (Index i = 0; i < nrow; i++) { |
| irow = rowIndex()[iptr]; |
| work.topRows(nrow)(i, j) = X(irow, j); // Gather operation |
| iptr++; |
| } |
| } |
| |
| // Matrix-vector product with transposed submatrix |
| Map<const Matrix<Scalar, Dynamic, Dynamic, ColMajor>, 0, OuterStride<> > A(&(Lval[luptr + nsupc]), nrow, nsupc, |
| OuterStride<>(lda)); |
| typename Dest::RowsBlockXpr U = X.derived().middleRows(fsupc, nsupc); |
| if (Conjugate) |
| U = U - A.adjoint() * work.topRows(nrow); |
| else |
| U = U - A.transpose() * work.topRows(nrow); |
| |
| // Triangular solve (of transposed diagonal block) |
| new (&A) Map<const Matrix<Scalar, Dynamic, Dynamic, ColMajor>, 0, OuterStride<> >(&(Lval[luptr]), nsupc, nsupc, |
| OuterStride<>(lda)); |
| if (Conjugate) |
| U = A.adjoint().template triangularView<UnitUpper>().solve(U); |
| else |
| U = A.transpose().template triangularView<UnitUpper>().solve(U); |
| } |
| } |
| } |
| |
| } // end namespace internal |
| |
| } // end namespace Eigen |
| |
| #endif // EIGEN_SPARSELU_MATRIX_H |