Eigen  5.0.1
 
Loading...
Searching...
No Matches
ProductEvaluators.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com>
5// Copyright (C) 2008-2010 Gael Guennebaud <gael.guennebaud@inria.fr>
6// Copyright (C) 2011 Jitse Niesen <jitse@maths.leeds.ac.uk>
7//
8// This Source Code Form is subject to the terms of the Mozilla
9// Public License v. 2.0. If a copy of the MPL was not distributed
10// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
11// SPDX-License-Identifier: MPL-2.0
12
13#ifndef EIGEN_PRODUCTEVALUATORS_H
14#define EIGEN_PRODUCTEVALUATORS_H
15
16// IWYU pragma: private
17#include "./InternalHeaderCheck.h"
18
19// C4804: unsafe use of type 'bool' in operation. Unavoidable in generic code
20// instantiated with bool scalars (e.g. += and * on bool).
21#if EIGEN_COMP_MSVC
22#pragma warning(push)
23#pragma warning(disable : 4804)
24#endif
25
26namespace Eigen {
27
28namespace internal {
29
38template <typename Lhs, typename Rhs, int Options>
39struct evaluator<Product<Lhs, Rhs, Options>> : public product_evaluator<Product<Lhs, Rhs, Options>> {
40 using XprType = Product<Lhs, Rhs, Options>;
41 using Base = product_evaluator<XprType>;
42
43 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE explicit evaluator(const XprType& xpr) : Base(xpr) {}
44};
45
46// A scalar factor cannot be folded into a unit diagonal or a permutation.
47template <typename Lhs, typename Shape = typename evaluator_traits<Lhs>::Shape>
48struct product_can_fold_scalar
49 : bool_constant<std::is_same<Shape, DenseShape>::value || std::is_same<Shape, SparseShape>::value ||
50 std::is_same<Shape, DiagonalShape>::value || std::is_same<Shape, SelfAdjointShape>::value> {};
51
52template <typename Lhs>
53struct product_can_fold_scalar<Lhs, TriangularShape> : bool_constant<(int(Lhs::Mode) & int(UnitDiag)) == 0> {};
54
55// The lazy selfadjoint/diagonal evaluator would conjugate a folded complex factor.
56template <typename Lhs, typename Rhs>
57struct product_evaluator_can_fold_scalar
58 : bool_constant<product_can_fold_scalar<Lhs>::value &&
59 !(std::is_same<typename evaluator_traits<Lhs>::Shape, SelfAdjointShape>::value &&
60 std::is_same<typename evaluator_traits<Rhs>::Shape, DiagonalShape>::value)> {};
61
62template <typename Xpr,
63 bool Fold = product_evaluator_can_fold_scalar<typename Xpr::Rhs::Lhs, typename Xpr::Rhs::Rhs>::value>
64struct scaled_product_evaluator_type {
65 // The assignment kernel extracts selfadjoint factors; materializing also protects nested aliases.
66 using type = std::conditional_t<product_can_fold_scalar<typename Xpr::Rhs::Lhs>::value, evaluator<EvalToTemp<Xpr>>,
67 binary_evaluator<Xpr>>;
68};
69
70template <typename Xpr>
71struct scaled_product_evaluator_type<Xpr, true> {
72 using type =
73 evaluator<remove_all_t<decltype((std::declval<Xpr>().lhs().functor().m_other * std::declval<Xpr>().rhs().lhs()) *
74 std::declval<Xpr>().rhs().rhs())>>;
75};
76
77// Catch "scalar * ( A * B )" and transform it to "(scalar*A) * B"
78// TODO: we should apply that rule only if that's really helpful
79template <typename Lhs, typename Rhs, typename Scalar1, typename Scalar2, typename Plain1>
80struct evaluator_assume_aliasing<CwiseBinaryOp<internal::scalar_product_op<Scalar1, Scalar2>,
81 const CwiseNullaryOp<internal::scalar_constant_op<Scalar1>, Plain1>,
82 const Product<Lhs, Rhs, DefaultProduct>>> : std::true_type {};
83template <typename Lhs, typename Rhs, typename Scalar1, typename Scalar2, typename Plain1>
84struct evaluator<CwiseBinaryOp<internal::scalar_product_op<Scalar1, Scalar2>,
85 const CwiseNullaryOp<internal::scalar_constant_op<Scalar1>, Plain1>,
86 const Product<Lhs, Rhs, DefaultProduct>>>
87 : scaled_product_evaluator_type<CwiseBinaryOp<internal::scalar_product_op<Scalar1, Scalar2>,
88 const CwiseNullaryOp<internal::scalar_constant_op<Scalar1>, Plain1>,
89 const Product<Lhs, Rhs, DefaultProduct>>>::type {
90 using XprType = CwiseBinaryOp<internal::scalar_product_op<Scalar1, Scalar2>,
91 const CwiseNullaryOp<internal::scalar_constant_op<Scalar1>, Plain1>,
92 const Product<Lhs, Rhs, DefaultProduct>>;
93 using Base = typename scaled_product_evaluator_type<XprType>::type;
94
95 template <bool Fold = product_evaluator_can_fold_scalar<Lhs, Rhs>::value, std::enable_if_t<Fold, int> = 0>
96 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE explicit evaluator(const XprType& xpr)
97 : Base(xpr.lhs().functor().m_other * xpr.rhs().lhs() * xpr.rhs().rhs()) {}
98
99 template <bool Fold = product_evaluator_can_fold_scalar<Lhs, Rhs>::value, std::enable_if_t<!Fold, int> = 0>
100 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE explicit evaluator(const XprType& xpr) : Base(xpr) {}
101};
102
103template <typename Lhs, typename Rhs, int DiagIndex>
104struct evaluator<Diagonal<const Product<Lhs, Rhs, DefaultProduct>, DiagIndex>>
105 : public evaluator<Diagonal<const Product<Lhs, Rhs, LazyProduct>, DiagIndex>> {
106 using XprType = Diagonal<const Product<Lhs, Rhs, DefaultProduct>, DiagIndex>;
107 using Base = evaluator<Diagonal<const Product<Lhs, Rhs, LazyProduct>, DiagIndex>>;
108
109 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE explicit evaluator(const XprType& xpr)
110 : Base(Diagonal<const Product<Lhs, Rhs, LazyProduct>, DiagIndex>(
111 Product<Lhs, Rhs, LazyProduct>(xpr.nestedExpression().lhs(), xpr.nestedExpression().rhs()), xpr.index())) {}
112};
113
114// Helper class to perform a matrix product with the destination at hand.
115// Depending on the sizes of the factors, there are different evaluation strategies
116// as controlled by internal::product_type.
117template <typename Lhs, typename Rhs, typename LhsShape = typename evaluator_traits<Lhs>::Shape,
118 typename RhsShape = typename evaluator_traits<Rhs>::Shape,
119 int ProductType = internal::product_type<Lhs, Rhs>::value>
120struct generic_product_impl;
121
122template <typename Lhs, typename Rhs>
123struct evaluator_assume_aliasing<Product<Lhs, Rhs, DefaultProduct>> : std::true_type {};
124
125// This is the default evaluator implementation for products:
126// It creates a temporary and call generic_product_impl
127template <typename Lhs, typename Rhs, int Options, int ProductTag, typename LhsShape, typename RhsShape>
128struct product_evaluator<Product<Lhs, Rhs, Options>, ProductTag, LhsShape, RhsShape>
129 : public evaluator<typename Product<Lhs, Rhs, Options>::PlainObject> {
130 using XprType = Product<Lhs, Rhs, Options>;
131 using PlainObject = typename XprType::PlainObject;
132 using Base = evaluator<PlainObject>;
133 enum { Flags = Base::Flags | EvalBeforeNestingBit };
134
135 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE explicit product_evaluator(const XprType& xpr)
136 : m_result(xpr.rows(), xpr.cols()) {
137 internal::construct_at<Base>(this, m_result);
138
139 // FIXME shall we handle nested_eval here?,
140 // if so, then we must take care at removing the call to nested_eval in the specializations (e.g., in
141 // permutation_matrix_product, transposition_matrix_product, etc.)
142 // typedef typename internal::nested_eval<Lhs,Rhs::ColsAtCompileTime>::type LhsNested;
143 // typedef typename internal::nested_eval<Rhs,Lhs::RowsAtCompileTime>::type RhsNested;
144 // typedef internal::remove_all_t<LhsNested> LhsNestedCleaned;
145 // typedef internal::remove_all_t<RhsNested> RhsNestedCleaned;
146 //
147 // const LhsNested lhs(xpr.lhs());
148 // const RhsNested rhs(xpr.rhs());
149 //
150 // generic_product_impl<LhsNestedCleaned, RhsNestedCleaned>::evalTo(m_result, lhs, rhs);
151
152 generic_product_impl<Lhs, Rhs, LhsShape, RhsShape, ProductTag>::evalTo(m_result, xpr.lhs(), xpr.rhs());
153 }
154
155 protected:
156 PlainObject m_result;
157};
158
159// The following three shortcuts are enabled only if the scalar types match exactly.
160// TODO: we could enable them for different scalar types when the product is not vectorized.
161
162// Dense = Product
163template <typename DstXprType, typename Lhs, typename Rhs, int Options, typename Scalar>
164struct Assignment<DstXprType, Product<Lhs, Rhs, Options>, internal::assign_op<Scalar, Scalar>, Dense2Dense,
165 std::enable_if_t<(Options == DefaultProduct || Options == AliasFreeProduct)>> {
166 using SrcXprType = Product<Lhs, Rhs, Options>;
167 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(DstXprType& dst, const SrcXprType& src,
168 const internal::assign_op<Scalar, Scalar>& func) {
169 resize_if_allowed(dst, src, func);
170 // FIXME shall we handle nested_eval here?
171 generic_product_impl<Lhs, Rhs>::evalTo(dst, src.lhs(), src.rhs());
172 }
173};
174
175// Dense += Product
176template <typename DstXprType, typename Lhs, typename Rhs, int Options, typename Scalar>
177struct Assignment<DstXprType, Product<Lhs, Rhs, Options>, internal::add_assign_op<Scalar, Scalar>, Dense2Dense,
178 std::enable_if_t<(Options == DefaultProduct || Options == AliasFreeProduct)>> {
179 using SrcXprType = Product<Lhs, Rhs, Options>;
180 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(DstXprType& dst, const SrcXprType& src,
181 const internal::add_assign_op<Scalar, Scalar>&) {
182 eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols());
183 // FIXME shall we handle nested_eval here?
184 generic_product_impl<Lhs, Rhs>::addTo(dst, src.lhs(), src.rhs());
185 }
186};
187
188// Dense -= Product
189template <typename DstXprType, typename Lhs, typename Rhs, int Options, typename Scalar>
190struct Assignment<DstXprType, Product<Lhs, Rhs, Options>, internal::sub_assign_op<Scalar, Scalar>, Dense2Dense,
191 std::enable_if_t<(Options == DefaultProduct || Options == AliasFreeProduct)>> {
192 using SrcXprType = Product<Lhs, Rhs, Options>;
193 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(DstXprType& dst, const SrcXprType& src,
194 const internal::sub_assign_op<Scalar, Scalar>&) {
195 eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols());
196 // FIXME shall we handle nested_eval here?
197 generic_product_impl<Lhs, Rhs>::subTo(dst, src.lhs(), src.rhs());
198 }
199};
200
201// Dense ?= scalar * Product
202// TODO: we should apply that rule if that's really helpful
203// for instance, this is not good for inner products
204template <typename DstXprType, typename Lhs, typename Rhs, typename AssignFunc, typename Scalar, typename ScalarBis,
205 typename Plain>
206struct Assignment<DstXprType,
207 CwiseBinaryOp<internal::scalar_product_op<ScalarBis, Scalar>,
208 const CwiseNullaryOp<internal::scalar_constant_op<ScalarBis>, Plain>,
209 const Product<Lhs, Rhs, DefaultProduct>>,
210 AssignFunc, Dense2Dense, std::enable_if_t<product_can_fold_scalar<Lhs>::value>> {
211 using SrcXprType = CwiseBinaryOp<internal::scalar_product_op<ScalarBis, Scalar>,
212 const CwiseNullaryOp<internal::scalar_constant_op<ScalarBis>, Plain>,
213 const Product<Lhs, Rhs, DefaultProduct>>;
214 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(DstXprType& dst, const SrcXprType& src,
215 const AssignFunc& func) {
216 call_assignment_no_alias(dst, (src.lhs().functor().m_other * src.rhs().lhs()) * src.rhs().rhs(), func);
217 }
218};
219
220//----------------------------------------
221// Catch "Dense ?= xpr + Product<>" expression to save one temporary
222// FIXME: consider enabling these rules for all product types, not only Dense and DefaultProduct.
223
224template <typename OtherXpr, typename Lhs, typename Rhs>
225struct evaluator_assume_aliasing<
226 CwiseBinaryOp<
227 internal::scalar_sum_op<typename OtherXpr::Scalar, typename Product<Lhs, Rhs, DefaultProduct>::Scalar>,
228 const OtherXpr, const Product<Lhs, Rhs, DefaultProduct>>,
229 DenseShape> : std::true_type {};
230
231template <typename OtherXpr, typename Lhs, typename Rhs>
232struct evaluator_assume_aliasing<
233 CwiseBinaryOp<
234 internal::scalar_difference_op<typename OtherXpr::Scalar, typename Product<Lhs, Rhs, DefaultProduct>::Scalar>,
235 const OtherXpr, const Product<Lhs, Rhs, DefaultProduct>>,
236 DenseShape> : std::true_type {};
237
238template <typename DstXprType, typename OtherXpr, typename ProductType, typename Func1, typename Func2>
239struct assignment_from_xpr_op_product {
240 template <typename SrcXprType, typename InitialFunc>
241 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(DstXprType& dst, const SrcXprType& src,
242 const InitialFunc& /*func*/) {
243 call_assignment_no_alias(dst, src.lhs(), Func1());
244 call_assignment_no_alias(dst, src.rhs(), Func2());
245 }
246};
247
248#define EIGEN_CATCH_ASSIGN_XPR_OP_PRODUCT(ASSIGN_OP, BINOP, ASSIGN_OP2) \
249 template <typename DstXprType, typename OtherXpr, typename Lhs, typename Rhs, typename DstScalar, \
250 typename SrcScalar, typename OtherScalar, typename ProdScalar> \
251 struct Assignment<DstXprType, \
252 CwiseBinaryOp<internal::BINOP<OtherScalar, ProdScalar>, const OtherXpr, \
253 const Product<Lhs, Rhs, DefaultProduct>>, \
254 internal::ASSIGN_OP<DstScalar, SrcScalar>, Dense2Dense> \
255 : assignment_from_xpr_op_product<DstXprType, OtherXpr, Product<Lhs, Rhs, DefaultProduct>, \
256 internal::ASSIGN_OP<DstScalar, OtherScalar>, \
257 internal::ASSIGN_OP2<DstScalar, ProdScalar>> {}
258
259EIGEN_CATCH_ASSIGN_XPR_OP_PRODUCT(assign_op, scalar_sum_op, add_assign_op);
260EIGEN_CATCH_ASSIGN_XPR_OP_PRODUCT(add_assign_op, scalar_sum_op, add_assign_op);
261EIGEN_CATCH_ASSIGN_XPR_OP_PRODUCT(sub_assign_op, scalar_sum_op, sub_assign_op);
262
263EIGEN_CATCH_ASSIGN_XPR_OP_PRODUCT(assign_op, scalar_difference_op, sub_assign_op);
264EIGEN_CATCH_ASSIGN_XPR_OP_PRODUCT(add_assign_op, scalar_difference_op, sub_assign_op);
265EIGEN_CATCH_ASSIGN_XPR_OP_PRODUCT(sub_assign_op, scalar_difference_op, add_assign_op);
266
267//----------------------------------------
268
269template <typename Lhs, typename Rhs>
270struct generic_product_impl<Lhs, Rhs, DenseShape, DenseShape, InnerProduct> {
271 using impl = inner_product_dispatch<Lhs, Rhs, false>;
272 template <typename Dst>
273 static EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE void evalTo(Dst& dst, const Lhs& lhs, const Rhs& rhs) {
274 dst.coeffRef(0, 0) = impl::run(lhs, rhs);
275 }
276
277 template <typename Dst>
278 static EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE void addTo(Dst& dst, const Lhs& lhs, const Rhs& rhs) {
279 dst.coeffRef(0, 0) += impl::run(lhs, rhs);
280 }
281
282 template <typename Dst>
283 static EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE void subTo(Dst& dst, const Lhs& lhs, const Rhs& rhs) {
284 dst.coeffRef(0, 0) -= impl::run(lhs, rhs);
285 }
286};
287
288/***********************************************************************
289 * Implementation of outer dense * dense vector product
290 ***********************************************************************/
291
292// Column major result
293template <typename Dst, typename Lhs, typename Rhs, typename Func>
294void EIGEN_DEVICE_FUNC outer_product_selector_run(Dst& dst, const Lhs& lhs, const Rhs& rhs, const Func& func,
295 const std::false_type&) {
296 evaluator<Rhs> rhsEval(rhs);
297 ei_declare_local_nested_eval(Lhs, lhs, Rhs::SizeAtCompileTime, actual_lhs);
298 // FIXME if cols is large enough, then it might be useful to make sure that lhs is sequentially stored
299 // FIXME not very good if rhs is real and lhs complex while alpha is real too
300 const Index cols = dst.cols();
301 for (Index j = 0; j < cols; ++j) func(dst.col(j), rhsEval.coeff(Index(0), j) * actual_lhs);
302}
303
304// Row major result
305template <typename Dst, typename Lhs, typename Rhs, typename Func>
306void EIGEN_DEVICE_FUNC outer_product_selector_run(Dst& dst, const Lhs& lhs, const Rhs& rhs, const Func& func,
307 const std::true_type&) {
308 evaluator<Lhs> lhsEval(lhs);
309 ei_declare_local_nested_eval(Rhs, rhs, Lhs::SizeAtCompileTime, actual_rhs);
310 // FIXME if rows is large enough, then it might be useful to make sure that rhs is sequentially stored
311 // FIXME not very good if lhs is real and rhs complex while alpha is real too
312 const Index rows = dst.rows();
313 for (Index i = 0; i < rows; ++i) func(dst.row(i), lhsEval.coeff(i, Index(0)) * actual_rhs);
314}
315
316template <typename Dst>
317EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool outer_product_use_small_assignment(const Dst& dst) {
318 return dst.rows() <= 16 && dst.cols() <= 16;
319}
320
321template <bool ApplyScale = true, typename Dst, typename Lhs, typename Rhs, typename Func, typename Scalar>
322void EIGEN_DEVICE_FUNC outer_product_selector_run_small(Dst& dst, const Lhs& lhs, const Rhs& rhs, const Func& func,
323 const Scalar& alpha, const std::false_type&) {
324 evaluator<Rhs> rhsEval(rhs);
325 ei_declare_local_nested_eval(Lhs, lhs, Rhs::SizeAtCompileTime, actual_lhs);
326 const Index rows = dst.rows();
327 const Index cols = dst.cols();
328 for (Index j = 0; j < cols; ++j) {
329 const typename Rhs::Scalar rhs_j = rhsEval.coeff(Index(0), j);
330 for (Index i = 0; i < rows; ++i) {
331 EIGEN_IF_CONSTEXPR (ApplyScale) {
332 func.assignCoeff(dst.coeffRef(i, j), internal::mul(alpha, internal::mul(rhs_j, actual_lhs.coeff(i, Index(0)))));
333 } else {
334 func.assignCoeff(dst.coeffRef(i, j), internal::mul(rhs_j, actual_lhs.coeff(i, Index(0))));
335 }
336 }
337 }
338}
339
340template <bool ApplyScale = true, typename Dst, typename Lhs, typename Rhs, typename Func, typename Scalar>
341void EIGEN_DEVICE_FUNC outer_product_selector_run_small(Dst& dst, const Lhs& lhs, const Rhs& rhs, const Func& func,
342 const Scalar& alpha, const std::true_type&) {
343 evaluator<Lhs> lhsEval(lhs);
344 ei_declare_local_nested_eval(Rhs, rhs, Lhs::SizeAtCompileTime, actual_rhs);
345 const Index rows = dst.rows();
346 const Index cols = dst.cols();
347 for (Index i = 0; i < rows; ++i) {
348 const typename Lhs::Scalar lhs_i = lhsEval.coeff(i, Index(0));
349 for (Index j = 0; j < cols; ++j) {
350 EIGEN_IF_CONSTEXPR (ApplyScale) {
351 func.assignCoeff(dst.coeffRef(i, j), internal::mul(alpha, internal::mul(lhs_i, actual_rhs.coeff(Index(0), j))));
352 } else {
353 func.assignCoeff(dst.coeffRef(i, j), internal::mul(lhs_i, actual_rhs.coeff(Index(0), j)));
354 }
355 }
356 }
357}
358
359template <typename Lhs, typename Rhs>
360struct generic_product_impl<Lhs, Rhs, DenseShape, DenseShape, OuterProduct> {
361 template <typename T>
362 struct is_row_major : bool_constant<(int(T::Flags) & RowMajorBit)> {};
363 using Scalar = typename Product<Lhs, Rhs>::Scalar;
364
365 // TODO: it would be nice to be able to exploit our *_assign_op functors for that purpose
366 struct set {
367 template <typename Dst, typename Src>
368 EIGEN_DEVICE_FUNC void operator()(const Dst& dst, const Src& src) const {
369 dst.const_cast_derived() = src;
370 }
371 };
372 struct add {
374 template <typename Dst, typename Src>
375 EIGEN_DEVICE_FUNC void operator()(const Dst& dst, const Src& src) const {
376 dst.const_cast_derived() += src;
377 }
378 };
379 struct sub {
380 template <typename Dst, typename Src>
381 EIGEN_DEVICE_FUNC void operator()(const Dst& dst, const Src& src) const {
382 dst.const_cast_derived() -= src;
383 }
384 };
386 struct adds {
387 Scalar m_scale;
389 EIGEN_DEVICE_FUNC explicit adds(const Scalar& s) : m_scale(s) {}
391 template <typename Dst, typename Src>
392 void EIGEN_DEVICE_FUNC operator()(const Dst& dst, const Src& src) const {
393 dst.const_cast_derived() += m_scale * src;
394 }
395 };
396
397 template <typename Dst>
398 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void evalTo(Dst& dst, const Lhs& lhs, const Rhs& rhs) {
399 if (internal::outer_product_use_small_assignment(dst)) {
400 internal::outer_product_selector_run_small<false>(
401 dst, lhs, rhs, internal::assign_op<typename Dst::Scalar, Scalar>(), Scalar(1), is_row_major<Dst>());
402 } else {
403 internal::outer_product_selector_run(dst, lhs, rhs, set(), is_row_major<Dst>());
404 }
405 }
406
407 template <typename Dst>
408 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void addTo(Dst& dst, const Lhs& lhs, const Rhs& rhs) {
409 if (internal::outer_product_use_small_assignment(dst)) {
410 internal::outer_product_selector_run_small<false>(
411 dst, lhs, rhs, internal::add_assign_op<typename Dst::Scalar, Scalar>(), Scalar(1), is_row_major<Dst>());
412 } else {
413 internal::outer_product_selector_run(dst, lhs, rhs, add(), is_row_major<Dst>());
414 }
415 }
416
417 template <typename Dst>
418 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void subTo(Dst& dst, const Lhs& lhs, const Rhs& rhs) {
419 if (internal::outer_product_use_small_assignment(dst)) {
420 internal::outer_product_selector_run_small<false>(
421 dst, lhs, rhs, internal::sub_assign_op<typename Dst::Scalar, Scalar>(), Scalar(1), is_row_major<Dst>());
422 } else {
423 internal::outer_product_selector_run(dst, lhs, rhs, sub(), is_row_major<Dst>());
424 }
425 }
426
427 template <typename Dst>
428 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void scaleAndAddTo(Dst& dst, const Lhs& lhs, const Rhs& rhs,
429 const Scalar& alpha) {
430 if (internal::outer_product_use_small_assignment(dst)) {
431 internal::outer_product_selector_run_small(dst, lhs, rhs, internal::add_assign_op<typename Dst::Scalar, Scalar>(),
432 alpha, is_row_major<Dst>());
433 } else {
434 internal::outer_product_selector_run(dst, lhs, rhs, adds(alpha), is_row_major<Dst>());
435 }
436 }
437};
438
439// This base class provides default implementations for evalTo, addTo, subTo, in terms of scaleAndAddTo
440template <typename Lhs, typename Rhs, typename Derived>
441struct generic_product_impl_base {
442 using Scalar = typename Product<Lhs, Rhs>::Scalar;
443
444 template <typename Dst>
445 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void evalTo(Dst& dst, const Lhs& lhs, const Rhs& rhs) {
446 dst.setZero();
447 scaleAndAddTo(dst, lhs, rhs, Scalar(1));
448 }
449
450 template <typename Dst>
451 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void addTo(Dst& dst, const Lhs& lhs, const Rhs& rhs) {
452 scaleAndAddTo(dst, lhs, rhs, Scalar(1));
453 }
454
455 template <typename Dst>
456 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void subTo(Dst& dst, const Lhs& lhs, const Rhs& rhs) {
457 scaleAndAddTo(dst, lhs, rhs, Scalar(-1));
458 }
459
460 template <typename Dst>
461 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void scaleAndAddTo(Dst& dst, const Lhs& lhs, const Rhs& rhs,
462 const Scalar& alpha) {
463 Derived::scaleAndAddTo(dst, lhs, rhs, alpha);
464 }
465};
466
467template <typename Lhs, typename Rhs>
468struct generic_product_impl<Lhs, Rhs, DenseShape, DenseShape, GemvProduct>
469 : generic_product_impl_base<Lhs, Rhs, generic_product_impl<Lhs, Rhs, DenseShape, DenseShape, GemvProduct>> {
470 using LhsNested = typename nested_eval<Lhs, 1>::type;
471 using RhsNested = typename nested_eval<Rhs, 1>::type;
472 using Scalar = typename Product<Lhs, Rhs>::Scalar;
473 enum { Side = Lhs::IsVectorAtCompileTime ? OnTheLeft : OnTheRight };
474 using MatrixType = internal::remove_all_t<std::conditional_t<int(Side) == OnTheRight, LhsNested, RhsNested>>;
475
476 template <typename Dest>
477 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void scaleAndAddTo(Dest& dst, const Lhs& lhs, const Rhs& rhs,
478 const Scalar& alpha) {
479 // Fallback to inner product if both the lhs and rhs is a runtime vector.
480 if (lhs.rows() == 1 && rhs.cols() == 1) {
481 dst.coeffRef(0, 0) += alpha * lhs.row(0).conjugate().dot(rhs.col(0));
482 return;
483 }
484 LhsNested actual_lhs(lhs);
485 RhsNested actual_rhs(rhs);
486 internal::gemv_dense_selector<Side, (int(MatrixType::Flags) & RowMajorBit) ? RowMajor : ColMajor,
487 bool(internal::blas_traits<MatrixType>::HasUsableDirectAccess)>::run(actual_lhs,
488 actual_rhs, dst,
489 alpha);
490 }
491};
492
493// Packet-cascade assignment for small fixed-size coefficient-based products
494// (dst = / += / -= lhs * rhs). The generic assignment path types the product's
495// packet via find_best_packet -- the widest packet that *exactly divides* the
496// vectorized extent, leaving no tail. For odd/non-power-of-two extents that
497// overshoots: with no exact divisor it falls back to a narrow packet, or all
498// the way to scalars (e.g. 3x3 double becomes scalar, ~3x slower than it needs
499// to be). find_largest_packet instead picks the widest packet that *fits*
500// (size <= extent) and we emit an explicit cascade -- full packet(s), then the
501// half packet, ... then a scalar tail -- so e.g. 3x3 double does one Packet2d
502// plus one scalar per column, and 6x6 double does one Packet4d plus one Packet2d
503// instead of three Packet2d. See find_largest_packet in XprHelper.h.
504
505// Emit the inner segment [begin, end) for a single outer index, cascading from
506// `Packet` down through its half packets to a scalar tail. `ColMajor` selects
507// whether the contiguous inner index is the row (true) or the column (false).
508// The bool `Terminal` partial specialization stops the recursion at the
509// narrowest packet without requiring C++17 `if constexpr`.
510template <typename Packet, bool ColMajor,
511 bool Terminal = std::is_same<typename unpacket_traits<Packet>::half, Packet>::value>
512struct product_packet_cascade {
513 template <typename Func, typename Dst, typename ProdEval>
514 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(const Func& func, Dst& dst, const ProdEval& prod, Index outer,
515 Index begin, Index end) {
516 constexpr int PacketSize = unpacket_traits<Packet>::size;
517 Index pktEnd = begin + numext::round_down(end - begin, PacketSize);
518 Index i = begin;
519 for (; i < pktEnd; i += PacketSize) {
520 Index r = ColMajor ? i : outer, c = ColMajor ? outer : i;
521 func.template assignPacket<Unaligned, Packet>(&dst.coeffRef(r, c), prod.template packet<Unaligned, Packet>(r, c));
522 }
523 // Fall back to the half packet for the remainder [i, end).
524 product_packet_cascade<typename unpacket_traits<Packet>::half, ColMajor>::run(func, dst, prod, outer, i, end);
525 }
526};
527
528// Narrowest packet in the chain: lay down its full packets, then a scalar tail.
529template <typename Packet, bool ColMajor>
530struct product_packet_cascade<Packet, ColMajor, /*Terminal=*/true> {
531 template <typename Func, typename Dst, typename ProdEval>
532 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(const Func& func, Dst& dst, const ProdEval& prod, Index outer,
533 Index begin, Index end) {
534 constexpr int PacketSize = unpacket_traits<Packet>::size;
535 Index pktEnd = begin + numext::round_down(end - begin, PacketSize);
536 Index i = begin;
537 for (; i < pktEnd; i += PacketSize) {
538 Index r = ColMajor ? i : outer, c = ColMajor ? outer : i;
539 func.template assignPacket<Unaligned, Packet>(&dst.coeffRef(r, c), prod.template packet<Unaligned, Packet>(r, c));
540 }
541 for (; i < end; ++i) {
542 Index r = ColMajor ? i : outer, c = ColMajor ? outer : i;
543 func.assignCoeff(dst.coeffRef(r, c), prod.coeff(r, c));
544 }
545 }
546};
547
548// Largest packet size in the (halving) chain that *exactly divides* Size, or 1
549// if none does. This is what the generic InnerVectorized assignment path can
550// actually use -- unlike find_best_packet, whose stop condition returns the
551// terminal (non-dividing) packet when the chain bottoms out above size 1 (on
552// x86 the chain ends at Packet2d / Packet4f, not a size-1 packet), so e.g.
553// find_best_packet<double,3> reports Packet2d even though 3 % 2 != 0.
554template <typename Packet, int Size,
555 bool Terminal = std::is_same<typename unpacket_traits<Packet>::half, Packet>::value>
556struct largest_exact_divisor_size { // recurse into the narrower half packet
557 static constexpr int s = unpacket_traits<Packet>::size;
558 static constexpr int value =
559 (Size % s == 0) ? s : largest_exact_divisor_size<typename unpacket_traits<Packet>::half, Size>::value;
560};
561template <typename Packet, int Size>
562struct largest_exact_divisor_size<Packet, Size, /*Terminal=*/true> { // narrowest packet in the chain
563 static constexpr int s = unpacket_traits<Packet>::size;
564 static constexpr int value = (Size % s == 0) ? s : 1;
565};
566
567// When is the cascade worth diverging from the generic assignment path, and
568// along which axis? Only for fixed-size products where the widest packet that
569// fits the vectorized extent (find_largest_packet) is strictly wider than the
570// widest that exactly divides it -- i.e. the extent is not a clean packet
571// multiple, so the generic path is forced down to a narrower packet or scalars.
572// Exact-fit sizes (2, 4, 8, ...) keep the proven generic path, as do dynamic
573// sizes (GEMM / generic kernels) and extents too small to hold even one packet
574// (e.g. 3-row float). Complex is included: the generic path scalarizes odd
575// extents (find_best<complex<double>,3> == Packet1cd), so the cascade's
576// Packet2cd/Packet4cf tail is a sizeable win there too.
577//
578// The destination must also store the vectorized axis contiguously
579// (DirectAccessBit and InnerStrideAtCompileTime == 1): the cascade writes whole
580// packets with a single unaligned store, so a strided destination such as
581// `m.row(i)` of a column-major matrix would clobber neighbouring coefficients.
582// The generic assignment path makes this check along with MightVectorize and
583// EIGEN_UNALIGNED_VECTORIZE; here we must replicate them since we bypass that path.
584template <typename ProdEval, typename Dst, typename Func>
585struct product_packet_cascade_traits {
586 using Scalar = typename ProdEval::Scalar;
587 using DstEval = evaluator<Dst>;
588 static constexpr int Rows = ProdEval::RowsAtCompileTime;
589 static constexpr int Cols = ProdEval::ColsAtCompileTime;
590 static constexpr bool DstRowMajor = bool(Dst::IsRowMajor);
591 static constexpr bool DstContiguous =
592 (int(DstEval::Flags) & DirectAccessBit) != 0 && int(Dst::InnerStrideAtCompileTime) == 1;
593 // Orientation of the packets the product evaluator natively produces. When a
594 // product can vectorize *both* the lhs (columns) and the rhs (rows), its
595 // packet() returns column packets (EvalToRowMajor == 0). The cascade axis must
596 // match this orientation -- and the destination's storage order must match it
597 // too, so the contiguous packet store lands on contiguous coefficients.
598 static constexpr bool ProdRowMajor = (int(ProdEval::Flags) & RowMajorBit) != 0;
599 static constexpr bool VecLhs = // column-major product & dst: vectorize down each column
600 bool(ProdEval::CanVectorizeLhs) && !ProdRowMajor && !DstRowMajor && Rows != Dynamic;
601 static constexpr bool VecRhs = // row-major product & dst: vectorize along each row
602 bool(ProdEval::CanVectorizeRhs) && ProdRowMajor && DstRowMajor && Cols != Dynamic;
603 static constexpr int Extent = VecLhs ? Rows : VecRhs ? Cols : 1;
604 using Packet = typename find_largest_packet<Scalar, Extent>::type;
605 static constexpr int LargestSize = unpacket_traits<Packet>::size;
606 static constexpr int GenericSize = largest_exact_divisor_size<typename packet_traits<Scalar>::type, Extent>::value;
607 static constexpr bool MightVectorize =
608 bool(EIGEN_UNALIGNED_VECTORIZE) &&
609 (int(DstEval::Flags) & int(ProdEval::Flags) & int(ActualPacketAccessBit)) != 0 &&
610 bool(functor_traits<Func>::PacketAccess);
611 // SameType mirrors the product's own PacketAccessBit (SameType && (CanVectorizeLhs ||
612 // CanVectorizeRhs)): CanVectorizeLhs/Rhs alone test only the operand's packet access, so a
613 // mixed-scalar product (e.g. double * complex) would otherwise enable the cascade and try to
614 // read the wrong-width packet out of an operand. Mixed types keep the generic (scalar) path.
615 static constexpr bool Enable = bool(ProdEval::SameType) && (VecLhs || VecRhs) && DstContiguous && MightVectorize &&
616 (LargestSize <= Extent) && (LargestSize > GenericSize);
617};
618
619// Run the cascade over every outer index of dst. Traits::VecLhs is a compile-time
620// constant, so it folds the bounds away and selects the cascade's ColMajor axis,
621// instantiating only the active orientation (no dead branch on either axis).
622template <typename Traits, typename Func, typename Dst, typename ProdEval>
623EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void product_run_packet_cascade(const Func& func, Dst& dst,
624 const ProdEval& prod) {
625 using Packet = typename Traits::Packet;
626 Index rows = dst.rows(), cols = dst.cols();
627 Index outerCount = Traits::VecLhs ? cols : rows;
628 Index innerEnd = Traits::VecLhs ? rows : cols;
629 for (Index outer = 0; outer < outerCount; ++outer)
630 product_packet_cascade<Packet, Traits::VecLhs>::run(func, dst, prod, outer, 0, innerEnd);
631}
632
633// Compound product assignment: take the packet cascade when the traits enable
634// it, otherwise the generic assignment path. Dispatch on a bool tag (rather than
635// a plain `if`) so the cascade is only *instantiated* for the vectorizable
636// fixed-size case -- a runtime branch would instantiate it for every product,
637// including scalar types with no packet support.
638template <typename Func, typename Dst, typename Lhs, typename Rhs>
639EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void product_packet_assign(std::true_type, const Func& func, Dst& dst,
640 const Lhs& lhs, const Rhs& rhs) {
641 using ProdEval = product_evaluator<Product<Lhs, Rhs, LazyProduct>, CoeffBasedProductMode, DenseShape, DenseShape>;
642 using Traits = product_packet_cascade_traits<ProdEval, Dst, Func>;
643 const ProdEval prodEval(lhs, rhs);
644 product_run_packet_cascade<Traits>(func, dst, prodEval);
645}
646template <typename Func, typename Dst, typename Lhs, typename Rhs>
647EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void product_packet_assign(std::false_type, const Func& func, Dst& dst,
648 const Lhs& lhs, const Rhs& rhs) {
649 call_assignment_no_alias(dst, lhs.lazyProduct(rhs), func);
650}
651
652template <typename Lhs, typename Rhs>
653struct generic_product_impl<Lhs, Rhs, DenseShape, DenseShape, CoeffBasedProductMode> {
654 using Scalar = typename Product<Lhs, Rhs>::Scalar;
655
656 template <typename Dst>
657 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void evalTo(Dst& dst, const Lhs& lhs, const Rhs& rhs) {
658 // Same as: dst.noalias() = lhs.lazyProduct(rhs);
659 // but easier on the compiler side.
660 // NB: unlike addTo/subTo below, plain assignment stays on the generic path.
661 // The packet cascade is a measured win for the compound (+=, -=) case, but
662 // for plain assignment the generic InnerVectorized path is already well
663 // tuned -- the cascade was at best neutral there and could regress float
664 // (whose packet half-chain bottoms out at Packet4f, so odd-size tails fall
665 // to scalars). See product_packet_cascade_traits.
666 call_assignment_no_alias(dst, lhs.lazyProduct(rhs), internal::assign_op<typename Dst::Scalar, Scalar>());
667 }
668
669 template <typename Dst>
670 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void addTo(Dst& dst, const Lhs& lhs, const Rhs& rhs) {
671 // dst.noalias() += lhs.lazyProduct(rhs);
672 using ProdEval = product_evaluator<Product<Lhs, Rhs, LazyProduct>, CoeffBasedProductMode, DenseShape, DenseShape>;
673 using Func = internal::add_assign_op<typename Dst::Scalar, Scalar>;
674 using Traits = product_packet_cascade_traits<ProdEval, Dst, Func>;
675 product_packet_assign(bool_constant<Traits::Enable>(), Func(), dst, lhs, rhs);
676 }
677
678 template <typename Dst>
679 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void subTo(Dst& dst, const Lhs& lhs, const Rhs& rhs) {
680 // dst.noalias() -= lhs.lazyProduct(rhs);
681 using ProdEval = product_evaluator<Product<Lhs, Rhs, LazyProduct>, CoeffBasedProductMode, DenseShape, DenseShape>;
682 using Func = internal::sub_assign_op<typename Dst::Scalar, Scalar>;
683 using Traits = product_packet_cascade_traits<ProdEval, Dst, Func>;
684 product_packet_assign(bool_constant<Traits::Enable>(), Func(), dst, lhs, rhs);
685 }
686
687 // This is a special evaluation path called from generic_product_impl<...,GemmProduct> in file GeneralMatrixMatrix.h
688 // This variant tries to extract scalar multiples from both the LHS and RHS and factor them out. For instance:
689 // dst {,+,-}= (s1*A)*(B*s2)
690 // will be rewritten as:
691 // dst {,+,-}= (s1*s2) * (A.lazyProduct(B))
692 // There are at least four benefits of doing so:
693 // 1 - huge performance gain for heap-allocated matrix types as it save costly allocations.
694 // 2 - it is faster than simply by-passing the heap allocation through stack allocation.
695 // 3 - it makes this fallback consistent with the heavy GEMM routine.
696 // 4 - it fully by-passes huge stack allocation attempts when multiplying huge fixed-size matrices.
697 // (see https://stackoverflow.com/questions/54738495)
698 // For small fixed sizes matrices, however, the gains are less obvious, it is sometimes x2 faster, but sometimes x3
699 // slower, and the behavior depends also a lot on the compiler... This is why this re-writing strategy is currently
700 // enabled only when falling back from the main GEMM.
701 template <typename Dst, typename Func>
702 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void eval_dynamic(Dst& dst, const Lhs& lhs, const Rhs& rhs,
703 const Func& func) {
704 enum {
705 HasScalarFactor = blas_traits<Lhs>::HasScalarFactor || blas_traits<Rhs>::HasScalarFactor,
706 ConjLhs = blas_traits<Lhs>::NeedToConjugate,
707 ConjRhs = blas_traits<Rhs>::NeedToConjugate
708 };
709 // FIXME: this should be auto, and extractScalarFactor should also return auto
710 // this is important for real*complex_mat
711 Scalar actualAlpha = combine_scalar_factors<Scalar>(lhs, rhs);
712
713 eval_dynamic_impl(dst, blas_traits<Lhs>::extract(lhs).template conjugateIf<ConjLhs>(),
714 blas_traits<Rhs>::extract(rhs).template conjugateIf<ConjRhs>(), func, actualAlpha,
715 bool_constant<HasScalarFactor>());
716 }
717
718 protected:
719 template <typename Dst, typename LhsT, typename RhsT, typename Func, typename Scalar>
720 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void eval_dynamic_impl(Dst& dst, const LhsT& lhs, const RhsT& rhs,
721 const Func& func, const Scalar& s /* == 1 */,
722 std::false_type) {
723 EIGEN_UNUSED_VARIABLE(s);
724 eigen_internal_assert(numext::is_exactly_one(s));
725 call_restricted_packet_assignment_no_alias(dst, lhs.lazyProduct(rhs), func);
726 }
727
728 template <typename Dst, typename LhsT, typename RhsT, typename Func, typename Scalar>
729 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void eval_dynamic_impl(Dst& dst, const LhsT& lhs, const RhsT& rhs,
730 const Func& func, const Scalar& s,
731 std::true_type) {
732 call_restricted_packet_assignment_no_alias(dst, s * lhs.lazyProduct(rhs), func);
733 }
734};
735
736// This specialization enforces the use of a coefficient-based evaluation strategy
737template <typename Lhs, typename Rhs>
738struct generic_product_impl<Lhs, Rhs, DenseShape, DenseShape, LazyCoeffBasedProductMode>
739 : generic_product_impl<Lhs, Rhs, DenseShape, DenseShape, CoeffBasedProductMode> {};
740
741template <int StorageOrder, int UnrollingIndex, typename Lhs, typename Rhs, typename Packet, int LoadMode>
742struct etor_product_packet_impl;
743
744struct product_empty_packet_evaluator {
745 template <typename Xpr>
746 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE explicit product_empty_packet_evaluator(const Xpr&) {}
747};
748
749template <typename Lhs, typename Rhs, int ProductTag>
750struct product_evaluator<Product<Lhs, Rhs, LazyProduct>, ProductTag, DenseShape, DenseShape>
751 : evaluator_base<Product<Lhs, Rhs, LazyProduct>> {
752 using XprType = Product<Lhs, Rhs, LazyProduct>;
753 using Scalar = typename XprType::Scalar;
754 using CoeffReturnType = typename XprType::CoeffReturnType;
755
756 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE explicit product_evaluator(const XprType& xpr)
757 : product_evaluator(xpr.lhs(), xpr.rhs()) {}
758
759 // Also construct directly from the product operands so packet-cascade callers
760 // do not have to materialize a temporary Product expression first.
761 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE product_evaluator(const Lhs& lhs, const Rhs& rhs)
762 : m_lhs(lhs),
763 m_rhs(rhs),
764 m_lhsImpl(m_lhs), // Real evaluator objects for packet products; empty placeholders for scalar-only products.
765 m_rhsImpl(m_rhs),
766 m_innerDim(lhs.cols()) {
767 EIGEN_INTERNAL_CHECK_COST_VALUE(NumTraits<Scalar>::MulCost);
768 EIGEN_INTERNAL_CHECK_COST_VALUE(NumTraits<Scalar>::AddCost);
769 EIGEN_INTERNAL_CHECK_COST_VALUE(CoeffReadCost);
770 }
771
772 // Everything below here is taken from CoeffBasedProduct.h
773
774 using LhsNested = typename internal::nested_eval<Lhs, Rhs::ColsAtCompileTime>::type;
775 using RhsNested = typename internal::nested_eval<Rhs, Lhs::RowsAtCompileTime>::type;
776
777 using LhsNestedCleaned = internal::remove_all_t<LhsNested>;
778 using RhsNestedCleaned = internal::remove_all_t<RhsNested>;
779
780 using LhsScalar = typename LhsNestedCleaned::Scalar;
781 using RhsScalar = typename RhsNestedCleaned::Scalar;
782
783 using LhsEtorType = evaluator<LhsNestedCleaned>;
784 using RhsEtorType = evaluator<RhsNestedCleaned>;
785
786 enum {
787 RowsAtCompileTime = LhsNestedCleaned::RowsAtCompileTime,
788 ColsAtCompileTime = RhsNestedCleaned::ColsAtCompileTime,
789 InnerSize = min_size_prefer_fixed(LhsNestedCleaned::ColsAtCompileTime, RhsNestedCleaned::RowsAtCompileTime),
790 MaxRowsAtCompileTime = LhsNestedCleaned::MaxRowsAtCompileTime,
791 MaxColsAtCompileTime = RhsNestedCleaned::MaxColsAtCompileTime
792 };
793
794 using LhsVecPacketType = typename find_best_packet<Scalar, RowsAtCompileTime>::type;
795 using RhsVecPacketType = typename find_best_packet<Scalar, ColsAtCompileTime>::type;
796
797 static constexpr int LhsCoeffReadCost = LhsEtorType::CoeffReadCost;
798 static constexpr int RhsCoeffReadCost = RhsEtorType::CoeffReadCost;
799 static constexpr int CoeffReadCost =
800 InnerSize == 0 ? NumTraits<Scalar>::ReadCost
801 : InnerSize == Dynamic
802 ? HugeCost
803 : InnerSize * (NumTraits<Scalar>::MulCost + int(LhsCoeffReadCost) + int(RhsCoeffReadCost)) +
804 (InnerSize - 1) * NumTraits<Scalar>::AddCost;
805
806 static constexpr bool Unroll = CoeffReadCost <= EIGEN_UNROLLING_LIMIT;
807
808 static constexpr int LhsFlags = LhsEtorType::Flags;
809 static constexpr int RhsFlags = RhsEtorType::Flags;
810
811 static constexpr int LhsRowMajor = LhsFlags & RowMajorBit;
812 static constexpr int RhsRowMajor = RhsFlags & RowMajorBit;
813
814 static constexpr int LhsVecPacketSize = unpacket_traits<LhsVecPacketType>::size;
815 static constexpr int RhsVecPacketSize = unpacket_traits<RhsVecPacketType>::size;
816
817 // Here, we don't care about alignment larger than the usable packet size.
818 static constexpr int LhsAlignment =
819 plain_enum_min(LhsEtorType::Alignment, LhsVecPacketSize* int(sizeof(typename LhsNestedCleaned::Scalar)));
820 static constexpr int RhsAlignment =
821 plain_enum_min(RhsEtorType::Alignment, RhsVecPacketSize* int(sizeof(typename RhsNestedCleaned::Scalar)));
822
823 static constexpr bool SameType =
824 std::is_same<typename LhsNestedCleaned::Scalar, typename RhsNestedCleaned::Scalar>::value;
825
826 static constexpr bool CanVectorizeRhs = bool(RhsRowMajor) && (RhsFlags & PacketAccessBit) && (ColsAtCompileTime != 1);
827 static constexpr bool CanVectorizeLhs = (!LhsRowMajor) && (LhsFlags & PacketAccessBit) && (RowsAtCompileTime != 1);
828
829 static constexpr int EvalToRowMajor = (MaxRowsAtCompileTime == 1 && MaxColsAtCompileTime != 1) ? 1
830 : (MaxColsAtCompileTime == 1 && MaxRowsAtCompileTime != 1)
831 ? 0
832 : (bool(RhsRowMajor) && !CanVectorizeLhs);
833
834 static constexpr int Flags = ((int(LhsFlags) | int(RhsFlags)) & HereditaryBits & ~RowMajorBit) |
835 (EvalToRowMajor ? RowMajorBit : 0)
836 // TODO: enable vectorization for mixed types
837 | (SameType && (CanVectorizeLhs || CanVectorizeRhs) ? PacketAccessBit : 0) |
838 (XprType::IsVectorAtCompileTime ? LinearAccessBit : 0);
839 using LhsPacketEtorType =
840 std::conditional_t<bool(int(Flags) & PacketAccessBit), LhsEtorType, product_empty_packet_evaluator>;
841 using RhsPacketEtorType =
842 std::conditional_t<bool(int(Flags) & PacketAccessBit), RhsEtorType, product_empty_packet_evaluator>;
843
844 static constexpr bool SingleTermCoeff = InnerSize == 1 && std::is_same<LhsPacketEtorType, LhsEtorType>::value &&
845 std::is_same<RhsPacketEtorType, RhsEtorType>::value;
846
847 static constexpr int LhsOuterStrideBytes =
848 int(LhsNestedCleaned::OuterStrideAtCompileTime) * int(sizeof(typename LhsNestedCleaned::Scalar));
849 static constexpr int RhsOuterStrideBytes =
850 int(RhsNestedCleaned::OuterStrideAtCompileTime) * int(sizeof(typename RhsNestedCleaned::Scalar));
851
852 static constexpr int Alignment =
853 bool(CanVectorizeLhs)
854 ? (LhsOuterStrideBytes <= 0 || (int(LhsOuterStrideBytes) % plain_enum_max(1, LhsAlignment)) != 0
855 ? 0
856 : LhsAlignment)
857 : bool(CanVectorizeRhs)
858 ? (RhsOuterStrideBytes <= 0 || (int(RhsOuterStrideBytes) % plain_enum_max(1, RhsAlignment)) != 0
859 ? 0
860 : RhsAlignment)
861 : 0;
862
863 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE const CoeffReturnType coeff(Index row, Index col) const {
864 return coeff_impl(row, col, bool_constant<SingleTermCoeff>());
865 }
866
867 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE CoeffReturnType coeff_impl(Index row, Index col,
868 std::true_type) const {
869 return fast_mult_op<LhsScalar, RhsScalar>()(m_lhsImpl.coeff(row, Index(0)), m_rhsImpl.coeff(Index(0), col));
870 }
871
872 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE CoeffReturnType coeff_impl(Index row, Index col,
873 std::false_type) const {
874 // fast_mult_op is cwiseProduct's scalar_product_op with a pmul-based scalar path, so the
875 // reduction (and its precision) is unchanged but complex scalars avoid std::complex::operator*
876 // (the slow libgcc __mul?c3). See fast_mult_op.
877 return m_lhs.row(row).transpose().binaryExpr(m_rhs.col(col), fast_mult_op<LhsScalar, RhsScalar>()).sum();
878 }
879
880 /* Allow index-based non-packet access. It is impossible though to allow index-based packed access,
881 * which is why we don't set the LinearAccessBit.
882 * TODO: this seems possible when the result is a vector
883 */
884 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE const CoeffReturnType coeff(Index index) const {
885 const Index row = (RowsAtCompileTime == 1 || MaxRowsAtCompileTime == 1) ? 0 : index;
886 const Index col = (RowsAtCompileTime == 1 || MaxRowsAtCompileTime == 1) ? index : 0;
887 return coeff(row, col);
888 }
889
890 template <int LoadMode, typename PacketType>
891 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const PacketType packet(Index row, Index col) const {
892 PacketType res;
893 using PacketImpl =
894 etor_product_packet_impl<bool(int(Flags) & RowMajorBit) ? RowMajor : ColMajor,
895 Unroll ? int(InnerSize) : Dynamic, LhsEtorType, RhsEtorType, PacketType, LoadMode>;
896 PacketImpl::run(row, col, m_lhsImpl, m_rhsImpl, m_innerDim, res);
897 return res;
898 }
899
900 template <int LoadMode, typename PacketType>
901 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const PacketType packet(Index index) const {
902 const Index row = (RowsAtCompileTime == 1 || MaxRowsAtCompileTime == 1) ? 0 : index;
903 const Index col = (RowsAtCompileTime == 1 || MaxRowsAtCompileTime == 1) ? index : 0;
904 return packet<LoadMode, PacketType>(row, col);
905 }
906
907 template <int LoadMode, typename PacketType>
908 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const PacketType packetSegment(Index row, Index col, Index begin,
909 Index count) const {
910 PacketType res;
911 using PacketImpl =
912 etor_product_packet_impl<bool(int(Flags) & RowMajorBit) ? RowMajor : ColMajor,
913 Unroll ? int(InnerSize) : Dynamic, LhsEtorType, RhsEtorType, PacketType, LoadMode>;
914 PacketImpl::run_segment(row, col, m_lhsImpl, m_rhsImpl, m_innerDim, res, begin, count);
915 return res;
916 }
917
918 template <int LoadMode, typename PacketType>
919 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const PacketType packetSegment(Index index, Index begin, Index count) const {
920 const Index row = (RowsAtCompileTime == 1 || MaxRowsAtCompileTime == 1) ? 0 : index;
921 const Index col = (RowsAtCompileTime == 1 || MaxRowsAtCompileTime == 1) ? index : 0;
922 return packetSegment<LoadMode, PacketType>(row, col, begin, count);
923 }
924
925 protected:
926 add_const_on_value_type_t<LhsNested> m_lhs;
927 add_const_on_value_type_t<RhsNested> m_rhs;
928
929 LhsPacketEtorType m_lhsImpl;
930 RhsPacketEtorType m_rhsImpl;
931
932 variable_if_dynamic<Index, InnerSize> m_innerDim;
933};
934
935template <typename Lhs, typename Rhs>
936struct product_evaluator<Product<Lhs, Rhs, DefaultProduct>, LazyCoeffBasedProductMode, DenseShape, DenseShape>
937 : product_evaluator<Product<Lhs, Rhs, LazyProduct>, CoeffBasedProductMode, DenseShape, DenseShape> {
938 using XprType = Product<Lhs, Rhs, DefaultProduct>;
939 using BaseProduct = Product<Lhs, Rhs, LazyProduct>;
940 using Base = product_evaluator<BaseProduct, CoeffBasedProductMode, DenseShape, DenseShape>;
941 enum { Flags = Base::Flags | EvalBeforeNestingBit };
942 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE explicit product_evaluator(const XprType& xpr)
943 : Base(BaseProduct(xpr.lhs(), xpr.rhs())) {}
944};
945
946/****************************************
947*** Coeff based product, Packet path ***
948****************************************/
949
950template <int UnrollingIndex, typename Lhs, typename Rhs, typename Packet, int LoadMode>
951struct etor_product_packet_impl<RowMajor, UnrollingIndex, Lhs, Rhs, Packet, LoadMode> {
952 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(Index row, Index col, const Lhs& lhs, const Rhs& rhs,
953 Index innerDim, Packet& res) {
954 etor_product_packet_impl<RowMajor, UnrollingIndex - 1, Lhs, Rhs, Packet, LoadMode>::run(row, col, lhs, rhs,
955 innerDim, res);
956 res = pmadd(pset1<Packet>(lhs.coeff(row, Index(UnrollingIndex - 1))),
957 rhs.template packet<LoadMode, Packet>(Index(UnrollingIndex - 1), col), res);
958 }
959 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run_segment(Index row, Index col, const Lhs& lhs, const Rhs& rhs,
960 Index innerDim, Packet& res, Index begin, Index count) {
961 etor_product_packet_impl<RowMajor, UnrollingIndex - 1, Lhs, Rhs, Packet, LoadMode>::run_segment(
962 row, col, lhs, rhs, innerDim, res, begin, count);
963 res = pmadd(pset1<Packet>(lhs.coeff(row, Index(UnrollingIndex - 1))),
964 rhs.template packetSegment<LoadMode, Packet>(Index(UnrollingIndex - 1), col, begin, count), res);
965 }
966};
967
968template <int UnrollingIndex, typename Lhs, typename Rhs, typename Packet, int LoadMode>
969struct etor_product_packet_impl<ColMajor, UnrollingIndex, Lhs, Rhs, Packet, LoadMode> {
970 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(Index row, Index col, const Lhs& lhs, const Rhs& rhs,
971 Index innerDim, Packet& res) {
972 etor_product_packet_impl<ColMajor, UnrollingIndex - 1, Lhs, Rhs, Packet, LoadMode>::run(row, col, lhs, rhs,
973 innerDim, res);
974 res = pmadd(lhs.template packet<LoadMode, Packet>(row, Index(UnrollingIndex - 1)),
975 pset1<Packet>(rhs.coeff(Index(UnrollingIndex - 1), col)), res);
976 }
977 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run_segment(Index row, Index col, const Lhs& lhs, const Rhs& rhs,
978 Index innerDim, Packet& res, Index begin, Index count) {
979 etor_product_packet_impl<ColMajor, UnrollingIndex - 1, Lhs, Rhs, Packet, LoadMode>::run_segment(
980 row, col, lhs, rhs, innerDim, res, begin, count);
981 res = pmadd(lhs.template packetSegment<LoadMode, Packet>(row, Index(UnrollingIndex - 1), begin, count),
982 pset1<Packet>(rhs.coeff(Index(UnrollingIndex - 1), col)), res);
983 }
984};
985
986template <typename Lhs, typename Rhs, typename Packet, int LoadMode>
987struct etor_product_packet_impl<RowMajor, 1, Lhs, Rhs, Packet, LoadMode> {
988 static EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE void run(Index row, Index col, const Lhs& lhs, const Rhs& rhs,
989 Index /*innerDim*/, Packet& res) {
990 res = pmul(pset1<Packet>(lhs.coeff(row, Index(0))), rhs.template packet<LoadMode, Packet>(Index(0), col));
991 }
992 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run_segment(Index row, Index col, const Lhs& lhs, const Rhs& rhs,
993 Index /*innerDim*/, Packet& res, Index begin,
994 Index count) {
995 res = pmul(pset1<Packet>(lhs.coeff(row, Index(0))),
996 rhs.template packetSegment<LoadMode, Packet>(Index(0), col, begin, count));
997 }
998};
999
1000template <typename Lhs, typename Rhs, typename Packet, int LoadMode>
1001struct etor_product_packet_impl<ColMajor, 1, Lhs, Rhs, Packet, LoadMode> {
1002 static EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE void run(Index row, Index col, const Lhs& lhs, const Rhs& rhs,
1003 Index /*innerDim*/, Packet& res) {
1004 res = pmul(lhs.template packet<LoadMode, Packet>(row, Index(0)), pset1<Packet>(rhs.coeff(Index(0), col)));
1005 }
1006 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run_segment(Index row, Index col, const Lhs& lhs, const Rhs& rhs,
1007 Index /*innerDim*/, Packet& res, Index begin,
1008 Index count) {
1009 res = pmul(lhs.template packetSegment<LoadMode, Packet>(row, Index(0), begin, count),
1010 pset1<Packet>(rhs.coeff(Index(0), col)));
1011 }
1012};
1013
1014template <typename Lhs, typename Rhs, typename Packet, int LoadMode>
1015struct etor_product_packet_impl<RowMajor, 0, Lhs, Rhs, Packet, LoadMode> {
1016 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(Index /*row*/, Index /*col*/, const Lhs& /*lhs*/,
1017 const Rhs& /*rhs*/, Index /*innerDim*/, Packet& res) {
1018 res = pset1<Packet>(typename unpacket_traits<Packet>::type(0));
1019 }
1020 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run_segment(Index /*row*/, Index /*col*/, const Lhs& /*lhs*/,
1021 const Rhs& /*rhs*/, Index /*innerDim*/, Packet& res,
1022 Index /*begin*/, Index /*count*/) {
1023 res = pset1<Packet>(typename unpacket_traits<Packet>::type(0));
1024 }
1025};
1026
1027template <typename Lhs, typename Rhs, typename Packet, int LoadMode>
1028struct etor_product_packet_impl<ColMajor, 0, Lhs, Rhs, Packet, LoadMode> {
1029 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(Index /*row*/, Index /*col*/, const Lhs& /*lhs*/,
1030 const Rhs& /*rhs*/, Index /*innerDim*/, Packet& res) {
1031 res = pset1<Packet>(typename unpacket_traits<Packet>::type(0));
1032 }
1033 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run_segment(Index /*row*/, Index /*col*/, const Lhs& /*lhs*/,
1034 const Rhs& /*rhs*/, Index /*innerDim*/, Packet& res,
1035 Index /*begin*/, Index /*count*/) {
1036 res = pset1<Packet>(typename unpacket_traits<Packet>::type(0));
1037 }
1038};
1039
1040template <typename Lhs, typename Rhs, typename Packet, int LoadMode>
1041struct etor_product_packet_impl<RowMajor, Dynamic, Lhs, Rhs, Packet, LoadMode> {
1042 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(Index row, Index col, const Lhs& lhs, const Rhs& rhs,
1043 Index innerDim, Packet& res) {
1044 res = pset1<Packet>(typename unpacket_traits<Packet>::type(0));
1045 for (Index i = 0; i < innerDim; ++i)
1046 res = pmadd(pset1<Packet>(lhs.coeff(row, i)), rhs.template packet<LoadMode, Packet>(i, col), res);
1047 }
1048 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run_segment(Index row, Index col, const Lhs& lhs, const Rhs& rhs,
1049 Index innerDim, Packet& res, Index begin, Index count) {
1050 res = pset1<Packet>(typename unpacket_traits<Packet>::type(0));
1051 for (Index i = 0; i < innerDim; ++i)
1052 res = pmadd(pset1<Packet>(lhs.coeff(row, i)), rhs.template packetSegment<LoadMode, Packet>(i, col, begin, count),
1053 res);
1054 }
1055};
1056
1057template <typename Lhs, typename Rhs, typename Packet, int LoadMode>
1058struct etor_product_packet_impl<ColMajor, Dynamic, Lhs, Rhs, Packet, LoadMode> {
1059 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(Index row, Index col, const Lhs& lhs, const Rhs& rhs,
1060 Index innerDim, Packet& res) {
1061 res = pset1<Packet>(typename unpacket_traits<Packet>::type(0));
1062 for (Index i = 0; i < innerDim; ++i)
1063 res = pmadd(lhs.template packet<LoadMode, Packet>(row, i), pset1<Packet>(rhs.coeff(i, col)), res);
1064 }
1065 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run_segment(Index row, Index col, const Lhs& lhs, const Rhs& rhs,
1066 Index innerDim, Packet& res, Index begin, Index count) {
1067 res = pset1<Packet>(typename unpacket_traits<Packet>::type(0));
1068 for (Index i = 0; i < innerDim; ++i)
1069 res = pmadd(lhs.template packetSegment<LoadMode, Packet>(row, i, begin, count), pset1<Packet>(rhs.coeff(i, col)),
1070 res);
1071 }
1072};
1073
1074/***************************************************************************
1075 * Triangular products
1076 ***************************************************************************/
1077template <int Mode, bool LhsIsTriangular, typename Lhs, bool LhsIsVector, typename Rhs, bool RhsIsVector>
1078struct triangular_product_impl;
1079
1080template <int ProductOrder>
1081struct diagonal_product_segment_impl;
1082
1083template <>
1084struct diagonal_product_segment_impl<OnTheLeft> {
1085 template <typename DstSegment, typename Coeffs, typename DiagonalType, typename Alpha>
1086 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(DstSegment& dst, const Coeffs& coeffs,
1087 const DiagonalType& diagonal, Index begin, Index /*col*/,
1088 const Alpha& alpha) {
1089 dst += alpha * diagonal.segment(begin, dst.size()).cwiseProduct(coeffs);
1090 }
1091 template <typename DstSegment, typename Coeffs, typename DiagonalType>
1092 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void runOverwrite(DstSegment& dst, const Coeffs& coeffs,
1093 const DiagonalType& diagonal, Index begin,
1094 Index /*col*/) {
1095 dst = diagonal.segment(begin, dst.size()).cwiseProduct(coeffs);
1096 }
1097};
1098
1099template <>
1100struct diagonal_product_segment_impl<OnTheRight> {
1101 template <typename DstSegment, typename Coeffs, typename DiagonalType, typename Alpha>
1102 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(DstSegment& dst, const Coeffs& coeffs,
1103 const DiagonalType& diagonal, Index /*begin*/, Index col,
1104 const Alpha& alpha) {
1105 dst += alpha * (coeffs * diagonal.coeff(col));
1106 }
1107 template <typename DstSegment, typename Coeffs, typename DiagonalType>
1108 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void runOverwrite(DstSegment& dst, const Coeffs& coeffs,
1109 const DiagonalType& diagonal, Index /*begin*/,
1110 Index col) {
1111 dst = coeffs * diagonal.coeff(col);
1112 }
1113};
1114
1115template <int Mode, int ProductOrder, typename MatrixType, typename DiagonalType>
1116struct triangular_diagonal_product_impl {
1117 using MatrixScalar = typename MatrixType::Scalar;
1118
1119 template <typename Dest, typename Alpha>
1120 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(Dest& dst, const MatrixType& matrix,
1121 const DiagonalType& diagonal, const Alpha& alpha) {
1122 eigen_assert(((ProductOrder == OnTheLeft && diagonal.size() == matrix.rows()) ||
1123 (ProductOrder == OnTheRight && diagonal.size() == matrix.cols())) &&
1124 "invalid matrix product");
1125
1126 const Index rows = matrix.rows();
1127 const Index cols = matrix.cols();
1128 for (Index col = 0; col < cols; ++col) {
1129 EIGEN_IF_CONSTEXPR ((Mode & Upper) == Upper) {
1130 const Index end = (std::min)(rows, ((Mode & (UnitDiag | ZeroDiag)) ? col : col + 1));
1131 addStoredSegment(dst, matrix, diagonal, 0, end, col, alpha);
1132 } else {
1133 const Index begin = ((Mode & (UnitDiag | ZeroDiag)) ? col + 1 : col);
1134 addStoredSegment(dst, matrix, diagonal, begin, rows - begin, col, alpha);
1135 }
1136
1137 EIGEN_IF_CONSTEXPR ((Mode & UnitDiag) == UnitDiag) {
1138 if (col < rows) addUnitCoeff(dst, diagonal, col, alpha);
1139 }
1140 }
1141 }
1142
1143 private:
1144 template <typename Dest, typename Alpha>
1145 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void addStoredSegment(Dest& dst, const MatrixType& matrix,
1146 const DiagonalType& diagonal, Index begin,
1147 Index size, Index col, const Alpha& alpha) {
1148 if (size <= 0) return;
1149 auto dstSegment = dst.col(col).segment(begin, size);
1150 diagonal_product_segment_impl<ProductOrder>::run(dstSegment, matrix.col(col).segment(begin, size), diagonal, begin,
1151 col, alpha);
1152 }
1153
1154 template <typename Dest, typename Alpha>
1155 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void addUnitCoeff(Dest& dst, const DiagonalType& diagonal, Index index,
1156 const Alpha& alpha) {
1157 EIGEN_IF_CONSTEXPR (ProductOrder == OnTheLeft) {
1158 dst.coeffRef(index, index) += alpha * (diagonal.coeff(index) * MatrixScalar(1));
1159 } else {
1160 dst.coeffRef(index, index) += alpha * (MatrixScalar(1) * diagonal.coeff(index));
1161 }
1162 }
1163};
1164
1165template <typename Lhs, typename Rhs, int ProductTag>
1166struct generic_product_impl<Lhs, Rhs, TriangularShape, DenseShape, ProductTag>
1167 : generic_product_impl_base<Lhs, Rhs, generic_product_impl<Lhs, Rhs, TriangularShape, DenseShape, ProductTag>> {
1168 using Scalar = typename Product<Lhs, Rhs>::Scalar;
1169
1170 template <typename Dest>
1171 static void scaleAndAddTo(Dest& dst, const Lhs& lhs, const Rhs& rhs, const Scalar& alpha) {
1172 triangular_product_impl<Lhs::Mode, true, typename Lhs::MatrixType, false, Rhs, Rhs::ColsAtCompileTime == 1>::run(
1173 dst, lhs.nestedExpression(), rhs, alpha);
1174 }
1175};
1176
1177template <typename Lhs, typename Rhs, int ProductTag>
1178struct generic_product_impl<Lhs, Rhs, DenseShape, TriangularShape, ProductTag>
1179 : generic_product_impl_base<Lhs, Rhs, generic_product_impl<Lhs, Rhs, DenseShape, TriangularShape, ProductTag>> {
1180 using Scalar = typename Product<Lhs, Rhs>::Scalar;
1181
1182 template <typename Dest>
1183 static void scaleAndAddTo(Dest& dst, const Lhs& lhs, const Rhs& rhs, const Scalar& alpha) {
1184 triangular_product_impl<Rhs::Mode, false, Lhs, Lhs::RowsAtCompileTime == 1, typename Rhs::MatrixType, false>::run(
1185 dst, lhs, rhs.nestedExpression(), alpha);
1186 }
1187};
1188
1189template <typename Lhs, typename Rhs, int ProductTag>
1190struct generic_product_impl<Lhs, Rhs, TriangularShape, DiagonalShape, ProductTag>
1191 : generic_product_impl_base<Lhs, Rhs, generic_product_impl<Lhs, Rhs, TriangularShape, DiagonalShape, ProductTag>> {
1192 using Scalar = typename Product<Lhs, Rhs>::Scalar;
1193
1194 template <typename Dest>
1195 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void scaleAndAddTo(Dest& dst, const Lhs& lhs, const Rhs& rhs,
1196 const Scalar& alpha) {
1197 triangular_diagonal_product_impl<Lhs::Mode, OnTheRight, typename Lhs::MatrixType,
1198 typename Rhs::DiagonalVectorType>::run(dst, lhs.nestedExpression(), rhs.diagonal(),
1199 alpha);
1200 }
1201};
1202
1203template <typename Lhs, typename Rhs, int ProductTag>
1204struct generic_product_impl<Lhs, Rhs, DiagonalShape, TriangularShape, ProductTag>
1205 : generic_product_impl_base<Lhs, Rhs, generic_product_impl<Lhs, Rhs, DiagonalShape, TriangularShape, ProductTag>> {
1206 using Scalar = typename Product<Lhs, Rhs>::Scalar;
1207
1208 template <typename Dest>
1209 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void scaleAndAddTo(Dest& dst, const Lhs& lhs, const Rhs& rhs,
1210 const Scalar& alpha) {
1211 triangular_diagonal_product_impl<Rhs::Mode, OnTheLeft, typename Rhs::MatrixType,
1212 typename Lhs::DiagonalVectorType>::run(dst, rhs.nestedExpression(), lhs.diagonal(),
1213 alpha);
1214 }
1215};
1216
1217/***************************************************************************
1218 * SelfAdjoint products
1219 ***************************************************************************/
1220template <typename Lhs, int LhsMode, bool LhsIsVector, typename Rhs, int RhsMode, bool RhsIsVector>
1221struct selfadjoint_product_impl;
1222
1223template <int Mode, int ProductOrder, typename MatrixType, typename DiagonalType>
1224struct selfadjoint_diagonal_product_impl {
1225 // Accumulating: dst += alpha * (matrix.selfadjointView<Mode>() * diagonal[asDiagonal])
1226 // (with the diagonal on the right or left as ProductOrder dictates).
1227 template <typename Dest, typename Alpha>
1228 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(Dest& dst, const MatrixType& matrix,
1229 const DiagonalType& diagonal, const Alpha& alpha) {
1230 runImpl<true>(dst, matrix, diagonal, alpha);
1231 }
1232
1233 // Overwriting: dst = matrix.selfadjointView<Mode>() * diagonal[asDiagonal].
1234 // Each output entry is written exactly once, so the caller can skip the
1235 // dst.setZero() pass that generic_product_impl_base::evalTo would do.
1236 template <typename Dest>
1237 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void runOverwrite(Dest& dst, const MatrixType& matrix,
1238 const DiagonalType& diagonal) {
1239 using Scalar = typename traits<MatrixType>::Scalar;
1240 runImpl<false>(dst, matrix, diagonal, Scalar(1));
1241 }
1242
1243 private:
1244 // Tile size for the blocked mirror pass. Tuned so that one BlockSize x
1245 // BlockSize source tile fits comfortably in L1 across common scalar / SIMD
1246 // combinations: 32x32 of double = 8 KB, of complex<double> = 16 KB, both
1247 // well under typical 32 KB L1. Smaller tiles leave SIMD work on the table
1248 // for AVX/AVX-512; larger tiles spill out of L1 on machines with smaller
1249 // caches.
1250 static constexpr Index BlockSize = 32;
1251
1252 // The mirror half writes the strict-other-triangle of dst. The naive
1253 // per-column form reads matrix.row(col).segment(...) which has stride =
1254 // leading dimension, so on an N x N source it streams cold cache lines.
1255 // We walk the mirror in BlockSize x BlockSize tiles instead: off-diagonal
1256 // tiles use a blocked conjugate-transpose, and the small diagonal tile
1257 // falls back to the per-column row-strided loop where the working set is
1258 // L1-hot.
1259 template <bool Accumulate, typename Dest, typename Alpha>
1260 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void runImpl(Dest& dst, const MatrixType& matrix,
1261 const DiagonalType& diagonal, const Alpha& alpha) {
1262 eigen_assert(matrix.rows() == matrix.cols() && "SelfAdjointView is only for squared matrices");
1263 eigen_assert(diagonal.size() == matrix.rows() && "invalid matrix product");
1264
1265 const Index size = matrix.rows();
1266
1267 // Stored half: one column-strided segment per output column.
1268 for (Index col = 0; col < size; ++col) {
1269 EIGEN_IF_CONSTEXPR ((Mode & Upper) == Upper) {
1270 storedSegment<Accumulate>(dst, matrix, diagonal, 0, col + 1, col, alpha);
1271 } else {
1272 storedSegment<Accumulate>(dst, matrix, diagonal, col, size - col, col, alpha);
1273 }
1274 }
1275
1276 // Mirror half.
1277 for (Index ib = 0; ib < size; ib += BlockSize) {
1278 const Index ib_end = numext::mini(size, ib + BlockSize);
1279 const Index br = ib_end - ib;
1280 EIGEN_IF_CONSTEXPR ((Mode & Upper) == Upper) {
1281 // Off-diagonal: write strict-lower of dst from strict-upper of source.
1282 for (Index jb = 0; jb < ib; jb += BlockSize) {
1283 const Index bc = numext::mini(jb + BlockSize, ib) - jb;
1284 mirrorBlock<Accumulate>(dst, matrix, diagonal, ib, jb, br, bc, alpha);
1285 }
1286 // Diagonal tile: in-tile strict-lower mirror.
1287 for (Index col = ib; col < ib_end; ++col)
1288 conjugateSegment<Accumulate>(dst, matrix, diagonal, col + 1, ib_end - col - 1, col, alpha);
1289 } else {
1290 // Off-diagonal: write strict-upper of dst from strict-lower of source.
1291 for (Index jb = ib_end; jb < size; jb += BlockSize) {
1292 const Index bc = numext::mini(size, jb + BlockSize) - jb;
1293 mirrorBlock<Accumulate>(dst, matrix, diagonal, ib, jb, br, bc, alpha);
1294 }
1295 // Diagonal tile: in-tile strict-upper mirror.
1296 for (Index col = ib; col < ib_end; ++col)
1297 conjugateSegment<Accumulate>(dst, matrix, diagonal, ib, col - ib, col, alpha);
1298 }
1299 }
1300 }
1301
1302 template <bool Accumulate, typename Dest, typename Alpha>
1303 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void storedSegment(Dest& dst, const MatrixType& matrix,
1304 const DiagonalType& diagonal, Index begin, Index size,
1305 Index col, const Alpha& alpha) {
1306 if (size <= 0) return;
1307 auto dstSegment = dst.col(col).segment(begin, size);
1308 auto srcSegment = matrix.col(col).segment(begin, size);
1309 EIGEN_IF_CONSTEXPR (Accumulate) {
1310 diagonal_product_segment_impl<ProductOrder>::run(dstSegment, srcSegment, diagonal, begin, col, alpha);
1311 } else {
1312 diagonal_product_segment_impl<ProductOrder>::runOverwrite(dstSegment, srcSegment, diagonal, begin, col);
1313 }
1314 }
1315
1316 template <bool Accumulate, typename Dest, typename Alpha>
1317 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void conjugateSegment(Dest& dst, const MatrixType& matrix,
1318 const DiagonalType& diagonal, Index begin,
1319 Index size, Index col, const Alpha& alpha) {
1320 if (size <= 0) return;
1321 auto dstSegment = dst.col(col).segment(begin, size);
1322 auto srcSegment = matrix.row(col).segment(begin, size).conjugate().transpose();
1323 EIGEN_IF_CONSTEXPR (Accumulate) {
1324 diagonal_product_segment_impl<ProductOrder>::run(dstSegment, srcSegment, diagonal, begin, col, alpha);
1325 } else {
1326 diagonal_product_segment_impl<ProductOrder>::runOverwrite(dstSegment, srcSegment, diagonal, begin, col);
1327 }
1328 }
1329
1330 // dst.block(ib, jb, br, bc) [+= alpha *] matrix.block(jb, ib, bc, br).adjoint() * <diag>,
1331 // where <diag> scales each output column (OnTheRight) or row (OnTheLeft).
1332 // Loop bounds in runImpl guarantee br > 0 && bc > 0.
1333 template <bool Accumulate, typename Dest, typename Alpha>
1334 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void mirrorBlock(Dest& dst, const MatrixType& matrix,
1335 const DiagonalType& diagonal, Index ib, Index jb,
1336 Index br, Index bc, const Alpha& alpha) {
1337 auto dstBlock = dst.block(ib, jb, br, bc);
1338 auto srcAdjoint = matrix.block(jb, ib, bc, br).adjoint();
1339 EIGEN_IF_CONSTEXPR (ProductOrder == OnTheRight) {
1340 auto scaled = srcAdjoint * diagonal.segment(jb, bc).asDiagonal();
1341 EIGEN_IF_CONSTEXPR (Accumulate) {
1342 dstBlock += alpha * scaled;
1343 } else {
1344 dstBlock = scaled;
1345 }
1346 } else {
1347 auto scaled = diagonal.segment(ib, br).asDiagonal() * srcAdjoint;
1348 EIGEN_IF_CONSTEXPR (Accumulate) {
1349 dstBlock += alpha * scaled;
1350 } else {
1351 dstBlock = scaled;
1352 }
1353 }
1354 }
1355};
1356
1357template <typename Lhs, typename Rhs, int ProductTag>
1358struct generic_product_impl<Lhs, Rhs, SelfAdjointShape, DenseShape, ProductTag>
1359 : generic_product_impl_base<Lhs, Rhs, generic_product_impl<Lhs, Rhs, SelfAdjointShape, DenseShape, ProductTag>> {
1360 using Scalar = typename Product<Lhs, Rhs>::Scalar;
1361
1362 template <typename Dest>
1363 static EIGEN_DEVICE_FUNC void scaleAndAddTo(Dest& dst, const Lhs& lhs, const Rhs& rhs, const Scalar& alpha) {
1364 selfadjoint_product_impl<typename Lhs::MatrixType, Lhs::Mode, false, Rhs, 0, Rhs::ColsAtCompileTime == 1>::run(
1365 dst, lhs.nestedExpression(), rhs, alpha);
1366 }
1367};
1368
1369template <typename Lhs, typename Rhs, int ProductTag>
1370struct generic_product_impl<Lhs, Rhs, DenseShape, SelfAdjointShape, ProductTag>
1371 : generic_product_impl_base<Lhs, Rhs, generic_product_impl<Lhs, Rhs, DenseShape, SelfAdjointShape, ProductTag>> {
1372 using Scalar = typename Product<Lhs, Rhs>::Scalar;
1373
1374 template <typename Dest>
1375 static void scaleAndAddTo(Dest& dst, const Lhs& lhs, const Rhs& rhs, const Scalar& alpha) {
1376 selfadjoint_product_impl<Lhs, 0, Lhs::RowsAtCompileTime == 1, typename Rhs::MatrixType, Rhs::Mode, false>::run(
1377 dst, lhs, rhs.nestedExpression(), alpha);
1378 }
1379};
1380
1381template <typename Lhs, typename Rhs, int ProductTag>
1382struct generic_product_impl<Lhs, Rhs, SelfAdjointShape, DiagonalShape, ProductTag>
1383 : generic_product_impl_base<Lhs, Rhs, generic_product_impl<Lhs, Rhs, SelfAdjointShape, DiagonalShape, ProductTag>> {
1384 using Scalar = typename Product<Lhs, Rhs>::Scalar;
1385 // The "Dense ?= scalar * Product" rewriting rule folds an outer alpha into the
1386 // SelfAdjointView via SelfAdjointView::operator*(scalar), whose nested
1387 // expression becomes (matrix * alpha). For complex alpha this is no longer
1388 // Hermitian — the mirror half of our kernel would produce conj(alpha) on the
1389 // off-triangle. Strip the scalar factor with blas_traits and re-fold it into
1390 // the kernel's alpha so the same scalar multiplies every output entry.
1391 using LhsBlasTraits = blas_traits<typename Lhs::MatrixType>;
1392 // A named constant, not bool(...): nvcc's front end re-emits that cast as a function type MSVC rejects.
1393 static constexpr bool ConjLhs = LhsBlasTraits::NeedToConjugate;
1394 using ActualLhsMatrix =
1395 decltype(LhsBlasTraits::extract(std::declval<const typename Lhs::MatrixType&>()).template conjugateIf<ConjLhs>());
1396 using ActualLhsMatrixType = remove_all_t<ActualLhsMatrix>;
1397 using Kernel =
1398 selfadjoint_diagonal_product_impl<Lhs::Mode, OnTheRight, ActualLhsMatrixType, typename Rhs::DiagonalVectorType>;
1399
1400 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE ActualLhsMatrix actualLhsMatrix(const typename Lhs::MatrixType& matrix) {
1401 return LhsBlasTraits::extract(matrix).template conjugateIf<ConjLhs>();
1402 }
1403
1404 template <typename Dest>
1405 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void evalTo(Dest& dst, const Lhs& lhs, const Rhs& rhs) {
1406 EIGEN_IF_CONSTEXPR (LhsBlasTraits::HasScalarFactor) {
1407 // Folded scalar factor present: zero dst then accumulate at the extracted alpha.
1408 Scalar factor = LhsBlasTraits::extractScalarFactor(lhs.nestedExpression());
1409 dst.setZero();
1410 Kernel::run(dst, actualLhsMatrix(lhs.nestedExpression()), rhs.diagonal(), factor);
1411 } else {
1412 // No scalar factor: kernel writes every entry exactly once, skip setZero.
1413 Kernel::runOverwrite(dst, actualLhsMatrix(lhs.nestedExpression()), rhs.diagonal());
1414 }
1415 }
1416
1417 template <typename Dest>
1418 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void scaleAndAddTo(Dest& dst, const Lhs& lhs, const Rhs& rhs,
1419 const Scalar& alpha) {
1420 Scalar combinedAlpha = alpha * LhsBlasTraits::extractScalarFactor(lhs.nestedExpression());
1421 Kernel::run(dst, actualLhsMatrix(lhs.nestedExpression()), rhs.diagonal(), combinedAlpha);
1422 }
1423};
1424
1425template <typename Lhs, typename Rhs, int ProductTag>
1426struct generic_product_impl<Lhs, Rhs, DiagonalShape, SelfAdjointShape, ProductTag>
1427 : generic_product_impl_base<Lhs, Rhs, generic_product_impl<Lhs, Rhs, DiagonalShape, SelfAdjointShape, ProductTag>> {
1428 using Scalar = typename Product<Lhs, Rhs>::Scalar;
1429 // See note on the SelfAdjointShape, DiagonalShape specialization above for why
1430 // we extract the scalar factor with blas_traits.
1431 using RhsBlasTraits = blas_traits<typename Rhs::MatrixType>;
1432 static constexpr bool ConjRhs = RhsBlasTraits::NeedToConjugate;
1433 using ActualRhsMatrix =
1434 decltype(RhsBlasTraits::extract(std::declval<const typename Rhs::MatrixType&>()).template conjugateIf<ConjRhs>());
1435 using ActualRhsMatrixType = remove_all_t<ActualRhsMatrix>;
1436 using Kernel =
1437 selfadjoint_diagonal_product_impl<Rhs::Mode, OnTheLeft, ActualRhsMatrixType, typename Lhs::DiagonalVectorType>;
1438
1439 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE ActualRhsMatrix actualRhsMatrix(const typename Rhs::MatrixType& matrix) {
1440 return RhsBlasTraits::extract(matrix).template conjugateIf<ConjRhs>();
1441 }
1442
1443 template <typename Dest>
1444 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void evalTo(Dest& dst, const Lhs& lhs, const Rhs& rhs) {
1445 EIGEN_IF_CONSTEXPR (RhsBlasTraits::HasScalarFactor) {
1446 Scalar factor = RhsBlasTraits::extractScalarFactor(rhs.nestedExpression());
1447 dst.setZero();
1448 Kernel::run(dst, actualRhsMatrix(rhs.nestedExpression()), lhs.diagonal(), factor);
1449 } else {
1450 Kernel::runOverwrite(dst, actualRhsMatrix(rhs.nestedExpression()), lhs.diagonal());
1451 }
1452 }
1453
1454 template <typename Dest>
1455 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void scaleAndAddTo(Dest& dst, const Lhs& lhs, const Rhs& rhs,
1456 const Scalar& alpha) {
1457 Scalar combinedAlpha = alpha * RhsBlasTraits::extractScalarFactor(rhs.nestedExpression());
1458 Kernel::run(dst, actualRhsMatrix(rhs.nestedExpression()), lhs.diagonal(), combinedAlpha);
1459 }
1460};
1461
1462/***************************************************************************
1463 * Products of two triangular or self-adjoint views
1464 ***************************************************************************/
1465
1472template <typename View, typename Shape = typename evaluator_traits<View>::Shape>
1473struct densified_view_operand {
1474 using Scalar = typename View::Scalar;
1475 template <typename Dense>
1476 static Scalar run(Dense& dense, const View& view) {
1477 dense = view;
1478 return Scalar(1);
1479 }
1480};
1481
1482template <typename View>
1483struct densified_view_operand<View, SelfAdjointShape> {
1484 using Scalar = typename View::Scalar;
1485 using BlasTraits = blas_traits<typename View::MatrixType>;
1486 static constexpr unsigned int UpLo = unsigned(int(View::Mode) & int(Upper | Lower));
1487 template <typename Dense>
1488 static Scalar run(Dense& dense, const View& view) {
1489 dense = BlasTraits::extract(view.nestedExpression())
1490 .template conjugateIf<bool(BlasTraits::NeedToConjugate)>()
1491 .template selfadjointView<UpLo>();
1492 return BlasTraits::extractScalarFactor(view.nestedExpression());
1493 }
1494};
1495
1503template <typename Lhs, typename Rhs, typename LhsShape, typename RhsShape, int ProductTag, bool DensifyRhs>
1504struct structured_view_product_impl;
1505
1506template <typename Lhs, typename Rhs, typename LhsShape, typename RhsShape, int ProductTag>
1507struct structured_view_product_impl<Lhs, Rhs, LhsShape, RhsShape, ProductTag, true>
1508 : generic_product_impl_base<Lhs, Rhs,
1509 structured_view_product_impl<Lhs, Rhs, LhsShape, RhsShape, ProductTag, true>> {
1510 using Scalar = typename Product<Lhs, Rhs>::Scalar;
1511 using RhsDenseType = typename Rhs::DenseMatrixType;
1512
1513 template <typename Dest>
1514 static void scaleAndAddTo(Dest& dst, const Lhs& lhs, const Rhs& rhs, const Scalar& alpha) {
1515 RhsDenseType rhsDense(rhs.rows(), rhs.cols());
1516 const Scalar factor = densified_view_operand<Rhs>::run(rhsDense, rhs);
1517 generic_product_impl<Lhs, RhsDenseType, LhsShape, DenseShape, ProductTag>::scaleAndAddTo(dst, lhs, rhsDense,
1518 alpha * factor);
1519 }
1520};
1521
1522template <typename Lhs, typename Rhs, typename LhsShape, typename RhsShape, int ProductTag>
1523struct structured_view_product_impl<Lhs, Rhs, LhsShape, RhsShape, ProductTag, false>
1524 : generic_product_impl_base<Lhs, Rhs,
1525 structured_view_product_impl<Lhs, Rhs, LhsShape, RhsShape, ProductTag, false>> {
1526 using Scalar = typename Product<Lhs, Rhs>::Scalar;
1527 using LhsDenseType = typename Lhs::DenseMatrixType;
1528
1529 template <typename Dest>
1530 static void scaleAndAddTo(Dest& dst, const Lhs& lhs, const Rhs& rhs, const Scalar& alpha) {
1531 LhsDenseType lhsDense(lhs.rows(), lhs.cols());
1532 const Scalar factor = densified_view_operand<Lhs>::run(lhsDense, lhs);
1533 generic_product_impl<LhsDenseType, Rhs, DenseShape, RhsShape, ProductTag>::scaleAndAddTo(dst, lhsDense, rhs,
1534 alpha * factor);
1535 }
1536};
1537
1538template <typename Lhs, typename Rhs, int ProductTag>
1539struct generic_product_impl<Lhs, Rhs, TriangularShape, TriangularShape, ProductTag>
1540 : structured_view_product_impl<Lhs, Rhs, TriangularShape, TriangularShape, ProductTag, true> {};
1541
1542template <typename Lhs, typename Rhs, int ProductTag>
1543struct generic_product_impl<Lhs, Rhs, TriangularShape, SelfAdjointShape, ProductTag>
1544 : structured_view_product_impl<Lhs, Rhs, TriangularShape, SelfAdjointShape, ProductTag, true> {};
1545
1546template <typename Lhs, typename Rhs, int ProductTag>
1547struct generic_product_impl<Lhs, Rhs, SelfAdjointShape, TriangularShape, ProductTag>
1548 : structured_view_product_impl<Lhs, Rhs, SelfAdjointShape, TriangularShape, ProductTag, false> {};
1549
1550template <typename Lhs, typename Rhs, int ProductTag>
1551struct generic_product_impl<Lhs, Rhs, SelfAdjointShape, SelfAdjointShape, ProductTag>
1552 : structured_view_product_impl<Lhs, Rhs, SelfAdjointShape, SelfAdjointShape, ProductTag, true> {};
1553
1554/***************************************************************************
1555 * Diagonal products
1556 ***************************************************************************/
1557
1558template <typename MatrixType, typename DiagonalType, typename Derived, int ProductOrder>
1559struct diagonal_product_evaluator_base : evaluator_base<Derived> {
1560 using Scalar = typename ScalarBinaryOpTraits<typename MatrixType::Scalar, typename DiagonalType::Scalar>::ReturnType;
1561
1562 public:
1563 enum {
1564 CoeffReadCost = int(NumTraits<Scalar>::MulCost) + int(evaluator<MatrixType>::CoeffReadCost) +
1565 int(evaluator<DiagonalType>::CoeffReadCost),
1566
1567 MatrixFlags = evaluator<MatrixType>::Flags,
1568 DiagFlags = evaluator<DiagonalType>::Flags,
1569
1570 StorageOrder_ = (Derived::MaxRowsAtCompileTime == 1 && Derived::MaxColsAtCompileTime != 1) ? RowMajor
1571 : (Derived::MaxColsAtCompileTime == 1 && Derived::MaxRowsAtCompileTime != 1) ? ColMajor
1572 : MatrixFlags & RowMajorBit ? RowMajor
1573 : ColMajor,
1574 SameStorageOrder_ = int(StorageOrder_) == ((MatrixFlags & RowMajorBit) ? RowMajor : ColMajor),
1575
1576 ScalarAccessOnDiag_ = !((int(StorageOrder_) == ColMajor && int(ProductOrder) == OnTheLeft) ||
1577 (int(StorageOrder_) == RowMajor && int(ProductOrder) == OnTheRight)),
1578 SameTypes_ = std::is_same<typename MatrixType::Scalar, typename DiagonalType::Scalar>::value,
1579 // FIXME currently we need same types, but in the future the next rule should be the one
1580 // Vectorizable_ = bool(int(MatrixFlags)&PacketAccessBit) && ((!_PacketOnDiag) || (SameTypes_ &&
1581 // bool(int(DiagFlags)&PacketAccessBit))),
1582 Vectorizable_ = bool(int(MatrixFlags) & PacketAccessBit) && SameTypes_ &&
1583 (SameStorageOrder_ || (MatrixFlags & LinearAccessBit) == LinearAccessBit) &&
1584 (ScalarAccessOnDiag_ || (bool(int(DiagFlags) & PacketAccessBit))),
1585 LinearAccessMask_ =
1586 (MatrixType::RowsAtCompileTime == 1 || MatrixType::ColsAtCompileTime == 1) ? LinearAccessBit : 0,
1587 Flags = ((HereditaryBits | LinearAccessMask_) & static_cast<unsigned int>(MatrixFlags)) |
1588 (Vectorizable_ ? PacketAccessBit : 0),
1589 Alignment = evaluator<MatrixType>::Alignment,
1590
1591 AsScalarProduct =
1592 (DiagonalType::SizeAtCompileTime == 1) ||
1593 (DiagonalType::SizeAtCompileTime == Dynamic && MatrixType::RowsAtCompileTime == 1 &&
1594 ProductOrder == OnTheLeft) ||
1595 (DiagonalType::SizeAtCompileTime == Dynamic && MatrixType::ColsAtCompileTime == 1 && ProductOrder == OnTheRight)
1596 };
1597
1598 EIGEN_DEVICE_FUNC diagonal_product_evaluator_base(const MatrixType& mat, const DiagonalType& diag)
1599 : m_diagImpl(diag), m_matImpl(mat) {
1600 EIGEN_INTERNAL_CHECK_COST_VALUE(NumTraits<Scalar>::MulCost);
1601 EIGEN_INTERNAL_CHECK_COST_VALUE(CoeffReadCost);
1602 }
1603
1604 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE const Scalar coeff(Index idx) const {
1605 EIGEN_IF_CONSTEXPR (AsScalarProduct)
1606 return m_diagImpl.coeff(0) * m_matImpl.coeff(idx);
1607 else
1608 return m_diagImpl.coeff(idx) * m_matImpl.coeff(idx);
1609 }
1610
1611 protected:
1612 template <int LoadMode, typename PacketType>
1613 EIGEN_STRONG_INLINE PacketType packet_impl(Index row, Index col, Index id, std::true_type) const {
1614 return internal::pmul(m_matImpl.template packet<LoadMode, PacketType>(row, col),
1615 internal::pset1<PacketType>(m_diagImpl.coeff(id)));
1616 }
1617
1618 template <int LoadMode, typename PacketType>
1619 EIGEN_STRONG_INLINE PacketType packet_impl(Index row, Index col, Index id, std::false_type) const {
1620 enum {
1621 InnerSize = (MatrixType::Flags & RowMajorBit) ? MatrixType::ColsAtCompileTime : MatrixType::RowsAtCompileTime,
1622 DiagonalPacketLoadMode = plain_enum_min(
1623 LoadMode, ((InnerSize * int(sizeof(Scalar))) % int(unpacket_traits<PacketType>::alignment) == 0)
1624 ? int(unpacket_traits<PacketType>::alignment)
1625 : int(evaluator<DiagonalType>::Alignment))
1626 };
1627 return internal::pmul(m_matImpl.template packet<LoadMode, PacketType>(row, col),
1628 m_diagImpl.template packet<DiagonalPacketLoadMode, PacketType>(id));
1629 }
1630
1631 template <int LoadMode, typename PacketType>
1632 EIGEN_STRONG_INLINE PacketType packet_segment_impl(Index row, Index col, Index id, Index begin, Index count,
1633 std::true_type) const {
1634 return internal::pmul(m_matImpl.template packetSegment<LoadMode, PacketType>(row, col, begin, count),
1635 internal::pset1<PacketType>(m_diagImpl.coeff(id)));
1636 }
1637
1638 template <int LoadMode, typename PacketType>
1639 EIGEN_STRONG_INLINE PacketType packet_segment_impl(Index row, Index col, Index id, Index begin, Index count,
1640 std::false_type) const {
1641 enum {
1642 InnerSize = (MatrixType::Flags & RowMajorBit) ? MatrixType::ColsAtCompileTime : MatrixType::RowsAtCompileTime,
1643 DiagonalPacketLoadMode = plain_enum_min(
1644 LoadMode, ((InnerSize * int(sizeof(Scalar))) % int(unpacket_traits<PacketType>::alignment) == 0)
1645 ? int(unpacket_traits<PacketType>::alignment)
1646 : int(evaluator<DiagonalType>::Alignment))
1647 };
1648 return internal::pmul(m_matImpl.template packetSegment<LoadMode, PacketType>(row, col, begin, count),
1649 m_diagImpl.template packetSegment<DiagonalPacketLoadMode, PacketType>(id, begin, count));
1650 }
1651
1652 evaluator<DiagonalType> m_diagImpl;
1653 evaluator<MatrixType> m_matImpl;
1654};
1655
1656// diagonal * dense
1657template <typename Lhs, typename Rhs, int ProductKind, int ProductTag>
1658struct product_evaluator<Product<Lhs, Rhs, ProductKind>, ProductTag, DiagonalShape, DenseShape>
1659 : diagonal_product_evaluator_base<Rhs, typename Lhs::DiagonalVectorType, Product<Lhs, Rhs, LazyProduct>,
1660 OnTheLeft> {
1661 using Base =
1662 diagonal_product_evaluator_base<Rhs, typename Lhs::DiagonalVectorType, Product<Lhs, Rhs, LazyProduct>, OnTheLeft>;
1663 using Base::coeff;
1664 using Base::m_diagImpl;
1665 using Base::m_matImpl;
1666 using Scalar = typename Base::Scalar;
1667
1668 using XprType = Product<Lhs, Rhs, ProductKind>;
1669 using PlainObject = typename XprType::PlainObject;
1670
1671 static constexpr int StorageOrder = Base::StorageOrder_;
1672 using IsRowMajor_t = bool_constant<StorageOrder == RowMajor>;
1673
1674 EIGEN_DEVICE_FUNC constexpr explicit product_evaluator(const XprType& xpr) : Base(xpr.rhs(), xpr.lhs().diagonal()) {}
1675
1676 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE const Scalar coeff(Index row, Index col) const {
1677 return m_diagImpl.coeff(row) * m_matImpl.coeff(row, col);
1678 }
1679
1680#ifndef EIGEN_GPUCC
1681 template <int LoadMode, typename PacketType>
1682 EIGEN_STRONG_INLINE PacketType packet(Index row, Index col) const {
1683 // FIXME: NVCC used to complain about the template keyword, but we have to check whether this is still the case.
1684 // See also similar calls below.
1685 return this->template packet_impl<LoadMode, PacketType>(row, col, row, IsRowMajor_t());
1686 }
1687
1688 template <int LoadMode, typename PacketType>
1689 EIGEN_STRONG_INLINE PacketType packet(Index idx) const {
1690 return packet<LoadMode, PacketType>(int(StorageOrder) == ColMajor ? idx : 0,
1691 int(StorageOrder) == ColMajor ? 0 : idx);
1692 }
1693
1694 template <int LoadMode, typename PacketType>
1695 EIGEN_STRONG_INLINE PacketType packetSegment(Index row, Index col, Index begin, Index count) const {
1696 // FIXME: NVCC used to complain about the template keyword, but we have to check whether this is still the case.
1697 // See also similar calls below.
1698 return this->template packet_segment_impl<LoadMode, PacketType>(row, col, row, begin, count, IsRowMajor_t());
1699 }
1700
1701 template <int LoadMode, typename PacketType>
1702 EIGEN_STRONG_INLINE PacketType packetSegment(Index idx, Index begin, Index count) const {
1703 return packetSegment<LoadMode, PacketType>(StorageOrder == ColMajor ? idx : 0, StorageOrder == ColMajor ? 0 : idx,
1704 begin, count);
1705 }
1706#endif
1707};
1708
1709// dense * diagonal
1710template <typename Lhs, typename Rhs, int ProductKind, int ProductTag>
1711struct product_evaluator<Product<Lhs, Rhs, ProductKind>, ProductTag, DenseShape, DiagonalShape>
1712 : diagonal_product_evaluator_base<Lhs, typename Rhs::DiagonalVectorType, Product<Lhs, Rhs, LazyProduct>,
1713 OnTheRight> {
1714 using Base = diagonal_product_evaluator_base<Lhs, typename Rhs::DiagonalVectorType, Product<Lhs, Rhs, LazyProduct>,
1715 OnTheRight>;
1716 using Base::coeff;
1717 using Base::m_diagImpl;
1718 using Base::m_matImpl;
1719 using Scalar = typename Base::Scalar;
1720
1721 using XprType = Product<Lhs, Rhs, ProductKind>;
1722 using PlainObject = typename XprType::PlainObject;
1723
1724 static constexpr int StorageOrder = Base::StorageOrder_;
1725 using IsColMajor_t = bool_constant<StorageOrder == ColMajor>;
1726
1727 EIGEN_DEVICE_FUNC constexpr explicit product_evaluator(const XprType& xpr) : Base(xpr.lhs(), xpr.rhs().diagonal()) {}
1728
1729 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE const Scalar coeff(Index row, Index col) const {
1730 return m_matImpl.coeff(row, col) * m_diagImpl.coeff(col);
1731 }
1732
1733#ifndef EIGEN_GPUCC
1734 template <int LoadMode, typename PacketType>
1735 EIGEN_STRONG_INLINE PacketType packet(Index row, Index col) const {
1736 return this->template packet_impl<LoadMode, PacketType>(row, col, col, IsColMajor_t());
1737 }
1738
1739 template <int LoadMode, typename PacketType>
1740 EIGEN_STRONG_INLINE PacketType packet(Index idx) const {
1741 return packet<LoadMode, PacketType>(StorageOrder == ColMajor ? idx : 0, StorageOrder == ColMajor ? 0 : idx);
1742 }
1743
1744 template <int LoadMode, typename PacketType>
1745 EIGEN_STRONG_INLINE PacketType packetSegment(Index row, Index col, Index begin, Index count) const {
1746 return this->template packet_segment_impl<LoadMode, PacketType>(row, col, col, begin, count, IsColMajor_t());
1747 }
1748
1749 template <int LoadMode, typename PacketType>
1750 EIGEN_STRONG_INLINE PacketType packetSegment(Index idx, Index begin, Index count) const {
1751 return packetSegment<LoadMode, PacketType>(StorageOrder == ColMajor ? idx : 0, StorageOrder == ColMajor ? 0 : idx,
1752 begin, count);
1753 }
1754#endif
1755};
1756
1757// Lazy evaluators for triangular/selfadjoint × diagonal products.
1758//
1759// The triangular-assignment dispatcher in TriangularMatrix.h routes
1760// `triangularView() ?= structured * diagonal` through call_triangular_assignment_loop,
1761// which constructs an evaluator over the Product expression. Without these
1762// specializations, evaluator<Product<TriangularView, Diagonal>> falls back to the
1763// default product_evaluator that materializes a full PlainObject — defeating the
1764// triangular-aware dispatcher. The kernel reads only the destination's active
1765// triangle one coefficient at a time, so a coeff()-only evaluator avoids the
1766// temporary at no loss in functionality.
1767
1768template <int Mode, int ProductOrder, typename MatrixType, typename DiagonalType, typename Derived>
1769struct triangular_diagonal_product_lazy_evaluator_base : evaluator_base<Derived> {
1770 using Scalar = typename ScalarBinaryOpTraits<typename MatrixType::Scalar, typename DiagonalType::Scalar>::ReturnType;
1771 using MatrixScalar = typename MatrixType::Scalar;
1772
1773 enum {
1774 CoeffReadCost = int(NumTraits<Scalar>::MulCost) + int(evaluator<MatrixType>::CoeffReadCost) +
1775 int(evaluator<DiagonalType>::CoeffReadCost),
1776 Flags = HereditaryBits & static_cast<unsigned int>(evaluator<MatrixType>::Flags),
1777 Alignment = 0
1778 };
1779
1780 EIGEN_DEVICE_FUNC triangular_diagonal_product_lazy_evaluator_base(const MatrixType& mat, const DiagonalType& diag)
1781 : m_diagImpl(diag), m_matImpl(mat) {}
1782
1783 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar coeff(Index row, Index col) const {
1784 const bool inActive = ((Mode & Upper) == Upper) ? (row <= col) : (row >= col);
1785 if (!inActive) return Scalar(0);
1786 if (row == col) {
1787 EIGEN_IF_CONSTEXPR ((Mode & UnitDiag) == UnitDiag) {
1788 return ProductOrder == OnTheLeft ? Scalar(m_diagImpl.coeff(row) * MatrixScalar(1))
1789 : Scalar(MatrixScalar(1) * m_diagImpl.coeff(col));
1790 }
1791 EIGEN_IF_CONSTEXPR ((Mode & ZeroDiag) == ZeroDiag) return Scalar(0);
1792 }
1793 return ProductOrder == OnTheLeft ? Scalar(m_diagImpl.coeff(row) * m_matImpl.coeff(row, col))
1794 : Scalar(m_matImpl.coeff(row, col) * m_diagImpl.coeff(col));
1795 }
1796
1797 protected:
1798 evaluator<DiagonalType> m_diagImpl;
1799 evaluator<MatrixType> m_matImpl;
1800};
1801
1802// Triangular × Diagonal
1803template <typename Lhs, typename Rhs, int ProductKind, int ProductTag>
1804struct product_evaluator<Product<Lhs, Rhs, ProductKind>, ProductTag, TriangularShape, DiagonalShape>
1805 : triangular_diagonal_product_lazy_evaluator_base<
1806 Lhs::Mode, OnTheRight, typename Lhs::MatrixType, typename Rhs::DiagonalVectorType,
1807 product_evaluator<Product<Lhs, Rhs, ProductKind>, ProductTag, TriangularShape, DiagonalShape>> {
1808 using XprType = Product<Lhs, Rhs, ProductKind>;
1809 using Base = triangular_diagonal_product_lazy_evaluator_base<
1810 Lhs::Mode, OnTheRight, typename Lhs::MatrixType, typename Rhs::DiagonalVectorType,
1811 product_evaluator<XprType, ProductTag, TriangularShape, DiagonalShape>>;
1812
1813 EIGEN_DEVICE_FUNC explicit product_evaluator(const XprType& xpr)
1814 : Base(xpr.lhs().nestedExpression(), xpr.rhs().diagonal()) {}
1815};
1816
1817// Diagonal × Triangular
1818template <typename Lhs, typename Rhs, int ProductKind, int ProductTag>
1819struct product_evaluator<Product<Lhs, Rhs, ProductKind>, ProductTag, DiagonalShape, TriangularShape>
1820 : triangular_diagonal_product_lazy_evaluator_base<
1821 Rhs::Mode, OnTheLeft, typename Rhs::MatrixType, typename Lhs::DiagonalVectorType,
1822 product_evaluator<Product<Lhs, Rhs, ProductKind>, ProductTag, DiagonalShape, TriangularShape>> {
1823 using XprType = Product<Lhs, Rhs, ProductKind>;
1824 using Base = triangular_diagonal_product_lazy_evaluator_base<
1825 Rhs::Mode, OnTheLeft, typename Rhs::MatrixType, typename Lhs::DiagonalVectorType,
1826 product_evaluator<XprType, ProductTag, DiagonalShape, TriangularShape>>;
1827
1828 EIGEN_DEVICE_FUNC explicit product_evaluator(const XprType& xpr)
1829 : Base(xpr.rhs().nestedExpression(), xpr.lhs().diagonal()) {}
1830};
1831
1832// Dense SelfAdjointView statically rejects the Upper|Lower mode (only one half is stored), so the
1833// off-stored coefficient is always reconstructed by conjugating its mirror.
1834template <int Mode, int ProductOrder, typename MatrixType, typename DiagonalType, typename ProductXpr,
1835 bool Materialize =
1836 NumTraits<typename MatrixType::Scalar>::IsComplex && blas_traits<MatrixType>::HasScalarFactor>
1837struct selfadjoint_diagonal_product_lazy_evaluator_base : evaluator_base<ProductXpr> {
1838 using Scalar = typename ScalarBinaryOpTraits<typename MatrixType::Scalar, typename DiagonalType::Scalar>::ReturnType;
1839
1840 enum {
1841 CoeffReadCost = int(NumTraits<Scalar>::MulCost) + int(evaluator<MatrixType>::CoeffReadCost) +
1842 int(evaluator<DiagonalType>::CoeffReadCost),
1843 Flags = HereditaryBits & static_cast<unsigned int>(evaluator<MatrixType>::Flags),
1844 Alignment = 0
1845 };
1846
1847 EIGEN_DEVICE_FUNC selfadjoint_diagonal_product_lazy_evaluator_base(const ProductXpr&, const MatrixType& mat,
1848 const DiagonalType& diag)
1849 : m_diagImpl(diag), m_matImpl(mat) {}
1850
1851 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar coeff(Index row, Index col) const {
1852 const bool storedHere = ((Mode & Upper) == Upper) ? (row <= col) : (row >= col);
1853 const Scalar matCoeff =
1854 storedHere ? Scalar(m_matImpl.coeff(row, col)) : Scalar(numext::conj(m_matImpl.coeff(col, row)));
1855 return ProductOrder == OnTheLeft ? Scalar(m_diagImpl.coeff(row) * matCoeff)
1856 : Scalar(matCoeff * m_diagImpl.coeff(col));
1857 }
1858
1859 protected:
1860 evaluator<DiagonalType> m_diagImpl;
1861 evaluator<MatrixType> m_matImpl;
1862};
1863
1864// Extract complex factors before conjugating mirrored entries; the temporary also protects nested aliases.
1865// The parameter is not named XprType: MSVC lets the dependent base's XprType (an EvalToTemp) hide it.
1866template <int Mode, int ProductOrder, typename MatrixType, typename DiagonalType, typename ProductXpr>
1867struct selfadjoint_diagonal_product_lazy_evaluator_base<Mode, ProductOrder, MatrixType, DiagonalType, ProductXpr, true>
1868 : evaluator<EvalToTemp<Product<typename ProductXpr::Lhs, typename ProductXpr::Rhs, DefaultProduct>>> {
1869 using DefaultProductType = Product<typename ProductXpr::Lhs, typename ProductXpr::Rhs, DefaultProduct>;
1870 using Base = evaluator<EvalToTemp<DefaultProductType>>;
1871 EIGEN_DEVICE_FUNC selfadjoint_diagonal_product_lazy_evaluator_base(const ProductXpr& xpr, const MatrixType&,
1872 const DiagonalType&)
1873 : Base(DefaultProductType(xpr.lhs(), xpr.rhs())) {}
1874};
1875
1876// SelfAdjoint × Diagonal
1877template <typename Lhs, typename Rhs, int ProductKind, int ProductTag>
1878struct product_evaluator<Product<Lhs, Rhs, ProductKind>, ProductTag, SelfAdjointShape, DiagonalShape>
1879 : selfadjoint_diagonal_product_lazy_evaluator_base<Lhs::Mode, OnTheRight, typename Lhs::MatrixType,
1880 typename Rhs::DiagonalVectorType,
1881 Product<Lhs, Rhs, ProductKind>> {
1882 using XprType = Product<Lhs, Rhs, ProductKind>;
1883 using Base = selfadjoint_diagonal_product_lazy_evaluator_base<Lhs::Mode, OnTheRight, typename Lhs::MatrixType,
1884 typename Rhs::DiagonalVectorType, XprType>;
1885
1886 EIGEN_DEVICE_FUNC explicit product_evaluator(const XprType& xpr)
1887 : Base(xpr, xpr.lhs().nestedExpression(), xpr.rhs().diagonal()) {}
1888};
1889
1890// Diagonal × SelfAdjoint
1891template <typename Lhs, typename Rhs, int ProductKind, int ProductTag>
1892struct product_evaluator<Product<Lhs, Rhs, ProductKind>, ProductTag, DiagonalShape, SelfAdjointShape>
1893 : selfadjoint_diagonal_product_lazy_evaluator_base<Rhs::Mode, OnTheLeft, typename Rhs::MatrixType,
1894 typename Lhs::DiagonalVectorType,
1895 Product<Lhs, Rhs, ProductKind>> {
1896 using XprType = Product<Lhs, Rhs, ProductKind>;
1897 using Base = selfadjoint_diagonal_product_lazy_evaluator_base<Rhs::Mode, OnTheLeft, typename Rhs::MatrixType,
1898 typename Lhs::DiagonalVectorType, XprType>;
1899
1900 EIGEN_DEVICE_FUNC explicit product_evaluator(const XprType& xpr)
1901 : Base(xpr, xpr.rhs().nestedExpression(), xpr.lhs().diagonal()) {}
1902};
1903
1904/***************************************************************************
1905 * Products with permutation matrices
1906 ***************************************************************************/
1907
1916template <typename ExpressionType, int Side, bool Transposed, typename ExpressionShape>
1917struct permutation_matrix_product {
1918 template <typename Dest, typename PermutationType>
1919 static EIGEN_DEVICE_FUNC void run(Dest&, const PermutationType&, const ExpressionType&) {
1920 static_assert(std::is_same<ExpressionShape, DenseShape>::value,
1921 "PERMUTATION_PRODUCTS_ARE_NOT_IMPLEMENTED_FOR_THIS_OPERAND_SHAPE");
1922 }
1923};
1924
1925template <typename ExpressionType, int Side, bool Transposed>
1926struct permutation_matrix_product<ExpressionType, Side, Transposed, DenseShape> {
1927 using MatrixType = typename nested_eval<ExpressionType, 1>::type;
1928 using MatrixTypeCleaned = remove_all_t<MatrixType>;
1929
1930 template <typename Dest, typename PermutationType>
1931 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(Dest& dst, const PermutationType& perm,
1932 const ExpressionType& xpr) {
1933 MatrixType mat(xpr);
1934 const Index n = Side == OnTheLeft ? mat.rows() : mat.cols();
1935 // FIXME we need an is_same for expression that is not sensitive to constness. For instance
1936 // is_same_xpr<Block<const Matrix>, Block<Matrix> >::value should be true.
1937 // if(std::is_same<MatrixTypeCleaned,Dest>::value && extract_data(dst) == extract_data(mat))
1938 if (is_same_dense(dst, mat)) {
1939 // apply the permutation inplace
1940 Matrix<bool, PermutationType::RowsAtCompileTime, 1, 0, PermutationType::MaxRowsAtCompileTime> mask(perm.size());
1941 mask.fill(false);
1942 Index r = 0;
1943 while (r < perm.size()) {
1944 // search for the next seed
1945 while (r < perm.size() && mask[r]) r++;
1946 if (r >= perm.size()) break;
1947 // we got one, let's follow it until we are back to the seed
1948 Index k0 = r++;
1949 Index kPrev = k0;
1950 mask.coeffRef(k0) = true;
1951 for (Index k = perm.indices().coeff(k0); k != k0; k = perm.indices().coeff(k)) {
1952 Block<Dest, Side == OnTheLeft ? 1 : Dest::RowsAtCompileTime,
1953 Side == OnTheRight ? 1 : Dest::ColsAtCompileTime>(dst, k)
1954 .swap(Block < Dest, Side == OnTheLeft ? 1 : Dest::RowsAtCompileTime,
1955 Side == OnTheRight
1956 ? 1
1957 : Dest::ColsAtCompileTime > (dst, ((Side == OnTheLeft) ^ Transposed) ? k0 : kPrev));
1958
1959 mask.coeffRef(k) = true;
1960 kPrev = k;
1961 }
1962 }
1963 } else {
1964 for (Index i = 0; i < n; ++i) {
1965 Block<Dest, Side == OnTheLeft ? 1 : Dest::RowsAtCompileTime, Side == OnTheRight ? 1 : Dest::ColsAtCompileTime>(
1966 dst, ((Side == OnTheLeft) ^ Transposed) ? perm.indices().coeff(i) : i)
1967
1968 =
1969
1970 Block < const MatrixTypeCleaned,
1971 Side == OnTheLeft ? 1 : MatrixTypeCleaned::RowsAtCompileTime,
1972 Side == OnTheRight ? 1
1973 : MatrixTypeCleaned::ColsAtCompileTime >
1974 (mat, ((Side == OnTheRight) ^ Transposed) ? perm.indices().coeff(i) : i);
1975 }
1976 }
1977 }
1978};
1979
1985template <typename ExpressionType, int Side, bool Transposed>
1986struct permutation_densified_matrix_product {
1987 using DenseType = typename ExpressionType::DenseMatrixType;
1988
1989 template <typename Dest, typename PermutationType>
1990 static EIGEN_DEVICE_FUNC void run(Dest& dst, const PermutationType& perm, const ExpressionType& xpr) {
1991 const DenseType dense(xpr);
1992 permutation_matrix_product<DenseType, Side, Transposed, DenseShape>::run(dst, perm, dense);
1993 }
1994};
1995
1996template <typename ExpressionType, int Side, bool Transposed>
1997struct permutation_matrix_product<ExpressionType, Side, Transposed, TriangularShape>
1998 : permutation_densified_matrix_product<ExpressionType, Side, Transposed> {};
1999
2000template <typename ExpressionType, int Side, bool Transposed>
2001struct permutation_matrix_product<ExpressionType, Side, Transposed, SelfAdjointShape>
2002 : permutation_densified_matrix_product<ExpressionType, Side, Transposed> {};
2003
2004template <typename Lhs, typename Rhs, int ProductTag, typename MatrixShape>
2005struct generic_product_impl<Lhs, Rhs, PermutationShape, MatrixShape, ProductTag> {
2006 template <typename Dest>
2007 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void evalTo(Dest& dst, const Lhs& lhs, const Rhs& rhs) {
2008 permutation_matrix_product<Rhs, OnTheLeft, false, MatrixShape>::run(dst, lhs, rhs);
2009 }
2010};
2011
2012template <typename Lhs, typename Rhs, int ProductTag, typename MatrixShape>
2013struct generic_product_impl<Lhs, Rhs, MatrixShape, PermutationShape, ProductTag> {
2014 template <typename Dest>
2015 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void evalTo(Dest& dst, const Lhs& lhs, const Rhs& rhs) {
2016 permutation_matrix_product<Lhs, OnTheRight, false, MatrixShape>::run(dst, rhs, lhs);
2017 }
2018};
2019
2020template <typename Lhs, typename Rhs, int ProductTag, typename MatrixShape>
2021struct generic_product_impl<Inverse<Lhs>, Rhs, PermutationShape, MatrixShape, ProductTag> {
2022 template <typename Dest>
2023 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void evalTo(Dest& dst, const Inverse<Lhs>& lhs, const Rhs& rhs) {
2024 permutation_matrix_product<Rhs, OnTheLeft, true, MatrixShape>::run(dst, lhs.nestedExpression(), rhs);
2025 }
2026};
2027
2028template <typename Lhs, typename Rhs, int ProductTag, typename MatrixShape>
2029struct generic_product_impl<Lhs, Inverse<Rhs>, MatrixShape, PermutationShape, ProductTag> {
2030 template <typename Dest>
2031 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void evalTo(Dest& dst, const Lhs& lhs, const Inverse<Rhs>& rhs) {
2032 permutation_matrix_product<Lhs, OnTheRight, true, MatrixShape>::run(dst, rhs.nestedExpression(), lhs);
2033 }
2034};
2035
2036/***************************************************************************
2037 * Products with transpositions matrices
2038 ***************************************************************************/
2039
2040// FIXME: consider unifying Transpositions and Permutation into a single shape.
2041
2046template <typename ExpressionType, int Side, bool Transposed, typename ExpressionShape>
2047struct transposition_matrix_product {
2048 using MatrixType = typename nested_eval<ExpressionType, 1>::type;
2049 using MatrixTypeCleaned = remove_all_t<MatrixType>;
2050
2051 template <typename Dest, typename TranspositionType>
2052 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(Dest& dst, const TranspositionType& tr,
2053 const ExpressionType& xpr) {
2054 // The in-place row/column swaps below need a dense operand.
2055 static_assert(std::is_same<ExpressionShape, DenseShape>::value,
2056 "TRANSPOSITIONS_PRODUCTS_ARE_ONLY_IMPLEMENTED_FOR_DENSE_OPERANDS");
2057 MatrixType mat(xpr);
2058 using StorageIndex = typename TranspositionType::StorageIndex;
2059 const Index size = tr.size();
2060 StorageIndex j = 0;
2061
2062 if (!is_same_dense(dst, mat)) dst = mat;
2063
2064 for (Index k = (Transposed ? size - 1 : 0); Transposed ? k >= 0 : k < size; Transposed ? --k : ++k)
2065 if (Index(j = tr.coeff(k)) != k) {
2066 EIGEN_IF_CONSTEXPR (Side == OnTheLeft) {
2067 dst.row(k).swap(dst.row(j));
2068 } else EIGEN_IF_CONSTEXPR (Side == OnTheRight) {
2069 dst.col(k).swap(dst.col(j));
2070 }
2071 }
2072 }
2073};
2074
2075template <typename Lhs, typename Rhs, int ProductTag, typename MatrixShape>
2076struct generic_product_impl<Lhs, Rhs, TranspositionsShape, MatrixShape, ProductTag> {
2077 template <typename Dest>
2078 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void evalTo(Dest& dst, const Lhs& lhs, const Rhs& rhs) {
2079 transposition_matrix_product<Rhs, OnTheLeft, false, MatrixShape>::run(dst, lhs, rhs);
2080 }
2081};
2082
2083template <typename Lhs, typename Rhs, int ProductTag, typename MatrixShape>
2084struct generic_product_impl<Lhs, Rhs, MatrixShape, TranspositionsShape, ProductTag> {
2085 template <typename Dest>
2086 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void evalTo(Dest& dst, const Lhs& lhs, const Rhs& rhs) {
2087 transposition_matrix_product<Lhs, OnTheRight, false, MatrixShape>::run(dst, rhs, lhs);
2088 }
2089};
2090
2091template <typename Lhs, typename Rhs, int ProductTag, typename MatrixShape>
2092struct generic_product_impl<Transpose<Lhs>, Rhs, TranspositionsShape, MatrixShape, ProductTag> {
2093 template <typename Dest>
2094 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void evalTo(Dest& dst, const Transpose<Lhs>& lhs, const Rhs& rhs) {
2095 transposition_matrix_product<Rhs, OnTheLeft, true, MatrixShape>::run(dst, lhs.nestedExpression(), rhs);
2096 }
2097};
2098
2099template <typename Lhs, typename Rhs, int ProductTag, typename MatrixShape>
2100struct generic_product_impl<Lhs, Transpose<Rhs>, MatrixShape, TranspositionsShape, ProductTag> {
2101 template <typename Dest>
2102 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void evalTo(Dest& dst, const Lhs& lhs, const Transpose<Rhs>& rhs) {
2103 transposition_matrix_product<Lhs, OnTheRight, true, MatrixShape>::run(dst, rhs.nestedExpression(), lhs);
2104 }
2105};
2106
2107/***************************************************************************
2108 * skew symmetric products
2109 * The 3x3 skew-symmetric operand is densified and the product is re-dispatched on the other
2110 * operand's shape, so shapes whose products with a dense matrix are evaluator-only (DiagonalShape)
2111 * resolve too.
2112 ***************************************************************************/
2113template <typename Lhs, typename Rhs, int ProductTag, typename MatrixShape>
2114struct generic_product_impl<Lhs, Rhs, SkewSymmetricShape, MatrixShape, ProductTag> {
2115 template <typename Dest>
2116 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void evalTo(Dest& dst, const Lhs& lhs, const Rhs& rhs) {
2117 call_assignment_no_alias(dst, typename Lhs::DenseMatrixType(lhs) * rhs);
2118 }
2119};
2120
2121template <typename Lhs, typename Rhs, int ProductTag, typename MatrixShape>
2122struct generic_product_impl<Lhs, Rhs, MatrixShape, SkewSymmetricShape, ProductTag> {
2123 template <typename Dest>
2124 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void evalTo(Dest& dst, const Lhs& lhs, const Rhs& rhs) {
2125 call_assignment_no_alias(dst, lhs * typename Rhs::DenseMatrixType(rhs));
2126 }
2127};
2128
2129template <typename Lhs, typename Rhs, int ProductTag>
2130struct generic_product_impl<Lhs, Rhs, SkewSymmetricShape, SkewSymmetricShape, ProductTag> {
2131 template <typename Dest>
2132 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void evalTo(Dest& dst, const Lhs& lhs, const Rhs& rhs) {
2133 generic_product_impl<typename Lhs::DenseMatrixType, typename Rhs::DenseMatrixType, DenseShape, DenseShape,
2134 ProductTag>::evalTo(dst, lhs, rhs);
2135 }
2136};
2137
2138template <typename Lhs, typename Rhs, int ProductTag, typename MatrixShape>
2139struct generic_product_impl<Lhs, Rhs, MatrixShape, HomogeneousShape, ProductTag>
2140 : generic_product_impl<Lhs, typename Rhs::PlainObject, MatrixShape, DenseShape, ProductTag> {};
2141
2142template <typename Lhs, typename Rhs, int ProductTag, typename MatrixShape>
2143struct generic_product_impl<Lhs, Rhs, HomogeneousShape, MatrixShape, ProductTag>
2144 : generic_product_impl<typename Lhs::PlainObject, Rhs, DenseShape, MatrixShape, ProductTag> {};
2145
2146template <typename Lhs, typename Rhs, int ProductTag>
2147struct generic_product_impl<Lhs, Rhs, PermutationShape, HomogeneousShape, ProductTag>
2148 : generic_product_impl<Lhs, Rhs, PermutationShape, DenseShape, ProductTag> {};
2149
2150template <typename Lhs, typename Rhs, int ProductTag>
2151struct generic_product_impl<Lhs, Rhs, HomogeneousShape, PermutationShape, ProductTag>
2152 : generic_product_impl<Lhs, Rhs, DenseShape, PermutationShape, ProductTag> {};
2153
2154} // end namespace internal
2155
2156} // end namespace Eigen
2157
2158#if EIGEN_COMP_MSVC
2159#pragma warning(pop)
2160#endif
2161
2162#endif // EIGEN_PRODUCTEVALUATORS_H
@ UnitDiag
Definition Constants.h:216
@ ZeroDiag
Definition Constants.h:218
@ Lower
Definition Constants.h:212
@ Upper
Definition Constants.h:214
@ ColMajor
Definition Constants.h:319
@ RowMajor
Definition Constants.h:321
@ OnTheLeft
Definition Constants.h:332
@ OnTheRight
Definition Constants.h:334
constexpr unsigned int ActualPacketAccessBit
Definition Constants.h:109
constexpr unsigned int PacketAccessBit
Definition Constants.h:98
constexpr unsigned int DirectAccessBit
Definition Constants.h:160
constexpr unsigned int EvalBeforeNestingBit
Definition Constants.h:75
constexpr unsigned int LinearAccessBit
Definition Constants.h:134
constexpr unsigned int RowMajorBit
Definition Constants.h:71
void operator()(const Dst &dst, const Src &src) const
Definition ProductEvaluators.h:392