sparse module: new API proposal for triangular solves and experimental

solver support with a sparse matrix as the rhs.
This commit is contained in:
Gael Guennebaud 2009-04-05 16:30:10 +00:00
parent 5a628567b0
commit 5b1d0cebc5
13 changed files with 335 additions and 64 deletions

View File

@ -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"

View File

@ -36,7 +36,7 @@ template<typename _Scalar> class AmbiVector
typedef _Scalar Scalar;
typedef typename NumTraits<Scalar>::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<typename _Scalar> 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<typename _Scalar> class AmbiVector
void restart();
Scalar& coeffRef(int i);
Scalar coeff(int i);
Scalar& coeff(int i);
class Iterator;
@ -112,6 +112,7 @@ template<typename _Scalar> 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<typename _Scalar> class AmbiVector
/** \returns the number of non zeros in the current sub vector */
template<typename Scalar>
void AmbiVector<Scalar>::nonZeros() const
int AmbiVector<Scalar>::nonZeros() const
{
if (m_mode==IsSparse)
return m_llSize;
@ -254,7 +255,7 @@ Scalar& AmbiVector<Scalar>::coeffRef(int i)
}
template<typename Scalar>
Scalar AmbiVector<Scalar>::coeff(int i)
Scalar& AmbiVector<Scalar>::coeff(int i)
{
if (m_mode==IsDense)
return m_buffer[i];
@ -264,7 +265,7 @@ Scalar AmbiVector<Scalar>::coeff(int i)
ei_assert(m_mode==IsSparse);
if ((m_llSize==0) || (i<llElements[m_llStart].index))
{
return Scalar(0);
return m_zero;
}
else
{
@ -275,7 +276,7 @@ Scalar AmbiVector<Scalar>::coeff(int i)
if (llElements[elid].index==i)
return llElements[m_llCurrent].value;
else
return Scalar(0);
return m_zero;
}
}
}

View File

@ -150,6 +150,21 @@ class SparseInnerVectorSet<DynamicSparseMatrix<_Scalar, _Options>, Size>
{
return operator=<SparseInnerVectorSet>(other);
}
int nonZeros() const
{
int count = 0;
for (int j=0; j<m_outerSize; ++j)
count += m_matrix._data()[m_outerStart+j].size();
return count;
}
const Scalar& lastCoeff() const
{
EIGEN_STATIC_ASSERT_VECTOR_ONLY(SparseInnerVectorSet);
ei_assert(m_matrix.data()[m_outerStart].size()>0);
return m_matrix.data()[m_outerStart].vale(m_matrix.data()[m_outerStart].size()-1);
}
// template<typename Sparse>
// inline SparseInnerVectorSet& operator=(const SparseMatrixBase<OtherDerived>& other)
@ -172,12 +187,12 @@ class SparseInnerVectorSet<DynamicSparseMatrix<_Scalar, _Options>, Size>
/***************************************************************************
* specialisation for SparseMatrix
***************************************************************************/
/*
template<typename _Scalar, int _Options, int Size>
class SparseInnerVectorSet<SparseMatrix<_Scalar, _Options>, Size>
: public SparseMatrixBase<SparseInnerVectorSet<SparseMatrix<_Scalar, _Options>, Size> >
{
typedef DynamicSparseMatrix<_Scalar, _Options> MatrixType;
typedef SparseMatrix<_Scalar, _Options> MatrixType;
enum { IsRowMajor = ei_traits<SparseInnerVectorSet>::IsRowMajor };
public:
@ -233,7 +248,20 @@ class SparseInnerVectorSet<SparseMatrix<_Scalar, _Options>, 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<typename Sparse>
// inline SparseInnerVectorSet& operator=(const SparseMatrixBase<OtherDerived>& other)
@ -251,7 +279,7 @@ class SparseInnerVectorSet<SparseMatrix<_Scalar, _Options>, Size>
const ei_int_if_dynamic<Size> m_outerSize;
};
*/
//----------
/** \returns the i-th row of the matrix \c *this. For row-major matrix only. */

View File

