Eigen-Contrib  5.0.1
 
Loading...
Searching...
No Matches
TensorEvaluator.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
5//
6// This Source Code Form is subject to the terms of the Mozilla
7// Public License v. 2.0. If a copy of the MPL was not distributed
8// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9// SPDX-License-Identifier: MPL-2.0
10
11#ifndef EIGEN_TENSOR_TENSOR_EVALUATOR_H
12#define EIGEN_TENSOR_TENSOR_EVALUATOR_H
13
14// IWYU pragma: private
15#include "./InternalHeaderCheck.h"
16
17namespace Eigen {
18
19namespace internal {
20
21// Whether functor_traits declare the functor repeatable: pure calls that may
22// be re-evaluated freely and invoked from concurrent threads. The primary
23// functor_traits template defaults IsRepeatable to false, but specializations
24// for custom functors routinely omit the member, so absence must also map to
25// the conservative default.
26template <typename Functor, typename = void>
27struct tensor_functor_is_repeatable : std::false_type {};
28
29template <typename Functor>
30struct tensor_functor_is_repeatable<Functor, void_t<decltype(functor_traits<Functor>::IsRepeatable)>>
31 : bool_constant<functor_traits<Functor>::IsRepeatable> {};
32
33} // namespace internal
34
35// Generic evaluator
46template <typename Derived, typename Device>
47struct TensorEvaluator {
48 typedef typename Derived::Index Index;
49 typedef typename Derived::Scalar Scalar;
50 typedef typename Derived::Scalar CoeffReturnType;
51 typedef typename PacketType<CoeffReturnType, Device>::type PacketReturnType;
52 typedef typename Derived::Dimensions Dimensions;
53 typedef Derived XprType;
54 static constexpr int PacketSize = PacketType<CoeffReturnType, Device>::size;
55 typedef typename internal::traits<Derived>::template MakePointer<Scalar>::Type TensorPointerType;
56 typedef StorageMemory<Scalar, Device> Storage;
57 typedef typename Storage::Type EvaluatorPointerType;
58
59 // NumDimensions is -1 for variable dim tensors
60 static constexpr int NumCoords =
61 internal::traits<Derived>::NumDimensions > 0 ? internal::traits<Derived>::NumDimensions : 0;
62 static constexpr int Layout = Derived::Layout;
63
64 enum {
65 IsAligned = Derived::IsAligned,
66 PacketAccess = (PacketType<CoeffReturnType, Device>::size > 1),
67 BlockAccess = internal::is_arithmetic<std::remove_const_t<Scalar>>::value,
68 PreferBlockAccess = false,
69 CoordAccess = NumCoords > 0,
70 RawAccess = true
71 };
72
73 typedef std::remove_const_t<Scalar> ScalarNoConst;
74
75 //===- Tensor block evaluation strategy (see TensorBlock.h) -------------===//
76 typedef internal::TensorBlockDescriptor<NumCoords, Index> TensorBlockDesc;
77 typedef internal::TensorBlockScratchAllocator<Device> TensorBlockScratch;
78
79 typedef typename internal::TensorMaterializedBlock<ScalarNoConst, NumCoords, Layout, Index> TensorBlock;
80 //===--------------------------------------------------------------------===//
81
82 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorEvaluator(const Derived& m, const Device& device)
83 : m_data(device.get(const_cast<TensorPointerType>(m.data()))), m_dims(m.dimensions()), m_device(device) {}
84
85 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Dimensions& dimensions() const { return m_dims; }
86
87 EIGEN_STRONG_INLINE bool evalSubExprsIfNeeded(EvaluatorPointerType dest) {
88 EIGEN_IF_CONSTEXPR (!NumTraits<std::remove_const_t<Scalar>>::RequireInitialization) {
89 if (dest) {
90 m_device.memcpy((void*)(m_device.get(dest)), m_device.get(m_data), m_dims.TotalSize() * sizeof(Scalar));
91 return false;
92 }
93 }
94 return true;
95 }
96
97#ifdef EIGEN_USE_THREADS
98 template <typename EvalSubExprsCallback>
99 EIGEN_STRONG_INLINE void evalSubExprsIfNeededAsync(EvaluatorPointerType dest, EvalSubExprsCallback done) {
100 // TODO(ezhulenev): ThreadPoolDevice memcpy is a blocking operation.
101 done(evalSubExprsIfNeeded(dest));
102 }
103#endif // EIGEN_USE_THREADS
104
105 EIGEN_STRONG_INLINE void cleanup() {}
106
107 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE CoeffReturnType coeff(Index index) const {
108 eigen_assert(m_data != nullptr);
109 return m_data[index];
110 }
111
112 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE CoeffReturnType& coeffRef(Index index) const {
113 eigen_assert(m_data != nullptr);
114 return m_data[index];
115 }
116
117 template <int LoadMode>
118 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType packet(Index index) const {
119 return internal::ploadt<PacketReturnType, LoadMode>(m_data + index);
120 }
121
122 // Return a packet starting at `index` where `umask` specifies which elements
123 // have to be loaded. Type/size of mask depends on PacketReturnType, e.g. for
124 // Packet16f, `umask` is of type uint16_t and if a bit is 1, corresponding
125 // float element will be loaded, otherwise 0 will be loaded.
126 // Function has been templatized to enable Sfinae.
127 template <typename PacketReturnTypeT>
128 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
129 std::enable_if_t<internal::unpacket_traits<PacketReturnTypeT>::masked_load_available, PacketReturnTypeT>
130 partialPacket(Index index, typename internal::unpacket_traits<PacketReturnTypeT>::mask_t umask) const {
131 return internal::ploadu<PacketReturnTypeT>(m_data + index, umask);
132 }
133
134 template <int StoreMode>
135 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void writePacket(Index index, const PacketReturnType& x) const {
136 return internal::pstoret<Scalar, PacketReturnType, StoreMode>(m_data + index, x);
137 }
138
139 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE CoeffReturnType coeff(const array<DenseIndex, NumCoords>& coords) const {
140 eigen_assert(m_data != nullptr);
141 EIGEN_IF_CONSTEXPR (static_cast<int>(Layout) == static_cast<int>(ColMajor)) {
142 return m_data[m_dims.IndexOfColMajor(coords)];
143 } else {
144 return m_data[m_dims.IndexOfRowMajor(coords)];
145 }
146 }
147
148 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE CoeffReturnType& coeffRef(const array<DenseIndex, NumCoords>& coords) const {
149 eigen_assert(m_data != nullptr);
150 EIGEN_IF_CONSTEXPR (static_cast<int>(Layout) == static_cast<int>(ColMajor)) {
151 return m_data[m_dims.IndexOfColMajor(coords)];
152 } else {
153 return m_data[m_dims.IndexOfRowMajor(coords)];
154 }
155 }
156
157 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorOpCost costPerCoeff(bool vectorized) const {
158 return TensorOpCost(sizeof(CoeffReturnType), 0, 0, vectorized, PacketType<CoeffReturnType, Device>::size);
159 }
160
161 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE internal::TensorBlockResourceRequirements getResourceRequirements() const {
162 return internal::TensorBlockResourceRequirements::any();
163 }
164
165 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorBlock block(TensorBlockDesc& desc, TensorBlockScratch& scratch,
166 bool /*root_of_expr_ast*/ = false) const {
167 eigen_assert(m_data != nullptr);
168 return TensorBlock::materialize(m_data, m_dims, desc, scratch);
169 }
170
171 template <typename TensorBlock>
172 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void writeBlock(const TensorBlockDesc& desc, const TensorBlock& block) {
173 eigen_assert(m_data != nullptr);
174
175 typedef typename TensorBlock::XprType TensorBlockExpr;
176 typedef internal::TensorBlockAssignment<Scalar, NumCoords, TensorBlockExpr, Index> TensorBlockAssign;
177
178 TensorBlockAssign::Run(
179 TensorBlockAssign::target(desc.dimensions(), internal::strides<Layout>(m_dims), m_data, desc.offset()),
180 block.expr());
181 }
182
183 EIGEN_DEVICE_FUNC EvaluatorPointerType data() const { return m_data; }
184
185 protected:
186 EvaluatorPointerType m_data;
187 Dimensions m_dims;
188 const Device EIGEN_DEVICE_REF m_device;
189};
190
191namespace internal {
192template <typename T>
193EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE T loadConstant(const T* address) {
194 return *address;
195}
196// Use the texture cache on CUDA devices whenever possible
197#if defined(EIGEN_CUDA_ARCH)
198template <>
199EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE float loadConstant(const float* address) {
200 return __ldg(address);
201}
202template <>
203EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE double loadConstant(const double* address) {
204 return __ldg(address);
205}
206template <>
207EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Eigen::half loadConstant(const Eigen::half* address) {
208 return Eigen::half(half_impl::raw_uint16_to_half(__ldg(&address->x)));
209}
210#endif
211
212} // namespace internal
213
214// Default evaluator for rvalues
215template <typename Derived, typename Device>
216struct TensorEvaluator<const Derived, Device> {
217 typedef typename Derived::Index Index;
218 typedef typename Derived::Scalar Scalar;
219 typedef typename Derived::Scalar CoeffReturnType;
220 typedef typename PacketType<CoeffReturnType, Device>::type PacketReturnType;
221 typedef typename Derived::Dimensions Dimensions;
222 typedef const Derived XprType;
223 typedef typename internal::traits<Derived>::template MakePointer<const Scalar>::Type TensorPointerType;
224 typedef StorageMemory<const Scalar, Device> Storage;
225 typedef typename Storage::Type EvaluatorPointerType;
226
227 typedef std::remove_const_t<Scalar> ScalarNoConst;
228
229 // NumDimensions is -1 for variable dim tensors
230 static constexpr int NumCoords =
231 internal::traits<Derived>::NumDimensions > 0 ? internal::traits<Derived>::NumDimensions : 0;
232 static constexpr int PacketSize = PacketType<CoeffReturnType, Device>::size;
233 static constexpr int Layout = Derived::Layout;
234
235 enum {
236 IsAligned = Derived::IsAligned,
237 PacketAccess = (PacketType<CoeffReturnType, Device>::size > 1),
238 BlockAccess = internal::is_arithmetic<ScalarNoConst>::value,
239 PreferBlockAccess = false,
240 CoordAccess = NumCoords > 0,
241 RawAccess = true
242 };
243
244 //===- Tensor block evaluation strategy (see TensorBlock.h) -------------===//
245 typedef internal::TensorBlockDescriptor<NumCoords, Index> TensorBlockDesc;
246 typedef internal::TensorBlockScratchAllocator<Device> TensorBlockScratch;
247
248 typedef typename internal::TensorMaterializedBlock<ScalarNoConst, NumCoords, Layout, Index> TensorBlock;
249 //===--------------------------------------------------------------------===//
250
251 EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC TensorEvaluator(const Derived& m, const Device& device)
252 : m_data(device.get(m.data())), m_dims(m.dimensions()), m_device(device) {}
253
254 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Dimensions& dimensions() const { return m_dims; }
255
256 EIGEN_STRONG_INLINE bool evalSubExprsIfNeeded(EvaluatorPointerType data) {
257 EIGEN_IF_CONSTEXPR (!NumTraits<std::remove_const_t<Scalar>>::RequireInitialization) {
258 if (data) {
259 m_device.memcpy((void*)(m_device.get(data)), m_device.get(m_data), m_dims.TotalSize() * sizeof(Scalar));
260 return false;
261 }
262 }
263 return true;
264 }
265
266#ifdef EIGEN_USE_THREADS
267 template <typename EvalSubExprsCallback>
268 EIGEN_STRONG_INLINE void evalSubExprsIfNeededAsync(EvaluatorPointerType dest, EvalSubExprsCallback done) {
269 // TODO(ezhulenev): ThreadPoolDevice memcpy is a blocking operation.
270 done(evalSubExprsIfNeeded(dest));
271 }
272#endif // EIGEN_USE_THREADS
273
274 EIGEN_STRONG_INLINE void cleanup() {}
275
276 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE CoeffReturnType coeff(Index index) const {
277 eigen_assert(m_data != nullptr);
278 return internal::loadConstant(m_data + index);
279 }
280
281 template <int LoadMode>
282 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType packet(Index index) const {
283 return internal::ploadt_ro<PacketReturnType, LoadMode>(m_data + index);
284 }
285
286 // Return a packet starting at `index` where `umask` specifies which elements
287 // have to be loaded. Type/size of mask depends on PacketReturnType, e.g. for
288 // Packet16f, `umask` is of type uint16_t and if a bit is 1, corresponding
289 // float element will be loaded, otherwise 0 will be loaded.
290 // Function has been templatized to enable Sfinae.
291 template <typename PacketReturnTypeT>
292 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
293 std::enable_if_t<internal::unpacket_traits<PacketReturnTypeT>::masked_load_available, PacketReturnTypeT>
294 partialPacket(Index index, typename internal::unpacket_traits<PacketReturnTypeT>::mask_t umask) const {
295 return internal::ploadu<PacketReturnTypeT>(m_data + index, umask);
296 }
297
298 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE CoeffReturnType coeff(const array<DenseIndex, NumCoords>& coords) const {
299 eigen_assert(m_data != nullptr);
300 const Index index = (static_cast<int>(Layout) == static_cast<int>(ColMajor)) ? m_dims.IndexOfColMajor(coords)
301 : m_dims.IndexOfRowMajor(coords);
302 return internal::loadConstant(m_data + index);
303 }
304
305 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorOpCost costPerCoeff(bool vectorized) const {
306 return TensorOpCost(sizeof(CoeffReturnType), 0, 0, vectorized, PacketType<CoeffReturnType, Device>::size);
307 }
308
309 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE internal::TensorBlockResourceRequirements getResourceRequirements() const {
310 return internal::TensorBlockResourceRequirements::any();
311 }
312
313 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorBlock block(TensorBlockDesc& desc, TensorBlockScratch& scratch,
314 bool /*root_of_expr_ast*/ = false) const {
315 eigen_assert(m_data != nullptr);
316 return TensorBlock::materialize(m_data, m_dims, desc, scratch);
317 }
318
319 EIGEN_DEVICE_FUNC EvaluatorPointerType data() const { return m_data; }
320
321 protected:
322 EvaluatorPointerType m_data;
323 Dimensions m_dims;
324 const Device EIGEN_DEVICE_REF m_device;
325};
326
327// -------------------- CwiseNullaryOp --------------------
328
329template <typename NullaryOp, typename ArgType, typename Device>
330struct TensorEvaluator<const TensorCwiseNullaryOp<NullaryOp, ArgType>, Device> {
331 typedef TensorCwiseNullaryOp<NullaryOp, ArgType> XprType;
332
333 EIGEN_DEVICE_FUNC TensorEvaluator(const XprType& op, const Device& device)
334 : m_functor(op.functor()), m_argImpl(op.nestedExpression(), device), m_wrapper() {}
335
336 typedef typename XprType::Index Index;
337 typedef typename XprType::Scalar Scalar;
338 typedef typename internal::traits<XprType>::Scalar CoeffReturnType;
339 typedef typename PacketType<CoeffReturnType, Device>::type PacketReturnType;
340 static constexpr int PacketSize = PacketType<CoeffReturnType, Device>::size;
341 typedef typename TensorEvaluator<ArgType, Device>::Dimensions Dimensions;
342 typedef StorageMemory<CoeffReturnType, Device> Storage;
343 typedef typename Storage::Type EvaluatorPointerType;
344 static constexpr int NumDims = internal::array_size<Dimensions>::value;
345 typedef std::remove_const_t<CoeffReturnType> ScalarNoConst;
346
347 // Only functors whose functor_traits declare IsRepeatable (e.g. the
348 // scalar_constant_op behind constant(), cwiseMax(Scalar) and clip(), and the
349 // index-seeded random generators) are known to be pure; unannotated custom
350 // functors conservatively default to non-repeatable, since a functor that
351 // carries state between calls cannot be re-evaluated or visited out of order.
352 static constexpr bool RepeatableFunctor = internal::tensor_functor_is_repeatable<NullaryOp>::value;
353
354 // nullary_wrapper dispatches to an indexed operator() whenever one exists,
355 // even if a zero-argument overload is also present.
356 static constexpr bool IndexDependentFunctor =
357 internal::has_unary_operator<NullaryOp, Index>::value || internal::has_binary_operator<NullaryOp, Index>::value;
358
359 // A lazy block rebuilds the nullary expression over the block's local
360 // extent with a copy of the functor, so it is only correct for repeatable
361 // functors evaluated through the zero-argument overload: an index-dependent
362 // functor would see indices restart at the block origin, and a
363 // non-repeatable one would restart its state per block. Repeatable indexed
364 // functors are instead materialized with their true tensor-linear indices.
365 static constexpr bool IndexIndependentFunctor =
366 RepeatableFunctor && !IndexDependentFunctor && internal::has_nullary_operator<NullaryOp, Index>::value;
367
368 static constexpr int Layout = TensorEvaluator<ArgType, Device>::Layout;
369 enum {
370 IsAligned = true,
371 PacketAccess = internal::functor_traits<NullaryOp>::PacketAccess
372#ifdef EIGEN_USE_SYCL
373 && (PacketType<CoeffReturnType, Device>::size > 1)
374#endif
375 ,
376 // A nullary leaf can serve any block; without this, a single constant()
377 // in an expression disables tiled evaluation for the whole tree. Never
378 // *prefer* block access on its own account, though. Blocks are declined
379 // when they could change behavior relative to coefficient evaluation. In
380 // particular, non-repeatable functors never serve blocks: even on a
381 // single-threaded device, block traversal permutes their call sequence
382 // relative to linear coefficient order.
383 BlockAccess = NumDims > 0 && internal::is_arithmetic<ScalarNoConst>::value &&
384 (IndexIndependentFunctor || (IndexDependentFunctor && RepeatableFunctor)),
385 PreferBlockAccess = false,
386 CoordAccess = false, // to be implemented
387 RawAccess = false
388 };
389
390 //===- Tensor block evaluation strategy (see TensorBlock.h) -------------===//
391 typedef internal::TensorBlockDescriptor<NumDims, Index> TensorBlockDesc;
392 typedef internal::TensorBlockScratchAllocator<Device> TensorBlockScratch;
393
394 // Lazy block for index-independent functors: rebuilds the nullary
395 // expression over the block extent. The nested map exists only to give the
396 // expression its dimensions; the nullary evaluator never touches its
397 // argument's data.
398 class TensorNullaryBlock {
399 public:
400 typedef TensorMap<const Tensor<ScalarNoConst, NumDims, Layout, Index>> ArgXprType;
401 typedef TensorCwiseNullaryOp<NullaryOp, const ArgXprType> XprType;
402 typedef typename internal::XprScalar<XprType>::type Scalar;
403
404 TensorNullaryBlock(const NullaryOp& functor, const DSizes<Index, NumDims>& dimensions)
405 : m_functor(functor), m_dimensions(dimensions) {}
406
407 constexpr internal::TensorBlockKind kind() const { return internal::TensorBlockKind::kExpr; }
408 XprType expr() const {
409 return XprType(ArgXprType(static_cast<const ScalarNoConst*>(nullptr), m_dimensions), m_functor);
410 }
411 const Scalar* data() const { return nullptr; }
412 void cleanup() {}
413
414 private:
415 NullaryOp m_functor;
416 DSizes<Index, NumDims> m_dimensions;
417 };
418
419 typedef internal::TensorMaterializedBlock<ScalarNoConst, NumDims, Layout, Index> MaterializedTensorBlock;
420 typedef std::conditional_t<IndexIndependentFunctor, TensorNullaryBlock, MaterializedTensorBlock> TensorBlock;
421 //===--------------------------------------------------------------------===//
422
423 EIGEN_DEVICE_FUNC const Dimensions& dimensions() const { return m_argImpl.dimensions(); }
424
425 EIGEN_STRONG_INLINE bool evalSubExprsIfNeeded(EvaluatorPointerType) { return true; }
426
427#ifdef EIGEN_USE_THREADS
428 template <typename EvalSubExprsCallback>
429 EIGEN_STRONG_INLINE void evalSubExprsIfNeededAsync(EvaluatorPointerType, EvalSubExprsCallback done) {
430 done(true);
431 }
432#endif // EIGEN_USE_THREADS
433
434 EIGEN_STRONG_INLINE void cleanup() {}
435
436 EIGEN_DEVICE_FUNC CoeffReturnType coeff(Index index) const { return m_wrapper(m_functor, index); }
437
438 template <int LoadMode>
439 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType packet(Index index) const {
440 return m_wrapper.template packetOp<PacketReturnType, Index>(m_functor, index);
441 }
442
443 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorOpCost costPerCoeff(bool vectorized) const {
444 // NullaryOps (constants, zero, identity, random) generate values from
445 // registers or minimal state — they do not load from memory. Report
446 // zero bytes_loaded so the cost model correctly classifies expressions
447 // containing many constants (e.g. Horner polynomials) as compute-bound
448 // rather than memory-bound.
449 return TensorOpCost(0, 0, 0, vectorized, PacketType<CoeffReturnType, Device>::size);
450 }
451
452 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE internal::TensorBlockResourceRequirements getResourceRequirements() const {
453 return internal::TensorBlockResourceRequirements::any();
454 }
455
456 // Fills `count` elements of an inner run, whose tensor-linear indices are
457 // tensor_index .. tensor_index + count - 1. Specialized on the functor's
458 // packet support so that packetOp is only instantiated when it exists.
459 template <typename Self, bool Vectorizable>
460 struct NullaryBlockFill {
461 static EIGEN_STRONG_INLINE void Run(const Self& self, ScalarNoConst* buffer, Index tensor_index, Index count) {
462 for (Index i = 0; i < count; ++i) {
463 buffer[i] = self.m_wrapper(self.m_functor, tensor_index + i);
464 }
465 }
466 };
467
468 template <typename Self>
469 struct NullaryBlockFill<Self, true> {
470 static EIGEN_STRONG_INLINE void Run(const Self& self, ScalarNoConst* buffer, Index tensor_index, Index count) {
471 Index i = 0;
472 for (; i + PacketSize <= count; i += PacketSize) {
473 internal::pstoreu(buffer + i,
474 self.m_wrapper.template packetOp<PacketReturnType, Index>(self.m_functor, tensor_index + i));
475 }
476 for (; i < count; ++i) {
477 buffer[i] = self.m_wrapper(self.m_functor, tensor_index + i);
478 }
479 }
480 };
481
482 template <typename Self, bool IndexIndependent>
483 struct NullaryBlockImpl {
484 // Index-independent functors: return a lazy block; the consumer folds the
485 // functor into its own (vectorized) evaluation loop.
486 static EIGEN_STRONG_INLINE TensorBlock Run(const Self& self, TensorBlockDesc& desc, TensorBlockScratch&) {
487 return TensorBlock(self.m_functor, desc.dimensions());
488 }
489 };
490
491 template <typename Self>
492 struct NullaryBlockImpl<Self, false> {
493 // Index-dependent functors: materialize the block by calling the functor
494 // with the block elements' true tensor-linear indices, so the values
495 // match linear evaluation semantics. Along the inner dimension a block
496 // row is contiguous in linear-index space, so each run is one
497 // packet-sized sweep.
498 static TensorBlock Run(const Self& self, TensorBlockDesc& desc, TensorBlockScratch& scratch) {
499 constexpr bool is_col_major = static_cast<int>(Layout) == static_cast<int>(ColMajor);
500 typedef NullaryBlockFill<Self, bool(PacketAccess)> Fill;
501
502 if (desc.size() == 0) {
503 return TensorBlock(internal::TensorBlockKind::kView, nullptr, desc.dimensions());
504 }
505
506 // Strides of the full tensor in linear-index space.
507 const Dimensions& dims = self.m_argImpl.dimensions();
508 array<Index, NumDims> tensor_strides;
509 EIGEN_IF_CONSTEXPR (is_col_major) {
510 tensor_strides[0] = 1;
511 for (int i = 1; i < NumDims; ++i) tensor_strides[i] = tensor_strides[i - 1] * dims[i - 1];
512 } else {
513 tensor_strides[NumDims - 1] = 1;
514 for (int i = NumDims - 2; i >= 0; --i) tensor_strides[i] = tensor_strides[i + 1] * dims[i + 1];
515 }
516
517 // Block iteration state, inner-most dimension first.
518 struct BlockIteratorState {
519 Index size;
520 Index count;
521 Index tensor_stride;
522 Index tensor_span;
523 };
524 array<BlockIteratorState, NumDims> it;
525 for (int i = 0; i < NumDims; ++i) {
526 const int dim = is_col_major ? i : NumDims - 1 - i;
527 const Index size = desc.dimension(dim);
528 const Index stride = tensor_strides[dim];
529 it[i] = {/*size=*/size, /*count=*/0, /*tensor_stride=*/stride, /*tensor_span=*/stride * (size - 1)};
530 }
531 eigen_assert(it[0].tensor_stride == 1);
532
533 const typename TensorBlock::Storage block_storage = TensorBlock::prepareStorage(desc, scratch);
534 ScalarNoConst* block_buffer = block_storage.data();
535
536 const Index inner_size = it[0].size;
537 Index tensor_index = desc.offset();
538 Index offset = 0;
539 for (;;) {
540 Fill::Run(self, block_buffer + offset, tensor_index, inner_size);
541 offset += inner_size;
542
543 // Advance the odometer over the outer dimensions.
544 int i = 1;
545 for (; i < NumDims; ++i) {
546 if (++it[i].count < it[i].size) {
547 tensor_index += it[i].tensor_stride;
548 break;
549 }
550 it[i].count = 0;
551 tensor_index -= it[i].tensor_span;
552 }
553 if (i == NumDims) break;
554 }
555
556 return block_storage.AsTensorMaterializedBlock();
557 }
558 };
559
560 EIGEN_STRONG_INLINE TensorBlock block(TensorBlockDesc& desc, TensorBlockScratch& scratch,
561 bool /*root_of_expr_ast*/ = false) const {
562 typedef TensorEvaluator<const TensorCwiseNullaryOp<NullaryOp, ArgType>, Device> Self;
563 return NullaryBlockImpl<Self, IndexIndependentFunctor>::Run(*this, desc, scratch);
564 }
565
566 EIGEN_DEVICE_FUNC EvaluatorPointerType data() const { return nullptr; }
567
568 private:
569 const NullaryOp m_functor;
570 TensorEvaluator<ArgType, Device> m_argImpl;
571 const internal::nullary_wrapper<CoeffReturnType, NullaryOp> m_wrapper;
572};
573
574// -------------------- CwiseUnaryOp --------------------
575
576template <typename UnaryOp, typename ArgType, typename Device>
577struct TensorEvaluator<const TensorCwiseUnaryOp<UnaryOp, ArgType>, Device> {
578 typedef TensorCwiseUnaryOp<UnaryOp, ArgType> XprType;
579
580 static constexpr int Layout = TensorEvaluator<ArgType, Device>::Layout;
581 enum {
582 IsAligned = TensorEvaluator<ArgType, Device>::IsAligned,
583 PacketAccess =
584 int(TensorEvaluator<ArgType, Device>::PacketAccess) & int(internal::functor_traits<UnaryOp>::PacketAccess),
585 BlockAccess = TensorEvaluator<ArgType, Device>::BlockAccess,
586 PreferBlockAccess = TensorEvaluator<ArgType, Device>::PreferBlockAccess,
587 CoordAccess = false, // to be implemented
588 RawAccess = false
589 };
590
591 EIGEN_DEVICE_FUNC TensorEvaluator(const XprType& op, const Device& device)
592 : m_device(device), m_functor(op.functor()), m_argImpl(op.nestedExpression(), device) {}
593
594 typedef typename XprType::Index Index;
595 typedef typename XprType::Scalar Scalar;
596 typedef std::remove_const_t<Scalar> ScalarNoConst;
597 typedef typename internal::traits<XprType>::Scalar CoeffReturnType;
598 typedef typename PacketType<CoeffReturnType, Device>::type PacketReturnType;
599 static constexpr int PacketSize = PacketType<CoeffReturnType, Device>::size;
600 typedef typename TensorEvaluator<ArgType, Device>::Dimensions Dimensions;
601 typedef StorageMemory<CoeffReturnType, Device> Storage;
602 typedef typename Storage::Type EvaluatorPointerType;
603 static constexpr int NumDims = internal::array_size<Dimensions>::value;
604
605 //===- Tensor block evaluation strategy (see TensorBlock.h) -------------===//
606 typedef internal::TensorBlockDescriptor<NumDims, Index> TensorBlockDesc;
607 typedef internal::TensorBlockScratchAllocator<Device> TensorBlockScratch;
608
609 typedef typename TensorEvaluator<const ArgType, Device>::TensorBlock ArgTensorBlock;
610
611 typedef internal::TensorCwiseUnaryBlock<UnaryOp, ArgTensorBlock> TensorBlock;
612 //===--------------------------------------------------------------------===//
613
614 EIGEN_DEVICE_FUNC const Dimensions& dimensions() const { return m_argImpl.dimensions(); }
615
616 EIGEN_STRONG_INLINE bool evalSubExprsIfNeeded(EvaluatorPointerType) {
617 m_argImpl.evalSubExprsIfNeeded(nullptr);
618 return true;
619 }
620
621#ifdef EIGEN_USE_THREADS
622 template <typename EvalSubExprsCallback>
623 EIGEN_STRONG_INLINE void evalSubExprsIfNeededAsync(EvaluatorPointerType, EvalSubExprsCallback done) {
624 m_argImpl.evalSubExprsIfNeededAsync(nullptr, [done](bool) { done(true); });
625 }
626#endif // EIGEN_USE_THREADS
627
628 EIGEN_STRONG_INLINE void cleanup() { m_argImpl.cleanup(); }
629
630 EIGEN_DEVICE_FUNC CoeffReturnType coeff(Index index) const { return m_functor(m_argImpl.coeff(index)); }
631
632 template <int LoadMode>
633 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType packet(Index index) const {
634 return m_functor.packetOp(m_argImpl.template packet<LoadMode>(index));
635 }
636
637 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorOpCost costPerCoeff(bool vectorized) const {
638 const double functor_cost = internal::functor_traits<UnaryOp>::Cost;
639 return m_argImpl.costPerCoeff(vectorized) + TensorOpCost(0, 0, functor_cost, vectorized, PacketSize);
640 }
641
642 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE internal::TensorBlockResourceRequirements getResourceRequirements() const {
643 static constexpr double functor_cost = internal::functor_traits<UnaryOp>::Cost;
644 return m_argImpl.getResourceRequirements().addCostPerCoeff({0, 0, functor_cost / PacketSize});
645 }
646
647 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorBlock block(TensorBlockDesc& desc, TensorBlockScratch& scratch,
648 bool /*root_of_expr_ast*/ = false) const {
649 // The forwarded destination buffer is sized for *our* output Scalar (the
650 // assign LHS), but the child block evaluator below us writes ArgScalar.
651 // For functors that change the scalar type (abs(complex)->real,
652 // isnan/isfinite->bool, ...) the buffer would be misinterpreted by any
653 // block-materializing child's prepareStorage (assert in debug, corruption
654 // in release). Drop the buffer in that case; the child falls back to
655 // scratch and writeBlock still lands the converted values in the LHS.
656 constexpr bool ScalarTypesMatch =
657 std::is_same<std::remove_const_t<Scalar>,
658 std::remove_const_t<typename TensorEvaluator<ArgType, Device>::Scalar>>::value;
659 EIGEN_IF_CONSTEXPR (!ScalarTypesMatch) {
660 desc.DropDestinationBuffer();
661 }
662 return TensorBlock(m_argImpl.block(desc, scratch), m_functor);
663 }
664
665 EIGEN_DEVICE_FUNC EvaluatorPointerType data() const { return nullptr; }
666
667 private:
668 const Device EIGEN_DEVICE_REF m_device;
669 const UnaryOp m_functor;
670 TensorEvaluator<ArgType, Device> m_argImpl;
671};
672
673// -------------------- CwiseBinaryOp --------------------
674
675template <typename BinaryOp, typename LeftArgType, typename RightArgType, typename Device>
676struct TensorEvaluator<const TensorCwiseBinaryOp<BinaryOp, LeftArgType, RightArgType>, Device> {
677 typedef TensorCwiseBinaryOp<BinaryOp, LeftArgType, RightArgType> XprType;
678
679 static constexpr int Layout = TensorEvaluator<LeftArgType, Device>::Layout;
680 enum {
681 IsAligned =
682 int(TensorEvaluator<LeftArgType, Device>::IsAligned) & int(TensorEvaluator<RightArgType, Device>::IsAligned),
683 PacketAccess = int(TensorEvaluator<LeftArgType, Device>::PacketAccess) &
684 int(TensorEvaluator<RightArgType, Device>::PacketAccess) &
685 int(internal::functor_traits<BinaryOp>::PacketAccess),
686 BlockAccess = int(TensorEvaluator<LeftArgType, Device>::BlockAccess) &
687 int(TensorEvaluator<RightArgType, Device>::BlockAccess),
688 PreferBlockAccess = int(TensorEvaluator<LeftArgType, Device>::PreferBlockAccess) |
689 int(TensorEvaluator<RightArgType, Device>::PreferBlockAccess),
690 CoordAccess = false, // to be implemented
691 RawAccess = false
692 };
693
694 EIGEN_DEVICE_FUNC TensorEvaluator(const XprType& op, const Device& device)
695 : m_device(device),
696 m_functor(op.functor()),
697 m_leftImpl(op.lhsExpression(), device),
698 m_rightImpl(op.rhsExpression(), device) {
699 EIGEN_STATIC_ASSERT((static_cast<int>(TensorEvaluator<LeftArgType, Device>::Layout) ==
700 static_cast<int>(TensorEvaluator<RightArgType, Device>::Layout) ||
701 internal::traits<XprType>::NumDimensions <= 1),
702 YOU_MADE_A_PROGRAMMING_MISTAKE);
703 eigen_assert(dimensions_match(m_leftImpl.dimensions(), m_rightImpl.dimensions()));
704 }
705
706 typedef typename XprType::Index Index;
707 typedef typename XprType::Scalar Scalar;
708 typedef typename internal::traits<XprType>::Scalar CoeffReturnType;
709 typedef typename PacketType<CoeffReturnType, Device>::type PacketReturnType;
710 static constexpr int PacketSize = PacketType<CoeffReturnType, Device>::size;
711 typedef typename TensorEvaluator<LeftArgType, Device>::Dimensions Dimensions;
712 typedef StorageMemory<CoeffReturnType, Device> Storage;
713 typedef typename Storage::Type EvaluatorPointerType;
714
715 static constexpr int NumDims = internal::array_size<typename TensorEvaluator<LeftArgType, Device>::Dimensions>::value;
716
717 //===- Tensor block evaluation strategy (see TensorBlock.h) -------------===//
718 typedef internal::TensorBlockDescriptor<NumDims, Index> TensorBlockDesc;
719 typedef internal::TensorBlockScratchAllocator<Device> TensorBlockScratch;
720
721 typedef typename TensorEvaluator<const LeftArgType, Device>::TensorBlock LeftTensorBlock;
722 typedef typename TensorEvaluator<const RightArgType, Device>::TensorBlock RightTensorBlock;
723
724 typedef internal::TensorCwiseBinaryBlock<BinaryOp, LeftTensorBlock, RightTensorBlock> TensorBlock;
725 //===--------------------------------------------------------------------===//
726
727 EIGEN_DEVICE_FUNC const Dimensions& dimensions() const {
728 // TODO: use right impl instead if right impl dimensions are known at compile time.
729 return m_leftImpl.dimensions();
730 }
731
732 EIGEN_STRONG_INLINE bool evalSubExprsIfNeeded(EvaluatorPointerType) {
733 m_leftImpl.evalSubExprsIfNeeded(nullptr);
734 m_rightImpl.evalSubExprsIfNeeded(nullptr);
735 return true;
736 }
737
738#ifdef EIGEN_USE_THREADS
739 template <typename EvalSubExprsCallback>
740 EIGEN_STRONG_INLINE void evalSubExprsIfNeededAsync(EvaluatorPointerType, EvalSubExprsCallback done) {
741 // TODO(ezhulenev): Evaluate two expression in parallel?
742 m_leftImpl.evalSubExprsIfNeededAsync(
743 nullptr, [this, done](bool) { m_rightImpl.evalSubExprsIfNeededAsync(nullptr, [done](bool) { done(true); }); });
744 }
745#endif // EIGEN_USE_THREADS
746
747 EIGEN_STRONG_INLINE void cleanup() {
748 m_leftImpl.cleanup();
749 m_rightImpl.cleanup();
750 }
751
752 EIGEN_DEVICE_FUNC CoeffReturnType coeff(Index index) const {
753 return m_functor(m_leftImpl.coeff(index), m_rightImpl.coeff(index));
754 }
755 template <int LoadMode>
756 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType packet(Index index) const {
757 return m_functor.packetOp(m_leftImpl.template packet<LoadMode>(index),
758 m_rightImpl.template packet<LoadMode>(index));
759 }
760
761 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorOpCost costPerCoeff(bool vectorized) const {
762 const double functor_cost = internal::functor_traits<BinaryOp>::Cost;
763 return m_leftImpl.costPerCoeff(vectorized) + m_rightImpl.costPerCoeff(vectorized) +
764 TensorOpCost(0, 0, functor_cost, vectorized, PacketSize);
765 }
766
767 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE internal::TensorBlockResourceRequirements getResourceRequirements() const {
768 static constexpr double functor_cost = internal::functor_traits<BinaryOp>::Cost;
769 return internal::TensorBlockResourceRequirements::merge(m_leftImpl.getResourceRequirements(),
770 m_rightImpl.getResourceRequirements())
771 .addCostPerCoeff({0, 0, functor_cost / PacketSize});
772 }
773
774 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorBlock block(TensorBlockDesc& desc, TensorBlockScratch& scratch,
775 bool /*root_of_expr_ast*/ = false) const {
776 desc.DropDestinationBuffer();
777 return TensorBlock(m_leftImpl.block(desc, scratch), m_rightImpl.block(desc, scratch), m_functor);
778 }
779
780 EIGEN_DEVICE_FUNC EvaluatorPointerType data() const { return nullptr; }
781
782 private:
783 const Device EIGEN_DEVICE_REF m_device;
784 const BinaryOp m_functor;
785 TensorEvaluator<LeftArgType, Device> m_leftImpl;
786 TensorEvaluator<RightArgType, Device> m_rightImpl;
787};
788
789// -------------------- CwiseTernaryOp --------------------
790
791template <typename TernaryOp, typename Arg1Type, typename Arg2Type, typename Arg3Type, typename Device>
792struct TensorEvaluator<const TensorCwiseTernaryOp<TernaryOp, Arg1Type, Arg2Type, Arg3Type>, Device> {
793 typedef TensorCwiseTernaryOp<TernaryOp, Arg1Type, Arg2Type, Arg3Type> XprType;
794
795 static constexpr int Layout = TensorEvaluator<Arg1Type, Device>::Layout;
796 enum {
797 IsAligned = TensorEvaluator<Arg1Type, Device>::IsAligned && TensorEvaluator<Arg2Type, Device>::IsAligned &&
798 TensorEvaluator<Arg3Type, Device>::IsAligned,
799 PacketAccess = TensorEvaluator<Arg1Type, Device>::PacketAccess && TensorEvaluator<Arg2Type, Device>::PacketAccess &&
800 TensorEvaluator<Arg3Type, Device>::PacketAccess && internal::functor_traits<TernaryOp>::PacketAccess,
801 BlockAccess = TensorEvaluator<Arg1Type, Device>::BlockAccess && TensorEvaluator<Arg2Type, Device>::BlockAccess &&
802 TensorEvaluator<Arg3Type, Device>::BlockAccess,
803 PreferBlockAccess = TensorEvaluator<Arg1Type, Device>::PreferBlockAccess ||
804 TensorEvaluator<Arg2Type, Device>::PreferBlockAccess ||
805 TensorEvaluator<Arg3Type, Device>::PreferBlockAccess,
806 CoordAccess = false, // to be implemented
807 RawAccess = false
808 };
809
810 EIGEN_DEVICE_FUNC TensorEvaluator(const XprType& op, const Device& device)
811 : m_functor(op.functor()),
812 m_arg1Impl(op.arg1Expression(), device),
813 m_arg2Impl(op.arg2Expression(), device),
814 m_arg3Impl(op.arg3Expression(), device) {
815 EIGEN_STATIC_ASSERT((static_cast<int>(TensorEvaluator<Arg1Type, Device>::Layout) ==
816 static_cast<int>(TensorEvaluator<Arg3Type, Device>::Layout) ||
817 internal::traits<XprType>::NumDimensions <= 1),
818 YOU_MADE_A_PROGRAMMING_MISTAKE);
819
820 EIGEN_STATIC_ASSERT((std::is_same<typename internal::traits<Arg1Type>::StorageKind,
821 typename internal::traits<Arg2Type>::StorageKind>::value),
822 STORAGE_KIND_MUST_MATCH)
823 EIGEN_STATIC_ASSERT((std::is_same<typename internal::traits<Arg1Type>::StorageKind,
824 typename internal::traits<Arg3Type>::StorageKind>::value),
825 STORAGE_KIND_MUST_MATCH)
826 EIGEN_STATIC_ASSERT(
827 (std::is_same<typename internal::traits<Arg1Type>::Index, typename internal::traits<Arg2Type>::Index>::value),
828 STORAGE_INDEX_MUST_MATCH)
829 EIGEN_STATIC_ASSERT(
830 (std::is_same<typename internal::traits<Arg1Type>::Index, typename internal::traits<Arg3Type>::Index>::value),
831 STORAGE_INDEX_MUST_MATCH)
832
833 eigen_assert(dimensions_match(m_arg1Impl.dimensions(), m_arg2Impl.dimensions()) &&
834 dimensions_match(m_arg1Impl.dimensions(), m_arg3Impl.dimensions()));
835 }
836
837 typedef typename XprType::Index Index;
838 typedef typename XprType::Scalar Scalar;
839 typedef typename internal::traits<XprType>::Scalar CoeffReturnType;
840 typedef typename PacketType<CoeffReturnType, Device>::type PacketReturnType;
841 static constexpr int PacketSize = PacketType<CoeffReturnType, Device>::size;
842 typedef typename TensorEvaluator<Arg1Type, Device>::Dimensions Dimensions;
843 typedef StorageMemory<CoeffReturnType, Device> Storage;
844 typedef typename Storage::Type EvaluatorPointerType;
845 static constexpr int NumDims = internal::array_size<Dimensions>::value;
846
847 //===- Tensor block evaluation strategy (see TensorBlock.h) -------------===//
848 typedef internal::TensorBlockDescriptor<NumDims, Index> TensorBlockDesc;
849 typedef internal::TensorBlockScratchAllocator<Device> TensorBlockScratch;
850
851 typedef typename TensorEvaluator<const Arg1Type, Device>::TensorBlock Arg1TensorBlock;
852 typedef typename TensorEvaluator<const Arg2Type, Device>::TensorBlock Arg2TensorBlock;
853 typedef typename TensorEvaluator<const Arg3Type, Device>::TensorBlock Arg3TensorBlock;
854
855 // Rebuilds the ternary expression over the arguments' block expressions,
856 // carrying the functor; mirrors TensorSelectOpBlockFactory.
857 struct TensorCwiseTernaryOpBlockFactory {
858 TernaryOp func;
859
860 template <typename Arg1XprType, typename Arg2XprType, typename Arg3XprType>
861 struct XprType {
862 typedef TensorCwiseTernaryOp<TernaryOp, const Arg1XprType, const Arg2XprType, const Arg3XprType> type;
863 };
864
865 template <typename Arg1XprType, typename Arg2XprType, typename Arg3XprType>
866 typename XprType<Arg1XprType, Arg2XprType, Arg3XprType>::type expr(const Arg1XprType& arg1, const Arg2XprType& arg2,
867 const Arg3XprType& arg3) const {
868 return typename XprType<Arg1XprType, Arg2XprType, Arg3XprType>::type(arg1, arg2, arg3, func);
869 }
870 };
871
872 typedef internal::TensorTernaryExprBlock<TensorCwiseTernaryOpBlockFactory, Arg1TensorBlock, Arg2TensorBlock,
873 Arg3TensorBlock>
874 TensorBlock;
875 //===--------------------------------------------------------------------===//
876
877 EIGEN_DEVICE_FUNC const Dimensions& dimensions() const {
878 // TODO: use arg2 or arg3 dimensions if they are known at compile time.
879 return m_arg1Impl.dimensions();
880 }
881
882 EIGEN_STRONG_INLINE bool evalSubExprsIfNeeded(EvaluatorPointerType) {
883 m_arg1Impl.evalSubExprsIfNeeded(nullptr);
884 m_arg2Impl.evalSubExprsIfNeeded(nullptr);
885 m_arg3Impl.evalSubExprsIfNeeded(nullptr);
886 return true;
887 }
888 EIGEN_STRONG_INLINE void cleanup() {
889 m_arg1Impl.cleanup();
890 m_arg2Impl.cleanup();
891 m_arg3Impl.cleanup();
892 }
893
894 EIGEN_DEVICE_FUNC CoeffReturnType coeff(Index index) const {
895 return m_functor(m_arg1Impl.coeff(index), m_arg2Impl.coeff(index), m_arg3Impl.coeff(index));
896 }
897 template <int LoadMode>
898 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType packet(Index index) const {
899 return m_functor.packetOp(m_arg1Impl.template packet<LoadMode>(index), m_arg2Impl.template packet<LoadMode>(index),
900 m_arg3Impl.template packet<LoadMode>(index));
901 }
902
903 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorOpCost costPerCoeff(bool vectorized) const {
904 const double functor_cost = internal::functor_traits<TernaryOp>::Cost;
905 return m_arg1Impl.costPerCoeff(vectorized) + m_arg2Impl.costPerCoeff(vectorized) +
906 m_arg3Impl.costPerCoeff(vectorized) + TensorOpCost(0, 0, functor_cost, vectorized, PacketSize);
907 }
908
909 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE internal::TensorBlockResourceRequirements getResourceRequirements() const {
910 static constexpr double functor_cost = internal::functor_traits<TernaryOp>::Cost;
911 return internal::TensorBlockResourceRequirements::merge(
912 m_arg1Impl.getResourceRequirements(),
913 internal::TensorBlockResourceRequirements::merge(m_arg2Impl.getResourceRequirements(),
914 m_arg3Impl.getResourceRequirements()))
915 .addCostPerCoeff({0, 0, functor_cost / PacketSize});
916 }
917
918 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorBlock block(TensorBlockDesc& desc, TensorBlockScratch& scratch,
919 bool /*root_of_expr_ast*/ = false) const {
920 // The forwarded destination buffer might alias one of the inputs; drop it
921 // like the binary evaluator does.
922 desc.DropDestinationBuffer();
923 return TensorBlock(m_arg1Impl.block(desc, scratch), m_arg2Impl.block(desc, scratch),
924 m_arg3Impl.block(desc, scratch), TensorCwiseTernaryOpBlockFactory{m_functor});
925 }
926
927 EIGEN_DEVICE_FUNC EvaluatorPointerType data() const { return nullptr; }
928
929 private:
930 const TernaryOp m_functor;
931 TensorEvaluator<Arg1Type, Device> m_arg1Impl;
932 TensorEvaluator<Arg2Type, Device> m_arg2Impl;
933 TensorEvaluator<Arg3Type, Device> m_arg3Impl;
934};
935
936// -------------------- SelectOp --------------------
937
938template <typename IfArgType, typename ThenArgType, typename ElseArgType, typename Device>
939struct TensorEvaluator<const TensorSelectOp<IfArgType, ThenArgType, ElseArgType>, Device> {
940 typedef TensorSelectOp<IfArgType, ThenArgType, ElseArgType> XprType;
941 typedef typename XprType::Scalar Scalar;
942
943 using TernarySelectOp = internal::scalar_boolean_select_op<typename internal::traits<ThenArgType>::Scalar,
944 typename internal::traits<ElseArgType>::Scalar,
945 typename internal::traits<IfArgType>::Scalar>;
946 static constexpr bool TernaryPacketAccess =
947 TensorEvaluator<ThenArgType, Device>::PacketAccess && TensorEvaluator<ElseArgType, Device>::PacketAccess &&
948 TensorEvaluator<IfArgType, Device>::PacketAccess && internal::functor_traits<TernarySelectOp>::PacketAccess;
949
950 static constexpr int Layout = TensorEvaluator<IfArgType, Device>::Layout;
951 enum {
952 IsAligned = TensorEvaluator<ThenArgType, Device>::IsAligned && TensorEvaluator<ElseArgType, Device>::IsAligned,
953 PacketAccess =
954 (TensorEvaluator<ThenArgType, Device>::PacketAccess && TensorEvaluator<ElseArgType, Device>::PacketAccess) ||
955 TernaryPacketAccess,
956 BlockAccess = TensorEvaluator<IfArgType, Device>::BlockAccess &&
957 TensorEvaluator<ThenArgType, Device>::BlockAccess &&
958 TensorEvaluator<ElseArgType, Device>::BlockAccess,
959 PreferBlockAccess = TensorEvaluator<IfArgType, Device>::PreferBlockAccess ||
960 TensorEvaluator<ThenArgType, Device>::PreferBlockAccess ||
961 TensorEvaluator<ElseArgType, Device>::PreferBlockAccess,
962 CoordAccess = false, // to be implemented
963 RawAccess = false
964 };
965
966 EIGEN_DEVICE_FUNC TensorEvaluator(const XprType& op, const Device& device)
967 : m_condImpl(op.ifExpression(), device),
968 m_thenImpl(op.thenExpression(), device),
969 m_elseImpl(op.elseExpression(), device) {
970 EIGEN_STATIC_ASSERT((static_cast<int>(TensorEvaluator<IfArgType, Device>::Layout) ==
971 static_cast<int>(TensorEvaluator<ThenArgType, Device>::Layout)),
972 YOU_MADE_A_PROGRAMMING_MISTAKE);
973 EIGEN_STATIC_ASSERT((static_cast<int>(TensorEvaluator<IfArgType, Device>::Layout) ==
974 static_cast<int>(TensorEvaluator<ElseArgType, Device>::Layout)),
975 YOU_MADE_A_PROGRAMMING_MISTAKE);
976 eigen_assert(dimensions_match(m_condImpl.dimensions(), m_thenImpl.dimensions()));
977 eigen_assert(dimensions_match(m_thenImpl.dimensions(), m_elseImpl.dimensions()));
978 }
979
980 typedef typename XprType::Index Index;
981 typedef typename internal::traits<XprType>::Scalar CoeffReturnType;
982 typedef typename PacketType<CoeffReturnType, Device>::type PacketReturnType;
983 static constexpr int PacketSize = PacketType<CoeffReturnType, Device>::size;
984 typedef typename TensorEvaluator<IfArgType, Device>::Dimensions Dimensions;
985 typedef StorageMemory<CoeffReturnType, Device> Storage;
986 typedef typename Storage::Type EvaluatorPointerType;
987
988 static constexpr int NumDims = internal::array_size<Dimensions>::value;
989
990 //===- Tensor block evaluation strategy (see TensorBlock.h) -------------===//
991 typedef internal::TensorBlockDescriptor<NumDims, Index> TensorBlockDesc;
992 typedef internal::TensorBlockScratchAllocator<Device> TensorBlockScratch;
993
994 typedef typename TensorEvaluator<const IfArgType, Device>::TensorBlock IfArgTensorBlock;
995 typedef typename TensorEvaluator<const ThenArgType, Device>::TensorBlock ThenArgTensorBlock;
996 typedef typename TensorEvaluator<const ElseArgType, Device>::TensorBlock ElseArgTensorBlock;
997
998 struct TensorSelectOpBlockFactory {
999 template <typename IfArgXprType, typename ThenArgXprType, typename ElseArgXprType>
1000 struct XprType {
1001 typedef TensorSelectOp<const IfArgXprType, const ThenArgXprType, const ElseArgXprType> type;
1002 };
1003
1004 template <typename IfArgXprType, typename ThenArgXprType, typename ElseArgXprType>
1005 typename XprType<IfArgXprType, ThenArgXprType, ElseArgXprType>::type expr(const IfArgXprType& if_expr,
1006 const ThenArgXprType& then_expr,
1007 const ElseArgXprType& else_expr) const {
1008 return typename XprType<IfArgXprType, ThenArgXprType, ElseArgXprType>::type(if_expr, then_expr, else_expr);
1009 }
1010 };
1011
1012 typedef internal::TensorTernaryExprBlock<TensorSelectOpBlockFactory, IfArgTensorBlock, ThenArgTensorBlock,
1013 ElseArgTensorBlock>
1014 TensorBlock;
1015 //===--------------------------------------------------------------------===//
1016
1017 EIGEN_DEVICE_FUNC const Dimensions& dimensions() const {
1018 // TODO: use then or else impl instead if they happen to be known at compile time.
1019 return m_condImpl.dimensions();
1020 }
1021
1022 EIGEN_STRONG_INLINE bool evalSubExprsIfNeeded(EvaluatorPointerType) {
1023 m_condImpl.evalSubExprsIfNeeded(nullptr);
1024 m_thenImpl.evalSubExprsIfNeeded(nullptr);
1025 m_elseImpl.evalSubExprsIfNeeded(nullptr);
1026 return true;
1027 }
1028
1029#ifdef EIGEN_USE_THREADS
1030 template <typename EvalSubExprsCallback>
1031 EIGEN_STRONG_INLINE void evalSubExprsIfNeededAsync(EvaluatorPointerType, EvalSubExprsCallback done) {
1032 m_condImpl.evalSubExprsIfNeeded(nullptr, [this, done](bool) {
1033 m_thenImpl.evalSubExprsIfNeeded(
1034 nullptr, [this, done](bool) { m_elseImpl.evalSubExprsIfNeeded(nullptr, [done](bool) { done(true); }); });
1035 });
1036 }
1037#endif // EIGEN_USE_THREADS
1038
1039 EIGEN_STRONG_INLINE void cleanup() {
1040 m_condImpl.cleanup();
1041 m_thenImpl.cleanup();
1042 m_elseImpl.cleanup();
1043 }
1044
1045 EIGEN_DEVICE_FUNC CoeffReturnType coeff(Index index) const {
1046 return m_condImpl.coeff(index) ? m_thenImpl.coeff(index) : m_elseImpl.coeff(index);
1047 }
1048
1049 template <int LoadMode, bool UseTernary = TernaryPacketAccess, std::enable_if_t<!UseTernary, bool> = true>
1050 EIGEN_DEVICE_FUNC PacketReturnType packet(Index index) const {
1051 EIGEN_ALIGN_TO_BOUNDARY(internal::unpacket_traits<PacketReturnType>::alignment)
1052 std::remove_const_t<Scalar> arr[PacketSize];
1053 EIGEN_UNROLL_LOOP
1054 for (Index i = 0; i < PacketSize; ++i) {
1055 arr[i] = m_condImpl.coeff(index + i) ? Scalar(-1) : Scalar(0);
1056 }
1057 return TernarySelectOp().template packetOp<PacketReturnType>(m_thenImpl.template packet<LoadMode>(index),
1058 m_elseImpl.template packet<LoadMode>(index),
1059 internal::pload<PacketReturnType>(arr));
1060 }
1061
1062 template <int LoadMode, bool UseTernary = TernaryPacketAccess, std::enable_if_t<UseTernary, bool> = true>
1063 EIGEN_DEVICE_FUNC PacketReturnType packet(Index index) const {
1064 return TernarySelectOp().template packetOp<PacketReturnType>(m_thenImpl.template packet<LoadMode>(index),
1065 m_elseImpl.template packet<LoadMode>(index),
1066 m_condImpl.template packet<LoadMode>(index));
1067 }
1068
1069 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorOpCost costPerCoeff(bool vectorized) const {
1070 return m_condImpl.costPerCoeff(vectorized) +
1071 m_thenImpl.costPerCoeff(vectorized).cwiseMax(m_elseImpl.costPerCoeff(vectorized));
1072 }
1073
1074 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE internal::TensorBlockResourceRequirements getResourceRequirements() const {
1075 auto then_req = m_thenImpl.getResourceRequirements();
1076 auto else_req = m_elseImpl.getResourceRequirements();
1077
1078 auto merged_req = internal::TensorBlockResourceRequirements::merge(then_req, else_req);
1079 merged_req.cost_per_coeff = then_req.cost_per_coeff.cwiseMax(else_req.cost_per_coeff);
1080
1081 return internal::TensorBlockResourceRequirements::merge(m_condImpl.getResourceRequirements(), merged_req);
1082 }
1083
1084 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorBlock block(TensorBlockDesc& desc, TensorBlockScratch& scratch,
1085 bool /*root_of_expr_ast*/ = false) const {
1086 // It's unsafe to pass destination buffer to underlying expressions, because
1087 // output might be aliased with one of the inputs.
1088 desc.DropDestinationBuffer();
1089
1090 return TensorBlock(m_condImpl.block(desc, scratch), m_thenImpl.block(desc, scratch),
1091 m_elseImpl.block(desc, scratch), TensorSelectOpBlockFactory());
1092 }
1093
1094 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE EvaluatorPointerType data() const { return nullptr; }
1095
1096#ifdef EIGEN_USE_SYCL
1097 // binding placeholder accessors to a command group handler for SYCL
1098 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void bind(cl::sycl::handler& cgh) const {
1099 m_condImpl.bind(cgh);
1100 m_thenImpl.bind(cgh);
1101 m_elseImpl.bind(cgh);
1102 }
1103#endif
1104 private:
1105 TensorEvaluator<IfArgType, Device> m_condImpl;
1106 TensorEvaluator<ThenArgType, Device> m_thenImpl;
1107 TensorEvaluator<ElseArgType, Device> m_elseImpl;
1108};
1109
1110} // end namespace Eigen
1111
1112#if defined(EIGEN_USE_SYCL) && defined(SYCL_COMPILER_IS_DPCPP)
1113template <typename Derived, typename Device>
1114struct cl::sycl::is_device_copyable<
1115 Eigen::TensorEvaluator<Derived, Device>,
1116 std::enable_if_t<!std::is_trivially_copyable<Eigen::TensorEvaluator<Derived, Device>>::value>> : std::true_type {};
1117#endif
1118
1119#endif // EIGEN_TENSOR_TENSOR_EVALUATOR_H
Tensor binary expression.
Definition TensorExpr.h:153
Tensor nullary expression.
Definition TensorExpr.h:41
Tensor unary expression.
Definition TensorExpr.h:90
Namespace containing all symbols from the Eigen library.
The tensor evaluator class.
Definition TensorEvaluator.h:47