Eigen-unsupported  3.4.1 (git rev 28ded8800c26864e537852658428ab44c8399e87)
 
Loading...
Searching...
No Matches
TensorReverse.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2014 Navdeep Jaitly <ndjaitly@google.com>
5// Benoit Steiner <benoit.steiner.goog@gmail.com>
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_CXX11_TENSOR_TENSOR_REVERSE_H
12#define EIGEN_CXX11_TENSOR_TENSOR_REVERSE_H
13namespace Eigen {
14
15namespace internal {
16template<typename ReverseDimensions, typename XprType>
17struct traits<TensorReverseOp<ReverseDimensions,
18 XprType> > : public traits<XprType>
19{
20 typedef typename XprType::Scalar Scalar;
21 typedef traits<XprType> XprTraits;
22 typedef typename XprTraits::StorageKind StorageKind;
23 typedef typename XprTraits::Index Index;
24 typedef typename XprType::Nested Nested;
25 typedef typename remove_reference<Nested>::type _Nested;
26 static const int NumDimensions = XprTraits::NumDimensions;
27 static const int Layout = XprTraits::Layout;
28 typedef typename XprTraits::PointerType PointerType;
29};
30
31template<typename ReverseDimensions, typename XprType>
32struct eval<TensorReverseOp<ReverseDimensions, XprType>, Eigen::Dense>
33{
34 typedef const TensorReverseOp<ReverseDimensions, XprType>& type;
35};
36
37template<typename ReverseDimensions, typename XprType>
38struct nested<TensorReverseOp<ReverseDimensions, XprType>, 1,
39 typename eval<TensorReverseOp<ReverseDimensions, XprType> >::type>
40{
41 typedef TensorReverseOp<ReverseDimensions, XprType> type;
42};
43
44} // end namespace internal
45
52template <typename ReverseDimensions, typename XprType>
53class TensorReverseOp : public TensorBase<TensorReverseOp<ReverseDimensions, XprType>, WriteAccessors> {
54 public:
56 typedef typename Eigen::internal::traits<TensorReverseOp>::Scalar Scalar;
57 typedef typename Eigen::NumTraits<Scalar>::Real RealScalar;
58 typedef typename XprType::CoeffReturnType CoeffReturnType;
59 typedef typename Eigen::internal::nested<TensorReverseOp>::type Nested;
60 typedef typename Eigen::internal::traits<TensorReverseOp>::StorageKind StorageKind;
61 typedef typename Eigen::internal::traits<TensorReverseOp>::Index Index;
62
63 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorReverseOp(
64 const XprType& expr, const ReverseDimensions& reverse_dims)
65 : m_xpr(expr), m_reverse_dims(reverse_dims) { }
66
67 EIGEN_DEVICE_FUNC
68 const ReverseDimensions& reverse() const { return m_reverse_dims; }
69
70 EIGEN_DEVICE_FUNC
71 const typename internal::remove_all<typename XprType::Nested>::type&
72 expression() const { return m_xpr; }
73
74 EIGEN_TENSOR_INHERIT_ASSIGNMENT_OPERATORS(TensorReverseOp)
75
76
77 protected:
78 typename XprType::Nested m_xpr;
79 const ReverseDimensions m_reverse_dims;
80};
81
82// Eval as rvalue
83template<typename ReverseDimensions, typename ArgType, typename Device>
84struct TensorEvaluator<const TensorReverseOp<ReverseDimensions, ArgType>, Device>
85{
87 typedef typename XprType::Index Index;
88 static const int NumDims = internal::array_size<ReverseDimensions>::value;
89 typedef DSizes<Index, NumDims> Dimensions;
90 typedef typename XprType::Scalar Scalar;
91 typedef typename XprType::CoeffReturnType CoeffReturnType;
92 typedef typename PacketType<CoeffReturnType, Device>::type PacketReturnType;
93 static const int PacketSize = PacketType<CoeffReturnType, Device>::size;
94 typedef StorageMemory<CoeffReturnType, Device> Storage;
95 typedef typename Storage::Type EvaluatorPointerType;
96
97 enum {
98 IsAligned = false,
99 PacketAccess = TensorEvaluator<ArgType, Device>::PacketAccess,
100 BlockAccess = NumDims > 0,
101 PreferBlockAccess = true,
102 Layout = TensorEvaluator<ArgType, Device>::Layout,
103 CoordAccess = false, // to be implemented
104 RawAccess = false
105 };
106
107 typedef internal::TensorIntDivisor<Index> IndexDivisor;
108
109 //===- Tensor block evaluation strategy (see TensorBlock.h) -------------===//
110 typedef internal::TensorBlockDescriptor<NumDims, Index> TensorBlockDesc;
111 typedef internal::TensorBlockScratchAllocator<Device> TensorBlockScratch;
112
113 typedef typename TensorEvaluator<const ArgType, Device>::TensorBlock
114 ArgTensorBlock;
115
116 typedef typename internal::TensorMaterializedBlock<CoeffReturnType, NumDims,
117 Layout, Index>
118 TensorBlock;
119 //===--------------------------------------------------------------------===//
120
121 EIGEN_STRONG_INLINE TensorEvaluator(const XprType& op, const Device& device)
122 : m_impl(op.expression(), device),
123 m_reverse(op.reverse()),
124 m_device(device)
125 {
126 // Reversing a scalar isn't supported yet. It would be a no-op anyway.
127 EIGEN_STATIC_ASSERT((NumDims > 0), YOU_MADE_A_PROGRAMMING_MISTAKE);
128
129 // Compute strides
130 m_dimensions = m_impl.dimensions();
131 if (static_cast<int>(Layout) == static_cast<int>(ColMajor)) {
132 m_strides[0] = 1;
133 for (int i = 1; i < NumDims; ++i) {
134 m_strides[i] = m_strides[i-1] * m_dimensions[i-1];
135 if (m_strides[i] > 0) m_fastStrides[i] = IndexDivisor(m_strides[i]);
136 }
137 } else {
138 m_strides[NumDims-1] = 1;
139 for (int i = NumDims - 2; i >= 0; --i) {
140 m_strides[i] = m_strides[i+1] * m_dimensions[i+1];
141 if (m_strides[i] > 0) m_fastStrides[i] = IndexDivisor(m_strides[i]);
142 }
143 }
144 }
145
146 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
147 const Dimensions& dimensions() const { return m_dimensions; }
148
149 EIGEN_STRONG_INLINE bool evalSubExprsIfNeeded(EvaluatorPointerType) {
150 m_impl.evalSubExprsIfNeeded(NULL);
151 return true;
152 }
153
154#ifdef EIGEN_USE_THREADS
155 template <typename EvalSubExprsCallback>
156 EIGEN_STRONG_INLINE void evalSubExprsIfNeededAsync(
157 EvaluatorPointerType, EvalSubExprsCallback done) {
158 m_impl.evalSubExprsIfNeededAsync(nullptr, [done](bool) { done(true); });
159 }
160#endif // EIGEN_USE_THREADS
161
162 EIGEN_STRONG_INLINE void cleanup() {
163 m_impl.cleanup();
164 }
165
166 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Index reverseIndex(
167 Index index) const {
168 eigen_assert(index < dimensions().TotalSize());
169 Index inputIndex = 0;
170 if (static_cast<int>(Layout) == static_cast<int>(ColMajor)) {
171 EIGEN_UNROLL_LOOP
172 for (int i = NumDims - 1; i > 0; --i) {
173 Index idx = index / m_fastStrides[i];
174 index -= idx * m_strides[i];
175 if (m_reverse[i]) {
176 idx = m_dimensions[i] - idx - 1;
177 }
178 inputIndex += idx * m_strides[i] ;
179 }
180 if (m_reverse[0]) {
181 inputIndex += (m_dimensions[0] - index - 1);
182 } else {
183 inputIndex += index;
184 }
185 } else {
186 EIGEN_UNROLL_LOOP
187 for (int i = 0; i < NumDims - 1; ++i) {
188 Index idx = index / m_fastStrides[i];
189 index -= idx * m_strides[i];
190 if (m_reverse[i]) {
191 idx = m_dimensions[i] - idx - 1;
192 }
193 inputIndex += idx * m_strides[i] ;
194 }
195 if (m_reverse[NumDims-1]) {
196 inputIndex += (m_dimensions[NumDims-1] - index - 1);
197 } else {
198 inputIndex += index;
199 }
200 }
201 return inputIndex;
202 }
203
204 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE CoeffReturnType coeff(
205 Index index) const {
206 return m_impl.coeff(reverseIndex(index));
207 }
208
209 template<int LoadMode>
210 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
211 PacketReturnType packet(Index index) const
212 {
213 EIGEN_STATIC_ASSERT((PacketSize > 1), YOU_MADE_A_PROGRAMMING_MISTAKE)
214 eigen_assert(index+PacketSize-1 < dimensions().TotalSize());
215
216 // TODO(ndjaitly): write a better packing routine that uses
217 // local structure.
218 EIGEN_ALIGN_MAX typename internal::remove_const<CoeffReturnType>::type
219 values[PacketSize];
220 EIGEN_UNROLL_LOOP
221 for (int i = 0; i < PacketSize; ++i) {
222 values[i] = coeff(index+i);
223 }
224 PacketReturnType rslt = internal::pload<PacketReturnType>(values);
225 return rslt;
226 }
227
228 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
229 internal::TensorBlockResourceRequirements getResourceRequirements() const {
230 const size_t target_size = m_device.lastLevelCacheSize();
231 // Block evaluation reads underlying memory in reverse order, and default
232 // cost model does not properly catch this in bytes stored/loaded.
233 return internal::TensorBlockResourceRequirements::skewed<Scalar>(
234 target_size)
235 .addCostPerCoeff({0, 0, 24});
236 }
237
238 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorBlock
239 block(TensorBlockDesc& desc, TensorBlockScratch& scratch,
240 bool /*root_of_expr_ast*/ = false) const {
241 // TODO(ezhulenev): If underlying tensor expression supports and prefers
242 // block evaluation we must use it. Currently we use coeff and packet
243 // access into the underlying tensor expression.
244 // static const bool useBlockAccessForArgType =
245 // TensorEvaluator<ArgType, Device>::BlockAccess &&
246 // TensorEvaluator<ArgType, Device>::PreferBlockAccess;
247
248 static const bool isColMajor =
249 static_cast<int>(Layout) == static_cast<int>(ColMajor);
250
251 static const Index inner_dim_idx = isColMajor ? 0 : NumDims - 1;
252 const bool inner_dim_reversed = m_reverse[inner_dim_idx];
253
254 // Offset in the output block.
255 Index block_offset = 0;
256
257 // Offset in the input Tensor.
258 Index input_offset = reverseIndex(desc.offset());
259
260 // Initialize output block iterator state. Dimension in this array are
261 // always in inner_most -> outer_most order (col major layout).
262 array<BlockIteratorState, NumDims> it;
263 for (int i = 0; i < NumDims; ++i) {
264 const int dim = isColMajor ? i : NumDims - 1 - i;
265 it[i].size = desc.dimension(dim);
266 it[i].count = 0;
267 it[i].reverse = m_reverse[dim];
268
269 it[i].block_stride =
270 i == 0 ? 1 : (it[i - 1].size * it[i - 1].block_stride);
271 it[i].block_span = it[i].block_stride * (it[i].size - 1);
272
273 it[i].input_stride = m_strides[dim];
274 it[i].input_span = it[i].input_stride * (it[i].size - 1);
275
276 if (it[i].reverse) {
277 it[i].input_stride = -1 * it[i].input_stride;
278 it[i].input_span = -1 * it[i].input_span;
279 }
280 }
281
282 // If multiple inner dimensions have the same reverse flag, check if we can
283 // merge them into a single virtual inner dimension.
284 int effective_inner_dim = 0;
285 for (int i = 1; i < NumDims; ++i) {
286 if (it[i].reverse != it[effective_inner_dim].reverse) break;
287 if (it[i].block_stride != it[effective_inner_dim].size) break;
288 if (it[i].block_stride != numext::abs(it[i].input_stride)) break;
289
290 it[i].size = it[effective_inner_dim].size * it[i].size;
291
292 it[i].block_stride = 1;
293 it[i].input_stride = (inner_dim_reversed ? -1 : 1);
294
295 it[i].block_span = it[i].block_stride * (it[i].size - 1);
296 it[i].input_span = it[i].input_stride * (it[i].size - 1);
297
298 effective_inner_dim = i;
299 }
300
301 eigen_assert(it[effective_inner_dim].block_stride == 1);
302 eigen_assert(it[effective_inner_dim].input_stride ==
303 (inner_dim_reversed ? -1 : 1));
304
305 const Index inner_dim_size = it[effective_inner_dim].size;
306
307 // Prepare storage for the materialized reverse result.
308 const typename TensorBlock::Storage block_storage =
309 TensorBlock::prepareStorage(desc, scratch);
310 CoeffReturnType* block_buffer = block_storage.data();
311
312 while (it[NumDims - 1].count < it[NumDims - 1].size) {
313 // Copy inner-most dimension data from reversed location in input.
314 Index dst = block_offset;
315 Index src = input_offset;
316
317 // NOTE(ezhulenev): Adding vectorized path with internal::preverse showed
318 // worse results in benchmarks than a simple coefficient loop.
319 if (inner_dim_reversed) {
320 for (Index i = 0; i < inner_dim_size; ++i) {
321 block_buffer[dst] = m_impl.coeff(src);
322 ++dst;
323 --src;
324 }
325 } else {
326 for (Index i = 0; i < inner_dim_size; ++i) {
327 block_buffer[dst] = m_impl.coeff(src);
328 ++dst;
329 ++src;
330 }
331 }
332
333 // For the 1d tensor we need to generate only one inner-most dimension.
334 if ((NumDims - effective_inner_dim) == 1) break;
335
336 // Update offset.
337 for (Index i = effective_inner_dim + 1; i < NumDims; ++i) {
338 if (++it[i].count < it[i].size) {
339 block_offset += it[i].block_stride;
340 input_offset += it[i].input_stride;
341 break;
342 }
343 if (i != NumDims - 1) it[i].count = 0;
344 block_offset -= it[i].block_span;
345 input_offset -= it[i].input_span;
346 }
347 }
348
349 return block_storage.AsTensorMaterializedBlock();
350 }
351
352 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorOpCost costPerCoeff(bool vectorized) const {
353 double compute_cost = NumDims * (2 * TensorOpCost::AddCost<Index>() +
354 2 * TensorOpCost::MulCost<Index>() +
355 TensorOpCost::DivCost<Index>());
356 for (int i = 0; i < NumDims; ++i) {
357 if (m_reverse[i]) {
358 compute_cost += 2 * TensorOpCost::AddCost<Index>();
359 }
360 }
361 return m_impl.costPerCoeff(vectorized) +
362 TensorOpCost(0, 0, compute_cost, false /* vectorized */, PacketSize);
363 }
364
365 EIGEN_DEVICE_FUNC typename Storage::Type data() const { return NULL; }
366
367#ifdef EIGEN_USE_SYCL
368 // binding placeholder accessors to a command group handler for SYCL
369 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void bind(cl::sycl::handler &cgh) const {
370 m_impl.bind(cgh);
371 }
372#endif
373
374 protected:
375 Dimensions m_dimensions;
376 array<Index, NumDims> m_strides;
377 array<IndexDivisor, NumDims> m_fastStrides;
378 TensorEvaluator<ArgType, Device> m_impl;
379 ReverseDimensions m_reverse;
380 const Device EIGEN_DEVICE_REF m_device;
381
382 private:
383 struct BlockIteratorState {
384 BlockIteratorState()
385 : size(0),
386 count(0),
387 reverse(false),
388 block_stride(0),
389 block_span(0),
390 input_stride(0),
391 input_span(0) {}
392
393 Index size;
394 Index count;
395 bool reverse;
396 Index block_stride;
397 Index block_span;
398 Index input_stride;
399 Index input_span;
400 };
401};
402
403// Eval as lvalue
404
405template <typename ReverseDimensions, typename ArgType, typename Device>
406struct TensorEvaluator<TensorReverseOp<ReverseDimensions, ArgType>, Device>
407 : public TensorEvaluator<const TensorReverseOp<ReverseDimensions, ArgType>,
408 Device> {
409 typedef TensorEvaluator<const TensorReverseOp<ReverseDimensions, ArgType>,
410 Device> Base;
411 typedef TensorReverseOp<ReverseDimensions, ArgType> XprType;
412 typedef typename XprType::Index Index;
413 static const int NumDims = internal::array_size<ReverseDimensions>::value;
414 typedef DSizes<Index, NumDims> Dimensions;
415
416 enum {
417 IsAligned = false,
418 PacketAccess = TensorEvaluator<ArgType, Device>::PacketAccess,
419 BlockAccess = false,
420 PreferBlockAccess = false,
421 Layout = TensorEvaluator<ArgType, Device>::Layout,
422 CoordAccess = false, // to be implemented
423 RawAccess = false
424 };
425 EIGEN_STRONG_INLINE TensorEvaluator(const XprType& op, const Device& device)
426 : Base(op, device) {}
427
428 typedef typename XprType::Scalar Scalar;
429 typedef typename XprType::CoeffReturnType CoeffReturnType;
430 typedef typename PacketType<CoeffReturnType, Device>::type PacketReturnType;
431 static const int PacketSize = PacketType<CoeffReturnType, Device>::size;
432
433 //===- Tensor block evaluation strategy (see TensorBlock.h) -------------===//
434 typedef internal::TensorBlockNotImplemented TensorBlock;
435 //===--------------------------------------------------------------------===//
436
437 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
438 const Dimensions& dimensions() const { return this->m_dimensions; }
439
440 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar& coeffRef(Index index) {
441 return this->m_impl.coeffRef(this->reverseIndex(index));
442 }
443
444 template <int StoreMode> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
445 void writePacket(Index index, const PacketReturnType& x) {
446 EIGEN_STATIC_ASSERT((PacketSize > 1), YOU_MADE_A_PROGRAMMING_MISTAKE)
447 eigen_assert(index+PacketSize-1 < dimensions().TotalSize());
448
449 // This code is pilfered from TensorMorphing.h
450 EIGEN_ALIGN_MAX CoeffReturnType values[PacketSize];
451 internal::pstore<CoeffReturnType, PacketReturnType>(values, x);
452 EIGEN_UNROLL_LOOP
453 for (int i = 0; i < PacketSize; ++i) {
454 this->coeffRef(index+i) = values[i];
455 }
456 }
457};
458
459
460} // end namespace Eigen
461
462#endif // EIGEN_CXX11_TENSOR_TENSOR_REVERSE_H
The tensor base class.
Definition TensorForwardDeclarations.h:56
Tensor reverse elements class.
Definition TensorReverse.h:53
WriteAccessors
Namespace containing all symbols from the Eigen library.
EIGEN_DEFAULT_DENSE_INDEX_TYPE Index
The tensor evaluator class.
Definition TensorEvaluator.h:27