@ -79,7 +79,7 @@ class SparseLDLT
protected:
typedef typename MatrixType::Scalar Scalar;
typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar;
typedef SparseMatrix<Scalar,LowerTriangular|UnitDiagBit> CholMatrixType;
typedef SparseMatrix<Scalar> CholMatrixType;
typedef Matrix<Scalar,MatrixType::ColsAtCompileTime,1> 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<MatrixType, Backend>::solveInPlace(MatrixBase<Derived> &b) const
return false;
if (m_matrix.nonZeros()>0) // otherwise L==I
m_matrix.solveTriangularInPlace(b);
m_matrix.template triangular<LowerTriangular|UnitDiagBit>().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<UpperTriangular|UnitDiagBit>().solveInPlace(b);
return true;
}

View File

@ -191,15 +191,15 @@ bool SparseLLT<MatrixType, Backend>::solveInPlace(MatrixBase<Derived> &b) const
const int size = m_matrix.rows();
ei_assert(size==b.rows());
m_matrix.template triangular<LowerTriangular>.solveInPlace(b);
m_matrix.template triangular<LowerTriangular>().solveInPlace(b);
// FIXME should be simply .adjoint() but it fails to compile...
if (NumTraits<Scalar>::IsComplex)
{
CholMatrixType aux = m_matrix.conjugate();
aux.transpose().template triangular<UpperTriangular>.solveInPlace(b);
aux.transpose().template triangular<UpperTriangular>().solveInPlace(b);
}
else
m_matrix.transpose().template triangular<UpperTriangular>.solveInPlace(b);
m_matrix.transpose().template triangular<UpperTriangular>().solveInPlace(b);
return true;
}

View File

@ -164,6 +164,7 @@ class SparseMatrix
{
ei_assert(m_data.index(m_data.size()-1)<inner && "wrong sorted insertion");
}
// std::cerr << size_t(m_outerIndex[outer+1]) << " == " << m_data.size() << "\n";
assert(size_t(m_outerIndex[outer+1]) == m_data.size());
int id = m_outerIndex[outer+1];
++m_outerIndex[outer+1];

View File

@ -308,12 +308,19 @@ template<typename Derived> class SparseMatrixBase
template<typename OtherDerived>
Derived& operator*=(const SparseMatrixBase<OtherDerived>& other);
// deprecated
template<typename OtherDerived>
typename ei_plain_matrix_type_column_major<OtherDerived>::type
solveTriangular(const MatrixBase<OtherDerived>& other) const;
// deprecated
template<typename OtherDerived>
void solveTriangularInPlace(MatrixBase<OtherDerived>& other) const;
// template<typename OtherDerived>
// void solveTriangularInPlace(SparseMatrixBase<OtherDerived>& other) const;
template<int Mode>
inline const SparseTriangular<Derived, Mode> triangular() const;
template<typename OtherDerived> Scalar dot(const MatrixBase<OtherDerived>& other) const;
template<typename OtherDerived> Scalar dot(const SparseMatrixBase<OtherDerived>& other) const;

View File

@ -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 <g.gael@free.fr>
//
// 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 <http://www.gnu.org/licenses/>.
#ifndef EIGEN_SPARSE_TRIANGULAR_H
#define EIGEN_SPARSE_TRIANGULAR_H
template<typename ExpressionType, int Mode> class SparseTriangular
{
public:
typedef typename ei_traits<ExpressionType>::Scalar Scalar;
typedef typename ei_meta_if<ei_must_nest_by_value<ExpressionType>::ret,
ExpressionType, const ExpressionType&>::ret ExpressionTypeNested;
typedef CwiseUnaryOp<ei_scalar_add_op<Scalar>, ExpressionType> ScalarAddReturnType;
inline SparseTriangular(const ExpressionType& matrix) : m_matrix(matrix) {}
/** \internal */
inline const ExpressionType& _expression() const { return m_matrix; }
template<typename OtherDerived>
typename ei_plain_matrix_type_column_major<OtherDerived>::type
solve(const MatrixBase<OtherDerived>& other) const;
template<typename OtherDerived> void solveInPlace(MatrixBase<OtherDerived>& other) const;
template<typename OtherDerived> void solveInPlace(SparseMatrixBase<OtherDerived>& other) const;
protected:
ExpressionTypeNested m_matrix;
};
template<typename Derived>
template<int Mode>
inline const SparseTriangular<Derived, Mode>
SparseMatrixBase<Derived>::triangular() const
{
return derived();
}
#endif // EIGEN_SPARSE_TRIANGULAR_H

