Home | History | Annotate | Download | only in SparseCore
      1 // This file is part of Eigen, a lightweight C++ template library
      2 // for linear algebra.
      3 //
      4 // Copyright (C) 2008-2015 Gael Guennebaud <gael.guennebaud (at) inria.fr>
      5 //
      6 // This Source Code Form is subject to the terms of the Mozilla
      7 // Public License v. 2.0. If a copy of the MPL was not distributed
      8 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
      9 
     10 #ifndef EIGEN_CONSERVATIVESPARSESPARSEPRODUCT_H
     11 #define EIGEN_CONSERVATIVESPARSESPARSEPRODUCT_H
     12 
     13 namespace Eigen {
     14 
     15 namespace internal {
     16 
     17 template<typename Lhs, typename Rhs, typename ResultType>
     18 static void conservative_sparse_sparse_product_impl(const Lhs& lhs, const Rhs& rhs, ResultType& res, bool sortedInsertion = false)
     19 {
     20   typedef typename remove_all<Lhs>::type::Scalar Scalar;
     21 
     22   // make sure to call innerSize/outerSize since we fake the storage order.
     23   Index rows = lhs.innerSize();
     24   Index cols = rhs.outerSize();
     25   eigen_assert(lhs.outerSize() == rhs.innerSize());
     26 
     27   ei_declare_aligned_stack_constructed_variable(bool,   mask,     rows, 0);
     28   ei_declare_aligned_stack_constructed_variable(Scalar, values,   rows, 0);
     29   ei_declare_aligned_stack_constructed_variable(Index,  indices,  rows, 0);
     30 
     31   std::memset(mask,0,sizeof(bool)*rows);
     32 
     33   evaluator<Lhs> lhsEval(lhs);
     34   evaluator<Rhs> rhsEval(rhs);
     35 
     36   // estimate the number of non zero entries
     37   // given a rhs column containing Y non zeros, we assume that the respective Y columns
     38   // of the lhs differs in average of one non zeros, thus the number of non zeros for
     39   // the product of a rhs column with the lhs is X+Y where X is the average number of non zero
     40   // per column of the lhs.
     41   // Therefore, we have nnz(lhs*rhs) = nnz(lhs) + nnz(rhs)
     42   Index estimated_nnz_prod = lhsEval.nonZerosEstimate() + rhsEval.nonZerosEstimate();
     43 
     44   res.setZero();
     45   res.reserve(Index(estimated_nnz_prod));
     46   // we compute each column of the result, one after the other
     47   for (Index j=0; j<cols; ++j)
     48   {
     49 
     50     res.startVec(j);
     51     Index nnz = 0;
     52     for (typename evaluator<Rhs>::InnerIterator rhsIt(rhsEval, j); rhsIt; ++rhsIt)
     53     {
     54       Scalar y = rhsIt.value();
     55       Index k = rhsIt.index();
     56       for (typename evaluator<Lhs>::InnerIterator lhsIt(lhsEval, k); lhsIt; ++lhsIt)
     57       {
     58         Index i = lhsIt.index();
     59         Scalar x = lhsIt.value();
     60         if(!mask[i])
     61         {
     62           mask[i] = true;
     63           values[i] = x * y;
     64           indices[nnz] = i;
     65           ++nnz;
     66         }
     67         else
     68           values[i] += x * y;
     69       }
     70     }
     71     if(!sortedInsertion)
     72     {
     73       // unordered insertion
     74       for(Index k=0; k<nnz; ++k)
     75       {
     76         Index i = indices[k];
     77         res.insertBackByOuterInnerUnordered(j,i) = values[i];
     78         mask[i] = false;
     79       }
     80     }
     81     else
     82     {
     83       // alternative ordered insertion code:
     84       const Index t200 = rows/11; // 11 == (log2(200)*1.39)
     85       const Index t = (rows*100)/139;
     86 
     87       // FIXME reserve nnz non zeros
     88       // FIXME implement faster sorting algorithms for very small nnz
     89       // if the result is sparse enough => use a quick sort
     90       // otherwise => loop through the entire vector
     91       // In order to avoid to perform an expensive log2 when the
     92       // result is clearly very sparse we use a linear bound up to 200.
     93       if((nnz<200 && nnz<t200) || nnz * numext::log2(int(nnz)) < t)
     94       {
     95         if(nnz>1) std::sort(indices,indices+nnz);
     96         for(Index k=0; k<nnz; ++k)
     97         {
     98           Index i = indices[k];
     99           res.insertBackByOuterInner(j,i) = values[i];
    100           mask[i] = false;
    101         }
    102       }
    103       else
    104       {
    105         // dense path
    106         for(Index i=0; i<rows; ++i)
    107         {
    108           if(mask[i])
    109           {
    110             mask[i] = false;
    111             res.insertBackByOuterInner(j,i) = values[i];
    112           }
    113         }
    114       }
    115     }
    116   }
    117   res.finalize();
    118 }
    119 
    120 
    121 } // end namespace internal
    122 
    123 namespace internal {
    124 
    125 template<typename Lhs, typename Rhs, typename ResultType,
    126   int LhsStorageOrder = (traits<Lhs>::Flags&RowMajorBit) ? RowMajor : ColMajor,
    127   int RhsStorageOrder = (traits<Rhs>::Flags&RowMajorBit) ? RowMajor : ColMajor,
    128   int ResStorageOrder = (traits<ResultType>::Flags&RowMajorBit) ? RowMajor : ColMajor>
    129 struct conservative_sparse_sparse_product_selector;
    130 
    131 template<typename Lhs, typename Rhs, typename ResultType>
    132 struct conservative_sparse_sparse_product_selector<Lhs,Rhs,ResultType,ColMajor,ColMajor,ColMajor>
    133 {
    134   typedef typename remove_all<Lhs>::type LhsCleaned;
    135   typedef typename LhsCleaned::Scalar Scalar;
    136 
    137   static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res)
    138   {
    139     typedef SparseMatrix<typename ResultType::Scalar,RowMajor,typename ResultType::StorageIndex> RowMajorMatrix;
    140     typedef SparseMatrix<typename ResultType::Scalar,ColMajor,typename ResultType::StorageIndex> ColMajorMatrixAux;
    141     typedef typename sparse_eval<ColMajorMatrixAux,ResultType::RowsAtCompileTime,ResultType::ColsAtCompileTime,ColMajorMatrixAux::Flags>::type ColMajorMatrix;
    142 
    143     // If the result is tall and thin (in the extreme case a column vector)
    144     // then it is faster to sort the coefficients inplace instead of transposing twice.
    145     // FIXME, the following heuristic is probably not very good.
    146     if(lhs.rows()>rhs.cols())
    147     {
    148       ColMajorMatrix resCol(lhs.rows(),rhs.cols());
    149       // perform sorted insertion
    150       internal::conservative_sparse_sparse_product_impl<Lhs,Rhs,ColMajorMatrix>(lhs, rhs, resCol, true);
    151       res = resCol.markAsRValue();
    152     }
    153     else
    154     {
    155       ColMajorMatrixAux resCol(lhs.rows(),rhs.cols());
    156       // ressort to transpose to sort the entries
    157       internal::conservative_sparse_sparse_product_impl<Lhs,Rhs,ColMajorMatrixAux>(lhs, rhs, resCol, false);
    158       RowMajorMatrix resRow(resCol);
    159       res = resRow.markAsRValue();
    160     }
    161   }
    162 };
    163 
    164 template<typename Lhs, typename Rhs, typename ResultType>
    165 struct conservative_sparse_sparse_product_selector<Lhs,Rhs,ResultType,RowMajor,ColMajor,ColMajor>
    166 {
    167   static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res)
    168   {
    169      typedef SparseMatrix<typename ResultType::Scalar,RowMajor,typename ResultType::StorageIndex> RowMajorMatrix;
    170      RowMajorMatrix rhsRow = rhs;
    171      RowMajorMatrix resRow(lhs.rows(), rhs.cols());
    172      internal::conservative_sparse_sparse_product_impl<RowMajorMatrix,Lhs,RowMajorMatrix>(rhsRow, lhs, resRow);
    173      res = resRow;
    174   }
    175 };
    176 
    177 template<typename Lhs, typename Rhs, typename ResultType>
    178 struct conservative_sparse_sparse_product_selector<Lhs,Rhs,ResultType,ColMajor,RowMajor,ColMajor>
    179 {
    180   static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res)
    181   {
    182     typedef SparseMatrix<typename ResultType::Scalar,RowMajor,typename ResultType::StorageIndex> RowMajorMatrix;
    183     RowMajorMatrix lhsRow = lhs;
    184     RowMajorMatrix resRow(lhs.rows(), rhs.cols());
    185     internal::conservative_sparse_sparse_product_impl<Rhs,RowMajorMatrix,RowMajorMatrix>(rhs, lhsRow, resRow);
    186     res = resRow;
    187   }
    188 };
    189 
    190 template<typename Lhs, typename Rhs, typename ResultType>
    191 struct conservative_sparse_sparse_product_selector<Lhs,Rhs,ResultType,RowMajor,RowMajor,ColMajor>
    192 {
    193   static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res)
    194   {
    195     typedef SparseMatrix<typename ResultType::Scalar,RowMajor,typename ResultType::StorageIndex> RowMajorMatrix;
    196     RowMajorMatrix resRow(lhs.rows(), rhs.cols());
    197     internal::conservative_sparse_sparse_product_impl<Rhs,Lhs,RowMajorMatrix>(rhs, lhs, resRow);
    198     res = resRow;
    199   }
    200 };
    201 
    202 
    203 template<typename Lhs, typename Rhs, typename ResultType>
    204 struct conservative_sparse_sparse_product_selector<Lhs,Rhs,ResultType,ColMajor,ColMajor,RowMajor>
    205 {
    206   typedef typename traits<typename remove_all<Lhs>::type>::Scalar Scalar;
    207 
    208   static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res)
    209   {
    210     typedef SparseMatrix<typename ResultType::Scalar,ColMajor,typename ResultType::StorageIndex> ColMajorMatrix;
    211     ColMajorMatrix resCol(lhs.rows(), rhs.cols());
    212     internal::conservative_sparse_sparse_product_impl<Lhs,Rhs,ColMajorMatrix>(lhs, rhs, resCol);
    213     res = resCol;
    214   }
    215 };
    216 
    217 template<typename Lhs, typename Rhs, typename ResultType>
    218 struct conservative_sparse_sparse_product_selector<Lhs,Rhs,ResultType,RowMajor,ColMajor,RowMajor>
    219 {
    220   static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res)
    221   {
    222     typedef SparseMatrix<typename ResultType::Scalar,ColMajor,typename ResultType::StorageIndex> ColMajorMatrix;
    223     ColMajorMatrix lhsCol = lhs;
    224     ColMajorMatrix resCol(lhs.rows(), rhs.cols());
    225     internal::conservative_sparse_sparse_product_impl<ColMajorMatrix,Rhs,ColMajorMatrix>(lhsCol, rhs, resCol);
    226     res = resCol;
    227   }
    228 };
    229 
    230 template<typename Lhs, typename Rhs, typename ResultType>
    231 struct conservative_sparse_sparse_product_selector<Lhs,Rhs,ResultType,ColMajor,RowMajor,RowMajor>
    232 {
    233   static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res)
    234   {
    235     typedef SparseMatrix<typename ResultType::Scalar,ColMajor,typename ResultType::StorageIndex> ColMajorMatrix;
    236     ColMajorMatrix rhsCol = rhs;
    237     ColMajorMatrix resCol(lhs.rows(), rhs.cols());
    238     internal::conservative_sparse_sparse_product_impl<Lhs,ColMajorMatrix,ColMajorMatrix>(lhs, rhsCol, resCol);
    239     res = resCol;
    240   }
    241 };
    242 
    243 template<typename Lhs, typename Rhs, typename ResultType>
    244 struct conservative_sparse_sparse_product_selector<Lhs,Rhs,ResultType,RowMajor,RowMajor,RowMajor>
    245 {
    246   static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res)
    247   {
    248     typedef SparseMatrix<typename ResultType::Scalar,RowMajor,typename ResultType::StorageIndex> RowMajorMatrix;
    249     typedef SparseMatrix<typename ResultType::Scalar,ColMajor,typename ResultType::StorageIndex> ColMajorMatrix;
    250     RowMajorMatrix resRow(lhs.rows(),rhs.cols());
    251     internal::conservative_sparse_sparse_product_impl<Rhs,Lhs,RowMajorMatrix>(rhs, lhs, resRow);
    252     // sort the non zeros:
    253     ColMajorMatrix resCol(resRow);
    254     res = resCol;
    255   }
    256 };
    257 
    258 } // end namespace internal
    259 
    260 
    261 namespace internal {
    262 
    263 template<typename Lhs, typename Rhs, typename ResultType>
    264 static void sparse_sparse_to_dense_product_impl(const Lhs& lhs, const Rhs& rhs, ResultType& res)
    265 {
    266   typedef typename remove_all<Lhs>::type::Scalar Scalar;
    267   Index cols = rhs.outerSize();
    268   eigen_assert(lhs.outerSize() == rhs.innerSize());
    269 
    270   evaluator<Lhs> lhsEval(lhs);
    271   evaluator<Rhs> rhsEval(rhs);
    272 
    273   for (Index j=0; j<cols; ++j)
    274   {
    275     for (typename evaluator<Rhs>::InnerIterator rhsIt(rhsEval, j); rhsIt; ++rhsIt)
    276     {
    277       Scalar y = rhsIt.value();
    278       Index k = rhsIt.index();
    279       for (typename evaluator<Lhs>::InnerIterator lhsIt(lhsEval, k); lhsIt; ++lhsIt)
    280       {
    281         Index i = lhsIt.index();
    282         Scalar x = lhsIt.value();
    283         res.coeffRef(i,j) += x * y;
    284       }
    285     }
    286   }
    287 }
    288 
    289 
    290 } // end namespace internal
    291 
    292 namespace internal {
    293 
    294 template<typename Lhs, typename Rhs, typename ResultType,
    295   int LhsStorageOrder = (traits<Lhs>::Flags&RowMajorBit) ? RowMajor : ColMajor,
    296   int RhsStorageOrder = (traits<Rhs>::Flags&RowMajorBit) ? RowMajor : ColMajor>
    297 struct sparse_sparse_to_dense_product_selector;
    298 
    299 template<typename Lhs, typename Rhs, typename ResultType>
    300 struct sparse_sparse_to_dense_product_selector<Lhs,Rhs,ResultType,ColMajor,ColMajor>
    301 {
    302   static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res)
    303   {
    304     internal::sparse_sparse_to_dense_product_impl<Lhs,Rhs,ResultType>(lhs, rhs, res);
    305   }
    306 };
    307 
    308 template<typename Lhs, typename Rhs, typename ResultType>
    309 struct sparse_sparse_to_dense_product_selector<Lhs,Rhs,ResultType,RowMajor,ColMajor>
    310 {
    311   static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res)
    312   {
    313     typedef SparseMatrix<typename ResultType::Scalar,ColMajor,typename ResultType::StorageIndex> ColMajorMatrix;
    314     ColMajorMatrix lhsCol(lhs);
    315     internal::sparse_sparse_to_dense_product_impl<ColMajorMatrix,Rhs,ResultType>(lhsCol, rhs, res);
    316   }
    317 };
    318 
    319 template<typename Lhs, typename Rhs, typename ResultType>
    320 struct sparse_sparse_to_dense_product_selector<Lhs,Rhs,ResultType,ColMajor,RowMajor>
    321 {
    322   static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res)
    323   {
    324     typedef SparseMatrix<typename ResultType::Scalar,ColMajor,typename ResultType::StorageIndex> ColMajorMatrix;
    325     ColMajorMatrix rhsCol(rhs);
    326     internal::sparse_sparse_to_dense_product_impl<Lhs,ColMajorMatrix,ResultType>(lhs, rhsCol, res);
    327   }
    328 };
    329 
    330 template<typename Lhs, typename Rhs, typename ResultType>
    331 struct sparse_sparse_to_dense_product_selector<Lhs,Rhs,ResultType,RowMajor,RowMajor>
    332 {
    333   static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res)
    334   {
    335     Transpose<ResultType> trRes(res);
    336     internal::sparse_sparse_to_dense_product_impl<Rhs,Lhs,Transpose<ResultType> >(rhs, lhs, trRes);
    337   }
    338 };
    339 
    340 
    341 } // end namespace internal
    342 
    343 } // end namespace Eigen
    344 
    345 #endif // EIGEN_CONSERVATIVESPARSESPARSEPRODUCT_H
    346