From 5b1d0cebc5c30975f6f392d6f7d49d35e2a3508f Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Sun, 5 Apr 2009 16:30:10 +0000 Subject: [PATCH] sparse module: new API proposal for triangular solves and experimental solver support with a sparse matrix as the rhs. --- Eigen/Sparse | 1 + Eigen/src/Sparse/AmbiVector.h | 15 +- Eigen/src/Sparse/SparseBlock.h | 36 ++++- Eigen/src/Sparse/SparseLDLT.h | 8 +- Eigen/src/Sparse/SparseLLT.h | 6 +- Eigen/src/Sparse/SparseMatrix.h | 1 + Eigen/src/Sparse/SparseMatrixBase.h | 7 + Eigen/src/Sparse/SparseTriangular.h | 60 ++++++++ Eigen/src/Sparse/SparseUtil.h | 1 + Eigen/src/Sparse/SuperLUSupport.h | 2 +- Eigen/src/Sparse/TriangularSolver.h | 218 +++++++++++++++++++++++----- test/sparse_product.cpp | 6 +- test/sparse_solvers.cpp | 38 ++++- 13 files changed, 335 insertions(+), 64 deletions(-) create mode 100644 Eigen/src/Sparse/SparseTriangular.h diff --git a/Eigen/Sparse b/Eigen/Sparse index 6de24d34d..b0f2b2a12 100644 --- a/Eigen/Sparse +++ b/Eigen/Sparse @@ -104,6 +104,7 @@ namespace Eigen { #include "src/Sparse/SparseFlagged.h" #include "src/Sparse/SparseProduct.h" #include "src/Sparse/SparseDiagonalProduct.h" +#include "src/Sparse/SparseTriangular.h" #include "src/Sparse/TriangularSolver.h" #include "src/Sparse/SparseLLT.h" #include "src/Sparse/SparseLDLT.h" diff --git a/Eigen/src/Sparse/AmbiVector.h b/Eigen/src/Sparse/AmbiVector.h index 75001a2fa..e7a7c8784 100644 --- a/Eigen/src/Sparse/AmbiVector.h +++ b/Eigen/src/Sparse/AmbiVector.h @@ -36,7 +36,7 @@ template class AmbiVector typedef _Scalar Scalar; typedef typename NumTraits::Real RealScalar; AmbiVector(int size) - : m_buffer(0), m_size(0), m_allocatedSize(0), m_allocatedElements(0), m_mode(-1) + : m_buffer(0), m_zero(0), m_size(0), m_allocatedSize(0), m_allocatedElements(0), m_mode(-1) { resize(size); } @@ -44,7 +44,7 @@ template class AmbiVector void init(RealScalar estimatedDensity); void init(int mode); - void nonZeros() const; + int nonZeros() const; /** Specifies a sub-vector to work on */ void setBounds(int start, int end) { m_start = start; m_end = end; } @@ -53,7 +53,7 @@ template class AmbiVector void restart(); Scalar& coeffRef(int i); - Scalar coeff(int i); + Scalar& coeff(int i); class Iterator; @@ -112,6 +112,7 @@ template class AmbiVector // used to store data in both mode Scalar* m_buffer; + Scalar m_zero; int m_size; int m_start; int m_end; @@ -131,7 +132,7 @@ template class AmbiVector /** \returns the number of non zeros in the current sub vector */ template -void AmbiVector::nonZeros() const +int AmbiVector::nonZeros() const { if (m_mode==IsSparse) return m_llSize; @@ -254,7 +255,7 @@ Scalar& AmbiVector::coeffRef(int i) } template -Scalar AmbiVector::coeff(int i) +Scalar& AmbiVector::coeff(int i) { if (m_mode==IsDense) return m_buffer[i]; @@ -264,7 +265,7 @@ Scalar AmbiVector::coeff(int i) ei_assert(m_mode==IsSparse); if ((m_llSize==0) || (i::coeff(int i) if (llElements[elid].index==i) return llElements[m_llCurrent].value; else - return Scalar(0); + return m_zero; } } } diff --git a/Eigen/src/Sparse/SparseBlock.h b/Eigen/src/Sparse/SparseBlock.h index c39066676..86567f1a5 100644 --- a/Eigen/src/Sparse/SparseBlock.h +++ b/Eigen/src/Sparse/SparseBlock.h @@ -150,6 +150,21 @@ class SparseInnerVectorSet, Size> { return operator=(other); } + + int nonZeros() const + { + int count = 0; + for (int j=0; j0); + return m_matrix.data()[m_outerStart].vale(m_matrix.data()[m_outerStart].size()-1); + } // template // inline SparseInnerVectorSet& operator=(const SparseMatrixBase& other) @@ -172,12 +187,12 @@ class SparseInnerVectorSet, Size> /*************************************************************************** * specialisation for SparseMatrix ***************************************************************************/ -/* + template class SparseInnerVectorSet, Size> : public SparseMatrixBase, Size> > { - typedef DynamicSparseMatrix<_Scalar, _Options> MatrixType; + typedef SparseMatrix<_Scalar, _Options> MatrixType; enum { IsRowMajor = ei_traits::IsRowMajor }; public: @@ -233,7 +248,20 @@ class SparseInnerVectorSet, Size> { return m_matrix._valuePtr() + m_matrix._outerIndexPtr()[m_outerStart]; } inline const int* _innerIndexPtr() const { return m_matrix._innerIndexPtr() + m_matrix._outerIndexPtr()[m_outerStart]; } - inline const int* _outerIndexPtr() const { return m_matrix._outerIndexPtr() + m_outerStart; } + inline const int* _outerIndexPtr() const + { return m_matrix._outerIndexPtr() + m_outerStart; } + + int nonZeros() const + { + return size_t(m_matrix._outerIndexPtr()[m_outerStart+m_outerSize.value()]) + - size_t(m_matrix._outerIndexPtr()[m_outerStart]); } + + const Scalar& lastCoeff() const + { + EIGEN_STATIC_ASSERT_VECTOR_ONLY(SparseInnerVectorSet); + ei_assert(nonZeros()>0); + return m_matrix._valuePtr()[m_matrix._outerIndexPtr()[m_outerStart+1]-1]; + } // template // inline SparseInnerVectorSet& operator=(const SparseMatrixBase& other) @@ -251,7 +279,7 @@ class SparseInnerVectorSet, Size> const ei_int_if_dynamic m_outerSize; }; -*/ + //---------- /** \returns the i-th row of the matrix \c *this. For row-major matrix only. */ diff --git a/Eigen/src/Sparse/SparseLDLT.h b/Eigen/src/Sparse/SparseLDLT.h index a1bac4d08..80d67ec8a 100644 --- a/Eigen/src/Sparse/SparseLDLT.h +++ b/Eigen/src/Sparse/SparseLDLT.h @@ -79,7 +79,7 @@ class SparseLDLT protected: typedef typename MatrixType::Scalar Scalar; typedef typename NumTraits::Real RealScalar; - typedef SparseMatrix CholMatrixType; + typedef SparseMatrix CholMatrixType; typedef Matrix VectorType; enum { @@ -137,7 +137,7 @@ class SparseLDLT * overloads the MemoryEfficient flags) * * \sa flags() */ - void settagss(int f) { m_flags = f; } + void settags(int f) { m_flags = f; } /** \returns the current flags */ int flags() const { return m_flags; } @@ -333,12 +333,12 @@ bool SparseLDLT::solveInPlace(MatrixBase &b) const return false; if (m_matrix.nonZeros()>0) // otherwise L==I - m_matrix.solveTriangularInPlace(b); + m_matrix.template triangular().solveInPlace(b); b = b.cwise() / m_diag; // FIXME should be .adjoint() but it fails to compile... if (m_matrix.nonZeros()>0) // otherwise L==I - m_matrix.transpose().solveTriangularInPlace(b); + m_matrix.transpose().template triangular().solveInPlace(b); return true; } diff --git a/Eigen/src/Sparse/SparseLLT.h b/Eigen/src/Sparse/SparseLLT.h index 864c4415a..40cefa189 100644 --- a/Eigen/src/Sparse/SparseLLT.h +++ b/Eigen/src/Sparse/SparseLLT.h @@ -191,15 +191,15 @@ bool SparseLLT::solveInPlace(MatrixBase &b) const const int size = m_matrix.rows(); ei_assert(size==b.rows()); - m_matrix.template triangular.solveInPlace(b); + m_matrix.template triangular().solveInPlace(b); // FIXME should be simply .adjoint() but it fails to compile... if (NumTraits::IsComplex) { CholMatrixType aux = m_matrix.conjugate(); - aux.transpose().template triangular.solveInPlace(b); + aux.transpose().template triangular().solveInPlace(b); } else - m_matrix.transpose().template triangular.solveInPlace(b); + m_matrix.transpose().template triangular().solveInPlace(b); return true; } diff --git a/Eigen/src/Sparse/SparseMatrix.h b/Eigen/src/Sparse/SparseMatrix.h index 3f09596bc..ad94e63ba 100644 --- a/Eigen/src/Sparse/SparseMatrix.h +++ b/Eigen/src/Sparse/SparseMatrix.h @@ -164,6 +164,7 @@ class SparseMatrix { ei_assert(m_data.index(m_data.size()-1) class SparseMatrixBase template Derived& operator*=(const SparseMatrixBase& other); + // deprecated template typename ei_plain_matrix_type_column_major::type solveTriangular(const MatrixBase& other) const; + // deprecated template void solveTriangularInPlace(MatrixBase& other) const; +// template +// void solveTriangularInPlace(SparseMatrixBase& other) const; + + template + inline const SparseTriangular triangular() const; template Scalar dot(const MatrixBase& other) const; template Scalar dot(const SparseMatrixBase& other) const; diff --git a/Eigen/src/Sparse/SparseTriangular.h b/Eigen/src/Sparse/SparseTriangular.h new file mode 100644 index 000000000..ca16caf96 --- /dev/null +++ b/Eigen/src/Sparse/SparseTriangular.h @@ -0,0 +1,60 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 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_SPARSE_TRIANGULAR_H +#define EIGEN_SPARSE_TRIANGULAR_H + +template class SparseTriangular +{ + public: + + typedef typename ei_traits::Scalar Scalar; + typedef typename ei_meta_if::ret, + ExpressionType, const ExpressionType&>::ret ExpressionTypeNested; + typedef CwiseUnaryOp, ExpressionType> ScalarAddReturnType; + + inline SparseTriangular(const ExpressionType& matrix) : m_matrix(matrix) {} + + /** \internal */ + inline const ExpressionType& _expression() const { return m_matrix; } + + template + typename ei_plain_matrix_type_column_major::type + solve(const MatrixBase& other) const; + template void solveInPlace(MatrixBase& other) const; + template void solveInPlace(SparseMatrixBase& other) const; + + protected: + ExpressionTypeNested m_matrix; +}; + +template +template +inline const SparseTriangular +SparseMatrixBase::triangular() const +{ + return derived(); +} + +#endif // EIGEN_SPARSE_TRIANGULAR_H diff --git a/Eigen/src/Sparse/SparseUtil.h b/Eigen/src/Sparse/SparseUtil.h index 393cdda6e..6610bdf30 100644 --- a/Eigen/src/Sparse/SparseUtil.h +++ b/Eigen/src/Sparse/SparseUtil.h @@ -113,6 +113,7 @@ template class SparseCwiseUnaryO template class SparseCwiseBinaryOp; template class SparseFlagged; +template class SparseTriangular; template class SparseDiagonalProduct; template struct ei_sparse_product_mode; diff --git a/Eigen/src/Sparse/SuperLUSupport.h b/Eigen/src/Sparse/SuperLUSupport.h index 3c9a4fcce..cf0f802ed 100644 --- a/Eigen/src/Sparse/SuperLUSupport.h +++ b/Eigen/src/Sparse/SuperLUSupport.h @@ -543,7 +543,7 @@ typename SparseLU::Scalar SparseLU::dete if (m_extractedDataAreDirty) extractData(); - // TODO this code coule be moved to the default/base backend + // 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::Flags) & RowMajorBit> +struct ei_sparse_solve_triangular_selector; + // forward substitution, row-major -template -struct ei_solve_triangular_selector +template +struct ei_sparse_solve_triangular_selector { typedef typename Rhs::Scalar Scalar; static void run(const Lhs& lhs, Rhs& other) @@ -45,7 +54,7 @@ struct ei_solve_triangular_selector lastIndex = it.index(); tmp -= lastVal * other.coeff(lastIndex,col); } - if (Lhs::Flags & UnitDiagBit) + if (Mode & UnitDiagBit) other.coeffRef(i,col) = tmp; else { @@ -58,8 +67,8 @@ struct ei_solve_triangular_selector }; // backward substitution, row-major -template -struct ei_solve_triangular_selector +template +struct ei_sparse_solve_triangular_selector { typedef typename Rhs::Scalar Scalar; static void run(const Lhs& lhs, Rhs& other) @@ -77,7 +86,7 @@ struct ei_solve_triangular_selector tmp -= it.value() * other.coeff(it.index(),col); } - if (Lhs::Flags & UnitDiagBit) + if (Mode & UnitDiagBit) other.coeffRef(i,col) = tmp; else { @@ -91,8 +100,8 @@ struct ei_solve_triangular_selector }; // forward substitution, col-major -template -struct ei_solve_triangular_selector +template +struct ei_sparse_solve_triangular_selector { typedef typename Rhs::Scalar Scalar; static void run(const Lhs& lhs, Rhs& other) @@ -101,26 +110,28 @@ struct ei_solve_triangular_selector { for(int i=0; i -struct ei_solve_triangular_selector +template +struct ei_sparse_solve_triangular_selector { typedef typename Rhs::Scalar Scalar; static void run(const Lhs& lhs, Rhs& other) @@ -129,29 +140,32 @@ struct ei_solve_triangular_selector { for(int i=lhs.cols()-1; i>=0; --i) { - if(!(Lhs::Flags & UnitDiagBit)) + Scalar& tmp = other.coeffRef(i,col); + if (tmp!=Scalar(0)) // optimization when other is actually sparse { - // FIXME lhs.coeff(i,i) might not be always efficient while it must simply be the - // last element of the column ! - other.coeffRef(i,col) /= lhs.coeff(i,i); + if(!(Mode & UnitDiagBit)) + { + // FIXME lhs.coeff(i,i) might not be always efficient while it must simply be the + // last element of the column ! + other.coeffRef(i,col) /= lhs.innerVector(i).lastCoeff(); + } + typename Lhs::InnerIterator it(lhs, i); + for(; it && it.index() +template template -void SparseMatrixBase::solveTriangularInPlace(MatrixBase& other) const +void SparseTriangular::solveInPlace(MatrixBase& other) const { - ei_assert(derived().cols() == derived().rows()); - ei_assert(derived().cols() == other.rows()); - ei_assert(!(Flags & ZeroDiagBit)); - ei_assert(Flags & (UpperTriangularBit|LowerTriangularBit)); + ei_assert(m_matrix.cols() == m_matrix.rows()); + ei_assert(m_matrix.cols() == other.rows()); + ei_assert(!(Mode & ZeroDiagBit)); + ei_assert(Mode & (UpperTriangularBit|LowerTriangularBit)); enum { copy = ei_traits::Flags & RowMajorBit }; @@ -159,19 +173,151 @@ void SparseMatrixBase::solveTriangularInPlace(MatrixBase& typename ei_plain_matrix_type_column_major::type, OtherDerived&>::ret OtherCopy; OtherCopy otherCopy(other.derived()); - ei_solve_triangular_selector::type>::run(derived(), otherCopy); + ei_sparse_solve_triangular_selector::type, Mode>::run(m_matrix, otherCopy); if (copy) other = otherCopy; } +template +template +typename ei_plain_matrix_type_column_major::type +SparseTriangular::solve(const MatrixBase& other) const +{ + typename ei_plain_matrix_type_column_major::type res(other); + solveInPlace(res); + return res; +} + +// pure sparse path + +template +struct ei_sparse_solve_triangular_sparse_selector; + +// forward substitution, col-major +template +struct ei_sparse_solve_triangular_sparse_selector +{ + typedef typename Rhs::Scalar Scalar; + static void run(const Lhs& lhs, Rhs& other) + { + const bool IsLowerTriangular = (UpLo==LowerTriangular); + AmbiVector tempVector(other.rows()*2); + tempVector.setBounds(0,other.rows()); + + Rhs res(other.rows(), other.cols()); + res.startFill(other.nonZeros()); + + for(int col=0 ; col=0; + i+=IsLowerTriangular?1:-1) + { + tempVector.restart(); + Scalar& ci = tempVector.coeffRef(i); + if (ci!=Scalar(0)) + { + // find + typename Lhs::InnerIterator it(lhs, i); + if(!(Mode & UnitDiagBit)) + { + if (IsLowerTriangular) + { + ei_assert(it.index()==i); + ci /= it.value(); + } + else + ci /= lhs.coeff(i,i); + } + tempVector.restart(); + if (IsLowerTriangular) + { + if (it.index()==i) + ++it; + for(; it; ++it) + tempVector.coeffRef(it.index()) -= ci * it.value(); + } + else + { + for(; it && it.index()::Iterator it(tempVector/*,1e-12*/); it; ++it) + { + ++ count; +// std::cerr << "fill " << it.index() << ", " << col << "\n"; +// std::cout << it.value() << " "; + res.fill(it.index(), col) = it.value(); + } +// std::cout << "tempVector.nonZeros() == " << int(count) << " / " << (other.rows()) << "\n"; + } + res.endFill(); + other = res.markAsRValue(); + } +}; + +template +template +void SparseTriangular::solveInPlace(SparseMatrixBase& other) const +{ + ei_assert(m_matrix.cols() == m_matrix.rows()); + ei_assert(m_matrix.cols() == other.rows()); + ei_assert(!(Mode & ZeroDiagBit)); + ei_assert(Mode & (UpperTriangularBit|LowerTriangularBit)); + +// enum { copy = ei_traits::Flags & RowMajorBit }; + +// typedef typename ei_meta_if::type, OtherDerived&>::ret OtherCopy; +// OtherCopy otherCopy(other.derived()); + + ei_sparse_solve_triangular_sparse_selector::run(m_matrix, other.derived()); + +// if (copy) +// other = otherCopy; +} + + +// deprecated stuff: + +/** \deprecated */ +template +template +void SparseMatrixBase::solveTriangularInPlace(MatrixBase& other) const +{ + this->template triangular().solveInPlace(other); +} + +/** \deprecated */ template template typename ei_plain_matrix_type_column_major::type SparseMatrixBase::solveTriangular(const MatrixBase& other) const { typename ei_plain_matrix_type_column_major::type res(other); - solveTriangularInPlace(res); + derived().solveTriangularInPlace(res); return res; } diff --git a/test/sparse_product.cpp b/test/sparse_product.cpp index 3a0356f00..00b5a3ed5 100644 --- a/test/sparse_product.cpp +++ b/test/sparse_product.cpp @@ -40,6 +40,7 @@ template void sparse_product(const SparseMatrixType& DenseMatrix refMat2 = DenseMatrix::Zero(rows, rows); DenseMatrix refMat3 = DenseMatrix::Zero(rows, rows); DenseMatrix refMat4 = DenseMatrix::Zero(rows, rows); + DenseMatrix refMat5 = DenseMatrix::Random(rows, rows); DenseMatrix dm4 = DenseMatrix::Zero(rows, rows); SparseMatrixType m2(rows, rows); SparseMatrixType m3(rows, rows); @@ -57,7 +58,10 @@ template void sparse_product(const SparseMatrixType& VERIFY_IS_APPROX(dm4=m2*refMat3.transpose(), refMat4=refMat2*refMat3.transpose()); VERIFY_IS_APPROX(dm4=m2.transpose()*refMat3, refMat4=refMat2.transpose()*refMat3); VERIFY_IS_APPROX(dm4=m2.transpose()*refMat3.transpose(), refMat4=refMat2.transpose()*refMat3.transpose()); - + + VERIFY_IS_APPROX(dm4=m2*(refMat3+refMat3), refMat4=refMat2*(refMat3+refMat3)); + VERIFY_IS_APPROX(dm4=m2.transpose()*(refMat3+refMat5)*0.5, refMat4=refMat2.transpose()*(refMat3+refMat5)*0.5); + // dense * sparse VERIFY_IS_APPROX(dm4=refMat2*m3, refMat4=refMat2*refMat3); VERIFY_IS_APPROX(dm4=refMat2*m3.transpose(), refMat4=refMat2*refMat3.transpose()); diff --git a/test/sparse_solvers.cpp b/test/sparse_solvers.cpp index 8ce4e2264..d1090dfed 100644 --- a/test/sparse_solvers.cpp +++ b/test/sparse_solvers.cpp @@ -63,17 +63,39 @@ template void sparse_solvers(int rows, int cols) SparseMatrix m2(rows, cols); DenseMatrix refMat2 = DenseMatrix::Zero(rows, cols); - // lower + // lower - dense + initSparse(density, refMat2, m2, ForceNonZeroDiag|MakeLowerTriangular, &zeroCoords, &nonzeroCoords); + VERIFY_IS_APPROX(refMat2.template marked().solveTriangular(vec2), + m2.template triangular().solve(vec3)); + + // upper - dense + initSparse(density, refMat2, m2, ForceNonZeroDiag|MakeUpperTriangular, &zeroCoords, &nonzeroCoords); + VERIFY_IS_APPROX(refMat2.template marked().solveTriangular(vec2), + m2.template triangular().solve(vec3)); + + // TODO test row major + + SparseMatrix matB(rows, rows); + DenseMatrix refMatB = DenseMatrix::Zero(rows, rows); + + // lower - sparse + initSparse(density, refMat2, m2, ForceNonZeroDiag|MakeLowerTriangular); + initSparse(density, refMatB, matB); + refMat2.template marked().solveTriangularInPlace(refMatB); + m2.template triangular().solveInPlace(matB); + VERIFY_IS_APPROX(matB.toDense(), refMatB); + + // upper - sparse + initSparse(density, refMat2, m2, ForceNonZeroDiag|MakeUpperTriangular); + initSparse(density, refMatB, matB); + refMat2.template marked().solveTriangularInPlace(refMatB); + m2.template triangular().solveInPlace(matB); + VERIFY_IS_APPROX(matB, refMatB); + + // test deprecated API initSparse(density, refMat2, m2, ForceNonZeroDiag|MakeLowerTriangular, &zeroCoords, &nonzeroCoords); VERIFY_IS_APPROX(refMat2.template marked().solveTriangular(vec2), m2.template marked().solveTriangular(vec3)); - - // upper - initSparse(density, refMat2, m2, ForceNonZeroDiag|MakeUpperTriangular, &zeroCoords, &nonzeroCoords); - VERIFY_IS_APPROX(refMat2.template marked().solveTriangular(vec2), - m2.template marked().solveTriangular(vec3)); - - // TODO test row major } // test LLT