ResultType vector_comprehension(const std::vector<T>& in_vec, Function func) { ResultType result; result.reserve(in_vec.size()); for (auto& elem : in_vec) { result.push_back(func(elem)); } return result; }
void fastSparseProduct(const Lhs& lhs, const Rhs& rhs, ResultType& res) { // initialize result res = ResultType(lhs.rows(), rhs.cols()); // if one of the matrices does not contain non zero elements // the result will only contain an empty matrix if( lhs.nonZeros() == 0 || rhs.nonZeros() == 0 ) return; typedef typename Eigen::internal::remove_all<Lhs>::type::Scalar Scalar; typedef typename Eigen::internal::remove_all<Lhs>::type::Index Index; // make sure to call innerSize/outerSize since we fake the storage order. Index rows = lhs.innerSize(); Index cols = rhs.outerSize(); eigen_assert(lhs.outerSize() == rhs.innerSize()); std::vector<bool> mask(rows,false); Eigen::Matrix<Scalar,Eigen::Dynamic,1> values(rows); Eigen::Matrix<Index, Eigen::Dynamic,1> indices(rows); // estimate the number of non zero entries // given a rhs column containing Y non zeros, we assume that the respective Y columns // of the lhs differs in average of one non zeros, thus the number of non zeros for // the product of a rhs column with the lhs is X+Y where X is the average number of non zero // per column of the lhs. // Therefore, we have nnz(lhs*rhs) = nnz(lhs) + nnz(rhs) Index estimated_nnz_prod = lhs.nonZeros() + rhs.nonZeros(); res.setZero(); res.reserve(Index(estimated_nnz_prod)); //const Scalar epsilon = std::numeric_limits< Scalar >::epsilon(); const Scalar epsilon = 0.0; // we compute each column of the result, one after the other for (Index j=0; j<cols; ++j) { Index nnz = 0; for (typename Rhs::InnerIterator rhsIt(rhs, j); rhsIt; ++rhsIt) { const Scalar y = rhsIt.value(); for (typename Lhs::InnerIterator lhsIt(lhs, rhsIt.index()); lhsIt; ++lhsIt) { const Scalar val = lhsIt.value() * y; if( std::abs( val ) > epsilon ) { const Index i = lhsIt.index(); if(!mask[i]) { mask[i] = true; values[i] = val; indices[nnz] = i; ++nnz; } else values[i] += val; } } } if( nnz > 1 ) { // sort indices for sorted insertion to avoid later copying QuickSort< 1 >::sort( indices.data(), indices.data()+nnz ); } res.startVec(j); // ordered insertion // still using insertBackByOuterInnerUnordered since we know what we are doing for(Index k=0; k<nnz; ++k) { const Index i = indices[k]; res.insertBackByOuterInnerUnordered(j,i) = values[i]; mask[i] = false; } } res.finalize(); }