Eigen-Contrib  5.0.1
 
Loading...
Searching...
No Matches
TensorChipping.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_CHIPPING_H
12#define EIGEN_TENSOR_TENSOR_CHIPPING_H
13
14// IWYU pragma: private
15#include "./InternalHeaderCheck.h"
16
17namespace Eigen {
18
19namespace internal {
20template <DenseIndex DimId, typename XprType>
21struct traits<TensorChippingOp<DimId, 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 - 1;
27 static constexpr int Layout = XprTraits::Layout;
28 typedef typename XprTraits::PointerType PointerType;
29};
30
31template <DenseIndex DimId, typename XprType>
32struct eval<TensorChippingOp<DimId, XprType>, Eigen::Dense> {
33 typedef const TensorChippingOp<DimId, XprType> EIGEN_DEVICE_REF type;
34};
35
36template <DenseIndex DimId>
37struct DimensionId {
38 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE DimensionId(DenseIndex dim) {
39 EIGEN_UNUSED_VARIABLE(dim);
40 eigen_assert(dim == DimId);
41 }
42 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE DenseIndex actualDim() const { return DimId; }
43};
44template <>
45struct DimensionId<Dynamic> {
46 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE DimensionId(DenseIndex dim) : actual_dim(dim) { eigen_assert(dim >= 0); }
47 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE DenseIndex actualDim() const { return actual_dim; }
48
49 private:
50 const DenseIndex actual_dim;
51};
52
53} // end namespace internal
54
58template <DenseIndex DimId, typename XprType>
59class TensorChippingOp : public TensorBase<TensorChippingOp<DimId, XprType> > {
60 public:
62 typedef typename Eigen::internal::traits<TensorChippingOp>::Scalar Scalar;
63 typedef typename Eigen::NumTraits<Scalar>::Real RealScalar;
64 typedef typename XprType::CoeffReturnType CoeffReturnType;
65 typedef typename Eigen::internal::ref_selector<TensorChippingOp>::type Nested;
66 typedef typename Eigen::internal::traits<TensorChippingOp>::StorageKind StorageKind;
67 typedef typename Eigen::internal::traits<TensorChippingOp>::Index Index;
68
69 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorChippingOp(const XprType& expr, const Index offset, const Index dim)
70 : m_xpr(expr), m_offset(offset), m_dim(dim) {
71 eigen_assert(dim < XprType::NumDimensions && dim >= 0 && "Chip_Dim_out_of_range");
72 }
73
74 EIGEN_DEVICE_FUNC const Index offset() const { return m_offset; }
75 EIGEN_DEVICE_FUNC const Index dim() const { return m_dim.actualDim(); }
76
77 EIGEN_DEVICE_FUNC const internal::remove_all_t<typename XprType::Nested>& expression() const { return m_xpr; }
78
79 EIGEN_INHERIT_ASSIGNMENT_OPERATORS(TensorChippingOp)
80
81 protected:
82 typename XprType::Nested m_xpr;
83 const Index m_offset;
84 const internal::DimensionId<DimId> m_dim;
85};
86
87// Eval as rvalue
88template <DenseIndex DimId, typename ArgType, typename Device>
89struct TensorEvaluator<const TensorChippingOp<DimId, ArgType>, Device> {
91 static constexpr int NumInputDims =
92 internal::array_size<typename TensorEvaluator<ArgType, Device>::Dimensions>::value;
93 static constexpr int NumDims = NumInputDims - 1;
94 typedef typename XprType::Index Index;
95 typedef DSizes<Index, NumDims> Dimensions;
96 typedef typename XprType::Scalar Scalar;
97 typedef typename XprType::CoeffReturnType CoeffReturnType;
98 typedef typename PacketType<CoeffReturnType, Device>::type PacketReturnType;
99 static constexpr int PacketSize = PacketType<CoeffReturnType, Device>::size;
100 typedef StorageMemory<CoeffReturnType, Device> Storage;
101 typedef typename Storage::Type EvaluatorPointerType;
102 static constexpr int Layout = TensorEvaluator<ArgType, Device>::Layout;
103
104 enum {
105 // Alignment can't be guaranteed at compile time since it depends on the
106 // slice offsets.
107 IsAligned = false,
108 PacketAccess = TensorEvaluator<ArgType, Device>::PacketAccess,
110 // Chipping of outer-most dimension is a trivial operation, because we can
111 // read and write directly from the underlying tensor using a single offset.
112 IsOuterChipping = (Layout == ColMajor && DimId == NumInputDims - 1) || (Layout == RowMajor && DimId == 0),
113 // Chipping inner-most dimension.
114 IsInnerChipping = (Layout == ColMajor && DimId == 0) || (Layout == RowMajor && DimId == NumInputDims - 1),
115 // Prefer block access if the underlying expression prefers it, otherwise
116 // only if chipping is not trivial.
117 PreferBlockAccess = TensorEvaluator<ArgType, Device>::PreferBlockAccess || !IsOuterChipping,
118 CoordAccess = false, // to be implemented
119 RawAccess = false
120 };
121
122 typedef std::remove_const_t<Scalar> ScalarNoConst;
123
124 //===- Tensor block evaluation strategy (see TensorBlock.h) -------------===//
125 typedef internal::TensorBlockDescriptor<NumDims, Index> TensorBlockDesc;
126 typedef internal::TensorBlockScratchAllocator<Device> TensorBlockScratch;
127
128 typedef internal::TensorBlockDescriptor<NumInputDims, Index> ArgTensorBlockDesc;
129 typedef typename TensorEvaluator<const ArgType, Device>::TensorBlock ArgTensorBlock;
130
131 typedef typename internal::TensorMaterializedBlock<ScalarNoConst, NumDims, Layout, Index> TensorBlock;
132 //===--------------------------------------------------------------------===//
133
134 EIGEN_STRONG_INLINE TensorEvaluator(const XprType& op, const Device& device)
135 : m_impl(op.expression(), device), m_dim(op.dim()), m_device(device) {
136 EIGEN_STATIC_ASSERT((NumInputDims >= 1), YOU_MADE_A_PROGRAMMING_MISTAKE);
137
138 const typename TensorEvaluator<ArgType, Device>::Dimensions& input_dims = m_impl.dimensions();
139 eigen_assert(NumInputDims > m_dim.actualDim() && op.offset() < input_dims[m_dim.actualDim()]);
140
141 int j = 0;
142 for (int i = 0; i < NumInputDims; ++i) {
143 if (i != m_dim.actualDim()) {
144 m_dimensions[j] = input_dims[i];
145 ++j;
146 }
147 }
148
149 m_stride = 1;
150 m_inputStride = 1;
151 EIGEN_IF_CONSTEXPR (static_cast<int>(Layout) == static_cast<int>(ColMajor)) {
152 for (int i = 0; i < m_dim.actualDim(); ++i) {
153 m_stride *= input_dims[i];
154 m_inputStride *= input_dims[i];
155 }
156 } else {
157 for (int i = NumInputDims - 1; i > m_dim.actualDim(); --i) {
158 m_stride *= input_dims[i];
159 m_inputStride *= input_dims[i];
160 }
161 }
162 m_inputStride *= input_dims[m_dim.actualDim()];
163 m_inputOffset = m_stride * op.offset();
164
165 // Check if chipping is effectively inner or outer: product of dimensions
166 // before or after the chipped dimension is `1`.
167 Index after_chipped_dim_product = 1;
168 for (int i = static_cast<int>(m_dim.actualDim()) + 1; i < NumInputDims; ++i) {
169 after_chipped_dim_product *= input_dims[i];
170 }
171
172 Index before_chipped_dim_product = 1;
173 for (int i = 0; i < m_dim.actualDim(); ++i) {
174 before_chipped_dim_product *= input_dims[i];
175 }
176
177 EIGEN_IF_CONSTEXPR (static_cast<int>(Layout) == static_cast<int>(ColMajor)) {
178 m_isEffectivelyInnerChipping = before_chipped_dim_product == 1;
179 m_isEffectivelyOuterChipping = after_chipped_dim_product == 1;
180 } else {
181 m_isEffectivelyInnerChipping = after_chipped_dim_product == 1;
182 m_isEffectivelyOuterChipping = before_chipped_dim_product == 1;
183 }
184 }
185
186 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Dimensions& dimensions() const { return m_dimensions; }
187
188 EIGEN_STRONG_INLINE bool evalSubExprsIfNeeded(EvaluatorPointerType) {
189 m_impl.evalSubExprsIfNeeded(nullptr);
190 return true;
191 }
192
193#ifdef EIGEN_USE_THREADS
194 template <typename EvalSubExprsCallback>
195 EIGEN_STRONG_INLINE void evalSubExprsIfNeededAsync(EvaluatorPointerType /*data*/, EvalSubExprsCallback done) {
196 m_impl.evalSubExprsIfNeededAsync(nullptr, [done](bool) { done(true); });
197 }
198#endif // EIGEN_USE_THREADS
199
200 EIGEN_STRONG_INLINE void cleanup() { m_impl.cleanup(); }
201
202 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE CoeffReturnType coeff(Index index) const {
203 return m_impl.coeff(srcCoeff(index));
204 }
205
206 template <int LoadMode>
207 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType packet(Index index) const {
208 eigen_assert(index + PacketSize - 1 < dimensions().TotalSize());
209
210 EIGEN_IF_CONSTEXPR (IsInnerChipping) {
211 return packetInnerChipping<LoadMode>(index);
212 } else EIGEN_IF_CONSTEXPR (IsOuterChipping) {
213 return packetOuterChipping<LoadMode>(index);
214 } else if (m_isEffectivelyInnerChipping) {
215 return packetInnerChipping<LoadMode>(index);
216 } else if (m_isEffectivelyOuterChipping) {
217 return packetOuterChipping<LoadMode>(index);
218 } else {
219 const Index idx = index / m_stride;
220 const Index rem = index - idx * m_stride;
221 if (rem + PacketSize <= m_stride) {
222 Index inputIndex = idx * m_inputStride + m_inputOffset + rem;
223 return m_impl.template packet<LoadMode>(inputIndex);
224 } else {
225 // Cross the stride boundary. Fallback to slow path.
226 EIGEN_ALIGN_TO_BOUNDARY(internal::unpacket_traits<PacketReturnType>::alignment)
227 std::remove_const_t<CoeffReturnType> values[PacketSize];
228 EIGEN_UNROLL_LOOP
229 for (int i = 0; i < PacketSize; ++i) {
230 values[i] = coeff(index);
231 ++index;
232 }
233 PacketReturnType rslt = internal::pload<PacketReturnType>(values);
234 return rslt;
235 }
236 }
237 }
238
239 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorOpCost costPerCoeff(bool vectorized) const {
240 double cost = 0;
241 EIGEN_IF_CONSTEXPR (static_cast<int>(Layout) == static_cast<int>(ColMajor)) {
242 if (m_dim.actualDim() == 0) {
243 cost += TensorOpCost::MulCost<Index>() + TensorOpCost::AddCost<Index>();
244 } else if (m_dim.actualDim() == NumInputDims - 1) {
245 cost += TensorOpCost::AddCost<Index>();
246 } else {
247 cost +=
248 3 * TensorOpCost::MulCost<Index>() + TensorOpCost::DivCost<Index>() + 3 * TensorOpCost::AddCost<Index>();
249 }
250 } else {
251 if (m_dim.actualDim() == NumInputDims - 1) {
252 cost += TensorOpCost::MulCost<Index>() + TensorOpCost::AddCost<Index>();
253 } else if (m_dim.actualDim() == 0) {
254 cost += TensorOpCost::AddCost<Index>();
255 } else {
256 cost +=
257 3 * TensorOpCost::MulCost<Index>() + TensorOpCost::DivCost<Index>() + 3 * TensorOpCost::AddCost<Index>();
258 }
259 }
260
261 return m_impl.costPerCoeff(vectorized) + TensorOpCost(0, 0, cost, vectorized, PacketSize);
262 }
263
264 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE internal::TensorBlockResourceRequirements getResourceRequirements() const {
265 const size_t target_size = m_device.lastLevelCacheSize();
266 return internal::TensorBlockResourceRequirements::merge(
267 internal::TensorBlockResourceRequirements::skewed<Scalar>(target_size), m_impl.getResourceRequirements());
268 }
269
270 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorBlock block(TensorBlockDesc& desc, TensorBlockScratch& scratch,
271 bool root_of_expr_ast = false) const {
272 const Index chip_dim = m_dim.actualDim();
273
274 DSizes<Index, NumInputDims> input_block_dims;
275 input_block_dims[chip_dim] = 1;
276 for (int i = 0; i < NumDims; ++i) {
277 input_block_dims[i < chip_dim ? i : i + 1] = desc.dimension(i);
278 }
279
280 ArgTensorBlockDesc arg_desc(srcCoeff(desc.offset()), input_block_dims);
281
282 // Try to reuse destination buffer for materializing argument block.
283 if (desc.HasDestinationBuffer()) {
284 DSizes<Index, NumInputDims> arg_destination_strides;
285 arg_destination_strides[chip_dim] = 0; // The size-one chipped dimension does not use its stride.
286 for (int i = 0; i < NumDims; ++i) {
287 arg_destination_strides[i < chip_dim ? i : i + 1] = desc.destination().strides()[i];
288 }
289
290 arg_desc.template AddDestinationBuffer<Layout>(desc.destination().template data<ScalarNoConst>(),
291 arg_destination_strides);
292 }
293
294 ArgTensorBlock arg_block = m_impl.block(arg_desc, scratch, root_of_expr_ast);
295 if (!arg_desc.HasDestinationBuffer()) desc.DropDestinationBuffer();
296
297 if (arg_block.data() != nullptr) {
298 // Forward argument block buffer if possible.
299 return TensorBlock(arg_block.kind(), arg_block.data(), desc.dimensions());
300
301 } else {
302 // Assign argument block expression to a buffer.
303
304 // Prepare storage for the materialized chipping result.
305 const typename TensorBlock::Storage block_storage = TensorBlock::prepareStorage(desc, scratch);
306
307 typedef internal::TensorBlockAssignment<ScalarNoConst, NumInputDims, typename ArgTensorBlock::XprType, Index>
308 TensorBlockAssignment;
309
310 TensorBlockAssignment::Run(
311 TensorBlockAssignment::target(arg_desc.dimensions(), internal::strides<Layout>(arg_desc.dimensions()),
312 block_storage.data()),
313 arg_block.expr());
314
315 return block_storage.AsTensorMaterializedBlock();
316 }
317 }
318
319 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE typename Storage::Type data() const {
320 typename Storage::Type result = constCast(m_impl.data());
321 if (isOuterChipping() && result) {
322 return result + m_inputOffset;
323 } else {
324 return nullptr;
325 }
326 }
327
328 protected:
329 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Index srcCoeff(Index index) const {
330 EIGEN_IF_CONSTEXPR (IsInnerChipping) {
331 return srcCoeffInnerChipping(index);
332 } else EIGEN_IF_CONSTEXPR (IsOuterChipping) {
333 return srcCoeffOuterChipping(index);
334 } else if (m_isEffectivelyInnerChipping) {
335 return srcCoeffInnerChipping(index);
336 } else if (m_isEffectivelyOuterChipping) {
337 return srcCoeffOuterChipping(index);
338 } else {
339 const Index idx = index / m_stride;
340 Index inputIndex = idx * m_inputStride + m_inputOffset;
341 index -= idx * m_stride;
342 inputIndex += index;
343 return inputIndex;
344 }
345 }
346
347 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Index srcCoeffInnerChipping(Index index) const {
348 // m_stride is equal to 1, so let's avoid the integer division.
349 eigen_assert(m_stride == 1);
350 return index * m_inputStride + m_inputOffset;
351 }
352
353 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Index srcCoeffOuterChipping(Index index) const {
354 // m_stride is always greater than index, so let's avoid the integer division.
355 eigen_assert(m_stride > index);
356 return index + m_inputOffset;
357 }
358
359 template <int LoadMode>
360 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType packetInnerChipping(Index index) const {
361 EIGEN_ALIGN_TO_BOUNDARY(internal::unpacket_traits<PacketReturnType>::alignment)
362 std::remove_const_t<CoeffReturnType> values[PacketSize];
363 Index inputIndex = srcCoeffInnerChipping(index);
364 EIGEN_UNROLL_LOOP
365 for (int i = 0; i < PacketSize; ++i) {
366 values[i] = m_impl.coeff(inputIndex);
367 inputIndex += m_inputStride;
368 }
369 PacketReturnType rslt = internal::pload<PacketReturnType>(values);
370 return rslt;
371 }
372
373 template <int LoadMode>
374 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType packetOuterChipping(Index index) const {
375 return m_impl.template packet<LoadMode>(srcCoeffOuterChipping(index));
376 }
377
378 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool isInnerChipping() const {
379 return IsInnerChipping || m_isEffectivelyInnerChipping;
380 }
381
382 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool isOuterChipping() const {
383 return IsOuterChipping || m_isEffectivelyOuterChipping;
384 }
385
386 Dimensions m_dimensions;
387 Index m_stride;
388 Index m_inputOffset;
389 Index m_inputStride;
390 TensorEvaluator<ArgType, Device> m_impl;
391 const internal::DimensionId<DimId> m_dim;
392 const Device EIGEN_DEVICE_REF m_device;
393
394 // If product of all dimensions after or before the chipped dimension is `1`,
395 // it is effectively the same as chipping innermost or outermost dimension.
396 bool m_isEffectivelyInnerChipping;
397 bool m_isEffectivelyOuterChipping;
398};
399
400// Eval as lvalue
401template <DenseIndex DimId, typename ArgType, typename Device>
402struct TensorEvaluator<TensorChippingOp<DimId, ArgType>, Device>
403 : public TensorEvaluator<const TensorChippingOp<DimId, ArgType>, Device> {
404 typedef TensorEvaluator<const TensorChippingOp<DimId, ArgType>, Device> Base;
405 typedef TensorChippingOp<DimId, ArgType> XprType;
406 static constexpr int NumInputDims =
407 internal::array_size<typename TensorEvaluator<ArgType, Device>::Dimensions>::value;
408 static constexpr int NumDims = NumInputDims - 1;
409 typedef typename XprType::Index Index;
410 typedef DSizes<Index, NumDims> Dimensions;
411 typedef typename XprType::Scalar Scalar;
412 typedef typename XprType::CoeffReturnType CoeffReturnType;
413 typedef typename PacketType<CoeffReturnType, Device>::type PacketReturnType;
414 static constexpr int PacketSize = PacketType<CoeffReturnType, Device>::size;
415
416 enum {
417 IsAligned = false,
418 PacketAccess = TensorEvaluator<ArgType, Device>::PacketAccess,
419 BlockAccess = TensorEvaluator<ArgType, Device>::RawAccess,
420 Layout = TensorEvaluator<ArgType, Device>::Layout,
421 RawAccess = false
422 };
423
424 //===- Tensor block evaluation strategy (see TensorBlock.h) -------------===//
425 typedef internal::TensorBlockDescriptor<NumDims, Index> TensorBlockDesc;
426 //===--------------------------------------------------------------------===//
427
428 EIGEN_STRONG_INLINE TensorEvaluator(const XprType& op, const Device& device) : Base(op, device) {}
429
430 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE CoeffReturnType& coeffRef(Index index) const {
431 return this->m_impl.coeffRef(this->srcCoeff(index));
432 }
433
434 template <int StoreMode>
435 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void writePacket(Index index, const PacketReturnType& x) const {
436 EIGEN_IF_CONSTEXPR (Base::IsInnerChipping) {
437 writePacketInnerChipping<StoreMode>(index, x);
438 } else EIGEN_IF_CONSTEXPR (Base::IsOuterChipping) {
439 writePacketOuterChipping<StoreMode>(index, x);
440 } else if (this->m_isEffectivelyInnerChipping) {
441 writePacketInnerChipping<StoreMode>(index, x);
442 } else if (this->m_isEffectivelyOuterChipping) {
443 writePacketOuterChipping<StoreMode>(index, x);
444 } else {
445 const Index idx = index / this->m_stride;
446 const Index rem = index - idx * this->m_stride;
447 if (rem + PacketSize <= this->m_stride) {
448 const Index inputIndex = idx * this->m_inputStride + this->m_inputOffset + rem;
449 this->m_impl.template writePacket<StoreMode>(inputIndex, x);
450 } else {
451 // Cross stride boundary. Fallback to slow path.
452 EIGEN_ALIGN_TO_BOUNDARY(internal::unpacket_traits<PacketReturnType>::alignment)
453 std::remove_const_t<CoeffReturnType> values[PacketSize];
454 internal::pstore<CoeffReturnType, PacketReturnType>(values, x);
455 EIGEN_UNROLL_LOOP
456 for (int i = 0; i < PacketSize; ++i) {
457 this->coeffRef(index) = values[i];
458 ++index;
459 }
460 }
461 }
462 }
463
464 template <typename TensorBlock>
465 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void writeBlock(const TensorBlockDesc& desc, const TensorBlock& block) {
466 eigen_assert(this->m_impl.data() != nullptr);
467
468 const Index chip_dim = this->m_dim.actualDim();
469
470 DSizes<Index, NumInputDims> input_block_dims;
471 input_block_dims[chip_dim] = 1;
472 for (int i = 0; i < NumDims; ++i) {
473 input_block_dims[i < chip_dim ? i : i + 1] = desc.dimension(i);
474 }
475
476 typedef TensorReshapingOp<const DSizes<Index, NumInputDims>, const typename TensorBlock::XprType> TensorBlockExpr;
477
478 typedef internal::TensorBlockAssignment<Scalar, NumInputDims, TensorBlockExpr, Index> TensorBlockAssign;
479
480 TensorBlockAssign::Run(
481 TensorBlockAssign::target(input_block_dims, internal::strides<Layout>(this->m_impl.dimensions()),
482 this->m_impl.data(), this->srcCoeff(desc.offset())),
483 block.expr().reshape(input_block_dims));
484 }
485
486 private:
487 template <int StoreMode>
488 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void writePacketInnerChipping(Index index, const PacketReturnType& x) const {
489 EIGEN_ALIGN_TO_BOUNDARY(internal::unpacket_traits<PacketReturnType>::alignment)
490 std::remove_const_t<CoeffReturnType> values[PacketSize];
491 internal::pstore<CoeffReturnType, PacketReturnType>(values, x);
492 Index inputIndex = this->srcCoeffInnerChipping(index);
493 EIGEN_UNROLL_LOOP
494 for (int i = 0; i < PacketSize; ++i) {
495 this->m_impl.coeffRef(inputIndex) = values[i];
496 inputIndex += this->m_inputStride;
497 }
498 }
499
500 template <int StoreMode>
501 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void writePacketOuterChipping(Index index, const PacketReturnType& x) const {
502 this->m_impl.template writePacket<StoreMode>(this->srcCoeffOuterChipping(index), x);
503 }
504};
505
506} // end namespace Eigen
507
508#endif // EIGEN_TENSOR_TENSOR_CHIPPING_H
The tensor base class.
Definition TensorForwardDeclarations.h:69
Definition TensorChipping.h:59
Namespace containing all symbols from the Eigen library.
The tensor evaluator class.
Definition TensorEvaluator.h:47