View File

@ -113,6 +113,7 @@ template<typename UnaryOp, typename MatrixType> class SparseCwiseUnaryO
template<typename BinaryOp, typename Lhs, typename Rhs> class SparseCwiseBinaryOp;
template<typename ExpressionType,
unsigned int Added, unsigned int Removed> class SparseFlagged;
template<typename ExpressionType, int Mode> class SparseTriangular;
template<typename Lhs, typename Rhs> class SparseDiagonalProduct;
template<typename Lhs, typename Rhs> struct ei_sparse_product_mode;

View File

@ -543,7 +543,7 @@ typename SparseLU<MatrixType,SuperLU>::Scalar SparseLU<MatrixType,SuperLU>::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<m_u.cols(); ++j)

View File

@ -25,9 +25,18 @@
#ifndef EIGEN_SPARSETRIANGULARSOLVER_H
#define EIGEN_SPARSETRIANGULARSOLVER_H
template<typename Lhs, typename Rhs, int Mode,
int UpLo = (Mode & LowerTriangularBit)
? LowerTriangular
: (Mode & UpperTriangularBit)
? UpperTriangular
: -1,
int StorageOrder = int(ei_traits<Lhs>::Flags) & RowMajorBit>
struct ei_sparse_solve_triangular_selector;
// forward substitution, row-major
template<typename Lhs, typename Rhs>
struct ei_solve_triangular_selector<Lhs,Rhs,LowerTriangular,RowMajor|IsSparse>
template<typename Lhs, typename Rhs, int Mode>
struct ei_sparse_solve_triangular_selector<Lhs,Rhs,Mode,LowerTriangular,RowMajor>
{
typedef typename Rhs::Scalar Scalar;
static void run(const Lhs& lhs, Rhs& other)
@ -45,7 +54,7 @@ struct ei_solve_triangular_selector<Lhs,Rhs,LowerTriangular,RowMajor|IsSparse>
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<Lhs,Rhs,LowerTriangular,RowMajor|IsSparse>
};
// backward substitution, row-major
template<typename Lhs, typename Rhs>
struct ei_solve_triangular_selector<Lhs,Rhs,UpperTriangular,RowMajor|IsSparse>
template<typename Lhs, typename Rhs, int Mode>
struct ei_sparse_solve_triangular_selector<Lhs,Rhs,Mode,UpperTriangular,RowMajor>
{
typedef typename Rhs::Scalar Scalar;
static void run(const Lhs& lhs, Rhs& other)
@ -77,7 +86,7 @@ struct ei_solve_triangular_selector<Lhs,Rhs,UpperTriangular,RowMajor|IsSparse>
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<Lhs,Rhs,UpperTriangular,RowMajor|IsSparse>
};
// forward substitution, col-major
template<typename Lhs, typename Rhs>
struct ei_solve_triangular_selector<Lhs,Rhs,LowerTriangular,ColMajor|IsSparse>
template<typename Lhs, typename Rhs, int Mode>
struct ei_sparse_solve_triangular_selector<Lhs,Rhs,Mode,LowerTriangular,ColMajor>
{
typedef typename Rhs::Scalar Scalar;
static void run(const Lhs& lhs, Rhs& other)
@ -101,26 +110,28 @@ struct ei_solve_triangular_selector<Lhs,Rhs,LowerTriangular,ColMajor|IsSparse>
{
for(int i=0; i<lhs.cols(); ++i)
{
typename Lhs::InnerIterator it(lhs, i);
if(!(Lhs::Flags & UnitDiagBit))
Scalar& tmp = other.coeffRef(i,col);
if (tmp!=Scalar(0)) // optimization when other is actually sparse
{
// std::cerr << it.value() << " ; " << it.index() << " == " << i << "\n";
ei_assert(it.index()==i);
other.coeffRef(i,col) /= it.value();
typename Lhs::InnerIterator it(lhs, i);
if(!(Mode & UnitDiagBit))
{
ei_assert(it.index()==i);
tmp /= it.value();
}
if (it.index()==i)
++it;
for(; it; ++it)
other.coeffRef(it.index(), col) -= tmp * it.value();
}
Scalar tmp = other.coeffRef(i,col);
if (it.index()==i)
++it;
for(; it; ++it)
other.coeffRef(it.index(), col) -= tmp * it.value();
}
}
}
};
// backward substitution, col-major
template<typename Lhs, typename Rhs>
struct ei_solve_triangular_selector<Lhs,Rhs,UpperTriangular,ColMajor|IsSparse>
template<typename Lhs, typename Rhs, int Mode>
struct ei_sparse_solve_triangular_selector<Lhs,Rhs,Mode,UpperTriangular,ColMajor>
{
typedef typename Rhs::Scalar Scalar;
static void run(const Lhs& lhs, Rhs& other)
@ -129,29 +140,32 @@ struct ei_solve_triangular_selector<Lhs,Rhs,UpperTriangular,ColMajor|IsSparse>
{
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()<i; ++it)
other.coeffRef(it.index(), col) -= tmp * it.value();
}
Scalar tmp = other.coeffRef(i,col);
typename Lhs::InnerIterator it(lhs, i);
for(; it && it.index()<i; ++it)
other.coeffRef(it.index(), col) -= tmp * it.value();
}
}
}
};
template<typename Derived>
template<typename ExpressionType,int Mode>
template<typename OtherDerived>
void SparseMatrixBase<Derived>::solveTriangularInPlace(MatrixBase<OtherDerived>& other) const
void SparseTriangular<ExpressionType,Mode>::solveInPlace(MatrixBase<OtherDerived>& 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<OtherDerived>::Flags & RowMajorBit };
@ -159,19 +173,151 @@ void SparseMatrixBase<Derived>::solveTriangularInPlace(MatrixBase<OtherDerived>&
typename ei_plain_matrix_type_column_major<OtherDerived>::type, OtherDerived&>::ret OtherCopy;
OtherCopy otherCopy(other.derived());
ei_solve_triangular_selector<Derived, typename ei_unref<OtherCopy>::type>::run(derived(), otherCopy);
ei_sparse_solve_triangular_selector<ExpressionType, typename ei_unref<OtherCopy>::type, Mode>::run(m_matrix, otherCopy);
if (copy)
other = otherCopy;
}
template<typename ExpressionType,int Mode>
template<typename OtherDerived>
typename ei_plain_matrix_type_column_major<OtherDerived>::type
SparseTriangular<ExpressionType,Mode>::solve(const MatrixBase<OtherDerived>& other) const
{
typename ei_plain_matrix_type_column_major<OtherDerived>::type res(other);
solveInPlace(res);
return res;
}
// pure sparse path
template<typename Lhs, typename Rhs, int Mode,
int UpLo = (Mode & LowerTriangularBit)
? LowerTriangular
: (Mode & UpperTriangularBit)
? UpperTriangular
: -1,
int StorageOrder = int(Lhs::Flags) & (RowMajorBit)>
struct ei_sparse_solve_triangular_sparse_selector;
// forward substitution, col-major
template<typename Lhs, typename Rhs, int Mode, int UpLo>
struct ei_sparse_solve_triangular_sparse_selector<Lhs,Rhs,Mode,UpLo,ColMajor>
{
typedef typename Rhs::Scalar Scalar;
static void run(const Lhs& lhs, Rhs& other)
{
const bool IsLowerTriangular = (UpLo==LowerTriangular);
AmbiVector<Scalar> tempVector(other.rows()*2);
tempVector.setBounds(0,other.rows());
Rhs res(other.rows(), other.cols());
res.startFill(other.nonZeros());
for(int col=0 ; col<other.cols() ; ++col)
{
// FIXME estimate number of non zeros
tempVector.init(.99/*float(other.col(col).nonZeros())/float(other.rows())*/);
tempVector.setZero();
tempVector.restart();
for (typename Rhs::InnerIterator rhsIt(other, col); rhsIt; ++rhsIt)
{
tempVector.coeffRef(rhsIt.index()) = rhsIt.value();
}
for(int i=IsLowerTriangular?0:lhs.cols()-1;
IsLowerTriangular?i<lhs.cols():i>=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()<i; ++it)
tempVector.coeffRef(it.index()) -= ci * it.value();
}
}
}
int count = 0;
// FIXME compute a reference value to filter zeros
for (typename AmbiVector<Scalar>::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<typename ExpressionType,int Mode>
template<typename OtherDerived>
void SparseTriangular<ExpressionType,Mode>::solveInPlace(SparseMatrixBase<OtherDerived>& 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<OtherDerived>::Flags & RowMajorBit };
// typedef typename ei_meta_if<copy,
// typename ei_plain_matrix_type_column_major<OtherDerived>::type, OtherDerived&>::ret OtherCopy;
// OtherCopy otherCopy(other.derived());
ei_sparse_solve_triangular_sparse_selector<ExpressionType, OtherDerived, Mode>::run(m_matrix, other.derived());
// if (copy)
// other = otherCopy;
}
// deprecated stuff:
/** \deprecated */
template<typename Derived>
template<typename OtherDerived>
void SparseMatrixBase<Derived>::solveTriangularInPlace(MatrixBase<OtherDerived>& other) const
{
this->template triangular<Flags&(UpperTriangularBit|LowerTriangularBit)>().solveInPlace(other);
}
/** \deprecated */
template<typename Derived>
template<typename OtherDerived>
typename ei_plain_matrix_type_column_major<OtherDerived>::type
SparseMatrixBase<Derived>::solveTriangular(const MatrixBase<OtherDerived>& other) const
{
typename ei_plain_matrix_type_column_major<OtherDerived>::type res(other);
solveTriangularInPlace(res);
derived().solveTriangularInPlace(res);
return res;
}

