Eigen  5.0.1
 
Loading...
Searching...
No Matches
BinaryFunctors.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2008-2010 Gael Guennebaud <gael.guennebaud@inria.fr>
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_BINARY_FUNCTORS_H
12#define EIGEN_BINARY_FUNCTORS_H
13
14// IWYU pragma: private
15#include "../InternalHeaderCheck.h"
16
17namespace Eigen {
18
19namespace internal {
20
21//---------- associative binary functors ----------
22
23template <typename Arg1, typename Arg2>
24struct binary_op_base {
25 using first_argument_type = Arg1;
26 using second_argument_type = Arg2;
27};
28
34template <typename LhsScalar, typename RhsScalar>
35struct scalar_sum_op : binary_op_base<LhsScalar, RhsScalar> {
36 using result_type = typename ScalarBinaryOpTraits<LhsScalar, RhsScalar, scalar_sum_op>::ReturnType;
37#ifdef EIGEN_SCALAR_BINARY_OP_PLUGIN
38 scalar_sum_op(){EIGEN_SCALAR_BINARY_OP_PLUGIN}
39#endif
40 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE result_type
41 operator()(const LhsScalar& a, const RhsScalar& b) const {
42 return a + b;
43 }
44 template <typename Packet>
45 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const {
46 return internal::padd(a, b);
47 }
48 template <typename Packet>
49 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE result_type predux(const Packet& a) const {
50 return internal::predux(a);
51 }
52};
53template <typename LhsScalar, typename RhsScalar>
54struct functor_traits<scalar_sum_op<LhsScalar, RhsScalar>> {
55 enum {
56 Cost = (int(NumTraits<LhsScalar>::AddCost) + int(NumTraits<RhsScalar>::AddCost)) / 2, // rough estimate!
57 PacketAccess = std::is_same<LhsScalar, RhsScalar>::value && packet_traits<LhsScalar>::HasAdd &&
58 packet_traits<RhsScalar>::HasAdd
59 // TODO: vectorize mixed sum
60 };
61};
62
63// Addition commutes for arithmetic and complex scalars. A user-defined Scalar may define a
64// non-commutative operator+ (e.g. concatenation), so it keeps the order-preserving default.
65template <typename Scalar>
66struct functor_is_commutative<scalar_sum_op<Scalar, Scalar>>
67 : bool_constant<is_arithmetic<Scalar>::value || NumTraits<Scalar>::IsComplex> {};
68
69template <>
70EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE bool scalar_sum_op<bool, bool>::operator()(const bool& a,
71 const bool& b) const {
72 return a | b;
73}
74
80template <typename LhsScalar, typename RhsScalar>
81struct scalar_product_op : binary_op_base<LhsScalar, RhsScalar> {
82 using result_type = typename ScalarBinaryOpTraits<LhsScalar, RhsScalar, scalar_product_op>::ReturnType;
83#ifdef EIGEN_SCALAR_BINARY_OP_PLUGIN
84 scalar_product_op(){EIGEN_SCALAR_BINARY_OP_PLUGIN}
85#endif
86 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE result_type
87 operator()(const LhsScalar& a, const RhsScalar& b) const {
88 return internal::mul(a, b);
89 }
90 template <typename Packet>
91 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const {
92 return internal::pmul(a, b);
93 }
94 template <typename Packet>
95 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE result_type predux(const Packet& a) const {
96 return internal::predux_mul(a);
97 }
98};
99template <typename LhsScalar, typename RhsScalar>
100struct functor_traits<scalar_product_op<LhsScalar, RhsScalar>> {
101 enum {
102 Cost = (int(NumTraits<LhsScalar>::MulCost) + int(NumTraits<RhsScalar>::MulCost)) / 2, // rough estimate!
103 PacketAccess = std::is_same<LhsScalar, RhsScalar>::value && packet_traits<LhsScalar>::HasMul &&
104 packet_traits<RhsScalar>::HasMul
105 // TODO: vectorize mixed product
106 };
107};
108
109// Multiplication commutes for arithmetic and complex scalars, but not for every user-defined
110// Scalar (e.g. quaternion-like or matrix-like types).
111template <typename Scalar>
112struct functor_is_commutative<scalar_product_op<Scalar, Scalar>>
113 : bool_constant<is_arithmetic<Scalar>::value || NumTraits<Scalar>::IsComplex> {};
114
115// Same as scalar_product_op, but its scalar path uses pmul instead of operator*. For complex scalars
116// pmul is Eigen's explicit (non-Annex-G) complex multiply, so this avoids std::complex::operator*,
117// which GCC lowers to the slow libgcc __mul?c3 call. The packet path (packetOp/predux) is inherited
118// unchanged -- it already uses pmul -- so the vectorized reduction and its precision are identical to
119// scalar_product_op (this only matters for the non-vectorized scalar tail). Used by the coeff-based
120// product's coeff(); pmul requires matching operand types, so mixed types fall back to operator*
121// (which is cheap for real*complex and never hits the complex*complex libcall).
122template <typename LhsScalar, typename RhsScalar>
123struct fast_mult_op : scalar_product_op<LhsScalar, RhsScalar> {
124 using result_type = typename scalar_product_op<LhsScalar, RhsScalar>::result_type;
125 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE result_type operator()(const LhsScalar& a, const RhsScalar& b) const {
126 return mul(a, b, bool_constant<std::is_same<LhsScalar, RhsScalar>::value>());
127 }
128
129 private:
130 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE result_type mul(const LhsScalar& a, const RhsScalar& b, std::true_type) const {
131 return pmul(a, b);
132 }
133 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE result_type mul(const LhsScalar& a, const RhsScalar& b, std::false_type) const {
134 return a * b;
135 }
136};
137template <typename LhsScalar, typename RhsScalar>
138struct functor_traits<fast_mult_op<LhsScalar, RhsScalar>> : functor_traits<scalar_product_op<LhsScalar, RhsScalar>> {};
139
140template <>
141EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE bool scalar_product_op<bool, bool>::operator()(const bool& a,
142 const bool& b) const {
143 return a & b;
144}
145
152template <typename LhsScalar, typename RhsScalar>
153struct scalar_conj_product_op : binary_op_base<LhsScalar, RhsScalar> {
154 enum { Conj = NumTraits<LhsScalar>::IsComplex };
155
156 using result_type = typename ScalarBinaryOpTraits<LhsScalar, RhsScalar, scalar_conj_product_op>::ReturnType;
157
158 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE result_type operator()(const LhsScalar& a, const RhsScalar& b) const {
159 return conj_helper<LhsScalar, RhsScalar, Conj, false>().pmul(a, b);
160 }
161
162 template <typename Packet>
163 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const {
164 return conj_helper<Packet, Packet, Conj, false>().pmul(a, b);
165 }
166};
167template <typename LhsScalar, typename RhsScalar>
168struct functor_traits<scalar_conj_product_op<LhsScalar, RhsScalar>> {
169 enum {
170 Cost = NumTraits<LhsScalar>::MulCost,
171 PacketAccess = std::is_same<LhsScalar, RhsScalar>::value && packet_traits<LhsScalar>::HasMul
172 };
173};
174
180template <typename LhsScalar, typename RhsScalar, int NaNPropagation>
181struct scalar_min_op : binary_op_base<LhsScalar, RhsScalar> {
182 using result_type = typename ScalarBinaryOpTraits<LhsScalar, RhsScalar, scalar_min_op>::ReturnType;
183 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE result_type operator()(const LhsScalar& a, const RhsScalar& b) const {
184 return internal::pmin<NaNPropagation>(a, b);
185 }
186 template <typename Packet>
187 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const {
188 return internal::pmin<NaNPropagation>(a, b);
189 }
190 template <typename Packet>
191 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE result_type predux(const Packet& a) const {
192 return internal::predux_min<NaNPropagation>(a);
193 }
194};
195
196template <typename LhsScalar, typename RhsScalar, int NaNPropagation>
197struct functor_traits<scalar_min_op<LhsScalar, RhsScalar, NaNPropagation>> {
198 enum {
199 Cost = (NumTraits<LhsScalar>::AddCost + NumTraits<RhsScalar>::AddCost) / 2,
200 PacketAccess = std::is_same<LhsScalar, RhsScalar>::value && packet_traits<LhsScalar>::HasMin
201 };
202};
203
204// min/max commute for arithmetic scalars only: PropagateNumbers and PropagateNaN treat NaN
205// operands symmetrically, PropagateFast leaves NaN results unspecified, and -0.0/+0.0 ties may
206// already resolve either way in the packet reduction paths. A custom scalar is excluded even
207// though it compares with operator<, because the generic std::min/std::max keep the first
208// operand when values compare equivalent, and equivalent custom values may be observably
209// distinct (e.g. carry a payload the comparison ignores).
210template <typename Scalar, int NaNPropagation>
211struct functor_is_commutative<scalar_min_op<Scalar, Scalar, NaNPropagation>> : is_arithmetic<Scalar> {};
212
218template <typename LhsScalar, typename RhsScalar, int NaNPropagation>
219struct scalar_max_op : binary_op_base<LhsScalar, RhsScalar> {
220 using result_type = typename ScalarBinaryOpTraits<LhsScalar, RhsScalar, scalar_max_op>::ReturnType;
221 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE result_type operator()(const LhsScalar& a, const RhsScalar& b) const {
222 return internal::pmax<NaNPropagation>(a, b);
223 }
224 template <typename Packet>
225 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const {
226 return internal::pmax<NaNPropagation>(a, b);
227 }
228 template <typename Packet>
229 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE result_type predux(const Packet& a) const {
230 return internal::predux_max<NaNPropagation>(a);
231 }
232};
233
234template <typename LhsScalar, typename RhsScalar, int NaNPropagation>
235struct functor_traits<scalar_max_op<LhsScalar, RhsScalar, NaNPropagation>> {
236 enum {
237 Cost = (NumTraits<LhsScalar>::AddCost + NumTraits<RhsScalar>::AddCost) / 2,
238 PacketAccess = std::is_same<LhsScalar, RhsScalar>::value && packet_traits<LhsScalar>::HasMax
239 };
240};
241
242template <typename Scalar, int NaNPropagation>
243struct functor_is_commutative<scalar_max_op<Scalar, Scalar, NaNPropagation>> : is_arithmetic<Scalar> {};
244
249template <typename LhsScalar, typename RhsScalar, ComparisonName cmp, bool UseTypedComparators = false>
250struct scalar_cmp_op;
251
252template <typename LhsScalar, typename RhsScalar, ComparisonName cmp, bool UseTypedComparators>
253struct functor_traits<scalar_cmp_op<LhsScalar, RhsScalar, cmp, UseTypedComparators>> {
254 enum {
255 Cost = (NumTraits<LhsScalar>::AddCost + NumTraits<RhsScalar>::AddCost) / 2,
256 PacketAccess = (UseTypedComparators || std::is_same<LhsScalar, bool>::value) &&
257 std::is_same<LhsScalar, RhsScalar>::value && packet_traits<LhsScalar>::HasCmp
258 };
259};
260
261template <typename LhsScalar, typename RhsScalar, bool UseTypedComparators>
262struct scalar_cmp_op<LhsScalar, RhsScalar, cmp_EQ, UseTypedComparators> : binary_op_base<LhsScalar, RhsScalar> {
263 using result_type = std::conditional_t<UseTypedComparators, LhsScalar, bool>;
264 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE result_type operator()(const LhsScalar& a, const RhsScalar& b) const {
265 return a == b ? result_type(1) : result_type(0);
266 }
267 template <typename Packet>
268 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const {
269 const Packet cst_one = pset1<Packet>(result_type(1));
270 return pand(pcmp_eq(a, b), cst_one);
271 }
272};
273
274template <typename LhsScalar, typename RhsScalar, bool UseTypedComparators>
275struct scalar_cmp_op<LhsScalar, RhsScalar, cmp_LT, UseTypedComparators> : binary_op_base<LhsScalar, RhsScalar> {
276 using result_type = std::conditional_t<UseTypedComparators, LhsScalar, bool>;
277 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE result_type operator()(const LhsScalar& a, const RhsScalar& b) const {
278 return a < b ? result_type(1) : result_type(0);
279 }
280 template <typename Packet>
281 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const {
282 const Packet cst_one = pset1<Packet>(result_type(1));
283 return pand(pcmp_lt(a, b), cst_one);
284 }
285};
286
287template <typename LhsScalar, typename RhsScalar, bool UseTypedComparators>
288struct scalar_cmp_op<LhsScalar, RhsScalar, cmp_LE, UseTypedComparators> : binary_op_base<LhsScalar, RhsScalar> {
289 using result_type = std::conditional_t<UseTypedComparators, LhsScalar, bool>;
290 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE result_type operator()(const LhsScalar& a, const RhsScalar& b) const {
291 return a <= b ? result_type(1) : result_type(0);
292 }
293 template <typename Packet>
294 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const {
295 const Packet cst_one = pset1<Packet>(result_type(1));
296 return pand(cst_one, pcmp_le(a, b));
297 }
298};
299
300template <typename LhsScalar, typename RhsScalar, bool UseTypedComparators>
301struct scalar_cmp_op<LhsScalar, RhsScalar, cmp_GT, UseTypedComparators> : binary_op_base<LhsScalar, RhsScalar> {
302 using result_type = std::conditional_t<UseTypedComparators, LhsScalar, bool>;
303 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE result_type operator()(const LhsScalar& a, const RhsScalar& b) const {
304 return a > b ? result_type(1) : result_type(0);
305 }
306 template <typename Packet>
307 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const {
308 const Packet cst_one = pset1<Packet>(result_type(1));
309 return pand(cst_one, pcmp_lt(b, a));
310 }
311};
312
313template <typename LhsScalar, typename RhsScalar, bool UseTypedComparators>
314struct scalar_cmp_op<LhsScalar, RhsScalar, cmp_GE, UseTypedComparators> : binary_op_base<LhsScalar, RhsScalar> {
315 using result_type = std::conditional_t<UseTypedComparators, LhsScalar, bool>;
316 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE result_type operator()(const LhsScalar& a, const RhsScalar& b) const {
317 return a >= b ? result_type(1) : result_type(0);
318 }
319 template <typename Packet>
320 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const {
321 const Packet cst_one = pset1<Packet>(result_type(1));
322 return pand(cst_one, pcmp_le(b, a));
323 }
324};
325
326template <typename LhsScalar, typename RhsScalar, bool UseTypedComparators>
327struct scalar_cmp_op<LhsScalar, RhsScalar, cmp_UNORD, UseTypedComparators> : binary_op_base<LhsScalar, RhsScalar> {
328 using result_type = std::conditional_t<UseTypedComparators, LhsScalar, bool>;
329 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE result_type operator()(const LhsScalar& a, const RhsScalar& b) const {
330 return !(a <= b || b <= a) ? result_type(1) : result_type(0);
331 }
332 template <typename Packet>
333 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const {
334 const Packet cst_one = pset1<Packet>(result_type(1));
335 return pandnot(cst_one, por(pcmp_le(a, b), pcmp_le(b, a)));
336 }
337};
338
339template <typename LhsScalar, typename RhsScalar, bool UseTypedComparators>
340struct scalar_cmp_op<LhsScalar, RhsScalar, cmp_NEQ, UseTypedComparators> : binary_op_base<LhsScalar, RhsScalar> {
341 using result_type = std::conditional_t<UseTypedComparators, LhsScalar, bool>;
342 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE result_type operator()(const LhsScalar& a, const RhsScalar& b) const {
343 return a != b ? result_type(1) : result_type(0);
344 }
345 template <typename Packet>
346 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const {
347 const Packet cst_one = pset1<Packet>(result_type(1));
348 return pandnot(cst_one, pcmp_eq(a, b));
349 }
350};
351
357template <typename Scalar>
358struct scalar_hypot_op<Scalar, Scalar> : binary_op_base<Scalar, Scalar> {
359 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Scalar operator()(const Scalar& x, const Scalar& y) const {
360 // This functor is used by hypotNorm only for which it is faster to first apply abs
361 // on all coefficients prior to reduction through hypot.
362 // This way we avoid calling abs on positive and real entries, and this also permits
363 // to seamlessly handle complexes. Otherwise we would have to handle both real and complexes
364 // through the same functor...
365 return internal::positive_real_hypot(x, y);
366 }
367};
368template <typename Scalar>
369struct functor_traits<scalar_hypot_op<Scalar, Scalar>> {
370 enum {
371 Cost = 3 * NumTraits<Scalar>::AddCost + 2 * NumTraits<Scalar>::MulCost + 2 * scalar_div_cost<Scalar, false>::value,
372 PacketAccess = false
373 };
374};
375
380template <typename Scalar, typename Exponent>
381struct scalar_pow_op : binary_op_base<Scalar, Exponent> {
382 using result_type = typename ScalarBinaryOpTraits<Scalar, Exponent, scalar_pow_op>::ReturnType;
383#ifdef EIGEN_SCALAR_BINARY_OP_PLUGIN
384 scalar_pow_op() {
385 using LhsScalar = Scalar;
386 using RhsScalar = Exponent;
387 EIGEN_SCALAR_BINARY_OP_PLUGIN
388 }
389#endif
390
391 EIGEN_DEVICE_FUNC constexpr inline result_type operator()(const Scalar& a, const Exponent& b) const {
392 return numext::pow(a, b);
393 }
394
395 template <typename Packet>
396 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const {
397 return generic_pow(a, b);
398 }
399};
400
401template <typename Scalar, typename Exponent>
402struct functor_traits<scalar_pow_op<Scalar, Exponent>> {
403 enum {
404 Cost = 5 * NumTraits<Scalar>::MulCost,
405 PacketAccess = (!NumTraits<Scalar>::IsComplex && !NumTraits<Scalar>::IsInteger && packet_traits<Scalar>::HasPow)
406 };
407};
408
409//---------- non associative binary functors ----------
410
416template <typename LhsScalar, typename RhsScalar>
417struct scalar_difference_op : binary_op_base<LhsScalar, RhsScalar> {
418 EIGEN_STATIC_ASSERT((!std::is_same<LhsScalar, bool>::value || !std::is_same<RhsScalar, bool>::value),
419 BOOLEAN_SUBTRACTION_IS_NOT_SUPPORTED__CAST_TO_A_SIGNED_INTEGER_TYPE)
420 using result_type = typename ScalarBinaryOpTraits<LhsScalar, RhsScalar, scalar_difference_op>::ReturnType;
421#ifdef EIGEN_SCALAR_BINARY_OP_PLUGIN
422 scalar_difference_op(){EIGEN_SCALAR_BINARY_OP_PLUGIN}
423#endif
424 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE result_type
425 operator()(const LhsScalar& a, const RhsScalar& b) const {
426 return a - b;
427 }
428 template <typename Packet>
429 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const {
430 return internal::psub(a, b);
431 }
432};
433template <typename LhsScalar, typename RhsScalar>
434struct functor_traits<scalar_difference_op<LhsScalar, RhsScalar>> {
435 enum {
436 Cost = (int(NumTraits<LhsScalar>::AddCost) + int(NumTraits<RhsScalar>::AddCost)) / 2,
437 PacketAccess = std::is_same<LhsScalar, RhsScalar>::value && packet_traits<LhsScalar>::HasSub &&
438 packet_traits<RhsScalar>::HasSub
439 };
440};
441
442template <typename Packet, bool IsInteger = NumTraits<typename unpacket_traits<Packet>::type>::IsInteger>
443struct maybe_raise_div_by_zero {
444 static EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE void run(Packet x) { EIGEN_UNUSED_VARIABLE(x); }
445};
446
447#ifndef EIGEN_GPU_COMPILE_PHASE
448template <typename Packet>
449struct maybe_raise_div_by_zero<Packet, true> {
450 static EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE void run(Packet x) {
451 if (EIGEN_PREDICT_FALSE(predux_any(pcmp_eq(x, pzero(x))))) {
452 // Use volatile variables to force a division by zero, which will
453 // result in the default platform behaviour (usually SIGFPE).
454 volatile typename unpacket_traits<Packet>::type zero = 0;
455 volatile typename unpacket_traits<Packet>::type val = 1;
456 val = val / zero;
457 }
458 }
459};
460#endif
461
467template <typename LhsScalar, typename RhsScalar>
468struct scalar_quotient_op : binary_op_base<LhsScalar, RhsScalar> {
469 using result_type = typename ScalarBinaryOpTraits<LhsScalar, RhsScalar, scalar_quotient_op>::ReturnType;
470#ifdef EIGEN_SCALAR_BINARY_OP_PLUGIN
471 scalar_quotient_op(){EIGEN_SCALAR_BINARY_OP_PLUGIN}
472#endif
473 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE result_type
474 operator()(const LhsScalar& a, const RhsScalar& b) const {
475 return numext::divide(a, b);
476 }
477 template <typename Packet>
478 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const {
479 return internal::pdiv(a, b);
480 }
481};
482template <typename LhsScalar, typename RhsScalar>
483struct functor_traits<scalar_quotient_op<LhsScalar, RhsScalar>> {
484 using result_type = typename scalar_quotient_op<LhsScalar, RhsScalar>::result_type;
485 enum {
486 PacketAccess = std::is_same<LhsScalar, RhsScalar>::value && packet_traits<LhsScalar>::HasDiv &&
487 packet_traits<RhsScalar>::HasDiv,
488 Cost = scalar_div_cost<result_type, PacketAccess>::value
489 };
490};
491
492// Packet16b is currently Eigen's only Boolean packet. Its loads contain valid bool objects, and its casts,
493// comparisons, and Boolean packet operations canonicalize every lane to 0 or 1. The direct bitwise fast paths below
494// rely on this invariant.
495
501template <typename Scalar>
502struct scalar_boolean_and_op {
503 using result_type = Scalar;
504 // `false` any value `a` that satisfies `a == Scalar(0)`
505 // `true` is the complement of `false`
506 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Scalar operator()(const Scalar& a, const Scalar& b) const {
507 return (a != Scalar(0)) && (b != Scalar(0)) ? Scalar(1) : Scalar(0);
508 }
509 template <typename Packet>
510 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const {
511 EIGEN_IF_CONSTEXPR ((std::is_same<Scalar, bool>::value)) {
512 return pand(a, b);
513 } else {
514 const Packet cst_one = pset1<Packet>(Scalar(1));
515 // and(a,b) == !or(!a,!b)
516 Packet not_a = pcmp_eq(a, pzero(a));
517 Packet not_b = pcmp_eq(b, pzero(b));
518 Packet a_nand_b = por(not_a, not_b);
519 return pandnot(cst_one, a_nand_b);
520 }
521 }
522};
523// Keep bool logical functors eager so scalar evaluator loops remain branch-free.
524template <>
525EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE bool scalar_boolean_and_op<bool>::operator()(const bool& a,
526 const bool& b) const {
527 return a & b;
528}
529template <typename Scalar>
530struct functor_traits<scalar_boolean_and_op<Scalar>> {
531 enum { Cost = NumTraits<Scalar>::AddCost, PacketAccess = packet_traits<Scalar>::HasCmp };
532};
533
539template <typename Scalar>
540struct scalar_boolean_or_op {
541 using result_type = Scalar;
542 // `false` any value `a` that satisfies `a == Scalar(0)`
543 // `true` is the complement of `false`
544 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Scalar operator()(const Scalar& a, const Scalar& b) const {
545 return (a != Scalar(0)) || (b != Scalar(0)) ? Scalar(1) : Scalar(0);
546 }
547 template <typename Packet>
548 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const {
549 EIGEN_IF_CONSTEXPR ((std::is_same<Scalar, bool>::value)) {
550 return por(a, b);
551 } else {
552 const Packet cst_one = pset1<Packet>(Scalar(1));
553 // if or(a,b) == 0, then a == 0 and b == 0
554 // or(a,b) == !nor(a,b)
555 Packet a_nor_b = pcmp_eq(por(a, b), pzero(a));
556 return pandnot(cst_one, a_nor_b);
557 }
558 }
559};
560template <>
561EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE bool scalar_boolean_or_op<bool>::operator()(const bool& a,
562 const bool& b) const {
563 return a | b;
564}
565template <typename Scalar>
566struct functor_traits<scalar_boolean_or_op<Scalar>> {
567 enum { Cost = NumTraits<Scalar>::AddCost, PacketAccess = packet_traits<Scalar>::HasCmp };
568};
569
575template <typename Scalar>
576struct scalar_boolean_xor_op {
577 using result_type = Scalar;
578 // `false` any value `a` that satisfies `a == Scalar(0)`
579 // `true` is the complement of `false`
580 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Scalar operator()(const Scalar& a, const Scalar& b) const {
581 return (a != Scalar(0)) != (b != Scalar(0)) ? Scalar(1) : Scalar(0);
582 }
583 template <typename Packet>
584 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const {
585 EIGEN_IF_CONSTEXPR ((std::is_same<Scalar, bool>::value)) {
586 return pxor(a, b);
587 } else {
588 const Packet cst_one = pset1<Packet>(Scalar(1));
589 // xor(a,b) == xor(!a,!b)
590 Packet not_a = pcmp_eq(a, pzero(a));
591 Packet not_b = pcmp_eq(b, pzero(b));
592 Packet a_xor_b = pxor(not_a, not_b);
593 return pand(cst_one, a_xor_b);
594 }
595 }
596};
597template <typename Scalar>
598struct functor_traits<scalar_boolean_xor_op<Scalar>> {
599 enum { Cost = NumTraits<Scalar>::AddCost, PacketAccess = packet_traits<Scalar>::HasCmp };
600};
601
602template <typename Scalar, bool IsComplex = NumTraits<Scalar>::IsComplex>
603struct bitwise_binary_impl {
604 static constexpr size_t Size = sizeof(Scalar);
605 using uint_t = typename numext::get_integer_by_size<Size>::unsigned_type;
606 static EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Scalar run_and(const Scalar& a, const Scalar& b) {
607 uint_t a_as_uint = numext::bit_cast<uint_t, Scalar>(a);
608 uint_t b_as_uint = numext::bit_cast<uint_t, Scalar>(b);
609 uint_t result = a_as_uint & b_as_uint;
610 return numext::bit_cast<Scalar, uint_t>(result);
611 }
612 static EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Scalar run_or(const Scalar& a, const Scalar& b) {
613 uint_t a_as_uint = numext::bit_cast<uint_t, Scalar>(a);
614 uint_t b_as_uint = numext::bit_cast<uint_t, Scalar>(b);
615 uint_t result = a_as_uint | b_as_uint;
616 return numext::bit_cast<Scalar, uint_t>(result);
617 }
618 static EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Scalar run_xor(const Scalar& a, const Scalar& b) {
619 uint_t a_as_uint = numext::bit_cast<uint_t, Scalar>(a);
620 uint_t b_as_uint = numext::bit_cast<uint_t, Scalar>(b);
621 uint_t result = a_as_uint ^ b_as_uint;
622 return numext::bit_cast<Scalar, uint_t>(result);
623 }
624};
625
626template <typename Scalar>
627struct bitwise_binary_impl<Scalar, true> {
628 using Real = typename NumTraits<Scalar>::Real;
629 static EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Scalar run_and(const Scalar& a, const Scalar& b) {
630 Real real_result = bitwise_binary_impl<Real>::run_and(numext::real(a), numext::real(b));
631 Real imag_result = bitwise_binary_impl<Real>::run_and(numext::imag(a), numext::imag(b));
632 return Scalar(real_result, imag_result);
633 }
634 static EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Scalar run_or(const Scalar& a, const Scalar& b) {
635 Real real_result = bitwise_binary_impl<Real>::run_or(numext::real(a), numext::real(b));
636 Real imag_result = bitwise_binary_impl<Real>::run_or(numext::imag(a), numext::imag(b));
637 return Scalar(real_result, imag_result);
638 }
639 static EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Scalar run_xor(const Scalar& a, const Scalar& b) {
640 Real real_result = bitwise_binary_impl<Real>::run_xor(numext::real(a), numext::real(b));
641 Real imag_result = bitwise_binary_impl<Real>::run_xor(numext::imag(a), numext::imag(b));
642 return Scalar(real_result, imag_result);
643 }
644};
645
651template <typename Scalar>
652struct scalar_bitwise_and_op {
653 EIGEN_STATIC_ASSERT(!NumTraits<Scalar>::RequireInitialization,
654 BITWISE OPERATIONS MAY ONLY BE PERFORMED ON PLAIN DATA TYPES)
655 EIGEN_STATIC_ASSERT((!std::is_same<Scalar, bool>::value), DONT USE BITWISE OPS ON BOOLEAN TYPES)
656 using result_type = Scalar;
657 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Scalar operator()(const Scalar& a, const Scalar& b) const {
658 return bitwise_binary_impl<Scalar>::run_and(a, b);
659 }
660 template <typename Packet>
661 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const {
662 return pand(a, b);
663 }
664};
665template <typename Scalar>
666struct functor_traits<scalar_bitwise_and_op<Scalar>> {
667 enum { Cost = NumTraits<Scalar>::AddCost, PacketAccess = true };
668};
669
675template <typename Scalar>
676struct scalar_bitwise_or_op {
677 EIGEN_STATIC_ASSERT(!NumTraits<Scalar>::RequireInitialization,
678 BITWISE OPERATIONS MAY ONLY BE PERFORMED ON PLAIN DATA TYPES)
679 EIGEN_STATIC_ASSERT((!std::is_same<Scalar, bool>::value), DONT USE BITWISE OPS ON BOOLEAN TYPES)
680 using result_type = Scalar;
681 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Scalar operator()(const Scalar& a, const Scalar& b) const {
682 return bitwise_binary_impl<Scalar>::run_or(a, b);
683 }
684 template <typename Packet>
685 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const {
686 return por(a, b);
687 }
688};
689template <typename Scalar>
690struct functor_traits<scalar_bitwise_or_op<Scalar>> {
691 enum { Cost = NumTraits<Scalar>::AddCost, PacketAccess = true };
692};
693
699template <typename Scalar>
700struct scalar_bitwise_xor_op {
701 EIGEN_STATIC_ASSERT(!NumTraits<Scalar>::RequireInitialization,
702 BITWISE OPERATIONS MAY ONLY BE PERFORMED ON PLAIN DATA TYPES)
703 EIGEN_STATIC_ASSERT((!std::is_same<Scalar, bool>::value), DONT USE BITWISE OPS ON BOOLEAN TYPES)
704 using result_type = Scalar;
705 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Scalar operator()(const Scalar& a, const Scalar& b) const {
706 return bitwise_binary_impl<Scalar>::run_xor(a, b);
707 }
708 template <typename Packet>
709 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const {
710 return pxor(a, b);
711 }
712};
713template <typename Scalar>
714struct functor_traits<scalar_bitwise_xor_op<Scalar>> {
715 enum { Cost = NumTraits<Scalar>::AddCost, PacketAccess = true };
716};
717
723template <typename LhsScalar, typename RhsScalar>
724struct scalar_absolute_difference_op : binary_op_base<LhsScalar, RhsScalar> {
725 using result_type = typename ScalarBinaryOpTraits<LhsScalar, RhsScalar, scalar_absolute_difference_op>::ReturnType;
726#ifdef EIGEN_SCALAR_BINARY_OP_PLUGIN
727 scalar_absolute_difference_op(){EIGEN_SCALAR_BINARY_OP_PLUGIN}
728#endif
729 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE result_type
730 operator()(const LhsScalar& a, const RhsScalar& b) const {
731 return numext::absdiff(a, b);
732 }
733 template <typename Packet>
734 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const {
735 return internal::pabsdiff(a, b);
736 }
737};
738template <typename LhsScalar, typename RhsScalar>
739struct functor_traits<scalar_absolute_difference_op<LhsScalar, RhsScalar>> {
740 enum {
741 Cost = (NumTraits<LhsScalar>::AddCost + NumTraits<RhsScalar>::AddCost) / 2,
742 PacketAccess = std::is_same<LhsScalar, RhsScalar>::value && packet_traits<LhsScalar>::HasAbsDiff
743 };
744};
745
746template <typename LhsScalar, typename RhsScalar>
747struct scalar_atan2_op {
748 using Scalar = LhsScalar;
749
750 static constexpr bool Enable =
751 std::is_same<LhsScalar, RhsScalar>::value && !NumTraits<Scalar>::IsInteger && !NumTraits<Scalar>::IsComplex;
752 EIGEN_STATIC_ASSERT(Enable, "LhsScalar and RhsScalar must be the same non-integer, non-complex type")
753
754 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Scalar operator()(const Scalar& y, const Scalar& x) const {
755 return numext::atan2(y, x);
756 }
757 template <typename Packet>
758 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& y, const Packet& x) const {
759 return internal::patan2(y, x);
760 }
761};
762
763template <typename LhsScalar, typename RhsScalar>
764struct functor_traits<scalar_atan2_op<LhsScalar, RhsScalar>> {
765 using Scalar = LhsScalar;
766 enum {
767 PacketAccess = std::is_same<LhsScalar, RhsScalar>::value && packet_traits<Scalar>::HasATan &&
768 packet_traits<Scalar>::HasDiv && !NumTraits<Scalar>::IsInteger && !NumTraits<Scalar>::IsComplex,
769 Cost = int(scalar_div_cost<Scalar, PacketAccess>::value) + int(functor_traits<scalar_atan_op<Scalar>>::Cost)
770 };
771};
772
773//---------- binary functors bound to a constant, thus appearing as a unary functor ----------
774
775// The following two classes permits to turn any binary functor into a unary one with one argument bound to a constant
776// value. They are analogues to the removed std::binder1st/binder2nd and are also compatible with packetOp.
777template <typename BinaryOp>
778struct bind1st_op : BinaryOp {
779 using first_argument_type = typename BinaryOp::first_argument_type;
780 using second_argument_type = typename BinaryOp::second_argument_type;
781 using result_type = typename BinaryOp::result_type;
782
783 EIGEN_DEVICE_FUNC constexpr explicit bind1st_op(const first_argument_type& val) : m_value(val) {}
784
785 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE result_type operator()(const second_argument_type& b) const {
786 return BinaryOp::operator()(m_value, b);
787 }
788
789 template <typename Packet>
790 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& b) const {
791 return BinaryOp::packetOp(internal::pset1<Packet>(m_value), b);
792 }
793
794 first_argument_type m_value;
795};
796template <typename BinaryOp>
797struct functor_traits<bind1st_op<BinaryOp>> : functor_traits<BinaryOp> {};
798
799template <typename BinaryOp>
800struct bind2nd_op : BinaryOp {
801 using first_argument_type = typename BinaryOp::first_argument_type;
802 using second_argument_type = typename BinaryOp::second_argument_type;
803 using result_type = typename BinaryOp::result_type;
804
805 EIGEN_DEVICE_FUNC constexpr explicit bind2nd_op(const second_argument_type& val) : m_value(val) {}
806
807 EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE result_type operator()(const first_argument_type& a) const {
808 return BinaryOp::operator()(a, m_value);
809 }
810
811 template <typename Packet>
812 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a) const {
813 return BinaryOp::packetOp(a, internal::pset1<Packet>(m_value));
814 }
815
816 second_argument_type m_value;
817};
818template <typename BinaryOp>
819struct functor_traits<bind2nd_op<BinaryOp>> : functor_traits<BinaryOp> {};
820
821} // end namespace internal
822
823} // end namespace Eigen
824
825#endif // EIGEN_BINARY_FUNCTORS_H