Eigen-Contrib  5.0.1
 
Loading...
Searching...
No Matches
TensorFunctors.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_FUNCTORS_H
12#define EIGEN_TENSOR_TENSOR_FUNCTORS_H
13
14// IWYU pragma: private
15#include "./InternalHeaderCheck.h"
16
17namespace Eigen {
18namespace internal {
19
23template <typename Scalar>
24struct scalar_mod_op {
25 EIGEN_DEVICE_FUNC scalar_mod_op(const Scalar& divisor) : m_divisor(divisor) {}
26 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar operator()(const Scalar& a) const { return a % m_divisor; }
27 const Scalar m_divisor;
28};
29template <typename Scalar>
30struct functor_traits<scalar_mod_op<Scalar>> {
31 enum { Cost = scalar_div_cost<Scalar, false>::value, PacketAccess = false };
32};
33
37template <typename Scalar>
38struct scalar_mod2_op {
39 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar operator()(const Scalar& a, const Scalar& b) const { return a % b; }
40};
41template <typename Scalar>
42struct functor_traits<scalar_mod2_op<Scalar>> {
43 enum { Cost = scalar_div_cost<Scalar, false>::value, PacketAccess = false };
44};
45
46template <typename Scalar>
47struct scalar_fmod_op {
48 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar operator()(const Scalar& a, const Scalar& b) const {
49 return numext::fmod(a, b);
50 }
51};
52template <typename Scalar>
53struct functor_traits<scalar_fmod_op<Scalar>> {
54 enum {
55 Cost = 13, // Reciprocal throughput of FPREM on Haswell.
56 PacketAccess = false
57 };
58};
59
60template <typename Reducer, typename Device>
61struct reducer_traits {
62 enum { Cost = 1, PacketAccess = false, IsStateful = false, IsExactlyAssociative = true };
63};
64
65// Marks reducers whose partial accumulators may be accumulated independently and combined in any
66// order. That requires reduce(value, accum) to be a pure combine -- so a partial accumulator may
67// be fed back through it -- and that combine to be associative and commutative with initialize()
68// as its identity, since the accumulators receive interleaved, not contiguous, operands.
69//
70// Neither property is implied by PacketAccess: a reducer may support packets yet transform each
71// value it accepts (e.g. a sum-of-squares reducer), in which case merging through reduce() is
72// wrong; and a pure combine may still be order-dependent (e.g. composition). Conservative
73// default; built-in combines opt in below, gated on scalar categories whose semantics Eigen
74// controls.
75template <typename Reducer>
76struct reducer_can_reorder_accumulators : std::false_type {};
77
78// Standard reduction functors
79template <typename T>
80struct SumReducer {
81 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void reduce(const T t, T* accum) const {
82 internal::scalar_sum_op<T> sum_op;
83 *accum = sum_op(*accum, t);
84 }
85 template <typename Packet>
86 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void reducePacket(const Packet& p, Packet* accum) const {
87 (*accum) = padd<Packet>(*accum, p);
88 }
89
90 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T initialize() const {
91 internal::scalar_cast_op<int, T> conv;
92 return conv(0);
93 }
94 template <typename Packet>
95 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet initializePacket() const {
96 return pset1<Packet>(initialize());
97 }
98 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T finalize(const T accum) const { return accum; }
99 template <typename Packet>
100 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet finalizePacket(const Packet& vaccum) const {
101 return vaccum;
102 }
103 template <typename Packet>
104 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T finalizeBoth(const T saccum, const Packet& vaccum) const {
105 internal::scalar_sum_op<T> sum_op;
106 return sum_op(saccum, predux(vaccum));
107 }
108};
109
110template <typename T, typename Device>
111struct reducer_traits<SumReducer<T>, Device> {
112 enum {
113 Cost = NumTraits<T>::AddCost,
114 PacketAccess = PacketType<T, Device>::HasAdd,
115 IsStateful = false,
116 IsExactlyAssociative = NumTraits<T>::IsInteger
117 };
118};
119
120template <typename T>
121struct MeanReducer {
122 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE MeanReducer() = default;
123
124 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void reduce(const T t, T* accum) {
125 internal::scalar_sum_op<T> sum_op;
126 *accum = sum_op(*accum, t);
127 scalarCount_++;
128 }
129 template <typename Packet>
130 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void reducePacket(const Packet& p, Packet* accum) {
131 (*accum) = padd<Packet>(*accum, p);
132 packetCount_++;
133 }
134
135 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T initialize() const {
136 internal::scalar_cast_op<int, T> conv;
137 return conv(0);
138 }
139 template <typename Packet>
140 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet initializePacket() const {
141 return pset1<Packet>(initialize());
142 }
143 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T finalize(const T accum) const {
144 internal::scalar_quotient_op<T> quotient_op;
145 return quotient_op(accum, T(scalarCount_));
146 }
147 template <typename Packet>
148 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet finalizePacket(const Packet& vaccum) const {
149 return pdiv(vaccum, pset1<Packet>(T(packetCount_)));
150 }
151 template <typename Packet>
152 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T finalizeBoth(const T saccum, const Packet& vaccum) const {
153 internal::scalar_sum_op<T> sum_op;
154 internal::scalar_quotient_op<T> quotient_op;
155 return quotient_op(sum_op(saccum, predux(vaccum)), T(scalarCount_ + packetCount_ * unpacket_traits<Packet>::size));
156 }
157
158 protected:
159 DenseIndex scalarCount_ = 0;
160 DenseIndex packetCount_ = 0;
161};
162
163template <typename T, typename Device>
164struct reducer_traits<MeanReducer<T>, Device> {
165 enum {
166 Cost = NumTraits<T>::AddCost,
167 PacketAccess = PacketType<T, Device>::HasAdd && PacketType<T, Device>::HasDiv && !NumTraits<T>::IsInteger,
168 IsStateful = true,
169 IsExactlyAssociative = NumTraits<T>::IsInteger
170 };
171};
172
173template <typename T, bool IsMax = true, bool IsInteger = true>
174struct MinMaxBottomValue {
175 EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE T bottom_value() { return Eigen::NumTraits<T>::lowest(); }
176};
177template <typename T>
178struct MinMaxBottomValue<T, true, false> {
179 EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE T bottom_value() { return -Eigen::NumTraits<T>::infinity(); }
180};
181template <typename T>
182struct MinMaxBottomValue<T, false, true> {
183 EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE T bottom_value() { return Eigen::NumTraits<T>::highest(); }
184};
185template <typename T>
186struct MinMaxBottomValue<T, false, false> {
187 EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE T bottom_value() { return Eigen::NumTraits<T>::infinity(); }
188};
189
190template <typename T, int NaNPropagation = PropagateFast>
191struct MaxReducer {
192 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void reduce(const T t, T* accum) const {
193 scalar_max_op<T, T, NaNPropagation> op;
194 *accum = op(t, *accum);
195 }
196 template <typename Packet>
197 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void reducePacket(const Packet& p, Packet* accum) const {
198 scalar_max_op<T, T, NaNPropagation> op;
199 (*accum) = op.packetOp(*accum, p);
200 }
201 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T initialize() const {
202 return MinMaxBottomValue<T, /*IsMax=*/true, Eigen::NumTraits<T>::IsInteger>::bottom_value();
203 }
204 template <typename Packet>
205 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet initializePacket() const {
206 return pset1<Packet>(initialize());
207 }
208 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T finalize(const T accum) const { return accum; }
209 template <typename Packet>
210 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet finalizePacket(const Packet& vaccum) const {
211 return vaccum;
212 }
213 template <typename Packet>
214 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T finalizeBoth(const T saccum, const Packet& vaccum) const {
215 scalar_max_op<T, T, NaNPropagation> op;
216 return op(saccum, op.predux(vaccum));
217 }
218};
219
220template <typename T, typename Device, int NaNPropagation>
221struct reducer_traits<MaxReducer<T, NaNPropagation>, Device> {
222 enum {
223 Cost = NumTraits<T>::AddCost,
224 PacketAccess = PacketType<T, Device>::HasMax,
225 IsStateful = false,
226 IsExactlyAssociative = (NaNPropagation != PropagateFast)
227 };
228};
229
230template <typename T, int NaNPropagation = PropagateFast>
231struct MinReducer {
232 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void reduce(const T t, T* accum) const {
233 scalar_min_op<T, T, NaNPropagation> op;
234 *accum = op(t, *accum);
235 }
236 template <typename Packet>
237 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void reducePacket(const Packet& p, Packet* accum) const {
238 scalar_min_op<T, T, NaNPropagation> op;
239 (*accum) = op.packetOp(*accum, p);
240 }
241 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T initialize() const {
242 return MinMaxBottomValue<T, /*IsMax=*/false, Eigen::NumTraits<T>::IsInteger>::bottom_value();
243 }
244 template <typename Packet>
245 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet initializePacket() const {
246 return pset1<Packet>(initialize());
247 }
248 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T finalize(const T accum) const { return accum; }
249 template <typename Packet>
250 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet finalizePacket(const Packet& vaccum) const {
251 return vaccum;
252 }
253 template <typename Packet>
254 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T finalizeBoth(const T saccum, const Packet& vaccum) const {
255 scalar_min_op<T, T, NaNPropagation> op;
256 return op(saccum, op.predux(vaccum));
257 }
258};
259
260template <typename T, typename Device, int NaNPropagation>
261struct reducer_traits<MinReducer<T, NaNPropagation>, Device> {
262 enum {
263 Cost = NumTraits<T>::AddCost,
264 PacketAccess = PacketType<T, Device>::HasMin,
265 IsStateful = false,
266 IsExactlyAssociative = (NaNPropagation != PropagateFast)
267 };
268};
269
270template <typename T>
271struct ProdReducer {
272 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void reduce(const T t, T* accum) const {
273 internal::scalar_product_op<T> prod_op;
274 (*accum) = prod_op(*accum, t);
275 }
276 template <typename Packet>
277 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void reducePacket(const Packet& p, Packet* accum) const {
278 (*accum) = pmul<Packet>(*accum, p);
279 }
280 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T initialize() const {
281 internal::scalar_cast_op<int, T> conv;
282 return conv(1);
283 }
284 template <typename Packet>
285 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet initializePacket() const {
286 return pset1<Packet>(initialize());
287 }
288 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T finalize(const T accum) const { return accum; }
289 template <typename Packet>
290 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet finalizePacket(const Packet& vaccum) const {
291 return vaccum;
292 }
293 template <typename Packet>
294 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T finalizeBoth(const T saccum, const Packet& vaccum) const {
295 internal::scalar_product_op<T> prod_op;
296 return prod_op(saccum, predux_mul(vaccum));
297 }
298};
299
300template <typename T, typename Device>
301struct reducer_traits<ProdReducer<T>, Device> {
302 enum {
303 Cost = NumTraits<T>::MulCost,
304 PacketAccess = PacketType<T, Device>::HasMul,
305 IsStateful = false,
306 IsExactlyAssociative = true
307 };
308};
309
310// Inputs have already been evaluated when reduce() is called; eager bitwise operations keep scalar reduction loops
311// branch-free.
312struct AndReducer {
313 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void reduce(bool t, bool* accum) const { *accum = *accum & t; }
314 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool initialize() const { return true; }
315 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool finalize(bool accum) const { return accum; }
316};
317
318template <typename Device>
319struct reducer_traits<AndReducer, Device> {
320 enum { Cost = 1, PacketAccess = false, IsStateful = false, IsExactlyAssociative = true };
321};
322
323struct OrReducer {
324 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void reduce(bool t, bool* accum) const { *accum = *accum | t; }
325 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool initialize() const { return false; }
326 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool finalize(bool accum) const { return accum; }
327};
328
329template <typename Device>
330struct reducer_traits<OrReducer, Device> {
331 enum { Cost = 1, PacketAccess = false, IsStateful = false, IsExactlyAssociative = true };
332};
333
334// Sum/prod reorder for scalars whose + and * commute; min/max additionally exclude custom scalars,
335// whose generic std::min/std::max keep the first operand when values compare equivalent.
336template <typename T>
337struct reducer_can_reorder_accumulators<SumReducer<T>>
338 : bool_constant<internal::is_arithmetic<T>::value || NumTraits<T>::IsComplex> {};
339template <typename T>
340struct reducer_can_reorder_accumulators<ProdReducer<T>>
341 : bool_constant<internal::is_arithmetic<T>::value || NumTraits<T>::IsComplex> {};
342template <typename T, int NaNPropagation>
343struct reducer_can_reorder_accumulators<MinReducer<T, NaNPropagation>> : internal::is_arithmetic<T> {};
344template <typename T, int NaNPropagation>
345struct reducer_can_reorder_accumulators<MaxReducer<T, NaNPropagation>> : internal::is_arithmetic<T> {};
346template <>
347struct reducer_can_reorder_accumulators<AndReducer> : std::true_type {};
348template <>
349struct reducer_can_reorder_accumulators<OrReducer> : std::true_type {};
350
351// Argmin/Argmax reducers. Returns the first occurrence if multiple locations
352// contain the same min/max value.
353template <typename T>
354struct ArgMaxPairReducer {
355 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void reduce(const T t, T* accum) const {
356 if (t.second < accum->second) {
357 return;
358 } else if (t.second > accum->second || accum->first > t.first) {
359 *accum = t;
360 }
361 }
362 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T initialize() const {
363 return T(0, NumTraits<typename T::second_type>::lowest());
364 }
365 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T finalize(const T& accum) const { return accum; }
366};
367
368template <typename T, typename Device>
369struct reducer_traits<ArgMaxPairReducer<T>, Device> {
370 enum { Cost = NumTraits<T>::AddCost, PacketAccess = false, IsStateful = false, IsExactlyAssociative = true };
371};
372
373template <typename T>
374struct ArgMinPairReducer {
375 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void reduce(const T& t, T* accum) const {
376 if (t.second > accum->second) {
377 return;
378 } else if (t.second < accum->second || accum->first > t.first) {
379 *accum = t;
380 }
381 }
382 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T initialize() const {
383 return T(0, NumTraits<typename T::second_type>::highest());
384 }
385 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T finalize(const T& accum) const { return accum; }
386};
387
388template <typename T, typename Device>
389struct reducer_traits<ArgMinPairReducer<T>, Device> {
390 enum { Cost = NumTraits<T>::AddCost, PacketAccess = false, IsStateful = false, IsExactlyAssociative = true };
391};
392
393template <typename T, typename Index, size_t NumDims>
394class GaussianGenerator {
395 public:
396 static constexpr bool PacketAccess = false;
397
398 EIGEN_DEVICE_FUNC GaussianGenerator(const array<T, NumDims>& means, const array<T, NumDims>& std_devs)
399 : m_means(means) {
400 EIGEN_UNROLL_LOOP
401 for (size_t i = 0; i < NumDims; ++i) {
402 m_two_sigmas[i] = std_devs[i] * std_devs[i] * 2;
403 }
404 }
405
406 EIGEN_DEVICE_FUNC T operator()(const array<Index, NumDims>& coordinates) const {
407 T tmp = T(0);
408 EIGEN_UNROLL_LOOP
409 for (size_t i = 0; i < NumDims; ++i) {
410 T offset = coordinates[i] - m_means[i];
411 tmp += offset * offset / m_two_sigmas[i];
412 }
413 return numext::exp(-tmp);
414 }
415
416 private:
417 array<T, NumDims> m_means;
418 array<T, NumDims> m_two_sigmas;
419};
420
421template <typename T, typename Index, size_t NumDims>
422struct functor_traits<GaussianGenerator<T, Index, NumDims>> {
423 enum {
424 Cost =
425 NumDims * (2 * NumTraits<T>::AddCost + NumTraits<T>::MulCost + functor_traits<scalar_quotient_op<T, T>>::Cost) +
426 functor_traits<scalar_exp_op<T>>::Cost,
427 PacketAccess = GaussianGenerator<T, Index, NumDims>::PacketAccess
428 };
429};
430
431template <typename Scalar>
432struct scalar_clamp_op {
433 EIGEN_DEVICE_FUNC inline scalar_clamp_op(const Scalar& _min, const Scalar& _max) : m_min(_min), m_max(_max) {}
434 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Scalar operator()(const Scalar& x) const {
435 return numext::mini(numext::maxi(x, m_min), m_max);
436 }
437 template <typename Packet>
438 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Packet packetOp(const Packet& x) const {
439 return internal::pmin(internal::pmax(x, pset1<Packet>(m_min)), pset1<Packet>(m_max));
440 }
441 const Scalar m_min;
442 const Scalar m_max;
443};
444template <typename Scalar>
445struct functor_traits<scalar_clamp_op<Scalar>> {
446 enum {
447 Cost = 2 * NumTraits<Scalar>::AddCost,
448 PacketAccess = (packet_traits<Scalar>::HasMin && packet_traits<Scalar>::HasMax)
449 };
450};
451
452} // end namespace internal
453} // end namespace Eigen
454
455#endif // EIGEN_TENSOR_TENSOR_FUNCTORS_H
Namespace containing all symbols from the Eigen library.