// 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_SPARSEPRODUCT_H #define EIGEN_SPARSEPRODUCT_H template struct ei_sparse_product_mode { enum { value = (Rhs::Flags&Lhs::Flags&SparseBit)==SparseBit ? SparseTimeSparseProduct : (Lhs::Flags&SparseBit)==SparseBit ? SparseTimeDenseProduct : DenseTimeSparseProduct }; }; template struct SparseProductReturnType { typedef const typename ei_nested::type LhsNested; typedef const typename ei_nested::type RhsNested; typedef SparseProduct Type; }; // sparse product return type specialization template struct SparseProductReturnType { typedef typename ei_traits::Scalar Scalar; enum { LhsRowMajor = ei_traits::Flags & RowMajorBit, RhsRowMajor = ei_traits::Flags & RowMajorBit, TransposeRhs = (!LhsRowMajor) && RhsRowMajor, TransposeLhs = LhsRowMajor && (!RhsRowMajor) }; // FIXME if we transpose let's evaluate to a LinkedVectorMatrix since it is the // type of the temporary to perform the transpose op typedef typename ei_meta_if, const typename ei_nested::type>::ret LhsNested; typedef typename ei_meta_if, const typename ei_nested::type>::ret RhsNested; typedef SparseProduct Type; }; template struct ei_traits > { // clean the nested types: typedef typename ei_cleantype::type _LhsNested; typedef typename ei_cleantype::type _RhsNested; typedef typename _LhsNested::Scalar Scalar; enum { LhsCoeffReadCost = _LhsNested::CoeffReadCost, RhsCoeffReadCost = _RhsNested::CoeffReadCost, LhsFlags = _LhsNested::Flags, RhsFlags = _RhsNested::Flags, RowsAtCompileTime = _LhsNested::RowsAtCompileTime, ColsAtCompileTime = _RhsNested::ColsAtCompileTime, InnerSize = EIGEN_ENUM_MIN(_LhsNested::ColsAtCompileTime, _RhsNested::RowsAtCompileTime), MaxRowsAtCompileTime = _LhsNested::MaxRowsAtCompileTime, MaxColsAtCompileTime = _RhsNested::MaxColsAtCompileTime, // LhsIsRowMajor = (LhsFlags & RowMajorBit)==RowMajorBit, // RhsIsRowMajor = (RhsFlags & RowMajorBit)==RowMajorBit, EvalToRowMajor = (RhsFlags & LhsFlags & RowMajorBit), ResultIsSparse = ProductMode==SparseTimeSparseProduct, RemovedBits = ~( (EvalToRowMajor ? 0 : RowMajorBit) | (ResultIsSparse ? 0 : SparseBit) ), Flags = (int(LhsFlags | RhsFlags) & HereditaryBits & RemovedBits) | EvalBeforeAssigningBit | EvalBeforeNestingBit, CoeffReadCost = Dynamic }; typedef typename ei_meta_if >, MatrixBase > >::ret Base; }; template class SparseProduct : ei_no_assignment_operator, public ei_traits >::Base { public: EIGEN_GENERIC_PUBLIC_INTERFACE(SparseProduct) private: typedef typename ei_traits::_LhsNested _LhsNested; typedef typename ei_traits::_RhsNested _RhsNested; public: template EIGEN_STRONG_INLINE SparseProduct(const Lhs& lhs, const Rhs& rhs) : m_lhs(lhs), m_rhs(rhs) { ei_assert(lhs.cols() == rhs.rows()); enum { ProductIsValid = _LhsNested::ColsAtCompileTime==Dynamic || _RhsNested::RowsAtCompileTime==Dynamic || int(_LhsNested::ColsAtCompileTime)==int(_RhsNested::RowsAtCompileTime), AreVectors = _LhsNested::IsVectorAtCompileTime && _RhsNested::IsVectorAtCompileTime, SameSizes = EIGEN_PREDICATE_SAME_MATRIX_SIZE(_LhsNested,_RhsNested) }; // note to the lost user: // * for a dot product use: v1.dot(v2) // * for a coeff-wise product use: v1.cwise()*v2 EIGEN_STATIC_ASSERT(ProductIsValid || !(AreVectors && SameSizes), INVALID_VECTOR_VECTOR_PRODUCT__IF_YOU_WANTED_A_DOT_OR_COEFF_WISE_PRODUCT_YOU_MUST_USE_THE_EXPLICIT_FUNCTIONS) EIGEN_STATIC_ASSERT(ProductIsValid || !(SameSizes && !AreVectors), INVALID_MATRIX_PRODUCT__IF_YOU_WANTED_A_COEFF_WISE_PRODUCT_YOU_MUST_USE_THE_EXPLICIT_FUNCTION) EIGEN_STATIC_ASSERT(ProductIsValid || SameSizes, INVALID_MATRIX_PRODUCT) } EIGEN_STRONG_INLINE int rows() const { return m_lhs.rows(); } EIGEN_STRONG_INLINE int cols() const { return m_rhs.cols(); } EIGEN_STRONG_INLINE const _LhsNested& lhs() const { return m_lhs; } EIGEN_STRONG_INLINE const _RhsNested& rhs() const { return m_rhs; } protected: LhsNested m_lhs; 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.reserve(int(ratioRes*rows*cols)); for (int j=0; j::Iterator it(tempVector); it; ++it) res.insertBack(j,it.index()) = it.value(); } res.finalize(); } template::Flags&RowMajorBit, int RhsStorageOrder = ei_traits::Flags&RowMajorBit, int ResStorageOrder = ei_traits::Flags&RowMajorBit> struct ei_sparse_product_selector; template struct ei_sparse_product_selector { typedef typename ei_traits::type>::Scalar Scalar; static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) { 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 { 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_impl(lhs, rhs, _res); res = _res; } }; template 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 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 { 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_impl(rhs, lhs, _res); res = _res.transpose(); } }; // NOTE eventually let's transpose one argument even in this case since it might be expensive if // the result is not dense. // template // struct ei_sparse_product_selector // { // static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) // { // // trivial product as lhs.row/rhs.col dot products // // loop over the preferred order of the result // } // }; // NOTE the 2 others cases (col row *) must never occurs since they are caught // by ProductReturnType which transform it to (col col *) by evaluating rhs. // template // template // inline Derived& SparseMatrixBase::lazyAssign(const SparseProduct& product) // { // // std::cout << "sparse product to dense\n"; // ei_sparse_product_selector< // typename ei_cleantype::type, // typename ei_cleantype::type, // typename ei_cleantype::type>::run(product.lhs(),product.rhs(),derived()); // return derived(); // } // sparse = sparse * sparse template template inline Derived& SparseMatrixBase::operator=(const SparseProduct& product) { ei_sparse_product_selector< typename ei_cleantype::type, typename ei_cleantype::type, Derived>::run(product.lhs(),product.rhs(),derived()); return derived(); } // dense = sparse * dense // template // template // Derived& MatrixBase::lazyAssign(const SparseProduct& product) // { // typedef typename ei_cleantype::type _Lhs; // typedef typename _Lhs::InnerIterator LhsInnerIterator; // enum { LhsIsRowMajor = (_Lhs::Flags&RowMajorBit)==RowMajorBit }; // derived().setZero(); // for (int j=0; j template Derived& MatrixBase::lazyAssign(const SparseProduct& product) { typedef typename ei_cleantype::type _Lhs; typedef typename ei_cleantype::type _Rhs; typedef typename _Lhs::InnerIterator LhsInnerIterator; enum { LhsIsRowMajor = (_Lhs::Flags&RowMajorBit)==RowMajorBit, LhsIsSelfAdjoint = (_Lhs::Flags&SelfAdjointBit)==SelfAdjointBit, ProcessFirstHalf = LhsIsSelfAdjoint && ( ((_Lhs::Flags&(UpperTriangularBit|LowerTriangularBit))==0) || ( (_Lhs::Flags&UpperTriangularBit) && !LhsIsRowMajor) || ( (_Lhs::Flags&LowerTriangularBit) && LhsIsRowMajor) ), ProcessSecondHalf = LhsIsSelfAdjoint && (!ProcessFirstHalf) }; derived().setZero(); for (int j=0; j res(derived().row(LhsIsRowMajor ? j : 0)); for (; (ProcessFirstHalf ? i && i.index() < j : i) ; ++i) { if (LhsIsSelfAdjoint) { int a = LhsIsRowMajor ? j : i.index(); int b = LhsIsRowMajor ? i.index() : j; Scalar v = i.value(); derived().row(a) += (v) * product.rhs().row(b); derived().row(b) += ei_conj(v) * product.rhs().row(a); } else if (LhsIsRowMajor) res += i.value() * product.rhs().row(i.index()); else derived().row(i.index()) += i.value() * product.rhs().row(j); } if (ProcessFirstHalf && i && (i.index()==j)) derived().row(j) += i.value() * product.rhs().row(j); } return derived(); } // dense = dense * sparse template template Derived& MatrixBase::lazyAssign(const SparseProduct& product) { typedef typename ei_cleantype::type _Rhs; typedef typename _Rhs::InnerIterator RhsInnerIterator; enum { RhsIsRowMajor = (_Rhs::Flags&RowMajorBit)==RowMajorBit }; derived().setZero(); for (int j=0; j template EIGEN_STRONG_INLINE const typename SparseProductReturnType::Type SparseMatrixBase::operator*(const SparseMatrixBase &other) const { return typename SparseProductReturnType::Type(derived(), other.derived()); } // sparse * dense template template EIGEN_STRONG_INLINE const typename SparseProductReturnType::Type SparseMatrixBase::operator*(const MatrixBase &other) const { return typename SparseProductReturnType::Type(derived(), other.derived()); } #endif // EIGEN_SPARSEPRODUCT_H