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