Eigen-Contrib  5.0.1
 
Loading...
Searching...
No Matches
TensorPatch.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_PATCH_H
12#define EIGEN_TENSOR_TENSOR_PATCH_H
13
14// IWYU pragma: private
15#include "./InternalHeaderCheck.h"
16
17namespace Eigen {
18
19namespace internal {
20template <typename PatchDim, typename XprType>
21struct traits<TensorPatchOp<PatchDim, XprType> > : 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 + 1;
27 static constexpr int Layout = XprTraits::Layout;
28 typedef typename XprTraits::PointerType PointerType;
29};
30
31template <typename PatchDim, typename XprType>
32struct eval<TensorPatchOp<PatchDim, XprType>, Eigen::Dense> {
33 typedef const TensorPatchOp<PatchDim, XprType>& type;
34};
35
36} // end namespace internal
37
43template <typename PatchDim, typename XprType>
44class TensorPatchOp : public TensorBase<TensorPatchOp<PatchDim, XprType>, ReadOnlyAccessors> {
45 public:
46 typedef typename Eigen::internal::traits<TensorPatchOp>::Scalar Scalar;
47 typedef typename Eigen::NumTraits<Scalar>::Real RealScalar;
48 typedef typename XprType::CoeffReturnType CoeffReturnType;
49 typedef typename Eigen::internal::ref_selector<TensorPatchOp>::type Nested;
50 typedef typename Eigen::internal::traits<TensorPatchOp>::StorageKind StorageKind;
51 typedef typename Eigen::internal::traits<TensorPatchOp>::Index Index;
52
53 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorPatchOp(const XprType& expr, const PatchDim& patch_dims)
54 : m_xpr(expr), m_patch_dims(patch_dims) {}
55
56 EIGEN_DEVICE_FUNC const PatchDim& patch_dims() const { return m_patch_dims; }
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 PatchDim m_patch_dims;
63};
64
65// Eval as rvalue
66template <typename PatchDim, typename ArgType, typename Device>
67struct TensorEvaluator<const TensorPatchOp<PatchDim, ArgType>, Device> {
69 typedef typename XprType::Index Index;
70 static constexpr int NumDims = internal::array_size<typename TensorEvaluator<ArgType, Device>::Dimensions>::value + 1;
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 = false,
82 PacketAccess = TensorEvaluator<ArgType, Device>::PacketAccess,
83 // block() reads the argument one coefficient at a time through coeff() --
84 // the contract the scalar executors already rely on for every evaluator --
85 // so it requires no capability bit from the argument (same as
86 // TensorReverse).
87 BlockAccess = NumDims > 1,
88 // The coeff/packet path pays a div/mod cascade per element; the block
89 // path copies whole in-bounds boxes patch by patch.
90 PreferBlockAccess = true,
91 CoordAccess = false,
92 RawAccess = false
93 };
94
95 //===- Tensor block evaluation strategy (see TensorBlock.h) -------------===//
96 typedef internal::TensorBlockDescriptor<NumDims, Index> TensorBlockDesc;
97 typedef internal::TensorBlockScratchAllocator<Device> TensorBlockScratch;
98 typedef typename internal::TensorMaterializedBlock<CoeffReturnType, NumDims, Layout, Index> TensorBlock;
99 //===--------------------------------------------------------------------===//
100
101 EIGEN_STRONG_INLINE TensorEvaluator(const XprType& op, const Device& device)
102 : m_impl(op.expression(), device), m_device(device) {
103 Index num_patches = 1;
104 const typename TensorEvaluator<ArgType, Device>::Dimensions& input_dims = m_impl.dimensions();
105 const PatchDim& patch_dims = op.patch_dims();
106 EIGEN_IF_CONSTEXPR (static_cast<int>(Layout) == static_cast<int>(ColMajor)) {
107 for (int i = 0; i < NumDims - 1; ++i) {
108 m_dimensions[i] = patch_dims[i];
109 num_patches *= (input_dims[i] - patch_dims[i] + 1);
110 }
111 m_dimensions[NumDims - 1] = num_patches;
112
113 m_inputStrides[0] = 1;
114 m_patchStrides[0] = 1;
115 for (int i = 1; i < NumDims - 1; ++i) {
116 m_inputStrides[i] = m_inputStrides[i - 1] * input_dims[i - 1];
117 m_patchStrides[i] = m_patchStrides[i - 1] * (input_dims[i - 1] - patch_dims[i - 1] + 1);
118 }
119 m_outputStrides[0] = 1;
120 for (int i = 1; i < NumDims; ++i) {
121 m_outputStrides[i] = m_outputStrides[i - 1] * m_dimensions[i - 1];
122 }
123 } else {
124 for (int i = 0; i < NumDims - 1; ++i) {
125 m_dimensions[i + 1] = patch_dims[i];
126 num_patches *= (input_dims[i] - patch_dims[i] + 1);
127 }
128 m_dimensions[0] = num_patches;
129
130 m_inputStrides[NumDims - 2] = 1;
131 m_patchStrides[NumDims - 2] = 1;
132 for (int i = NumDims - 3; i >= 0; --i) {
133 m_inputStrides[i] = m_inputStrides[i + 1] * input_dims[i + 1];
134 m_patchStrides[i] = m_patchStrides[i + 1] * (input_dims[i + 1] - patch_dims[i + 1] + 1);
135 }
136 m_outputStrides[NumDims - 1] = 1;
137 for (int i = NumDims - 2; i >= 0; --i) {
138 m_outputStrides[i] = m_outputStrides[i + 1] * m_dimensions[i + 1];
139 }
140 }
141 }
142
143 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Dimensions& dimensions() const { return m_dimensions; }
144
145 EIGEN_STRONG_INLINE bool evalSubExprsIfNeeded(EvaluatorPointerType /*data*/) {
146 m_impl.evalSubExprsIfNeeded(nullptr);
147 return true;
148 }
149
150 EIGEN_STRONG_INLINE void cleanup() { m_impl.cleanup(); }
151
152 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE CoeffReturnType coeff(Index index) const {
153 Index output_stride_index = (static_cast<int>(Layout) == static_cast<int>(ColMajor)) ? NumDims - 1 : 0;
154 // Find the location of the first element of the patch.
155 Index patchIndex = index / m_outputStrides[output_stride_index];
156 // Find the offset of the element wrt the location of the first element.
157 Index patchOffset = index - patchIndex * m_outputStrides[output_stride_index];
158 Index inputIndex = 0;
159 EIGEN_IF_CONSTEXPR (static_cast<int>(Layout) == static_cast<int>(ColMajor)) {
160 EIGEN_UNROLL_LOOP
161 for (int i = NumDims - 2; i > 0; --i) {
162 const Index patchIdx = patchIndex / m_patchStrides[i];
163 patchIndex -= patchIdx * m_patchStrides[i];
164 const Index offsetIdx = patchOffset / m_outputStrides[i];
165 patchOffset -= offsetIdx * m_outputStrides[i];
166 inputIndex += (patchIdx + offsetIdx) * m_inputStrides[i];
167 }
168 } else {
169 EIGEN_UNROLL_LOOP
170 for (int i = 0; i < NumDims - 2; ++i) {
171 const Index patchIdx = patchIndex / m_patchStrides[i];
172 patchIndex -= patchIdx * m_patchStrides[i];
173 const Index offsetIdx = patchOffset / m_outputStrides[i + 1];
174 patchOffset -= offsetIdx * m_outputStrides[i + 1];
175 inputIndex += (patchIdx + offsetIdx) * m_inputStrides[i];
176 }
177 }
178 inputIndex += (patchIndex + patchOffset);
179 return m_impl.coeff(inputIndex);
180 }
181
182 template <int LoadMode>
183 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType packet(Index index) const {
184 eigen_assert(index + PacketSize - 1 < dimensions().TotalSize());
185
186 Index output_stride_index = (static_cast<int>(Layout) == static_cast<int>(ColMajor)) ? NumDims - 1 : 0;
187 Index indices[2] = {index, index + PacketSize - 1};
188 Index patchIndices[2] = {indices[0] / m_outputStrides[output_stride_index],
189 indices[1] / m_outputStrides[output_stride_index]};
190 Index patchOffsets[2] = {indices[0] - patchIndices[0] * m_outputStrides[output_stride_index],
191 indices[1] - patchIndices[1] * m_outputStrides[output_stride_index]};
192
193 Index inputIndices[2] = {0, 0};
194 EIGEN_IF_CONSTEXPR (static_cast<int>(Layout) == static_cast<int>(ColMajor)) {
195 EIGEN_UNROLL_LOOP
196 for (int i = NumDims - 2; i > 0; --i) {
197 const Index patchIdx[2] = {patchIndices[0] / m_patchStrides[i], patchIndices[1] / m_patchStrides[i]};
198 patchIndices[0] -= patchIdx[0] * m_patchStrides[i];
199 patchIndices[1] -= patchIdx[1] * m_patchStrides[i];
200
201 const Index offsetIdx[2] = {patchOffsets[0] / m_outputStrides[i], patchOffsets[1] / m_outputStrides[i]};
202 patchOffsets[0] -= offsetIdx[0] * m_outputStrides[i];
203 patchOffsets[1] -= offsetIdx[1] * m_outputStrides[i];
204
205 inputIndices[0] += (patchIdx[0] + offsetIdx[0]) * m_inputStrides[i];
206 inputIndices[1] += (patchIdx[1] + offsetIdx[1]) * m_inputStrides[i];
207 }
208 } else {
209 EIGEN_UNROLL_LOOP
210 for (int i = 0; i < NumDims - 2; ++i) {
211 const Index patchIdx[2] = {patchIndices[0] / m_patchStrides[i], patchIndices[1] / m_patchStrides[i]};
212 patchIndices[0] -= patchIdx[0] * m_patchStrides[i];
213 patchIndices[1] -= patchIdx[1] * m_patchStrides[i];
214
215 const Index offsetIdx[2] = {patchOffsets[0] / m_outputStrides[i + 1], patchOffsets[1] / m_outputStrides[i + 1]};
216 patchOffsets[0] -= offsetIdx[0] * m_outputStrides[i + 1];
217 patchOffsets[1] -= offsetIdx[1] * m_outputStrides[i + 1];
218
219 inputIndices[0] += (patchIdx[0] + offsetIdx[0]) * m_inputStrides[i];
220 inputIndices[1] += (patchIdx[1] + offsetIdx[1]) * m_inputStrides[i];
221 }
222 }
223 inputIndices[0] += (patchIndices[0] + patchOffsets[0]);
224 inputIndices[1] += (patchIndices[1] + patchOffsets[1]);
225
226 if (inputIndices[1] - inputIndices[0] == PacketSize - 1) {
227 PacketReturnType rslt = m_impl.template packet<Unaligned>(inputIndices[0]);
228 return rslt;
229 } else {
230 EIGEN_ALIGN_TO_BOUNDARY(internal::unpacket_traits<PacketReturnType>::alignment)
231 CoeffReturnType values[PacketSize];
232 values[0] = m_impl.coeff(inputIndices[0]);
233 values[PacketSize - 1] = m_impl.coeff(inputIndices[1]);
234 EIGEN_UNROLL_LOOP
235 for (int i = 1; i < PacketSize - 1; ++i) {
236 values[i] = coeff(index + i);
237 }
238 PacketReturnType rslt = internal::pload<PacketReturnType>(values);
239 return rslt;
240 }
241 }
242
243 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE internal::TensorBlockResourceRequirements getResourceRequirements() const {
244 const size_t target_size = m_device.firstLevelCacheSize();
245 // Every output coefficient is read once from the argument and stored
246 // once. Pass the full cost explicitly rather than adding to skewed()'s
247 // default load+store seed, which would double-count the baseline byte
248 // traffic and halve the tile size.
249 const TensorOpCost cost_per_coeff =
250 m_impl.costPerCoeff(/*vectorized=*/false) + TensorOpCost(0, sizeof(CoeffReturnType), 0);
251 return internal::TensorBlockResourceRequirements::withShapeAndSize<Scalar>(
252 internal::TensorBlockShapeType::kSkewedInnerDims, target_size, cost_per_coeff);
253 }
254
255 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorBlock block(TensorBlockDesc& desc, TensorBlockScratch& scratch,
256 bool /*root_of_expr_ast*/ = false) const {
257 constexpr bool is_col_major = static_cast<int>(Layout) == static_cast<int>(ColMajor);
258
259 if (desc.size() == 0) {
260 return TensorBlock(internal::TensorBlockKind::kView, nullptr, desc.dimensions());
261 }
262
263 typename TensorBlock::Storage block_storage = TensorBlock::prepareStorage(desc, scratch);
264 CoeffReturnType* block_buffer = block_storage.data();
265
266 // Output coordinates of the block's corner.
267 array<Index, NumDims> coords;
268 Index remaining = desc.offset();
269 EIGEN_IF_CONSTEXPR (is_col_major) {
270 for (int i = NumDims - 1; i > 0; --i) {
271 coords[i] = remaining / m_outputStrides[i];
272 remaining -= coords[i] * m_outputStrides[i];
273 }
274 coords[0] = remaining;
275 } else {
276 for (int i = 0; i < NumDims - 1; ++i) {
277 coords[i] = remaining / m_outputStrides[i];
278 remaining -= coords[i] * m_outputStrides[i];
279 }
280 coords[NumDims - 1] = remaining;
281 }
282
283 const int patch_dim = is_col_major ? NumDims - 1 : 0;
284 const int inner_dim = is_col_major ? 0 : NumDims - 1;
285
286 const Index num_patches_in_block = desc.dimension(patch_dim);
287 // The input's inner-most stride is 1 by construction, so the inner run is
288 // contiguous on both sides.
289 const Index inner_size = desc.dimension(inner_dim);
290
291 // The within-patch dimensions between the inner-most one and the patch
292 // index, ordered inner-most to outer-most, plus the input offset the
293 // block's corner contributes on every within-patch dimension.
294 array<Index, NumDims> mid_sizes;
295 array<Index, NumDims> mid_src_stride;
296 array<Index, NumDims> mid_count;
297 int num_mid = 0;
298 Index src_corner = 0;
299 for (int k = 0; k < NumDims - 1; ++k) {
300 const int d = is_col_major ? k : NumDims - 1 - k; // output dimension
301 const int in_d = is_col_major ? d : d - 1; // input-strides index
302 src_corner += coords[d] * m_inputStrides[in_d];
303 if (k > 0) {
304 mid_sizes[num_mid] = desc.dimension(d);
305 mid_src_stride[num_mid] = m_inputStrides[in_d];
306 ++num_mid;
307 }
308 }
309
310 // The loop nest below visits the block in exactly its memory order (the
311 // storage returned by prepareStorage() is dense with the block's own
312 // layout-order strides), so the destination is one running cursor.
313 Index dst = 0;
314 for (Index p = 0; p < num_patches_in_block; ++p) {
315 // Input offset of this patch's first element.
316 Index patch_index = coords[patch_dim] + p;
317 Index src_patch = 0;
318 EIGEN_IF_CONSTEXPR (is_col_major) {
319 for (int i = NumDims - 2; i > 0; --i) {
320 const Index idx = patch_index / m_patchStrides[i];
321 patch_index -= idx * m_patchStrides[i];
322 src_patch += idx * m_inputStrides[i];
323 }
324 } else {
325 for (int i = 0; i < NumDims - 2; ++i) {
326 const Index idx = patch_index / m_patchStrides[i];
327 patch_index -= idx * m_patchStrides[i];
328 src_patch += idx * m_inputStrides[i];
329 }
330 }
331 src_patch += patch_index;
332
333 Index src = src_patch + src_corner;
334 for (int k = 0; k < num_mid; ++k) mid_count[k] = 0;
335 for (;;) {
336 for (Index j = 0; j < inner_size; ++j) {
337 block_buffer[dst + j] = m_impl.coeff(src + j);
338 }
339 dst += inner_size;
340 int k = 0;
341 for (; k < num_mid; ++k) {
342 if (++mid_count[k] < mid_sizes[k]) {
343 src += mid_src_stride[k];
344 break;
345 }
346 mid_count[k] = 0;
347 src -= mid_src_stride[k] * (mid_sizes[k] - 1);
348 }
349 if (k == num_mid) break;
350 }
351 }
352 eigen_assert(dst == desc.size());
353
354 return block_storage.AsTensorMaterializedBlock();
355 }
356
357 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorOpCost costPerCoeff(bool vectorized) const {
358 const double compute_cost = NumDims * (TensorOpCost::DivCost<Index>() + TensorOpCost::MulCost<Index>() +
359 2 * TensorOpCost::AddCost<Index>());
360 return m_impl.costPerCoeff(vectorized) + TensorOpCost(0, 0, compute_cost, vectorized, PacketSize);
361 }
362
363 EIGEN_DEVICE_FUNC EvaluatorPointerType data() const { return nullptr; }
364
365 protected:
366 Dimensions m_dimensions;
367 array<Index, NumDims> m_outputStrides;
368 array<Index, NumDims - 1> m_inputStrides;
369 array<Index, NumDims - 1> m_patchStrides;
370
371 TensorEvaluator<ArgType, Device> m_impl;
372 const Device EIGEN_DEVICE_REF m_device;
373};
374
375} // end namespace Eigen
376
377#endif // EIGEN_TENSOR_TENSOR_PATCH_H
The tensor base class.
Definition TensorForwardDeclarations.h:69
Tensor patch class.
Definition TensorPatch.h:44
Namespace containing all symbols from the Eigen library.
The tensor evaluator class.
Definition TensorEvaluator.h:47