// This file is part of Eigen, a lightweight C++ template library // for linear algebra. // // Copyright (C) 2008-2010 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 SparseProductReturnType { typedef typename ei_traits::Scalar Scalar; enum { LhsRowMajor = ei_traits::Flags & RowMajorBit, RhsRowMajor = ei_traits::Flags & RowMajorBit, TransposeRhs = /*false,*/ (!LhsRowMajor) && RhsRowMajor, TransposeLhs = /*false*/ 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 > { typedef DenseStorageMatrix DenseStorageType; // 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, EvalToRowMajor = (RhsFlags & LhsFlags & RowMajorBit), RemovedBits = ~(EvalToRowMajor ? 0 : RowMajorBit), Flags = (int(LhsFlags | RhsFlags) & HereditaryBits & RemovedBits) | EvalBeforeAssigningBit | EvalBeforeNestingBit, CoeffReadCost = Dynamic }; typedef Sparse StorageType; typedef SparseMatrixBase > Base; }; template class SparseProduct : ei_no_assignment_operator, public ei_traits >::Base { public: typedef typename ei_traits >::Base Base; EIGEN_DENSE_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; }; template static void ei_sparse_product_impl2(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(); ei_assert(lhs.outerSize() == rhs.innerSize()); std::vector mask(rows,false); Matrix values(rows); Matrix indices(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); int t200 = rows/(log2(200)*1.39); int t = (rows*100)/139; res.resize(rows, cols); res.reserve(int(ratioRes*rows*cols)); // we compute each column of the result, one after the other for (int j=0; j use a quick sort // otherwise => loop through the entire vector // In order to avoid to perform an expensive log2 when the // result is clearly very sparse we use a linear bound up to 200. // if((nnz<200 && nnz1) std::sort(indices.data(),indices.data()+nnz); // for(int k=0; k static void ei_sparse_product_impl(const Lhs& lhs, const Rhs& rhs, ResultType& res) { // return ei_sparse_product_impl2(lhs,rhs,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) { // std::cerr << __LINE__ << "\n"; 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) { // std::cerr << __LINE__ << "\n"; // 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) { // std::cerr << __LINE__ << "\n"; // 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) { // std::cerr << "here...\n"; typedef SparseMatrix ColMajorMatrix; ColMajorMatrix colLhs(lhs); ColMajorMatrix colRhs(rhs); // std::cerr << "more...\n"; ei_sparse_product_impl(colLhs, colRhs, res); // std::cerr << "OK.\n"; // 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 the 2 others cases (col row *) must never occurs since they are caught // by ProductReturnType which transform it to (col col *) by evaluating rhs. // sparse = sparse * sparse template template inline Derived& SparseMatrixBase::operator=(const SparseProduct& product) { // std::cerr << "there..." << typeid(Lhs).name() << " " << typeid(Lhs).name() << " " << (Derived::Flags&&RowMajorBit) << "\n"; ei_sparse_product_selector< typename ei_cleantype::type, typename ei_cleantype::type, Derived>::run(product.lhs(),product.rhs(),derived()); return derived(); } template::Flags&RowMajorBit, int RhsStorageOrder = ei_traits::Flags&RowMajorBit, int ResStorageOrder = ei_traits::Flags&RowMajorBit> struct ei_sparse_product_selector2; template struct ei_sparse_product_selector2 { typedef typename ei_traits::type>::Scalar Scalar; static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) { ei_sparse_product_impl2(lhs, rhs, res); } }; template struct ei_sparse_product_selector2 { static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) { // typedef SparseMatrix RowMajorMatrix; // RowMajorMatrix rhsRow = rhs; // RowMajorMatrix resRow(res.rows(), res.cols()); // ei_sparse_product_impl2(rhsRow, lhs, resRow); // res = resRow; } }; template struct ei_sparse_product_selector2 { static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) { typedef SparseMatrix RowMajorMatrix; RowMajorMatrix lhsRow = lhs; RowMajorMatrix resRow(res.rows(), res.cols()); ei_sparse_product_impl2(rhs, lhsRow, resRow); res = resRow; } }; template struct ei_sparse_product_selector2 { static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) { typedef SparseMatrix RowMajorMatrix; RowMajorMatrix resRow(res.rows(), res.cols()); ei_sparse_product_impl2(rhs, lhs, resRow); res = resRow; } }; template struct ei_sparse_product_selector2 { typedef typename ei_traits::type>::Scalar Scalar; static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) { typedef SparseMatrix ColMajorMatrix; ColMajorMatrix resCol(res.rows(), res.cols()); ei_sparse_product_impl2(lhs, rhs, resCol); res = resCol; } }; template struct ei_sparse_product_selector2 { static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) { typedef SparseMatrix ColMajorMatrix; ColMajorMatrix lhsCol = lhs; ColMajorMatrix resCol(res.rows(), res.cols()); ei_sparse_product_impl2(lhsCol, rhs, resCol); res = resCol; } }; template struct ei_sparse_product_selector2 { static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) { typedef SparseMatrix ColMajorMatrix; ColMajorMatrix rhsCol = rhs; ColMajorMatrix resCol(res.rows(), res.cols()); ei_sparse_product_impl2(lhs, rhsCol, resCol); res = resCol; } }; template struct ei_sparse_product_selector2 { static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) { typedef SparseMatrix ColMajorMatrix; // ColMajorMatrix lhsTr(lhs); // ColMajorMatrix rhsTr(rhs); // ColMajorMatrix aux(res.rows(), res.cols()); // ei_sparse_product_impl2(rhs, lhs, aux); // // ColMajorMatrix aux2 = aux.transpose(); // res = aux; typedef SparseMatrix ColMajorMatrix; ColMajorMatrix lhsCol(lhs); ColMajorMatrix rhsCol(rhs); ColMajorMatrix resCol(res.rows(), res.cols()); ei_sparse_product_impl2(lhsCol, rhsCol, resCol); res = resCol; } }; template template inline void SparseMatrixBase::_experimentalNewProduct(const Lhs& lhs, const Rhs& rhs) { //derived().resize(lhs.rows(), rhs.cols()); ei_sparse_product_selector2< typename ei_cleantype::type, typename ei_cleantype::type, Derived>::run(lhs,rhs,derived()); } template struct ei_traits > : ei_traits, Lhs, Rhs> > { typedef Dense StorageType; typedef DenseStorageMatrix DenseStorageType; }; template class SparseTimeDenseProduct : public ProductBase, Lhs, Rhs> { public: EIGEN_PRODUCT_PUBLIC_INTERFACE(SparseTimeDenseProduct) SparseTimeDenseProduct(const Lhs& lhs, const Rhs& rhs) : Base(lhs,rhs) {} template void scaleAndAddTo(Dest& dest, Scalar alpha) const { typedef typename ei_cleantype::type _Lhs; typedef typename ei_cleantype::type _Rhs; typedef typename _Lhs::InnerIterator LhsInnerIterator; enum { LhsIsRowMajor = (_Lhs::Flags&RowMajorBit)==RowMajorBit }; for(int j=0; j dest_j(dest.row(LhsIsRowMajor ? j : 0)); for(LhsInnerIterator it(m_lhs,j); it ;++it) { if(LhsIsRowMajor) dest_j += (alpha*it.value()) * m_rhs.row(it.index()); else if(Rhs::ColsAtCompileTime==1) dest.coeffRef(it.index()) += it.value() * rhs_j; else dest.row(it.index()) += (alpha*it.value()) * m_rhs.row(j); } } } private: SparseTimeDenseProduct& operator=(const SparseTimeDenseProduct&); }; // dense = dense * sparse template struct ei_traits > : ei_traits, Lhs, Rhs> > { typedef Dense StorageType; }; template class DenseTimeSparseProduct : public ProductBase, Lhs, Rhs> { public: EIGEN_PRODUCT_PUBLIC_INTERFACE(DenseTimeSparseProduct) DenseTimeSparseProduct(const Lhs& lhs, const Rhs& rhs) : Base(lhs,rhs) {} template void scaleAndAddTo(Dest& dest, Scalar alpha) const { typedef typename ei_cleantype::type _Rhs; typedef typename _Rhs::InnerIterator RhsInnerIterator; enum { RhsIsRowMajor = (_Rhs::Flags&RowMajorBit)==RowMajorBit }; 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 SparseTimeDenseProduct SparseMatrixBase::operator*(const MatrixBase &other) const { return SparseTimeDenseProduct(derived(), other.derived()); } #endif // EIGEN_SPARSEPRODUCT_H