Eigen-Contrib  5.0.1
 
Loading...
Searching...
No Matches
TensorInflation.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2015 Ke Yang <yangke@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_INFLATION_H
12#define EIGEN_TENSOR_TENSOR_INFLATION_H
13
14// IWYU pragma: private
15#include "./InternalHeaderCheck.h"
16
17namespace Eigen {
18
19namespace internal {
20template <typename Strides, typename XprType>
21struct traits<TensorInflationOp<Strides, XprType>> : public traits<XprType> {
22 typedef typename XprType::Scalar Scalar;
23 typedef traits<XprType> XprTraits;
24 typedef typename XprTraits::StorageKind StorageKind;
25 typedef typename XprTraits::Index Index;
26 static constexpr int NumDimensions = XprTraits::NumDimensions;
27 static constexpr int Layout = XprTraits::Layout;
28 typedef typename XprTraits::PointerType PointerType;
29};
30
31template <typename Strides, typename XprType>
32struct eval<TensorInflationOp<Strides, XprType>, Eigen::Dense> {
33 typedef const TensorInflationOp<Strides, XprType>& type;
34};
35
36} // end namespace internal
37
43template <typename Strides, typename XprType>
44class TensorInflationOp : public TensorBase<TensorInflationOp<Strides, XprType>, ReadOnlyAccessors> {
45 public:
46 typedef typename Eigen::internal::traits<TensorInflationOp>::Scalar Scalar;
47 typedef typename Eigen::NumTraits<Scalar>::Real RealScalar;
48 typedef typename XprType::CoeffReturnType CoeffReturnType;
49 typedef typename Eigen::internal::ref_selector<TensorInflationOp>::type Nested;
50 typedef typename Eigen::internal::traits<TensorInflationOp>::StorageKind StorageKind;
51 typedef typename Eigen::internal::traits<TensorInflationOp>::Index Index;
52
53 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorInflationOp(const XprType& expr, const Strides& strides)
54 : m_xpr(expr), m_strides(strides) {}
55
56 EIGEN_DEVICE_FUNC const Strides& strides() const { return m_strides; }
57
58 EIGEN_DEVICE_FUNC const internal::remove_all_t<typename XprType::Nested>& expression() const { return m_xpr; }
59
60 protected:
61 typename XprType::Nested m_xpr;
62 const Strides m_strides;
63};
64
65// Eval as rvalue
66template <typename Strides, typename ArgType, typename Device>
67struct TensorEvaluator<const TensorInflationOp<Strides, ArgType>, Device> {
69 typedef typename XprType::Index Index;
70 static constexpr int NumDims = internal::array_size<typename TensorEvaluator<ArgType, Device>::Dimensions>::value;
71 typedef DSizes<Index, NumDims> Dimensions;
72 typedef typename XprType::Scalar Scalar;
73 typedef typename XprType::CoeffReturnType CoeffReturnType;
74 typedef typename PacketType<CoeffReturnType, Device>::type PacketReturnType;
75 static constexpr int PacketSize = PacketType<CoeffReturnType, Device>::size;
76 typedef StorageMemory<CoeffReturnType, Device> Storage;
77 typedef typename Storage::Type EvaluatorPointerType;
78
79 static constexpr int Layout = TensorEvaluator<ArgType, Device>::Layout;
80 enum {
81 IsAligned = /*TensorEvaluator<ArgType, Device>::IsAligned*/ false,
82 PacketAccess = TensorEvaluator<ArgType, Device>::PacketAccess,
83 // block() reads the argument through coeff(), and under a ThreadPool the
84 // tiled executor shares this evaluator across concurrent block tasks, so
85 // the argument must be safe to read repeatedly and concurrently. Either bit
86 // establishes that: BlockAccess is what a nullary functor that carries
87 // state between calls clears (the built-in random generators are pure in
88 // seed and index and keep it), and RawAccess means coeff() is a plain
89 // buffer read. Requiring BlockAccess alone would needlessly exclude raw
90 // arguments whose scalar is not arithmetic, such as complex tensors.
91 BlockAccess =
93 // The coeff/packet path pays a div/mod walk plus a hole check per output
94 // scalar; the block path is a zero-fill plus a sparse copy of the stride
95 // lattice.
96 PreferBlockAccess = true,
97 CoordAccess = false, // to be implemented
98 RawAccess = false
99 };
100
101 //===- Tensor block evaluation strategy (see TensorBlock.h) -------------===//
102 typedef internal::TensorBlockDescriptor<NumDims, Index> TensorBlockDesc;
103 typedef internal::TensorBlockScratchAllocator<Device> TensorBlockScratch;
104 typedef internal::TensorMaterializedBlock<CoeffReturnType, NumDims, Layout, Index> TensorBlock;
105 //===--------------------------------------------------------------------===//
106
107 EIGEN_STRONG_INLINE TensorEvaluator(const XprType& op, const Device& device)
108 : m_impl(op.expression(), device), m_strides(op.strides()), m_device(device) {
109 m_dimensions = m_impl.dimensions();
110 // Expand each dimension to the inflated dimension.
111 for (int i = 0; i < NumDims; ++i) {
112 m_dimensions[i] = (m_dimensions[i] - 1) * op.strides()[i] + 1;
113 }
114
115 // Remember the strides for fast division.
116 for (int i = 0; i < NumDims; ++i) {
117 m_fastStrides[i] = internal::TensorIntDivisor<Index>(m_strides[i]);
118 }
119
120 const typename TensorEvaluator<ArgType, Device>::Dimensions& input_dims = m_impl.dimensions();
121 EIGEN_IF_CONSTEXPR (static_cast<int>(Layout) == static_cast<int>(ColMajor)) {
122 m_outputStrides[0] = 1;
123 m_inputStrides[0] = 1;
124 for (int i = 1; i < NumDims; ++i) {
125 m_outputStrides[i] = m_outputStrides[i - 1] * m_dimensions[i - 1];
126 m_inputStrides[i] = m_inputStrides[i - 1] * input_dims[i - 1];
127 }
128 } else { // RowMajor
129 m_outputStrides[NumDims - 1] = 1;
130 m_inputStrides[NumDims - 1] = 1;
131 for (int i = NumDims - 2; i >= 0; --i) {
132 m_outputStrides[i] = m_outputStrides[i + 1] * m_dimensions[i + 1];
133 m_inputStrides[i] = m_inputStrides[i + 1] * input_dims[i + 1];
134 }
135 }
136 }
137
138 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Dimensions& dimensions() const { return m_dimensions; }
139
140 EIGEN_STRONG_INLINE bool evalSubExprsIfNeeded(EvaluatorPointerType /*data*/) {
141 m_impl.evalSubExprsIfNeeded(nullptr);
142 return true;
143 }
144 EIGEN_STRONG_INLINE void cleanup() { m_impl.cleanup(); }
145
146 // Computes the input index given the output index. Returns true if the output
147 // index doesn't fall into a hole.
148 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool getInputIndex(Index index, Index* inputIndex) const {
149 eigen_assert(index < dimensions().TotalSize());
150 *inputIndex = 0;
151 EIGEN_IF_CONSTEXPR (static_cast<int>(Layout) == static_cast<int>(ColMajor)) {
152 EIGEN_UNROLL_LOOP
153 for (int i = NumDims - 1; i > 0; --i) {
154 const Index idx = index / m_outputStrides[i];
155 if (idx != idx / m_fastStrides[i] * m_strides[i]) {
156 return false;
157 }
158 *inputIndex += idx / m_strides[i] * m_inputStrides[i];
159 index -= idx * m_outputStrides[i];
160 }
161 if (index != index / m_fastStrides[0] * m_strides[0]) {
162 return false;
163 }
164 *inputIndex += index / m_strides[0];
165 return true;
166 } else {
167 EIGEN_UNROLL_LOOP
168 for (int i = 0; i < NumDims - 1; ++i) {
169 const Index idx = index / m_outputStrides[i];
170 if (idx != idx / m_fastStrides[i] * m_strides[i]) {
171 return false;
172 }
173 *inputIndex += idx / m_strides[i] * m_inputStrides[i];
174 index -= idx * m_outputStrides[i];
175 }
176 if (index != index / m_fastStrides[NumDims - 1] * m_strides[NumDims - 1]) {
177 return false;
178 }
179 *inputIndex += index / m_strides[NumDims - 1];
180 }
181 return true;
182 }
183
184 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE CoeffReturnType coeff(Index index) const {
185 Index inputIndex = 0;
186 if (getInputIndex(index, &inputIndex)) {
187 return m_impl.coeff(inputIndex);
188 } else {
189 return Scalar(0);
190 }
191 }
192
193 // TODO(yangke): optimize this function so that we can detect and produce
194 // all-zero packets
195 template <int LoadMode>
196 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType packet(Index index) const {
197 EIGEN_STATIC_ASSERT((PacketSize > 1), YOU_MADE_A_PROGRAMMING_MISTAKE)
198 eigen_assert(index + PacketSize - 1 < dimensions().TotalSize());
199
200 EIGEN_ALIGN_TO_BOUNDARY(internal::unpacket_traits<PacketReturnType>::alignment)
201 std::remove_const_t<CoeffReturnType> values[PacketSize];
202 EIGEN_UNROLL_LOOP
203 for (int i = 0; i < PacketSize; ++i) {
204 values[i] = coeff(index + i);
205 }
206 PacketReturnType rslt = internal::pload<PacketReturnType>(values);
207 return rslt;
208 }
209
210 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE internal::TensorBlockResourceRequirements getResourceRequirements() const {
211 const size_t target_size = m_device.lastLevelCacheSize();
212 // One store per output coefficient for the zero fill, plus a
213 // lattice-density-weighted argument read and overwrite store, so that
214 // ThreadPool scheduling sees the true cost of expensive arguments.
215 const double density = latticeDensity();
216 const TensorOpCost cost_per_coeff =
217 density * m_impl.costPerCoeff(/*vectorized=*/false) +
218 TensorOpCost(/*bytes_loaded=*/0, /*bytes_stored=*/(1.0 + density) * sizeof(CoeffReturnType),
219 /*compute_cycles=*/0);
220 // withShapeAndSize rather than skewed(), because skewed() seeds a load and
221 // a store per coefficient that this model already accounts for.
222 return internal::TensorBlockResourceRequirements::withShapeAndSize<Scalar>(
223 internal::TensorBlockShapeType::kSkewedInnerDims, target_size, cost_per_coeff);
224 }
225
226 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorBlock block(TensorBlockDesc& desc, TensorBlockScratch& scratch,
227 bool /*root_of_expr_ast*/ = false) const {
228 constexpr bool is_col_major = static_cast<int>(Layout) == static_cast<int>(ColMajor);
229
230 // If one of the dimensions is zero, return empty block view.
231 if (desc.size() == 0) {
232 return TensorBlock(internal::TensorBlockKind::kView, nullptr, desc.dimensions());
233 }
234
235 // Everything outside the stride lattice is a hole, so zero-fill first and
236 // then copy the covered input values onto the lattice.
237 typename TensorBlock::Storage block_storage = TensorBlock::prepareStorage(desc, scratch);
238 CoeffReturnType* block_buffer = block_storage.data();
239
240 // Output coordinates of the block's corner.
241 array<Index, NumDims> coords;
242 extract_coordinates(desc.offset(), coords);
243
244 // First lattice point inside the block and the lattice extent, per dim.
245 const DSizes<Index, NumDims>& block_strides = block_storage.strides();
246 array<Index, NumDims> lattice_count;
247 Index dst_offset = 0;
248 Index src_offset = 0;
249 for (int i = 0; i < NumDims; ++i) {
250 const Index stride = m_strides[i];
251 const Index first_input = numext::div_ceil(coords[i], stride);
252 const Index first = first_input * stride; // output coordinate
253 const Index end = coords[i] + desc.dimension(i); // exclusive
254 if (first >= end) {
255 // No lattice point along this dimension: the block is all holes.
256 Map<ArrayX<CoeffReturnType>>(block_buffer, desc.size()).setZero();
257 return block_storage.AsTensorMaterializedBlock();
258 }
259 lattice_count[i] = numext::div_ceil(end - first, stride);
260 dst_offset += (first - coords[i]) * block_strides[i];
261 src_offset += first_input * m_inputStrides[i];
262 }
263
264 // The fill is pure waste when the lattice covers every coefficient, which
265 // is the whole block for identity striding.
266 bool dense = true;
267 for (int i = 0; i < NumDims; ++i) dense = dense && (lattice_count[i] == desc.dimension(i));
268 if (!dense) Map<ArrayX<CoeffReturnType>>(block_buffer, desc.size()).setZero();
269
270 // Iterate the lattice (dimensions ordered inner-most to outer-most).
271 array<BlockIteratorState, NumDims> it;
272 for (int i = 0; i < NumDims; ++i) {
273 const int dim = is_col_major ? i : NumDims - 1 - i;
274 const Index size = lattice_count[dim];
275 const Index dst_stride = block_strides[dim] * m_strides[dim];
276 const Index src_stride = m_inputStrides[dim];
277 it[i] = {/*size=*/size,
278 /*count=*/0,
279 /*dst_stride=*/dst_stride,
280 /*dst_span=*/dst_stride * (size - 1),
281 /*src_stride=*/src_stride,
282 /*src_span=*/src_stride * (size - 1)};
283 }
284
285 const Index inner_size = it[0].size;
286 const Index inner_dst_stride = it[0].dst_stride;
287 // m_inputStrides is 1 on the inner dimension in both layouts, so the
288 // argument is read as a contiguous run.
289 eigen_assert(it[0].src_stride == 1);
290 Index dst = dst_offset;
291 Index src = src_offset;
292 while (it[NumDims - 1].count < it[NumDims - 1].size) {
293 for (Index j = 0; j < inner_size; ++j) {
294 block_buffer[dst + j * inner_dst_stride] = m_impl.coeff(src + j);
295 }
296
297 EIGEN_IF_CONSTEXPR (NumDims == 1) break;
298
299 for (int i = 1; i < NumDims; ++i) {
300 if (++it[i].count < it[i].size) {
301 dst += it[i].dst_stride;
302 src += it[i].src_stride;
303 break;
304 }
305 if (i != NumDims - 1) it[i].count = 0;
306 dst -= it[i].dst_span;
307 src -= it[i].src_span;
308 }
309 }
310
311 return block_storage.AsTensorMaterializedBlock();
312 }
313
314 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorOpCost costPerCoeff(bool vectorized) const {
315 const double compute_cost = NumDims * (3 * TensorOpCost::DivCost<Index>() + 3 * TensorOpCost::MulCost<Index>() +
316 2 * TensorOpCost::AddCost<Index>());
317 if (m_dimensions.TotalSize() == 0) return TensorOpCost();
318 return m_impl.costPerCoeff(vectorized) +
319 TensorOpCost(sizeof(CoeffReturnType) * latticeDensity(), 0, compute_cost, vectorized, PacketSize);
320 }
321
322 EIGEN_DEVICE_FUNC EvaluatorPointerType data() const { return nullptr; }
323
324 protected:
325 // Fraction of output coefficients that fall on the stride lattice.
326 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE double latticeDensity() const {
327 const double output_size = static_cast<double>(m_dimensions.TotalSize());
328 if (output_size == 0) return 0.0;
329 return static_cast<double>(m_impl.dimensions().TotalSize()) / output_size;
330 }
331
332 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void extract_coordinates(Index index, array<Index, NumDims>& coords) const {
333 EIGEN_IF_CONSTEXPR (static_cast<int>(Layout) == static_cast<int>(ColMajor)) {
334 for (int i = NumDims - 1; i > 0; --i) {
335 const Index idx = index / m_outputStrides[i];
336 index -= idx * m_outputStrides[i];
337 coords[i] = idx;
338 }
339 coords[0] = index;
340 } else {
341 for (int i = 0; i < NumDims - 1; ++i) {
342 const Index idx = index / m_outputStrides[i];
343 index -= idx * m_outputStrides[i];
344 coords[i] = idx;
345 }
346 coords[NumDims - 1] = index;
347 }
348 }
349
350 Dimensions m_dimensions;
351 array<Index, NumDims> m_outputStrides;
352 array<Index, NumDims> m_inputStrides;
353 TensorEvaluator<ArgType, Device> m_impl;
354 const Strides m_strides;
355 array<internal::TensorIntDivisor<Index>, NumDims> m_fastStrides;
356 const Device EIGEN_DEVICE_REF m_device;
357
358 private:
359 struct BlockIteratorState {
360 Index size;
361 Index count;
362 Index dst_stride;
363 Index dst_span;
364 Index src_stride;
365 Index src_span;
366 };
367};
368
369} // end namespace Eigen
370
371#endif // EIGEN_TENSOR_TENSOR_INFLATION_H
The tensor base class.
Definition TensorForwardDeclarations.h:69
Tensor inflation class.
Definition TensorInflation.h:44
Namespace containing all symbols from the Eigen library.
The tensor evaluator class.
Definition TensorEvaluator.h:47