Eigen  3.4.90 (git rev 5a9f66fb35d03a4da9ef8976e67a61b30aa16dcf)
 
Loading...
Searching...
No Matches
GeneralProduct.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 <[email protected]>
5// Copyright (C) 2008-2011 Gael Guennebaud <[email protected]>
6//
7// This Source Code Form is subject to the terms of the Mozilla
8// Public License v. 2.0. If a copy of the MPL was not distributed
9// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
10
11#ifndef EIGEN_GENERAL_PRODUCT_H
12#define EIGEN_GENERAL_PRODUCT_H
13
14// IWYU pragma: private
15#include "./InternalHeaderCheck.h"
16
17namespace Eigen {
18
19enum { Large = 2, Small = 3 };
20
21// Define the threshold value to fallback from the generic matrix-matrix product
22// implementation (heavy) to the lightweight coeff-based product one.
23// See generic_product_impl<Lhs,Rhs,DenseShape,DenseShape,GemmProduct>
24// in products/GeneralMatrixMatrix.h for more details.
25// TODO This threshold should also be used in the compile-time selector below.
26#ifndef EIGEN_GEMM_TO_COEFFBASED_THRESHOLD
27// This default value has been obtained on a Haswell architecture.
28#define EIGEN_GEMM_TO_COEFFBASED_THRESHOLD 20
29#endif
30
31namespace internal {
32
33template <int Rows, int Cols, int Depth>
34struct product_type_selector;
35
36template <int Size, int MaxSize>
37struct product_size_category {
38 enum {
39#ifndef EIGEN_GPU_COMPILE_PHASE
40 is_large = MaxSize == Dynamic || Size >= EIGEN_CACHEFRIENDLY_PRODUCT_THRESHOLD ||
41 (Size == Dynamic && MaxSize >= EIGEN_CACHEFRIENDLY_PRODUCT_THRESHOLD),
42#else
43 is_large = 0,
44#endif
45 value = is_large ? Large
46 : Size == 1 ? 1
47 : Small
48 };
49};
50
51template <typename Lhs, typename Rhs>
52struct product_type {
53 typedef remove_all_t<Lhs> Lhs_;
54 typedef remove_all_t<Rhs> Rhs_;
55 enum {
56 MaxRows = traits<Lhs_>::MaxRowsAtCompileTime,
57 Rows = traits<Lhs_>::RowsAtCompileTime,
58 MaxCols = traits<Rhs_>::MaxColsAtCompileTime,
59 Cols = traits<Rhs_>::ColsAtCompileTime,
60 MaxDepth = min_size_prefer_fixed(traits<Lhs_>::MaxColsAtCompileTime, traits<Rhs_>::MaxRowsAtCompileTime),
61 Depth = min_size_prefer_fixed(traits<Lhs_>::ColsAtCompileTime, traits<Rhs_>::RowsAtCompileTime)
62 };
63
64 // the splitting into different lines of code here, introducing the _select enums and the typedef below,
65 // is to work around an internal compiler error with gcc 4.1 and 4.2.
66 private:
67 enum {
68 rows_select = product_size_category<Rows, MaxRows>::value,
69 cols_select = product_size_category<Cols, MaxCols>::value,
70 depth_select = product_size_category<Depth, MaxDepth>::value
71 };
72 typedef product_type_selector<rows_select, cols_select, depth_select> selector;
73
74 public:
75 enum { value = selector::ret, ret = selector::ret };
76#ifdef EIGEN_DEBUG_PRODUCT
77 static void debug() {
78 EIGEN_DEBUG_VAR(Rows);
79 EIGEN_DEBUG_VAR(Cols);
80 EIGEN_DEBUG_VAR(Depth);
81 EIGEN_DEBUG_VAR(rows_select);
82 EIGEN_DEBUG_VAR(cols_select);
83 EIGEN_DEBUG_VAR(depth_select);
84 EIGEN_DEBUG_VAR(value);
85 }
86#endif
87};
88
89/* The following allows to select the kind of product at compile time
90 * based on the three dimensions of the product.
91 * This is a compile time mapping from {1,Small,Large}^3 -> {product types} */
92// FIXME I'm not sure the current mapping is the ideal one.
93template <int M, int N>
94struct product_type_selector<M, N, 1> {
95 enum { ret = OuterProduct };
96};
97template <int M>
98struct product_type_selector<M, 1, 1> {
99 enum { ret = LazyCoeffBasedProductMode };
100};
101template <int N>
102struct product_type_selector<1, N, 1> {
103 enum { ret = LazyCoeffBasedProductMode };
104};
105template <int Depth>
106struct product_type_selector<1, 1, Depth> {
107 enum { ret = InnerProduct };
108};
109template <>
110struct product_type_selector<1, 1, 1> {
111 enum { ret = InnerProduct };
112};
113template <>
114struct product_type_selector<Small, 1, Small> {
115 enum { ret = CoeffBasedProductMode };
116};
117template <>
118struct product_type_selector<1, Small, Small> {
119 enum { ret = CoeffBasedProductMode };
120};
121template <>
122struct product_type_selector<Small, Small, Small> {
123 enum { ret = CoeffBasedProductMode };
124};
125template <>
126struct product_type_selector<Small, Small, 1> {
127 enum { ret = LazyCoeffBasedProductMode };
128};
129template <>
130struct product_type_selector<Small, Large, 1> {
131 enum { ret = LazyCoeffBasedProductMode };
132};
133template <>
134struct product_type_selector<Large, Small, 1> {
135 enum { ret = LazyCoeffBasedProductMode };
136};
137template <>
138struct product_type_selector<1, Large, Small> {
139 enum { ret = CoeffBasedProductMode };
140};
141template <>
142struct product_type_selector<1, Large, Large> {
143 enum { ret = GemvProduct };
144};
145template <>
146struct product_type_selector<1, Small, Large> {
147 enum { ret = CoeffBasedProductMode };
148};
149template <>
150struct product_type_selector<Large, 1, Small> {
151 enum { ret = CoeffBasedProductMode };
152};
153template <>
154struct product_type_selector<Large, 1, Large> {
155 enum { ret = GemvProduct };
156};
157template <>
158struct product_type_selector<Small, 1, Large> {
159 enum { ret = CoeffBasedProductMode };
160};
161template <>
162struct product_type_selector<Small, Small, Large> {
163 enum { ret = GemmProduct };
164};
165template <>
166struct product_type_selector<Large, Small, Large> {
167 enum { ret = GemmProduct };
168};
169template <>
170struct product_type_selector<Small, Large, Large> {
171 enum { ret = GemmProduct };
172};
173template <>
174struct product_type_selector<Large, Large, Large> {
175 enum { ret = GemmProduct };
176};
177template <>
178struct product_type_selector<Large, Small, Small> {
179 enum { ret = CoeffBasedProductMode };
180};
181template <>
182struct product_type_selector<Small, Large, Small> {
183 enum { ret = CoeffBasedProductMode };
184};
185template <>
186struct product_type_selector<Large, Large, Small> {
187 enum { ret = GemmProduct };
188};
189
190} // end namespace internal
191
192/***********************************************************************
193 * Implementation of Inner Vector Vector Product
194 ***********************************************************************/
195
196// FIXME : maybe the "inner product" could return a Scalar
197// instead of a 1x1 matrix ??
198// Pro: more natural for the user
199// Cons: this could be a problem if in a meta unrolled algorithm a matrix-matrix
200// product ends up to a row-vector times col-vector product... To tackle this use
201// case, we could have a specialization for Block<MatrixType,1,1> with: operator=(Scalar x);
202
203/***********************************************************************
204 * Implementation of Outer Vector Vector Product
205 ***********************************************************************/
206
207/***********************************************************************
208 * Implementation of General Matrix Vector Product
209 ***********************************************************************/
210
211/* According to the shape/flags of the matrix we have to distinghish 3 different cases:
212 * 1 - the matrix is col-major, BLAS compatible and M is large => call fast BLAS-like colmajor routine
213 * 2 - the matrix is row-major, BLAS compatible and N is large => call fast BLAS-like rowmajor routine
214 * 3 - all other cases are handled using a simple loop along the outer-storage direction.
215 * Therefore we need a lower level meta selector.
216 * Furthermore, if the matrix is the rhs, then the product has to be transposed.
217 */
218namespace internal {
219
220template <int Side, int StorageOrder, bool BlasCompatible>
221struct gemv_dense_selector;
222
223} // end namespace internal
224
225namespace internal {
226
227template <typename Scalar, int Size, int MaxSize, bool Cond>
228struct gemv_static_vector_if;
229
230template <typename Scalar, int Size, int MaxSize>
231struct gemv_static_vector_if<Scalar, Size, MaxSize, false> {
232 EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC Scalar* data() {
233 eigen_internal_assert(false && "should never be called");
234 return 0;
235 }
236};
237
238template <typename Scalar, int Size>
239struct gemv_static_vector_if<Scalar, Size, Dynamic, true> {
240 EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC Scalar* data() { return 0; }
241};
242
243template <typename Scalar, int Size, int MaxSize>
244struct gemv_static_vector_if<Scalar, Size, MaxSize, true> {
245#if EIGEN_MAX_STATIC_ALIGN_BYTES != 0
246 internal::plain_array<Scalar, internal::min_size_prefer_fixed(Size, MaxSize), 0, AlignedMax> m_data;
247 EIGEN_STRONG_INLINE Scalar* data() { return m_data.array; }
248#else
249 // Some architectures cannot align on the stack,
250 // => let's manually enforce alignment by allocating more data and return the address of the first aligned element.
251 internal::plain_array<Scalar, internal::min_size_prefer_fixed(Size, MaxSize) + EIGEN_MAX_ALIGN_BYTES, 0> m_data;
252 EIGEN_STRONG_INLINE Scalar* data() {
253 return reinterpret_cast<Scalar*>((std::uintptr_t(m_data.array) & ~(std::size_t(EIGEN_MAX_ALIGN_BYTES - 1))) +
254 EIGEN_MAX_ALIGN_BYTES);
255 }
256#endif
257};
258
259// The vector is on the left => transposition
260template <int StorageOrder, bool BlasCompatible>
261struct gemv_dense_selector<OnTheLeft, StorageOrder, BlasCompatible> {
262 template <typename Lhs, typename Rhs, typename Dest>
263 static void run(const Lhs& lhs, const Rhs& rhs, Dest& dest, const typename Dest::Scalar& alpha) {
264 Transpose<Dest> destT(dest);
265 enum { OtherStorageOrder = StorageOrder == RowMajor ? ColMajor : RowMajor };
266 gemv_dense_selector<OnTheRight, OtherStorageOrder, BlasCompatible>::run(rhs.transpose(), lhs.transpose(), destT,
267 alpha);
268 }
269};
270
271template <>
272struct gemv_dense_selector<OnTheRight, ColMajor, true> {
273 template <typename Lhs, typename Rhs, typename Dest>
274 static inline void run(const Lhs& lhs, const Rhs& rhs, Dest& dest, const typename Dest::Scalar& alpha) {
275 typedef typename Lhs::Scalar LhsScalar;
276 typedef typename Rhs::Scalar RhsScalar;
277 typedef typename Dest::Scalar ResScalar;
278
279 typedef internal::blas_traits<Lhs> LhsBlasTraits;
280 typedef typename LhsBlasTraits::DirectLinearAccessType ActualLhsType;
281 typedef internal::blas_traits<Rhs> RhsBlasTraits;
282 typedef typename RhsBlasTraits::DirectLinearAccessType ActualRhsType;
283
284 typedef Map<Matrix<ResScalar, Dynamic, 1>, plain_enum_min(AlignedMax, internal::packet_traits<ResScalar>::size)>
285 MappedDest;
286
287 ActualLhsType actualLhs = LhsBlasTraits::extract(lhs);
288 ActualRhsType actualRhs = RhsBlasTraits::extract(rhs);
289
290 ResScalar actualAlpha = combine_scalar_factors(alpha, lhs, rhs);
291
292 // make sure Dest is a compile-time vector type (bug 1166)
293 typedef std::conditional_t<Dest::IsVectorAtCompileTime, Dest, typename Dest::ColXpr> ActualDest;
294
295 enum {
296 // FIXME find a way to allow an inner stride on the result if packet_traits<Scalar>::size==1
297 // on, the other hand it is good for the cache to pack the vector anyways...
298 EvalToDestAtCompileTime = (ActualDest::InnerStrideAtCompileTime == 1),
299 ComplexByReal = (NumTraits<LhsScalar>::IsComplex) && (!NumTraits<RhsScalar>::IsComplex),
300 MightCannotUseDest = ((!EvalToDestAtCompileTime) || ComplexByReal) && (ActualDest::MaxSizeAtCompileTime != 0)
301 };
302
303 typedef const_blas_data_mapper<LhsScalar, Index, ColMajor> LhsMapper;
304 typedef const_blas_data_mapper<RhsScalar, Index, RowMajor> RhsMapper;
305 RhsScalar compatibleAlpha = get_factor<ResScalar, RhsScalar>::run(actualAlpha);
306
307 if (!MightCannotUseDest) {
308 // shortcut if we are sure to be able to use dest directly,
309 // this ease the compiler to generate cleaner and more optimzized code for most common cases
310 general_matrix_vector_product<Index, LhsScalar, LhsMapper, ColMajor, LhsBlasTraits::NeedToConjugate, RhsScalar,
311 RhsMapper, RhsBlasTraits::NeedToConjugate>::run(actualLhs.rows(), actualLhs.cols(),
312 LhsMapper(actualLhs.data(),
313 actualLhs.outerStride()),
314 RhsMapper(actualRhs.data(),
315 actualRhs.innerStride()),
316 dest.data(), 1, compatibleAlpha);
317 } else {
318 gemv_static_vector_if<ResScalar, ActualDest::SizeAtCompileTime, ActualDest::MaxSizeAtCompileTime,
319 MightCannotUseDest>
320 static_dest;
321
322 const bool alphaIsCompatible = (!ComplexByReal) || (numext::is_exactly_zero(numext::imag(actualAlpha)));
323 const bool evalToDest = EvalToDestAtCompileTime && alphaIsCompatible;
324
325 ei_declare_aligned_stack_constructed_variable(ResScalar, actualDestPtr, dest.size(),
326 evalToDest ? dest.data() : static_dest.data());
327
328 if (!evalToDest) {
329#ifdef EIGEN_DENSE_STORAGE_CTOR_PLUGIN
330 Index size = dest.size();
331 EIGEN_DENSE_STORAGE_CTOR_PLUGIN
332#endif
333 if (!alphaIsCompatible) {
334 MappedDest(actualDestPtr, dest.size()).setZero();
335 compatibleAlpha = RhsScalar(1);
336 } else
337 MappedDest(actualDestPtr, dest.size()) = dest;
338 }
339
340 general_matrix_vector_product<Index, LhsScalar, LhsMapper, ColMajor, LhsBlasTraits::NeedToConjugate, RhsScalar,
341 RhsMapper, RhsBlasTraits::NeedToConjugate>::run(actualLhs.rows(), actualLhs.cols(),
342 LhsMapper(actualLhs.data(),
343 actualLhs.outerStride()),
344 RhsMapper(actualRhs.data(),
345 actualRhs.innerStride()),
346 actualDestPtr, 1, compatibleAlpha);
347
348 if (!evalToDest) {
349 if (!alphaIsCompatible)
350 dest.matrix() += actualAlpha * MappedDest(actualDestPtr, dest.size());
351 else
352 dest = MappedDest(actualDestPtr, dest.size());
353 }
354 }
355 }
356};
357
358template <>
359struct gemv_dense_selector<OnTheRight, RowMajor, true> {
360 template <typename Lhs, typename Rhs, typename Dest>
361 static void run(const Lhs& lhs, const Rhs& rhs, Dest& dest, const typename Dest::Scalar& alpha) {
362 typedef typename Lhs::Scalar LhsScalar;
363 typedef typename Rhs::Scalar RhsScalar;
364 typedef typename Dest::Scalar ResScalar;
365
366 typedef internal::blas_traits<Lhs> LhsBlasTraits;
367 typedef typename LhsBlasTraits::DirectLinearAccessType ActualLhsType;
368 typedef internal::blas_traits<Rhs> RhsBlasTraits;
369 typedef typename RhsBlasTraits::DirectLinearAccessType ActualRhsType;
370 typedef internal::remove_all_t<ActualRhsType> ActualRhsTypeCleaned;
371
372 std::add_const_t<ActualLhsType> actualLhs = LhsBlasTraits::extract(lhs);
373 std::add_const_t<ActualRhsType> actualRhs = RhsBlasTraits::extract(rhs);
374
375 ResScalar actualAlpha = combine_scalar_factors(alpha, lhs, rhs);
376
377 enum {
378 // FIXME find a way to allow an inner stride on the result if packet_traits<Scalar>::size==1
379 // on, the other hand it is good for the cache to pack the vector anyways...
380 DirectlyUseRhs =
381 ActualRhsTypeCleaned::InnerStrideAtCompileTime == 1 || ActualRhsTypeCleaned::MaxSizeAtCompileTime == 0
382 };
383
384 gemv_static_vector_if<RhsScalar, ActualRhsTypeCleaned::SizeAtCompileTime,
385 ActualRhsTypeCleaned::MaxSizeAtCompileTime, !DirectlyUseRhs>
386 static_rhs;
387
388 ei_declare_aligned_stack_constructed_variable(
389 RhsScalar, actualRhsPtr, actualRhs.size(),
390 DirectlyUseRhs ? const_cast<RhsScalar*>(actualRhs.data()) : static_rhs.data());
391
392 if (!DirectlyUseRhs) {
393#ifdef EIGEN_DENSE_STORAGE_CTOR_PLUGIN
394 Index size = actualRhs.size();
395 EIGEN_DENSE_STORAGE_CTOR_PLUGIN
396#endif
397 Map<typename ActualRhsTypeCleaned::PlainObject>(actualRhsPtr, actualRhs.size()) = actualRhs;
398 }
399
400 typedef const_blas_data_mapper<LhsScalar, Index, RowMajor> LhsMapper;
401 typedef const_blas_data_mapper<RhsScalar, Index, ColMajor> RhsMapper;
402 general_matrix_vector_product<Index, LhsScalar, LhsMapper, RowMajor, LhsBlasTraits::NeedToConjugate, RhsScalar,
403 RhsMapper, RhsBlasTraits::NeedToConjugate>::
404 run(actualLhs.rows(), actualLhs.cols(), LhsMapper(actualLhs.data(), actualLhs.outerStride()),
405 RhsMapper(actualRhsPtr, 1), dest.data(),
406 dest.col(0).innerStride(), // NOTE if dest is not a vector at compile-time, then dest.innerStride() might
407 // be wrong. (bug 1166)
408 actualAlpha);
409 }
410};
411
412template <>
413struct gemv_dense_selector<OnTheRight, ColMajor, false> {
414 template <typename Lhs, typename Rhs, typename Dest>
415 static void run(const Lhs& lhs, const Rhs& rhs, Dest& dest, const typename Dest::Scalar& alpha) {
416 EIGEN_STATIC_ASSERT((!nested_eval<Lhs, 1>::Evaluate),
417 EIGEN_INTERNAL_COMPILATION_ERROR_OR_YOU_MADE_A_PROGRAMMING_MISTAKE);
418 // TODO if rhs is large enough it might be beneficial to make sure that dest is sequentially stored in memory,
419 // otherwise use a temp
420 typename nested_eval<Rhs, 1>::type actual_rhs(rhs);
421 const Index size = rhs.rows();
422 for (Index k = 0; k < size; ++k) dest += (alpha * actual_rhs.coeff(k)) * lhs.col(k);
423 }
424};
425
426template <>
427struct gemv_dense_selector<OnTheRight, RowMajor, false> {
428 template <typename Lhs, typename Rhs, typename Dest>
429 static void run(const Lhs& lhs, const Rhs& rhs, Dest& dest, const typename Dest::Scalar& alpha) {
430 EIGEN_STATIC_ASSERT((!nested_eval<Lhs, 1>::Evaluate),
431 EIGEN_INTERNAL_COMPILATION_ERROR_OR_YOU_MADE_A_PROGRAMMING_MISTAKE);
432 typename nested_eval<Rhs, Lhs::RowsAtCompileTime>::type actual_rhs(rhs);
433 const Index rows = dest.rows();
434 for (Index i = 0; i < rows; ++i)
435 dest.coeffRef(i) += alpha * (lhs.row(i).cwiseProduct(actual_rhs.transpose())).sum();
436 }
437};
438
439} // end namespace internal
440
441/***************************************************************************
442 * Implementation of matrix base methods
443 ***************************************************************************/
444
451template <typename Derived>
452template <typename OtherDerived>
453EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Product<Derived, OtherDerived> MatrixBase<Derived>::operator*(
454 const MatrixBase<OtherDerived>& other) const {
455 // A note regarding the function declaration: In MSVC, this function will sometimes
456 // not be inlined since DenseStorage is an unwindable object for dynamic
457 // matrices and product types are holding a member to store the result.
458 // Thus it does not help tagging this function with EIGEN_STRONG_INLINE.
459 enum {
460 ProductIsValid = Derived::ColsAtCompileTime == Dynamic || OtherDerived::RowsAtCompileTime == Dynamic ||
461 int(Derived::ColsAtCompileTime) == int(OtherDerived::RowsAtCompileTime),
462 AreVectors = Derived::IsVectorAtCompileTime && OtherDerived::IsVectorAtCompileTime,
463 SameSizes = EIGEN_PREDICATE_SAME_MATRIX_SIZE(Derived, OtherDerived)
464 };
465 // note to the lost user:
466 // * for a dot product use: v1.dot(v2)
467 // * for a coeff-wise product use: v1.cwiseProduct(v2)
468 EIGEN_STATIC_ASSERT(
469 ProductIsValid || !(AreVectors && SameSizes),
470 INVALID_VECTOR_VECTOR_PRODUCT__IF_YOU_WANTED_A_DOT_OR_COEFF_WISE_PRODUCT_YOU_MUST_USE_THE_EXPLICIT_FUNCTIONS)
471 EIGEN_STATIC_ASSERT(ProductIsValid || !(SameSizes && !AreVectors),
472 INVALID_MATRIX_PRODUCT__IF_YOU_WANTED_A_COEFF_WISE_PRODUCT_YOU_MUST_USE_THE_EXPLICIT_FUNCTION)
473 EIGEN_STATIC_ASSERT(ProductIsValid || SameSizes, INVALID_MATRIX_PRODUCT)
474#ifdef EIGEN_DEBUG_PRODUCT
475 internal::product_type<Derived, OtherDerived>::debug();
476#endif
477
478 return Product<Derived, OtherDerived>(derived(), other.derived());
479}
480
492template <typename Derived>
493template <typename OtherDerived>
494EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Product<Derived, OtherDerived, LazyProduct>
496 enum {
497 ProductIsValid = Derived::ColsAtCompileTime == Dynamic || OtherDerived::RowsAtCompileTime == Dynamic ||
498 int(Derived::ColsAtCompileTime) == int(OtherDerived::RowsAtCompileTime),
499 AreVectors = Derived::IsVectorAtCompileTime && OtherDerived::IsVectorAtCompileTime,
500 SameSizes = EIGEN_PREDICATE_SAME_MATRIX_SIZE(Derived, OtherDerived)
501 };
502 // note to the lost user:
503 // * for a dot product use: v1.dot(v2)
504 // * for a coeff-wise product use: v1.cwiseProduct(v2)
505 EIGEN_STATIC_ASSERT(
506 ProductIsValid || !(AreVectors && SameSizes),
507 INVALID_VECTOR_VECTOR_PRODUCT__IF_YOU_WANTED_A_DOT_OR_COEFF_WISE_PRODUCT_YOU_MUST_USE_THE_EXPLICIT_FUNCTIONS)
508 EIGEN_STATIC_ASSERT(ProductIsValid || !(SameSizes && !AreVectors),
509 INVALID_MATRIX_PRODUCT__IF_YOU_WANTED_A_COEFF_WISE_PRODUCT_YOU_MUST_USE_THE_EXPLICIT_FUNCTION)
510 EIGEN_STATIC_ASSERT(ProductIsValid || SameSizes, INVALID_MATRIX_PRODUCT)
511
512 return Product<Derived, OtherDerived, LazyProduct>(derived(), other.derived());
513}
514
515} // end namespace Eigen
516
517#endif // EIGEN_PRODUCT_H
Derived & derived()
Definition EigenBase.h:49
Base class for all dense matrices, vectors, and expressions.
Definition MatrixBase.h:52
const Product< Derived, OtherDerived, LazyProduct > lazyProduct(const MatrixBase< OtherDerived > &other) const
Definition GeneralProduct.h:495
const Product< Derived, OtherDerived > operator*(const MatrixBase< OtherDerived > &other) const
Definition GeneralProduct.h:453
Expression of the product of two arbitrary matrices or vectors.
Definition Product.h:202
@ ColMajor
Definition Constants.h:318
@ RowMajor
Definition Constants.h:320
@ OnTheLeft
Definition Constants.h:331
@ OnTheRight
Definition Constants.h:333
Namespace containing all symbols from the Eigen library.
Definition Core:137
const int Dynamic
Definition Constants.h:25