Eigen  5.0.1
 
Loading...
Searching...
No Matches
ConservativeSparseSparseProduct.h
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// SPDX-License-Identifier: MPL-2.0
10
11#ifndef EIGEN_CONSERVATIVESPARSESPARSEPRODUCT_H
12#define EIGEN_CONSERVATIVESPARSESPARSEPRODUCT_H
13
14// IWYU pragma: private
15#include "./InternalHeaderCheck.h"
16
17namespace Eigen {
18
19namespace internal {
20
21template <typename Lhs, typename Rhs, typename ResultType>
22static void conservative_sparse_sparse_product_impl(const Lhs& lhs, const Rhs& rhs, ResultType& res,
23 bool sortedInsertion = false) {
24 using LhsScalar = typename remove_all_t<Lhs>::Scalar;
25 using RhsScalar = typename remove_all_t<Rhs>::Scalar;
26 using ResScalar = typename remove_all_t<ResultType>::Scalar;
27
28 // make sure to call innerSize/outerSize since we fake the storage order.
29 Index rows = lhs.innerSize();
30 Index cols = rhs.outerSize();
31 eigen_assert(lhs.outerSize() == rhs.innerSize());
32
33 ei_declare_aligned_stack_constructed_variable(bool, mask, rows, 0);
34 ei_declare_aligned_stack_constructed_variable(ResScalar, values, rows, 0);
35 ei_declare_aligned_stack_constructed_variable(Index, indices, rows, 0);
36
37 std::fill_n(mask, rows, false);
38
39 evaluator<Lhs> lhsEval(lhs);
40 evaluator<Rhs> rhsEval(rhs);
41
42 // estimate the number of non zero entries
43 // given a rhs column containing Y non zeros, we assume that the respective Y columns
44 // of the lhs differs in average of one non zeros, thus the number of non zeros for
45 // the product of a rhs column with the lhs is X+Y where X is the average number of non zero
46 // per column of the lhs.
47 // Therefore, we have nnz(lhs*rhs) = nnz(lhs) + nnz(rhs)
48 Index estimated_nnz_prod = lhsEval.nonZerosEstimate() + rhsEval.nonZerosEstimate();
49
50 res.setZero();
51 res.reserve(Index(estimated_nnz_prod));
52 // we compute each column of the result, one after the other
53 for (Index j = 0; j < cols; ++j) {
54 res.startVec(j);
55 Index nnz = 0;
56 for (typename evaluator<Rhs>::InnerIterator rhsIt(rhsEval, j); rhsIt; ++rhsIt) {
57 RhsScalar y = rhsIt.value();
58 Index k = rhsIt.index();
59 for (typename evaluator<Lhs>::InnerIterator lhsIt(lhsEval, k); lhsIt; ++lhsIt) {
60 Index i = lhsIt.index();
61 LhsScalar x = lhsIt.value();
62 if (!mask[i]) {
63 mask[i] = true;
64 values[i] = x * y;
65 indices[nnz] = i;
66 ++nnz;
67 } else
68 values[i] += x * y;
69 }
70 }
71 if (!sortedInsertion) {
72 // unordered insertion
73 for (Index k = 0; k < nnz; ++k) {
74 Index i = indices[k];
75 res.insertBackByOuterInnerUnordered(j, i) = values[i];
76 mask[i] = false;
77 }
78 } else {
79 // alternative ordered insertion code:
80 const Index t200 = rows / 11; // 11 == (log2(200)*1.39)
81 const Index t = (rows * 100) / 139;
82
83 // if the result is sparse enough => use a quick sort
84 // otherwise => loop through the entire vector
85 // In order to avoid to perform an expensive log2 when the
86 // result is clearly very sparse we use a linear bound up to 200.
87 if ((nnz < 200 && nnz < t200) || nnz * numext::log2(int(nnz)) < t) {
88 if (nnz > 1) std::sort(indices, indices + nnz);
89 for (Index k = 0; k < nnz; ++k) {
90 Index i = indices[k];
91 res.insertBackByOuterInner(j, i) = values[i];
92 mask[i] = false;
93 }
94 } else {
95 // dense path
96 for (Index i = 0; i < rows; ++i) {
97 if (mask[i]) {
98 mask[i] = false;
99 res.insertBackByOuterInner(j, i) = values[i];
100 }
101 }
102 }
103 }
104 }
105 res.finalize();
106}
107
108// Helper template to generate new sparse matrix types
109template <class Source, int Order>
110using WithStorageOrder = SparseMatrix<typename Source::Scalar, Order, typename Source::StorageIndex>;
111
112template <typename Lhs, typename Rhs, typename ResultType,
113 int LhsStorageOrder = (traits<Lhs>::Flags & RowMajorBit) ? RowMajor : ColMajor,
114 int RhsStorageOrder = (traits<Rhs>::Flags & RowMajorBit) ? RowMajor : ColMajor,
115 int ResStorageOrder = (traits<ResultType>::Flags & RowMajorBit) ? RowMajor : ColMajor>
116struct conservative_sparse_sparse_product_selector;
117
118template <typename Lhs, typename Rhs, typename ResultType>
119struct conservative_sparse_sparse_product_selector<Lhs, Rhs, ResultType, ColMajor, ColMajor, ColMajor> {
120 static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) {
121 using RowMajorMatrix = WithStorageOrder<ResultType, RowMajor>;
122 using ColMajorMatrixAux = WithStorageOrder<ResultType, ColMajor>;
123
124 // If the result is tall and thin (in the extreme case a column vector)
125 // then it is faster to sort the coefficients inplace instead of transposing twice.
126 // The dimension-only test here ignores nnz / per-column density; a proper
127 // cost model using estimated_nnz_prod would pick the right path more often.
128 if (lhs.rows() > rhs.cols()) {
129 using ColMajorMatrix = typename sparse_eval<ColMajorMatrixAux, ResultType::RowsAtCompileTime,
130 ResultType::ColsAtCompileTime, ColMajorMatrixAux::Flags>::type;
131 ColMajorMatrix resCol(lhs.rows(), rhs.cols());
132 // perform sorted insertion
133 internal::conservative_sparse_sparse_product_impl<Lhs, Rhs, ColMajorMatrix>(lhs, rhs, resCol, true);
134 res = resCol.markAsRValue();
135 } else {
136 ColMajorMatrixAux resCol(lhs.rows(), rhs.cols());
137 // resort to transpose to sort the entries
138 internal::conservative_sparse_sparse_product_impl<Lhs, Rhs, ColMajorMatrixAux>(lhs, rhs, resCol, false);
139 RowMajorMatrix resRow(resCol);
140 res = resRow.markAsRValue();
141 }
142 }
143};
144
145template <typename Lhs, typename Rhs, typename ResultType>
146struct conservative_sparse_sparse_product_selector<Lhs, Rhs, ResultType, RowMajor, ColMajor, ColMajor> {
147 static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) {
148 using RowMajorRhs = WithStorageOrder<Rhs, RowMajor>;
149 using RowMajorRes = WithStorageOrder<ResultType, RowMajor>;
150 RowMajorRhs rhsRow = rhs;
151 RowMajorRes resRow(lhs.rows(), rhs.cols());
152 internal::conservative_sparse_sparse_product_impl<RowMajorRhs, Lhs, RowMajorRes>(rhsRow, lhs, resRow);
153 res = resRow;
154 }
155};
156
157template <typename Lhs, typename Rhs, typename ResultType>
158struct conservative_sparse_sparse_product_selector<Lhs, Rhs, ResultType, ColMajor, RowMajor, ColMajor> {
159 static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) {
160 using RowMajorLhs = WithStorageOrder<Lhs, RowMajor>;
161 using RowMajorRes = WithStorageOrder<ResultType, RowMajor>;
162 RowMajorLhs lhsRow = lhs;
163 RowMajorRes resRow(lhs.rows(), rhs.cols());
164 internal::conservative_sparse_sparse_product_impl<Rhs, RowMajorLhs, RowMajorRes>(rhs, lhsRow, resRow);
165 res = resRow;
166 }
167};
168
169template <typename Lhs, typename Rhs, typename ResultType>
170struct conservative_sparse_sparse_product_selector<Lhs, Rhs, ResultType, RowMajor, RowMajor, ColMajor> {
171 static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) {
172 using RowMajorRes = WithStorageOrder<ResultType, RowMajor>;
173 RowMajorRes resRow(lhs.rows(), rhs.cols());
174 internal::conservative_sparse_sparse_product_impl<Rhs, Lhs, RowMajorRes>(rhs, lhs, resRow);
175 res = resRow;
176 }
177};
178
179template <typename Lhs, typename Rhs, typename ResultType>
180struct conservative_sparse_sparse_product_selector<Lhs, Rhs, ResultType, ColMajor, ColMajor, RowMajor> {
181 static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) {
182 using ColMajorRes = WithStorageOrder<ResultType, ColMajor>;
183 ColMajorRes resCol(lhs.rows(), rhs.cols());
184 internal::conservative_sparse_sparse_product_impl<Lhs, Rhs, ColMajorRes>(lhs, rhs, resCol);
185 res = resCol;
186 }
187};
188
189template <typename Lhs, typename Rhs, typename ResultType>
190struct conservative_sparse_sparse_product_selector<Lhs, Rhs, ResultType, RowMajor, ColMajor, RowMajor> {
191 static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) {
192 using ColMajorLhs = WithStorageOrder<Lhs, ColMajor>;
193 using ColMajorRes = WithStorageOrder<ResultType, ColMajor>;
194 ColMajorLhs lhsCol = lhs;
195 ColMajorRes resCol(lhs.rows(), rhs.cols());
196 internal::conservative_sparse_sparse_product_impl<ColMajorLhs, Rhs, ColMajorRes>(lhsCol, rhs, resCol);
197 res = resCol;
198 }
199};
200
201template <typename Lhs, typename Rhs, typename ResultType>
202struct conservative_sparse_sparse_product_selector<Lhs, Rhs, ResultType, ColMajor, RowMajor, RowMajor> {
203 static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) {
204 using ColMajorRhs = WithStorageOrder<Rhs, ColMajor>;
205 using ColMajorRes = WithStorageOrder<ResultType, ColMajor>;
206 ColMajorRhs rhsCol = rhs;
207 ColMajorRes resCol(lhs.rows(), rhs.cols());
208 internal::conservative_sparse_sparse_product_impl<Lhs, ColMajorRhs, ColMajorRes>(lhs, rhsCol, resCol);
209 res = resCol;
210 }
211};
212
213template <typename Lhs, typename Rhs, typename ResultType>
214struct conservative_sparse_sparse_product_selector<Lhs, Rhs, ResultType, RowMajor, RowMajor, RowMajor> {
215 static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) {
216 using ColMajorRes = WithStorageOrder<ResultType, ColMajor>;
217 using RowMajorRes = WithStorageOrder<ResultType, RowMajor>;
218 RowMajorRes resRow(lhs.rows(), rhs.cols());
219 internal::conservative_sparse_sparse_product_impl<Rhs, Lhs, RowMajorRes>(rhs, lhs, resRow);
220 // sort the non zeros:
221 ColMajorRes resCol(resRow);
222 res = resCol;
223 }
224};
225
226template <typename Lhs, typename Rhs, typename ResultType>
227static void sparse_sparse_to_dense_product_impl(const Lhs& lhs, const Rhs& rhs, ResultType& res) {
228 using LhsScalar = typename remove_all_t<Lhs>::Scalar;
229 using RhsScalar = typename remove_all_t<Rhs>::Scalar;
230 Index cols = rhs.outerSize();
231 eigen_assert(lhs.outerSize() == rhs.innerSize());
232
233 evaluator<Lhs> lhsEval(lhs);
234 evaluator<Rhs> rhsEval(rhs);
235
236 for (Index j = 0; j < cols; ++j) {
237 for (typename evaluator<Rhs>::InnerIterator rhsIt(rhsEval, j); rhsIt; ++rhsIt) {
238 RhsScalar y = rhsIt.value();
239 Index k = rhsIt.index();
240 for (typename evaluator<Lhs>::InnerIterator lhsIt(lhsEval, k); lhsIt; ++lhsIt) {
241 Index i = lhsIt.index();
242 LhsScalar x = lhsIt.value();
243 res.coeffRef(i, j) += x * y;
244 }
245 }
246 }
247}
248
249} // end namespace internal
250
251namespace internal {
252
253template <typename Lhs, typename Rhs, typename ResultType,
254 int LhsStorageOrder = (traits<Lhs>::Flags & RowMajorBit) ? RowMajor : ColMajor,
255 int RhsStorageOrder = (traits<Rhs>::Flags & RowMajorBit) ? RowMajor : ColMajor>
256struct sparse_sparse_to_dense_product_selector;
257
258template <typename Lhs, typename Rhs, typename ResultType>
259struct sparse_sparse_to_dense_product_selector<Lhs, Rhs, ResultType, ColMajor, ColMajor> {
260 static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) {
261 internal::sparse_sparse_to_dense_product_impl<Lhs, Rhs, ResultType>(lhs, rhs, res);
262 }
263};
264
265template <typename Lhs, typename Rhs, typename ResultType>
266struct sparse_sparse_to_dense_product_selector<Lhs, Rhs, ResultType, RowMajor, ColMajor> {
267 static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) {
268 using ColMajorLhs = WithStorageOrder<Lhs, ColMajor>;
269 ColMajorLhs lhsCol(lhs);
270 internal::sparse_sparse_to_dense_product_impl<ColMajorLhs, Rhs, ResultType>(lhsCol, rhs, res);
271 }
272};
273
274template <typename Lhs, typename Rhs, typename ResultType>
275struct sparse_sparse_to_dense_product_selector<Lhs, Rhs, ResultType, ColMajor, RowMajor> {
276 static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) {
277 using ColMajorRhs = WithStorageOrder<Rhs, ColMajor>;
278 ColMajorRhs rhsCol(rhs);
279 internal::sparse_sparse_to_dense_product_impl<Lhs, ColMajorRhs, ResultType>(lhs, rhsCol, res);
280 }
281};
282
283template <typename Lhs, typename Rhs, typename ResultType>
284struct sparse_sparse_to_dense_product_selector<Lhs, Rhs, ResultType, RowMajor, RowMajor> {
285 static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) {
286 Transpose<ResultType> trRes(res);
287 internal::sparse_sparse_to_dense_product_impl<Rhs, Lhs, Transpose<ResultType>>(rhs, lhs, trRes);
288 }
289};
290
291} // end namespace internal
292
293} // end namespace Eigen
294
295#endif // EIGEN_CONSERVATIVESPARSESPARSEPRODUCT_H
@ ColMajor
Definition Constants.h:319
@ RowMajor
Definition Constants.h:321
constexpr unsigned int RowMajorBit
Definition Constants.h:71