View File

@ -40,6 +40,7 @@ template<typename SparseMatrixType> 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<typename SparseMatrixType> 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());

View File

@ -63,17 +63,39 @@ template<typename Scalar> void sparse_solvers(int rows, int cols)
SparseMatrix<Scalar> m2(rows, cols);
DenseMatrix refMat2 = DenseMatrix::Zero(rows, cols);
// lower
// lower - dense
initSparse<Scalar>(density, refMat2, m2, ForceNonZeroDiag|MakeLowerTriangular, &zeroCoords, &nonzeroCoords);
VERIFY_IS_APPROX(refMat2.template marked<LowerTriangular>().solveTriangular(vec2),
m2.template triangular<LowerTriangular>().solve(vec3));
// upper - dense
initSparse<Scalar>(density, refMat2, m2, ForceNonZeroDiag|MakeUpperTriangular, &zeroCoords, &nonzeroCoords);
VERIFY_IS_APPROX(refMat2.template marked<UpperTriangular>().solveTriangular(vec2),
m2.template triangular<UpperTriangular>().solve(vec3));
// TODO test row major
SparseMatrix<Scalar> matB(rows, rows);
DenseMatrix refMatB = DenseMatrix::Zero(rows, rows);
// lower - sparse
initSparse<Scalar>(density, refMat2, m2, ForceNonZeroDiag|MakeLowerTriangular);
initSparse<Scalar>(density, refMatB, matB);
refMat2.template marked<LowerTriangular>().solveTriangularInPlace(refMatB);
m2.template triangular<LowerTriangular>().solveInPlace(matB);
VERIFY_IS_APPROX(matB.toDense(), refMatB);
// upper - sparse
initSparse<Scalar>(density, refMat2, m2, ForceNonZeroDiag|MakeUpperTriangular);
initSparse<Scalar>(density, refMatB, matB);
refMat2.template marked<UpperTriangular>().solveTriangularInPlace(refMatB);
m2.template triangular<UpperTriangular>().solveInPlace(matB);
VERIFY_IS_APPROX(matB, refMatB);
// test deprecated API
initSparse<Scalar>(density, refMat2, m2, ForceNonZeroDiag|MakeLowerTriangular, &zeroCoords, &nonzeroCoords);
VERIFY_IS_APPROX(refMat2.template marked<LowerTriangular>().solveTriangular(vec2),
m2.template marked<LowerTriangular>().solveTriangular(vec3));
// upper
initSparse<Scalar>(density, refMat2, m2, ForceNonZeroDiag|MakeUpperTriangular, &zeroCoords, &nonzeroCoords);
VERIFY_IS_APPROX(refMat2.template marked<UpperTriangular>().solveTriangular(vec2),
m2.template marked<UpperTriangular>().solveTriangular(vec3));
// TODO test row major
}
// test LLT