diff --git a/Eigen/src/Sparse/SparseProduct.h b/Eigen/src/Sparse/SparseProduct.h index d3ef57455..c98a71e99 100644 --- a/Eigen/src/Sparse/SparseProduct.h +++ b/Eigen/src/Sparse/SparseProduct.h @@ -171,6 +171,55 @@ class SparseProduct : ei_no_assignment_operator, RhsNested m_rhs; }; +// perform a pseudo in-place sparse * sparse product assuming all matrices are col major +template +static void ei_sparse_product_impl(const Lhs& lhs, const Rhs& rhs, ResultType& res) +{ + typedef typename ei_traits::type>::Scalar Scalar; + + // make sure to call innerSize/outerSize since we fake the storage order. + int rows = lhs.innerSize(); + int cols = rhs.outerSize(); + //int size = lhs.outerSize(); + ei_assert(lhs.outerSize() == rhs.innerSize()); + + // allocate a temporary buffer + AmbiVector tempVector(rows); + + // estimate the number of non zero entries + float ratioLhs = float(lhs.nonZeros())/(float(lhs.rows())*float(lhs.cols())); + float avgNnzPerRhsColumn = float(rhs.nonZeros())/float(cols); + float ratioRes = std::min(ratioLhs * avgNnzPerRhsColumn, 1.f); + + res.resize(rows, cols); + res.startFill(int(ratioRes*rows*cols)); + for (int j=0; j::Iterator it(tempVector); it; ++it) + if (ResultType::Flags&RowMajorBit) + res.fill(j,it.index()) = it.value(); + else + res.fill(it.index(), j) = it.value(); + } + res.endFill(); +} + template::Flags&RowMajorBit, int RhsStorageOrder = ei_traits::Flags&RowMajorBit, @@ -184,58 +233,21 @@ struct ei_sparse_product_selector static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) { - // make sure to call innerSize/outerSize since we fake the storage order. - int rows = lhs.innerSize(); - int cols = rhs.outerSize(); - //int size = lhs.outerSize(); - ei_assert(lhs.outerSize() == rhs.innerSize()); - - // allocate a temporary buffer - AmbiVector tempVector(rows); - - // estimate the number of non zero entries - float ratioLhs = float(lhs.nonZeros())/(float(lhs.rows())*float(lhs.cols())); - float avgNnzPerRhsColumn = float(rhs.nonZeros())/float(cols); - float ratioRes = std::min(ratioLhs * avgNnzPerRhsColumn, 1.f); - - res.resize(rows, cols); - res.startFill(int(ratioRes*rows*cols)); - for (int j=0; j::Iterator it(tempVector); it; ++it) - if (ResultType::Flags&RowMajorBit) - res.fill(j,it.index()) = it.value(); - else - res.fill(it.index(), j) = it.value(); - } - res.endFill(); + typename ei_cleantype::type _res(res.rows(), res.cols()); + ei_sparse_product_impl(lhs, rhs, _res); + res.swap(_res); } }; template struct ei_sparse_product_selector { - typedef SparseMatrix SparseTemporaryType; static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) { + // we need a col-major matrix to hold the result + typedef SparseMatrix SparseTemporaryType; SparseTemporaryType _res(res.rows(), res.cols()); - ei_sparse_product_selector::run(lhs, rhs, _res); + ei_sparse_product_impl(lhs, rhs, _res); res = _res; } }; @@ -246,20 +258,21 @@ struct ei_sparse_product_selector static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) { // let's transpose the product to get a column x column product - ei_sparse_product_selector::run(rhs, lhs, res); + typename ei_cleantype::type _res(res.rows(), res.cols()); + ei_sparse_product_impl(rhs, lhs, _res); + res.swap(_res); } }; template struct ei_sparse_product_selector { - typedef SparseMatrix SparseTemporaryType; static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) { // let's transpose the product to get a column x column product + typedef SparseMatrix SparseTemporaryType; SparseTemporaryType _res(res.cols(), res.rows()); - ei_sparse_product_selector - ::run(rhs, lhs, _res); + ei_sparse_product_impl(rhs, lhs, _res); res = _res.transpose(); } }; @@ -322,7 +335,7 @@ inline Derived& SparseMatrixBase::operator=(const SparseProduct template Derived& MatrixBase::lazyAssign(const SparseProduct& product) -{ +{ typedef typename ei_cleantype::type _Lhs; typedef typename ei_cleantype::type _Rhs; typedef typename _Lhs::InnerIterator LhsInnerIterator; diff --git a/test/sparse_product.cpp b/test/sparse_product.cpp index 95b3546c1..d2c3b1c98 100644 --- a/test/sparse_product.cpp +++ b/test/sparse_product.cpp @@ -30,14 +30,13 @@ template void sparse_product(const SparseMatrixType& const int cols = ref.cols(); typedef typename SparseMatrixType::Scalar Scalar; enum { Flags = SparseMatrixType::Flags }; - + double density = std::max(8./(rows*cols), 0.01); typedef Matrix DenseMatrix; typedef Matrix DenseVector; Scalar eps = 1e-6; // test matrix-matrix product - /* { DenseMatrix refMat2 = DenseMatrix::Zero(rows, rows); DenseMatrix refMat3 = DenseMatrix::Zero(rows, rows); @@ -65,9 +64,10 @@ template void sparse_product(const SparseMatrixType& VERIFY_IS_APPROX(dm4=refMat2*m3.transpose(), refMat4=refMat2*refMat3.transpose()); VERIFY_IS_APPROX(dm4=refMat2.transpose()*m3, refMat4=refMat2.transpose()*refMat3); VERIFY_IS_APPROX(dm4=refMat2.transpose()*m3.transpose(), refMat4=refMat2.transpose()*refMat3.transpose()); + + VERIFY_IS_APPROX(m3=m3*m3, refMat3=refMat3*refMat3); } - */ - + // test matrix - diagonal product { DenseMatrix refM2 = DenseMatrix::Zero(rows, rows); @@ -77,46 +77,44 @@ template void sparse_product(const SparseMatrixType& SparseMatrixType m3(rows, rows); initSparse(density, refM2, m2); initSparse(density, refM3, m3); -// std::cerr << "foo\n" << (m2*d1).toDense() << "\n\n" << refM2*d1 << "\n\n"; VERIFY_IS_APPROX(m3=m2*d1, refM3=refM2*d1); VERIFY_IS_APPROX(m3=m2.transpose()*d1, refM3=refM2.transpose()*d1); VERIFY_IS_APPROX(m3=d1*m2, refM3=d1*refM2); -// std::cerr << "foo\n" << (d1*m2.transpose()).toDense() << "\n\n" << d1 * refM2.transpose() << "\n\n"; VERIFY_IS_APPROX(m3=d1*m2.transpose(), refM3=d1 * refM2.transpose()); } - + // test self adjoint products -// { -// DenseMatrix b = DenseMatrix::Random(rows, rows); -// DenseMatrix x = DenseMatrix::Random(rows, rows); -// DenseMatrix refX = DenseMatrix::Random(rows, rows); -// DenseMatrix refUp = DenseMatrix::Zero(rows, rows); -// DenseMatrix refLo = DenseMatrix::Zero(rows, rows); -// DenseMatrix refS = DenseMatrix::Zero(rows, rows); -// SparseMatrixType mUp(rows, rows); -// SparseMatrixType mLo(rows, rows); -// SparseMatrixType mS(rows, rows); -// do { -// initSparse(density, refUp, mUp, ForceRealDiag|/*ForceNonZeroDiag|*/MakeUpperTriangular); -// } while (refUp.isZero()); -// refLo = refUp.transpose().conjugate(); -// mLo = mUp.transpose().conjugate(); -// refS = refUp + refLo; -// refS.diagonal() *= 0.5; -// mS = mUp + mLo; -// for (int k=0; k()*b, refX=refS*b); -// VERIFY_IS_APPROX(x=mLo.template marked()*b, refX=refS*b); -// VERIFY_IS_APPROX(x=mS.template marked()*b, refX=refS*b); -// } + { + DenseMatrix b = DenseMatrix::Random(rows, rows); + DenseMatrix x = DenseMatrix::Random(rows, rows); + DenseMatrix refX = DenseMatrix::Random(rows, rows); + DenseMatrix refUp = DenseMatrix::Zero(rows, rows); + DenseMatrix refLo = DenseMatrix::Zero(rows, rows); + DenseMatrix refS = DenseMatrix::Zero(rows, rows); + SparseMatrixType mUp(rows, rows); + SparseMatrixType mLo(rows, rows); + SparseMatrixType mS(rows, rows); + do { + initSparse(density, refUp, mUp, ForceRealDiag|/*ForceNonZeroDiag|*/MakeUpperTriangular); + } while (refUp.isZero()); + refLo = refUp.transpose().conjugate(); + mLo = mUp.transpose().conjugate(); + refS = refUp + refLo; + refS.diagonal() *= 0.5; + mS = mUp + mLo; + for (int k=0; k()*b, refX=refS*b); + VERIFY_IS_APPROX(x=mLo.template marked()*b, refX=refS*b); + VERIFY_IS_APPROX(x=mS.template marked()*b, refX=refS*b); + } } @@ -126,7 +124,7 @@ void test_sparse_product() CALL_SUBTEST( sparse_product(SparseMatrix(8, 8)) ); CALL_SUBTEST( sparse_product(SparseMatrix >(16, 16)) ); CALL_SUBTEST( sparse_product(SparseMatrix(33, 33)) ); - + CALL_SUBTEST( sparse_product(DynamicSparseMatrix(8, 8)) ); } }