diff options
author | Rasmus Munk Larsen <rmlarsen@google.com> | 2020-10-13 21:48:31 +0000 |
---|---|---|
committer | Rasmus Munk Larsen <rmlarsen@google.com> | 2020-10-13 21:48:31 +0000 |
commit | c6953f799b01d36f4236b64f351cc1446e0abe17 (patch) | |
tree | 9abcded97c6effc010d08787c5b43ef7bb043b54 | |
parent | 807e51528d220c0efed870f0505dea81a5776085 (diff) |
Add packet generic ops `predux_fmin`, `predux_fmin_nan`, `predux_fmax`, and `predux_fmax_nan` that implement reductions with `PropagateNaN`, and `PropagateNumbers` semantics. Add (slow) generic implementations for most reductions.
-rw-r--r-- | Eigen/src/Core/GenericPacketMath.h | 380 | ||||
-rw-r--r-- | Eigen/src/Core/functors/BinaryFunctors.h | 42 | ||||
-rw-r--r-- | test/packetmath.cpp | 62 | ||||
-rw-r--r-- | unsupported/Eigen/CXX11/src/Tensor/TensorBase.h | 22 | ||||
-rw-r--r-- | unsupported/Eigen/CXX11/src/Tensor/TensorFunctors.h | 41 | ||||
-rw-r--r-- | unsupported/test/cxx11_tensor_expr.cpp | 94 |
6 files changed, 371 insertions, 270 deletions
diff --git a/Eigen/src/Core/GenericPacketMath.h b/Eigen/src/Core/GenericPacketMath.h index 32cedd0b1..a734d99b7 100644 --- a/Eigen/src/Core/GenericPacketMath.h +++ b/Eigen/src/Core/GenericPacketMath.h @@ -215,74 +215,29 @@ pmul(const bool& a, const bool& b) { return a && b; } template<typename Packet> EIGEN_DEVICE_FUNC inline Packet pdiv(const Packet& a, const Packet& b) { return a/b; } -/** \internal \returns the min of \a a and \a b (coeff-wise). - If \a a or \b b is NaN, the return value is implementation defined. */ +/** \internal \returns one bits */ template<typename Packet> EIGEN_DEVICE_FUNC inline Packet -pmin(const Packet& a, const Packet& b) { return numext::mini(a, b); } +ptrue(const Packet& /*a*/) { Packet b; memset((void*)&b, 0xff, sizeof(b)); return b;} -/** \internal \returns the max of \a a and \a b (coeff-wise) - If \a a or \b b is NaN, the return value is implementation defined. */ +/** \internal \returns zero bits */ template<typename Packet> EIGEN_DEVICE_FUNC inline Packet -pmax(const Packet& a, const Packet& b) { return numext::maxi(a, b); } +pzero(const Packet& /*a*/) { Packet b; memset((void*)&b, 0, sizeof(b)); return b;} -/** \internal \returns the absolute value of \a a */ +/** \internal \returns a <= b as a bit mask */ template<typename Packet> EIGEN_DEVICE_FUNC inline Packet -pabs(const Packet& a) { using std::abs; return abs(a); } -template<> EIGEN_DEVICE_FUNC inline unsigned int -pabs(const unsigned int& a) { return a; } -template<> EIGEN_DEVICE_FUNC inline unsigned long -pabs(const unsigned long& a) { return a; } -template<> EIGEN_DEVICE_FUNC inline unsigned long long -pabs(const unsigned long long& a) { return a; } +pcmp_le(const Packet& a, const Packet& b) { return a<=b ? ptrue(a) : pzero(a); } -/** \internal \returns the phase angle of \a a */ +/** \internal \returns a < b as a bit mask */ template<typename Packet> EIGEN_DEVICE_FUNC inline Packet -parg(const Packet& a) { using numext::arg; return arg(a); } - - -/** \internal \returns \a a logically shifted by N bits to the right */ -template<int N> EIGEN_DEVICE_FUNC inline int -parithmetic_shift_right(const int& a) { return a >> N; } -template<int N> EIGEN_DEVICE_FUNC inline long int -parithmetic_shift_right(const long int& a) { return a >> N; } - -/** \internal \returns \a a arithmetically shifted by N bits to the right */ -template<int N> EIGEN_DEVICE_FUNC inline int -plogical_shift_right(const int& a) { return static_cast<int>(static_cast<unsigned int>(a) >> N); } -template<int N> EIGEN_DEVICE_FUNC inline long int -plogical_shift_right(const long int& a) { return static_cast<long>(static_cast<unsigned long>(a) >> N); } - -/** \internal \returns \a a shifted by N bits to the left */ -template<int N> EIGEN_DEVICE_FUNC inline int -plogical_shift_left(const int& a) { return a << N; } -template<int N> EIGEN_DEVICE_FUNC inline long int -plogical_shift_left(const long int& a) { return a << N; } - -/** \internal \returns the significant and exponent of the underlying floating point numbers - * See https://en.cppreference.com/w/cpp/numeric/math/frexp - */ -template <typename Packet> -EIGEN_DEVICE_FUNC inline Packet pfrexp(const Packet& a, Packet& exponent) { - int exp; - EIGEN_USING_STD(frexp); - Packet result = frexp(a, &exp); - exponent = static_cast<Packet>(exp); - return result; -} +pcmp_lt(const Packet& a, const Packet& b) { return a<b ? ptrue(a) : pzero(a); } -/** \internal \returns a * 2^exponent - * See https://en.cppreference.com/w/cpp/numeric/math/ldexp - */ +/** \internal \returns a == b as a bit mask */ template<typename Packet> EIGEN_DEVICE_FUNC inline Packet -pldexp(const Packet &a, const Packet &exponent) { - EIGEN_USING_STD(ldexp) - return ldexp(a, static_cast<int>(exponent)); -} +pcmp_eq(const Packet& a, const Packet& b) { return a==b ? ptrue(a) : pzero(a); } -/** \internal \returns zero bits */ +/** \internal \returns a < b or a==NaN or b==NaN as a bit mask */ template<typename Packet> EIGEN_DEVICE_FUNC inline Packet -pzero(const Packet& /*a*/) { Packet b; memset((void*)&b, 0, sizeof(b)); return b;} - +pcmp_lt_or_nan(const Packet& a, const Packet& b) { return a>=b ? pzero(a) : ptrue(a); } template<> EIGEN_DEVICE_FUNC inline float pzero<float>(const float& a) { EIGEN_UNUSED_VARIABLE(a) return 0.f; @@ -293,10 +248,6 @@ template<> EIGEN_DEVICE_FUNC inline double pzero<double>(const double& a) { return 0.; } -/** \internal \returns one bits */ -template<typename Packet> EIGEN_DEVICE_FUNC inline Packet -ptrue(const Packet& /*a*/) { Packet b; memset((void*)&b, 0xff, sizeof(b)); return b;} - template <typename RealScalar> EIGEN_DEVICE_FUNC inline std::complex<RealScalar> ptrue(const std::complex<RealScalar>& /*a*/) { RealScalar b; @@ -341,22 +292,6 @@ pxor(const Packet& a, const Packet& b) { template<typename Packet> EIGEN_DEVICE_FUNC inline Packet pandnot(const Packet& a, const Packet& b) { return pand(a, pxor(ptrue(b), b)); } -/** \internal \returns a <= b as a bit mask */ -template<typename Packet> EIGEN_DEVICE_FUNC inline Packet -pcmp_le(const Packet& a, const Packet& b) { return a<=b ? ptrue(a) : pzero(a); } - -/** \internal \returns a < b as a bit mask */ -template<typename Packet> EIGEN_DEVICE_FUNC inline Packet -pcmp_lt(const Packet& a, const Packet& b) { return a<b ? ptrue(a) : pzero(a); } - -/** \internal \returns a == b as a bit mask */ -template<typename Packet> EIGEN_DEVICE_FUNC inline Packet -pcmp_eq(const Packet& a, const Packet& b) { return a==b ? ptrue(a) : pzero(a); } - -/** \internal \returns a < b or a==NaN or b==NaN as a bit mask */ -template<typename Packet> EIGEN_DEVICE_FUNC inline Packet -pcmp_lt_or_nan(const Packet& a, const Packet& b) { return a>=b ? pzero(a) : ptrue(a); } - /** \internal \returns \a or \b for each field in packet according to \mask */ template<typename Packet> EIGEN_DEVICE_FUNC inline Packet pselect(const Packet& mask, const Packet& a, const Packet& b) { @@ -378,6 +313,119 @@ template<> EIGEN_DEVICE_FUNC inline bool pselect<bool>( return cond ? a : b; } +/** \internal \returns the min or of \a a and \a b (coeff-wise) + If either \a a or \a b are NaN, the result is implementation defined. */ +template<int NaNPropagation> +struct pminmax_impl { + template <typename Packet, typename Op> + static EIGEN_DEVICE_FUNC inline Packet run(const Packet& a, const Packet& b, Op op) { + return op(a,b); + } +}; + +/** \internal \returns the min or max of \a a and \a b (coeff-wise) + If either \a a or \a b are NaN, NaN is returned. */ +template<> +struct pminmax_impl<PropagateNaN> { + template <typename Packet, typename Op> + static EIGEN_DEVICE_FUNC inline Packet run(const Packet& a, const Packet& b, Op op) { + Packet not_nan_mask_a = pcmp_eq(a, a); + Packet not_nan_mask_b = pcmp_eq(b, b); + return pselect(not_nan_mask_a, + pselect(not_nan_mask_b, op(a, b), b), + a); + } +}; + +/** \internal \returns the min or max of \a a and \a b (coeff-wise) + If both \a a and \a b are NaN, NaN is returned. + Equivalent to std::fmin(a, b). */ +template<> +struct pminmax_impl<PropagateNumbers> { + template <typename Packet, typename Op> + static EIGEN_DEVICE_FUNC inline Packet run(const Packet& a, const Packet& b, Op op) { + Packet not_nan_mask_a = pcmp_eq(a, a); + Packet not_nan_mask_b = pcmp_eq(b, b); + return pselect(not_nan_mask_a, + pselect(not_nan_mask_b, op(a, b), a), + b); + } +}; + +/** \internal \returns the min of \a a and \a b (coeff-wise). + If \a a or \b b is NaN, the return value is implementation defined. */ +template<typename Packet> EIGEN_DEVICE_FUNC inline Packet +pmin(const Packet& a, const Packet& b) { return numext::mini(a,b); } + +/** \internal \returns the min of \a a and \a b (coeff-wise). + NaNPropagation determines the NaN propagation semantics. */ +template<int NaNPropagation, typename Packet> EIGEN_DEVICE_FUNC inline Packet +pmin(const Packet& a, const Packet& b) { return pminmax_impl<NaNPropagation>::run(a,b, pmin<Packet>); } + +/** \internal \returns the max of \a a and \a b (coeff-wise) + If \a a or \b b is NaN, the return value is implementation defined. */ +template<typename Packet> EIGEN_DEVICE_FUNC inline Packet +pmax(const Packet& a, const Packet& b) { return numext::maxi(a, b); } + +/** \internal \returns the max of \a a and \a b (coeff-wise). + NaNPropagation determines the NaN propagation semantics. */ +template<int NaNPropagation, typename Packet> EIGEN_DEVICE_FUNC inline Packet +pmax(const Packet& a, const Packet& b) { return pminmax_impl<NaNPropagation>::run(a,b, pmax<Packet>); } + +/** \internal \returns the absolute value of \a a */ +template<typename Packet> EIGEN_DEVICE_FUNC inline Packet +pabs(const Packet& a) { return numext::abs(a); } +template<> EIGEN_DEVICE_FUNC inline unsigned int +pabs(const unsigned int& a) { return a; } +template<> EIGEN_DEVICE_FUNC inline unsigned long +pabs(const unsigned long& a) { return a; } +template<> EIGEN_DEVICE_FUNC inline unsigned long long +pabs(const unsigned long long& a) { return a; } + +/** \internal \returns the phase angle of \a a */ +template<typename Packet> EIGEN_DEVICE_FUNC inline Packet +parg(const Packet& a) { using numext::arg; return arg(a); } + + +/** \internal \returns \a a logically shifted by N bits to the right */ +template<int N> EIGEN_DEVICE_FUNC inline int +parithmetic_shift_right(const int& a) { return a >> N; } +template<int N> EIGEN_DEVICE_FUNC inline long int +parithmetic_shift_right(const long int& a) { return a >> N; } + +/** \internal \returns \a a arithmetically shifted by N bits to the right */ +template<int N> EIGEN_DEVICE_FUNC inline int +plogical_shift_right(const int& a) { return static_cast<int>(static_cast<unsigned int>(a) >> N); } +template<int N> EIGEN_DEVICE_FUNC inline long int +plogical_shift_right(const long int& a) { return static_cast<long>(static_cast<unsigned long>(a) >> N); } + +/** \internal \returns \a a shifted by N bits to the left */ +template<int N> EIGEN_DEVICE_FUNC inline int +plogical_shift_left(const int& a) { return a << N; } +template<int N> EIGEN_DEVICE_FUNC inline long int +plogical_shift_left(const long int& a) { return a << N; } + +/** \internal \returns the significant and exponent of the underlying floating point numbers + * See https://en.cppreference.com/w/cpp/numeric/math/frexp + */ +template <typename Packet> +EIGEN_DEVICE_FUNC inline Packet pfrexp(const Packet& a, Packet& exponent) { + int exp; + EIGEN_USING_STD(frexp); + Packet result = frexp(a, &exp); + exponent = static_cast<Packet>(exp); + return result; +} + +/** \internal \returns a * 2^exponent + * See https://en.cppreference.com/w/cpp/numeric/math/ldexp + */ +template<typename Packet> EIGEN_DEVICE_FUNC inline Packet +pldexp(const Packet &a, const Packet &exponent) { + EIGEN_USING_STD(ldexp) + return ldexp(a, static_cast<int>(exponent)); +} + /** \internal \returns the min of \a a and \a b (coeff-wise) */ template<typename Packet> EIGEN_DEVICE_FUNC inline Packet pabsdiff(const Packet& a, const Packet& b) { return pselect(pcmp_lt(a, b), psub(b, a), psub(a, b)); } @@ -507,57 +555,6 @@ template<typename Scalar> EIGEN_DEVICE_FUNC inline void prefetch(const Scalar* a #endif } -/** \internal \returns the first element of a packet */ -template<typename Packet> EIGEN_DEVICE_FUNC inline typename unpacket_traits<Packet>::type pfirst(const Packet& a) -{ return a; } - -/** \internal \returns the sum of the elements of \a a*/ -template<typename Packet> EIGEN_DEVICE_FUNC inline typename unpacket_traits<Packet>::type predux(const Packet& a) -{ return a; } - -/** \internal \returns the sum of the elements of upper and lower half of \a a if \a a is larger than 4. - * For a packet {a0, a1, a2, a3, a4, a5, a6, a7}, it returns a half packet {a0+a4, a1+a5, a2+a6, a3+a7} - * For packet-size smaller or equal to 4, this boils down to a noop. - */ -template<typename Packet> EIGEN_DEVICE_FUNC inline -typename conditional<(unpacket_traits<Packet>::size%8)==0,typename unpacket_traits<Packet>::half,Packet>::type -predux_half_dowto4(const Packet& a) -{ return a; } - -/** \internal \returns the product of the elements of \a a */ -template<typename Packet> EIGEN_DEVICE_FUNC inline typename unpacket_traits<Packet>::type predux_mul(const Packet& a) -{ return a; } - -/** \internal \returns the min of the elements of \a a */ -template<typename Packet> EIGEN_DEVICE_FUNC inline typename unpacket_traits<Packet>::type predux_min(const Packet& a) -{ return a; } - -/** \internal \returns the max of the elements of \a a */ -template<typename Packet> EIGEN_DEVICE_FUNC inline typename unpacket_traits<Packet>::type predux_max(const Packet& a) -{ return a; } - -/** \internal \returns true if all coeffs of \a a means "true" - * It is supposed to be called on values returned by pcmp_*. - */ -// not needed yet -// template<typename Packet> EIGEN_DEVICE_FUNC inline bool predux_all(const Packet& a) -// { return bool(a); } - -/** \internal \returns true if any coeffs of \a a means "true" - * It is supposed to be called on values returned by pcmp_*. - */ -template<typename Packet> EIGEN_DEVICE_FUNC inline bool predux_any(const Packet& a) -{ - // Dirty but generic implementation where "true" is assumed to be non 0 and all the sames. - // It is expected that "true" is either: - // - Scalar(1) - // - bits full of ones (NaN for floats), - // - or first bit equals to 1 (1 for ints, smallest denormal for floats). - // For all these cases, taking the sum is just fine, and this boils down to a no-op for scalars. - typedef typename unpacket_traits<Packet>::type Scalar; - return numext::not_equal_strict(predux(a), Scalar(0)); -} - /** \internal \returns the reversed elements of \a a*/ template<typename Packet> EIGEN_DEVICE_FUNC inline Packet preverse(const Packet& a) { return a; } @@ -656,53 +653,104 @@ Packet print(const Packet& a) { using numext::rint; return rint(a); } template<typename Packet> EIGEN_DECLARE_FUNCTION_ALLOWING_MULTIPLE_DEFINITIONS Packet pceil(const Packet& a) { using numext::ceil; return ceil(a); } +/** \internal \returns the first element of a packet */ +template<typename Packet> +EIGEN_DEVICE_FUNC inline typename unpacket_traits<Packet>::type +pfirst(const Packet& a) +{ return a; } -/** \internal \returns the max of \a a and \a b (coeff-wise) - If both \a a and \a b are NaN, NaN is returned. - Equivalent to std::fmax(a, b). */ -template<typename Packet> EIGEN_DEVICE_FUNC inline Packet -pfmax(const Packet& a, const Packet& b) { - Packet not_nan_mask_a = pcmp_eq(a, a); - Packet not_nan_mask_b = pcmp_eq(b, b); - return pselect(not_nan_mask_a, - pselect(not_nan_mask_b, pmax(a, b), a), - b); +/** \internal \returns the sum of the elements of upper and lower half of \a a if \a a is larger than 4. + * For a packet {a0, a1, a2, a3, a4, a5, a6, a7}, it returns a half packet {a0+a4, a1+a5, a2+a6, a3+a7} + * For packet-size smaller or equal to 4, this boils down to a noop. + */ +template<typename Packet> +EIGEN_DEVICE_FUNC inline typename conditional<(unpacket_traits<Packet>::size%8)==0,typename unpacket_traits<Packet>::half,Packet>::type +predux_half_dowto4(const Packet& a) +{ return a; } + +// Slow generic implementation of Packet reduction. +template <typename Packet, typename Op> +EIGEN_DEVICE_FUNC inline typename unpacket_traits<Packet>::type +predux_helper(const Packet& a, Op op) { + typedef typename unpacket_traits<Packet>::type Scalar; + const size_t n = unpacket_traits<Packet>::size; + Scalar elements[n]; + pstoreu<Scalar>(elements, a); + for(size_t k = n / 2; k > 0; k /= 2) { + for(size_t i = 0; i < k; ++i) { + elements[i] = op(elements[i], elements[i + k]); + } + } + return elements[0]; } -/** \internal \returns the min of \a a and \a b (coeff-wise) - If both \a a and \a b are NaN, NaN is returned. - Equivalent to std::fmin(a, b). */ -template<typename Packet> EIGEN_DEVICE_FUNC inline Packet -pfmin(const Packet& a, const Packet& b) { - Packet not_nan_mask_a = pcmp_eq(a, a); - Packet not_nan_mask_b = pcmp_eq(b, b); - return pselect(not_nan_mask_a, - pselect(not_nan_mask_b, pmin(a, b), a), - b); +/** \internal \returns the sum of the elements of \a a*/ +template<typename Packet> +EIGEN_DEVICE_FUNC inline typename unpacket_traits<Packet>::type +predux(const Packet& a) +{ + return predux_helper(a, padd<typename unpacket_traits<Packet>::type>); } -/** \internal \returns the max of \a a and \a b (coeff-wise) - If either \a a or \a b are NaN, NaN is returned. */ -template<typename Packet> EIGEN_DEVICE_FUNC inline Packet -pfmax_nan(const Packet& a, const Packet& b) { - Packet not_nan_mask_a = pcmp_eq(a, a); - Packet not_nan_mask_b = pcmp_eq(b, b); - return pselect(not_nan_mask_a, - pselect(not_nan_mask_b, pmax(a, b), b), - a); +/** \internal \returns the product of the elements of \a a */ +template<typename Packet> +EIGEN_DEVICE_FUNC inline typename unpacket_traits<Packet>::type +predux_mul(const Packet& a) +{ + return predux_helper(a, pmul<typename unpacket_traits<Packet>::type>); } -/** \internal \returns the min of \a a and \a b (coeff-wise) - If either \a a or \a b are NaN, NaN is returned. */ -template<typename Packet> EIGEN_DEVICE_FUNC inline Packet -pfmin_nan(const Packet& a, const Packet& b) { - Packet not_nan_mask_a = pcmp_eq(a, a); - Packet not_nan_mask_b = pcmp_eq(b, b); - return pselect(not_nan_mask_a, - pselect(not_nan_mask_b, pmin(a, b), b), - a); +/** \internal \returns the min of the elements of \a a */ +template<typename Packet> +EIGEN_DEVICE_FUNC inline typename unpacket_traits<Packet>::type +predux_min(const Packet& a) +{ + return predux_helper(a, pmin<PropagateFast, typename unpacket_traits<Packet>::type>); } +template<int NaNPropagation, typename Packet> +EIGEN_DEVICE_FUNC inline typename unpacket_traits<Packet>::type +predux_min(const Packet& a) +{ + return predux_helper(a, pmin<NaNPropagation, typename unpacket_traits<Packet>::type>); +} + +/** \internal \returns the max of the elements of \a a */ +template<typename Packet> +EIGEN_DEVICE_FUNC inline typename unpacket_traits<Packet>::type +predux_max(const Packet& a) +{ + return predux_helper(a, pmax<PropagateFast, typename unpacket_traits<Packet>::type>); +} + +template<int NaNPropagation, typename Packet> +EIGEN_DEVICE_FUNC inline typename unpacket_traits<Packet>::type +predux_max(const Packet& a) +{ + return predux_helper(a, pmax<NaNPropagation, typename unpacket_traits<Packet>::type>); +} + +/** \internal \returns true if all coeffs of \a a means "true" + * It is supposed to be called on values returned by pcmp_*. + */ +// not needed yet +// template<typename Packet> EIGEN_DEVICE_FUNC inline bool predux_all(const Packet& a) +// { return bool(a); } + +/** \internal \returns true if any coeffs of \a a means "true" + * It is supposed to be called on values returned by pcmp_*. + */ +template<typename Packet> EIGEN_DEVICE_FUNC inline bool predux_any(const Packet& a) +{ + // Dirty but generic implementation where "true" is assumed to be non 0 and all the sames. + // It is expected that "true" is either: + // - Scalar(1) + // - bits full of ones (NaN for floats), + // - or first bit equals to 1 (1 for ints, smallest denormal for floats). + // For all these cases, taking the sum is just fine, and this boils down to a no-op for scalars. + typedef typename unpacket_traits<Packet>::type Scalar; + return numext::not_equal_strict(predux(a), Scalar(0)); +} /*************************************************************************** * The following functions might not have to be overwritten for vectorized types diff --git a/Eigen/src/Core/functors/BinaryFunctors.h b/Eigen/src/Core/functors/BinaryFunctors.h index 55650bb8d..f3509c4b9 100644 --- a/Eigen/src/Core/functors/BinaryFunctors.h +++ b/Eigen/src/Core/functors/BinaryFunctors.h @@ -140,29 +140,18 @@ struct scalar_min_op : binary_op_base<LhsScalar,RhsScalar> typedef typename ScalarBinaryOpTraits<LhsScalar,RhsScalar,scalar_min_op>::ReturnType result_type; EIGEN_EMPTY_STRUCT_CTOR(scalar_min_op) EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE result_type operator() (const LhsScalar& a, const RhsScalar& b) const { - if (NaNPropagation == PropagateFast) { - return numext::mini(a, b); - } else if (NaNPropagation == PropagateNumbers) { - return internal::pfmin(a,b); - } else if (NaNPropagation == PropagateNaN) { - return internal::pfmin_nan(a,b); - } + return internal::pmin<NaNPropagation>(a, b); } template<typename Packet> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const { - if (NaNPropagation == PropagateFast) { - return internal::pmin(a,b); - } else if (NaNPropagation == PropagateNumbers) { - return internal::pfmin(a,b); - } else if (NaNPropagation == PropagateNaN) { - return internal::pfmin_nan(a,b); - } + return internal::pmin<NaNPropagation>(a,b); } - // TODO(rmlarsen): Handle all NaN propagation semantics reductions. template<typename Packet> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE result_type predux(const Packet& a) const - { return internal::predux_min(a); } + { + return internal::predux_min<NaNPropagation>(a); + } }; template<typename LhsScalar,typename RhsScalar, int NaNPropagation> @@ -184,29 +173,18 @@ struct scalar_max_op : binary_op_base<LhsScalar,RhsScalar> typedef typename ScalarBinaryOpTraits<LhsScalar,RhsScalar,scalar_max_op>::ReturnType result_type; EIGEN_EMPTY_STRUCT_CTOR(scalar_max_op) EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE result_type operator() (const LhsScalar& a, const RhsScalar& b) const { - if (NaNPropagation == PropagateFast) { - return numext::maxi(a, b); - } else if (NaNPropagation == PropagateNumbers) { - return internal::pfmax(a,b); - } else if (NaNPropagation == PropagateNaN) { - return internal::pfmax_nan(a,b); - } + return internal::pmax<NaNPropagation>(a,b); } template<typename Packet> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const { - if (NaNPropagation == PropagateFast) { - return internal::pmax(a,b); - } else if (NaNPropagation == PropagateNumbers) { - return internal::pfmax(a,b); - } else if (NaNPropagation == PropagateNaN) { - return internal::pfmax_nan(a,b); - } + return internal::pmax<NaNPropagation>(a,b); } - // TODO(rmlarsen): Handle all NaN propagation semantics reductions. template<typename Packet> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE result_type predux(const Packet& a) const - { return internal::predux_max(a); } + { + return internal::predux_max<NaNPropagation>(a); + } }; template<typename LhsScalar,typename RhsScalar, int NaNPropagation> diff --git a/test/packetmath.cpp b/test/packetmath.cpp index 53c41c967..c6a1648ba 100644 --- a/test/packetmath.cpp +++ b/test/packetmath.cpp @@ -801,10 +801,6 @@ void packetmath_notcomplex() { Array<Scalar, Dynamic, 1>::Map(data1, PacketSize * 4).setRandom(); - ref[0] = data1[0]; - for (int i = 0; i < PacketSize; ++i) ref[0] = (std::min)(ref[0], data1[i]); - VERIFY(internal::isApprox(ref[0], internal::predux_min(internal::pload<Packet>(data1))) && "internal::predux_min"); - VERIFY((!PacketTraits::Vectorizable) || PacketTraits::HasMin); VERIFY((!PacketTraits::Vectorizable) || PacketTraits::HasMax); @@ -817,13 +813,16 @@ void packetmath_notcomplex() { using ::fmin; using ::fmax; #endif - CHECK_CWISE2_IF(PacketTraits::HasMin, fmin, internal::pfmin); - CHECK_CWISE2_IF(PacketTraits::HasMax, fmax, internal::pfmax); + CHECK_CWISE2_IF(PacketTraits::HasMin, fmin, (internal::pmin<PropagateNumbers>)); + CHECK_CWISE2_IF(PacketTraits::HasMax, fmax, internal::pmax<PropagateNumbers>); CHECK_CWISE1(numext::abs, internal::pabs); CHECK_CWISE2_IF(PacketTraits::HasAbsDiff, REF_ABS_DIFF, internal::pabsdiff); ref[0] = data1[0]; - for (int i = 0; i < PacketSize; ++i) ref[0] = (std::max)(ref[0], data1[i]); + for (int i = 0; i < PacketSize; ++i) ref[0] = internal::pmin(ref[0], data1[i]); + VERIFY(internal::isApprox(ref[0], internal::predux_min(internal::pload<Packet>(data1))) && "internal::predux_min"); + ref[0] = data1[0]; + for (int i = 0; i < PacketSize; ++i) ref[0] = internal::pmax(ref[0], data1[i]); VERIFY(internal::isApprox(ref[0], internal::predux_max(internal::pload<Packet>(data1))) && "internal::predux_max"); for (int i = 0; i < PacketSize; ++i) ref[i] = data1[0] + Scalar(i); @@ -852,16 +851,47 @@ void packetmath_notcomplex() { } } - for (int i = 0; i < PacketSize; ++i) { - data1[i] = internal::random<bool>() ? std::numeric_limits<Scalar>::quiet_NaN() : Scalar(0); - data1[i + PacketSize] = internal::random<bool>() ? std::numeric_limits<Scalar>::quiet_NaN() : Scalar(0); + + // Test NaN propagation. + if (!NumTraits<Scalar>::IsInteger) { + // Test reductions with no NaNs. + ref[0] = data1[0]; + for (int i = 0; i < PacketSize; ++i) ref[0] = internal::pmin<PropagateNumbers>(ref[0], data1[i]); + VERIFY(internal::isApprox(ref[0], internal::predux_min<PropagateNumbers>(internal::pload<Packet>(data1))) && "internal::predux_min<PropagateNumbers>"); + ref[0] = data1[0]; + for (int i = 0; i < PacketSize; ++i) ref[0] = internal::pmin<PropagateNaN>(ref[0], data1[i]); + VERIFY(internal::isApprox(ref[0], internal::predux_min<PropagateNaN>(internal::pload<Packet>(data1))) && "internal::predux_min<PropagateNaN>"); + ref[0] = data1[0]; + for (int i = 0; i < PacketSize; ++i) ref[0] = internal::pmax<PropagateNumbers>(ref[0], data1[i]); + VERIFY(internal::isApprox(ref[0], internal::predux_max<PropagateNumbers>(internal::pload<Packet>(data1))) && "internal::predux_max<PropagateNumbers>"); + ref[0] = data1[0]; + for (int i = 0; i < PacketSize; ++i) ref[0] = internal::pmax<PropagateNaN>(ref[0], data1[i]); + VERIFY(internal::isApprox(ref[0], internal::predux_max<PropagateNaN>(internal::pload<Packet>(data1))) && "internal::predux_max<PropagateNumbers>"); + // A single NaN. + const size_t index = std::numeric_limits<size_t>::quiet_NaN() % PacketSize; + data1[index] = std::numeric_limits<Scalar>::quiet_NaN(); + VERIFY(PacketSize==1 || !(numext::isnan)(internal::predux_min<PropagateNumbers>(internal::pload<Packet>(data1)))); + VERIFY((numext::isnan)(internal::predux_min<PropagateNaN>(internal::pload<Packet>(data1)))); + VERIFY(PacketSize==1 || !(numext::isnan)(internal::predux_max<PropagateNumbers>(internal::pload<Packet>(data1)))); + VERIFY((numext::isnan)(internal::predux_max<PropagateNaN>(internal::pload<Packet>(data1)))); + // All NaNs. + for (int i = 0; i < 4 * PacketSize; ++i) data1[i] = std::numeric_limits<Scalar>::quiet_NaN(); + VERIFY((numext::isnan)(internal::predux_min<PropagateNumbers>(internal::pload<Packet>(data1)))); + VERIFY((numext::isnan)(internal::predux_min<PropagateNaN>(internal::pload<Packet>(data1)))); + VERIFY((numext::isnan)(internal::predux_max<PropagateNumbers>(internal::pload<Packet>(data1)))); + VERIFY((numext::isnan)(internal::predux_max<PropagateNaN>(internal::pload<Packet>(data1)))); + + // Test NaN propagation for coefficient-wise min and max. + for (int i = 0; i < PacketSize; ++i) { + data1[i] = internal::random<bool>() ? std::numeric_limits<Scalar>::quiet_NaN() : Scalar(0); + data1[i + PacketSize] = internal::random<bool>() ? std::numeric_limits<Scalar>::quiet_NaN() : Scalar(0); + } + // Note: NaN propagation is implementation defined for pmin/pmax, so we do not test it here. + CHECK_CWISE2_IF(PacketTraits::HasMin, fmin, (internal::pmin<PropagateNumbers>)); + CHECK_CWISE2_IF(PacketTraits::HasMax, fmax, internal::pmax<PropagateNumbers>); + CHECK_CWISE2_IF(PacketTraits::HasMin, propagate_nan_min, (internal::pmin<PropagateNaN>)); + CHECK_CWISE2_IF(PacketTraits::HasMax, propagate_nan_max, internal::pmax<PropagateNaN>); } - // Test NaN propagation for pfmin and pfmax. It should be equivalent to std::fmin. - // Note: NaN propagation is implementation defined for pmin/pmax, so we do not test it here. - CHECK_CWISE2_IF(PacketTraits::HasMin, fmin, internal::pfmin); - CHECK_CWISE2_IF(PacketTraits::HasMax, fmax, internal::pfmax); - CHECK_CWISE2_IF(PacketTraits::HasMin, propagate_nan_min, internal::pfmin_nan); - CHECK_CWISE2_IF(PacketTraits::HasMax, propagate_nan_max, internal::pfmax_nan); } template <> diff --git a/unsupported/Eigen/CXX11/src/Tensor/TensorBase.h b/unsupported/Eigen/CXX11/src/Tensor/TensorBase.h index ef332dd19..3a70d8517 100644 --- a/unsupported/Eigen/CXX11/src/Tensor/TensorBase.h +++ b/unsupported/Eigen/CXX11/src/Tensor/TensorBase.h @@ -682,28 +682,30 @@ class TensorBase<Derived, ReadOnlyAccessors> return TensorReductionOp<internal::ProdReducer<CoeffReturnType>, const DimensionList<Index, NumDimensions>, const Derived>(derived(), in_dims, internal::ProdReducer<CoeffReturnType>()); } - template <typename Dims> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE - const TensorReductionOp<internal::MaxReducer<CoeffReturnType>, const Dims, const Derived> + template <typename Dims,int NanPropagation=PropagateFast> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE + const TensorReductionOp<internal::MaxReducer<CoeffReturnType,NanPropagation>, const Dims, const Derived> maximum(const Dims& dims) const { - return TensorReductionOp<internal::MaxReducer<CoeffReturnType>, const Dims, const Derived>(derived(), dims, internal::MaxReducer<CoeffReturnType>()); + return TensorReductionOp<internal::MaxReducer<CoeffReturnType,NanPropagation>, const Dims, const Derived>(derived(), dims, internal::MaxReducer<CoeffReturnType,NanPropagation>()); } - const TensorReductionOp<internal::MaxReducer<CoeffReturnType>, const DimensionList<Index, NumDimensions>, const Derived> + template <int NanPropagation=PropagateFast> + const TensorReductionOp<internal::MaxReducer<CoeffReturnType,NanPropagation>, const DimensionList<Index, NumDimensions>, const Derived> maximum() const { DimensionList<Index, NumDimensions> in_dims; - return TensorReductionOp<internal::MaxReducer<CoeffReturnType>, const DimensionList<Index, NumDimensions>, const Derived>(derived(), in_dims, internal::MaxReducer<CoeffReturnType>()); + return TensorReductionOp<internal::MaxReducer<CoeffReturnType,NanPropagation>, const DimensionList<Index, NumDimensions>, const Derived>(derived(), in_dims, internal::MaxReducer<CoeffReturnType,NanPropagation>()); } - template <typename Dims> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE - const TensorReductionOp<internal::MinReducer<CoeffReturnType>, const Dims, const Derived> + template <typename Dims,int NanPropagation=PropagateFast> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE + const TensorReductionOp<internal::MinReducer<CoeffReturnType,NanPropagation>, const Dims, const Derived> minimum(const Dims& dims) const { - return TensorReductionOp<internal::MinReducer<CoeffReturnType>, const Dims, const Derived>(derived(), dims, internal::MinReducer<CoeffReturnType>()); + return TensorReductionOp<internal::MinReducer<CoeffReturnType,NanPropagation>, const Dims, const Derived>(derived(), dims, internal::MinReducer<CoeffReturnType,NanPropagation>()); } - const TensorReductionOp<internal::MinReducer<CoeffReturnType>, const DimensionList<Index, NumDimensions>, const Derived> + template <int NanPropagation=PropagateFast> + const TensorReductionOp<internal::MinReducer<CoeffReturnType,NanPropagation>, const DimensionList<Index, NumDimensions>, const Derived> minimum() const { DimensionList<Index, NumDimensions> in_dims; - return TensorReductionOp<internal::MinReducer<CoeffReturnType>, const DimensionList<Index, NumDimensions>, const Derived>(derived(), in_dims, internal::MinReducer<CoeffReturnType>()); + return TensorReductionOp<internal::MinReducer<CoeffReturnType,NanPropagation>, const DimensionList<Index, NumDimensions>, const Derived>(derived(), in_dims, internal::MinReducer<CoeffReturnType,NanPropagation>()); } template <typename Dims> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE diff --git a/unsupported/Eigen/CXX11/src/Tensor/TensorFunctors.h b/unsupported/Eigen/CXX11/src/Tensor/TensorFunctors.h index 2edc45f1a..fd8fa00fa 100644 --- a/unsupported/Eigen/CXX11/src/Tensor/TensorFunctors.h +++ b/unsupported/Eigen/CXX11/src/Tensor/TensorFunctors.h @@ -192,17 +192,19 @@ struct MinMaxBottomValue<T, false, false> { }; -template <typename T> struct MaxReducer +template <typename T, int NaNPropagation=PropagateFast> struct MaxReducer { EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void reduce(const T t, T* accum) const { - if (t > *accum) { *accum = t; } + scalar_max_op<T, T, NaNPropagation> op; + *accum = op(t, *accum); } template <typename Packet> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void reducePacket(const Packet& p, Packet* accum) const { - (*accum) = pmax<Packet>(*accum, p); + scalar_max_op<T, T, NaNPropagation> op; + (*accum) = op.packetOp(*accum, p); } EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T initialize() const { - return MinMaxBottomValue<T, true, Eigen::NumTraits<T>::IsInteger>::bottom_value(); + return MinMaxBottomValue<T, /*IsMax=*/true, Eigen::NumTraits<T>::IsInteger>::bottom_value(); } template <typename Packet> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet initializePacket() const { @@ -217,32 +219,34 @@ template <typename T> struct MaxReducer } template <typename Packet> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T finalizeBoth(const T saccum, const Packet& vaccum) const { - return numext::maxi(saccum, predux_max(vaccum)); + scalar_max_op<T, T, NaNPropagation> op; + return op(saccum, op.predux(vaccum)); } }; -template <typename T, typename Device> -struct reducer_traits<MaxReducer<T>, Device> { +template <typename T, typename Device, int NaNPropagation> + struct reducer_traits<MaxReducer<T, NaNPropagation>, Device> { enum { Cost = NumTraits<T>::AddCost, PacketAccess = PacketType<T, Device>::HasMax, IsStateful = false, - IsExactlyAssociative = true + IsExactlyAssociative = (NaNPropagation!=PropagateFast) }; }; - -template <typename T> struct MinReducer +template <typename T, int NaNPropagation=PropagateFast> struct MinReducer { EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void reduce(const T t, T* accum) const { - if (t < *accum) { *accum = t; } + scalar_min_op<T, T, NaNPropagation> op; + *accum = op(t, *accum); } template <typename Packet> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void reducePacket(const Packet& p, Packet* accum) const { - (*accum) = pmin<Packet>(*accum, p); + scalar_min_op<T, T, NaNPropagation> op; + (*accum) = op.packetOp(*accum, p); } EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T initialize() const { - return MinMaxBottomValue<T, false, Eigen::NumTraits<T>::IsInteger>::bottom_value(); + return MinMaxBottomValue<T, /*IsMax=*/false, Eigen::NumTraits<T>::IsInteger>::bottom_value(); } template <typename Packet> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet initializePacket() const { @@ -257,21 +261,21 @@ template <typename T> struct MinReducer } template <typename Packet> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T finalizeBoth(const T saccum, const Packet& vaccum) const { - return numext::mini(saccum, predux_min(vaccum)); + scalar_min_op<T, T, NaNPropagation> op; + return op(saccum, op.predux(vaccum)); } }; -template <typename T, typename Device> -struct reducer_traits<MinReducer<T>, Device> { +template <typename T, typename Device, int NaNPropagation> + struct reducer_traits<MinReducer<T, NaNPropagation>, Device> { enum { Cost = NumTraits<T>::AddCost, PacketAccess = PacketType<T, Device>::HasMin, IsStateful = false, - IsExactlyAssociative = true + IsExactlyAssociative = (NaNPropagation!=PropagateFast) }; }; - template <typename T> struct ProdReducer { EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void reduce(const T t, T* accum) const { @@ -282,7 +286,6 @@ template <typename T> struct ProdReducer EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void reducePacket(const Packet& p, Packet* accum) const { (*accum) = pmul<Packet>(*accum, p); } - EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T initialize() const { internal::scalar_cast_op<int, T> conv; return conv(1); diff --git a/unsupported/test/cxx11_tensor_expr.cpp b/unsupported/test/cxx11_tensor_expr.cpp index 7fac3b4ed..556d01d4d 100644 --- a/unsupported/test/cxx11_tensor_expr.cpp +++ b/unsupported/test/cxx11_tensor_expr.cpp @@ -302,12 +302,17 @@ static void test_select() template <typename Scalar> void test_minmax_nan_propagation_templ() { for (int size = 1; size < 17; ++size) { - const Scalar kNan = std::numeric_limits<Scalar>::quiet_NaN(); + std::cout << "size = " << size << std::endl; + const Scalar kNaN = std::numeric_limits<Scalar>::quiet_NaN(); + const Scalar kInf = std::numeric_limits<Scalar>::infinity(); const Scalar kZero(0); - Tensor<Scalar, 1> vec_nan(size); + Tensor<Scalar, 1> vec_all_nan(size); + Tensor<Scalar, 1> vec_one_nan(size); Tensor<Scalar, 1> vec_zero(size); - vec_nan.setConstant(kNan); + vec_all_nan.setConstant(kNaN); vec_zero.setZero(); + vec_one_nan.setZero(); + vec_one_nan(size/2) = kNaN; auto verify_all_nan = [&](const Tensor<Scalar, 1>& v) { for (int i = 0; i < size; ++i) { @@ -326,12 +331,12 @@ void test_minmax_nan_propagation_templ() { // max(nan, 0) = nan // max(0, nan) = nan // max(0, 0) = 0 - verify_all_nan(vec_nan.template cwiseMax<PropagateNaN>(kNan)); - verify_all_nan(vec_nan.template cwiseMax<PropagateNaN>(vec_nan)); - verify_all_nan(vec_nan.template cwiseMax<PropagateNaN>(kZero)); - verify_all_nan(vec_nan.template cwiseMax<PropagateNaN>(vec_zero)); - verify_all_nan(vec_zero.template cwiseMax<PropagateNaN>(kNan)); - verify_all_nan(vec_zero.template cwiseMax<PropagateNaN>(vec_nan)); + verify_all_nan(vec_all_nan.template cwiseMax<PropagateNaN>(kNaN)); + verify_all_nan(vec_all_nan.template cwiseMax<PropagateNaN>(vec_all_nan)); + verify_all_nan(vec_all_nan.template cwiseMax<PropagateNaN>(kZero)); + verify_all_nan(vec_all_nan.template cwiseMax<PropagateNaN>(vec_zero)); + verify_all_nan(vec_zero.template cwiseMax<PropagateNaN>(kNaN)); + verify_all_nan(vec_zero.template cwiseMax<PropagateNaN>(vec_all_nan)); verify_all_zero(vec_zero.template cwiseMax<PropagateNaN>(kZero)); verify_all_zero(vec_zero.template cwiseMax<PropagateNaN>(vec_zero)); @@ -340,12 +345,12 @@ void test_minmax_nan_propagation_templ() { // max(nan, 0) = 0 // max(0, nan) = 0 // max(0, 0) = 0 - verify_all_nan(vec_nan.template cwiseMax<PropagateNumbers>(kNan)); - verify_all_nan(vec_nan.template cwiseMax<PropagateNumbers>(vec_nan)); - verify_all_zero(vec_nan.template cwiseMax<PropagateNumbers>(kZero)); - verify_all_zero(vec_nan.template cwiseMax<PropagateNumbers>(vec_zero)); - verify_all_zero(vec_zero.template cwiseMax<PropagateNumbers>(kNan)); - verify_all_zero(vec_zero.template cwiseMax<PropagateNumbers>(vec_nan)); + verify_all_nan(vec_all_nan.template cwiseMax<PropagateNumbers>(kNaN)); + verify_all_nan(vec_all_nan.template cwiseMax<PropagateNumbers>(vec_all_nan)); + verify_all_zero(vec_all_nan.template cwiseMax<PropagateNumbers>(kZero)); + verify_all_zero(vec_all_nan.template cwiseMax<PropagateNumbers>(vec_zero)); + verify_all_zero(vec_zero.template cwiseMax<PropagateNumbers>(kNaN)); + verify_all_zero(vec_zero.template cwiseMax<PropagateNumbers>(vec_all_nan)); verify_all_zero(vec_zero.template cwiseMax<PropagateNumbers>(kZero)); verify_all_zero(vec_zero.template cwiseMax<PropagateNumbers>(vec_zero)); @@ -354,12 +359,12 @@ void test_minmax_nan_propagation_templ() { // min(nan, 0) = nan // min(0, nan) = nan // min(0, 0) = 0 - verify_all_nan(vec_nan.template cwiseMin<PropagateNaN>(kNan)); - verify_all_nan(vec_nan.template cwiseMin<PropagateNaN>(vec_nan)); - verify_all_nan(vec_nan.template cwiseMin<PropagateNaN>(kZero)); - verify_all_nan(vec_nan.template cwiseMin<PropagateNaN>(vec_zero)); - verify_all_nan(vec_zero.template cwiseMin<PropagateNaN>(kNan)); - verify_all_nan(vec_zero.template cwiseMin<PropagateNaN>(vec_nan)); + verify_all_nan(vec_all_nan.template cwiseMin<PropagateNaN>(kNaN)); + verify_all_nan(vec_all_nan.template cwiseMin<PropagateNaN>(vec_all_nan)); + verify_all_nan(vec_all_nan.template cwiseMin<PropagateNaN>(kZero)); + verify_all_nan(vec_all_nan.template cwiseMin<PropagateNaN>(vec_zero)); + verify_all_nan(vec_zero.template cwiseMin<PropagateNaN>(kNaN)); + verify_all_nan(vec_zero.template cwiseMin<PropagateNaN>(vec_all_nan)); verify_all_zero(vec_zero.template cwiseMin<PropagateNaN>(kZero)); verify_all_zero(vec_zero.template cwiseMin<PropagateNaN>(vec_zero)); @@ -368,14 +373,49 @@ void test_minmax_nan_propagation_templ() { // min(nan, 0) = 0 // min(0, nan) = 0 // min(0, 0) = 0 - verify_all_nan(vec_nan.template cwiseMin<PropagateNumbers>(kNan)); - verify_all_nan(vec_nan.template cwiseMin<PropagateNumbers>(vec_nan)); - verify_all_zero(vec_nan.template cwiseMin<PropagateNumbers>(kZero)); - verify_all_zero(vec_nan.template cwiseMin<PropagateNumbers>(vec_zero)); - verify_all_zero(vec_zero.template cwiseMin<PropagateNumbers>(kNan)); - verify_all_zero(vec_zero.template cwiseMin<PropagateNumbers>(vec_nan)); + verify_all_nan(vec_all_nan.template cwiseMin<PropagateNumbers>(kNaN)); + verify_all_nan(vec_all_nan.template cwiseMin<PropagateNumbers>(vec_all_nan)); + verify_all_zero(vec_all_nan.template cwiseMin<PropagateNumbers>(kZero)); + verify_all_zero(vec_all_nan.template cwiseMin<PropagateNumbers>(vec_zero)); + verify_all_zero(vec_zero.template cwiseMin<PropagateNumbers>(kNaN)); + verify_all_zero(vec_zero.template cwiseMin<PropagateNumbers>(vec_all_nan)); verify_all_zero(vec_zero.template cwiseMin<PropagateNumbers>(kZero)); verify_all_zero(vec_zero.template cwiseMin<PropagateNumbers>(vec_zero)); + + // Test min and max reduction + Tensor<Scalar, 0> val; + val = vec_zero.minimum(); + VERIFY_IS_EQUAL(val(), kZero); + val = vec_zero.template minimum<PropagateNaN>(); + VERIFY_IS_EQUAL(val(), kZero); + val = vec_zero.template minimum<PropagateNumbers>(); + VERIFY_IS_EQUAL(val(), kZero); + val = vec_zero.maximum(); + VERIFY_IS_EQUAL(val(), kZero); + val = vec_zero.template maximum<PropagateNaN>(); + VERIFY_IS_EQUAL(val(), kZero); + val = vec_zero.template maximum<PropagateNumbers>(); + VERIFY_IS_EQUAL(val(), kZero); + + // Test NaN propagation for tensor of all NaNs. + val = vec_all_nan.template minimum<PropagateNaN>(); + VERIFY((numext::isnan)(val())); + val = vec_all_nan.template minimum<PropagateNumbers>(); + VERIFY_IS_EQUAL(val(), kInf); + val = vec_all_nan.template maximum<PropagateNaN>(); + VERIFY((numext::isnan)(val())); + val = vec_all_nan.template maximum<PropagateNumbers>(); + VERIFY_IS_EQUAL(val(), -kInf); + + // Test NaN propagation for tensor with a single NaN. + val = vec_one_nan.template minimum<PropagateNaN>(); + VERIFY((numext::isnan)(val())); + val = vec_one_nan.template minimum<PropagateNumbers>(); + VERIFY_IS_EQUAL(val(), (size == 1 ? kInf : kZero)); + val = vec_one_nan.template maximum<PropagateNaN>(); + VERIFY((numext::isnan)(val())); + val = vec_one_nan.template maximum<PropagateNumbers>(); + VERIFY_IS_EQUAL(val(), (size == 1 ? -kInf : kZero)); } } |