/*************************************************************************** * Copyright (c) Johan Mabille, Sylvain Corlay and Wolf Vollprecht * * Copyright (c) QuantStack * * * * Distributed under the terms of the BSD 3-Clause License. * * * * The full license is in the file LICENSE, distributed with this software. * ****************************************************************************/ /** * @brief standard mathematical functions for xexpressions */ #ifndef XTENSOR_MATH_HPP #define XTENSOR_MATH_HPP #include #include #include #include #include #include #include #include #include "xaccumulator.hpp" #include "xeval.hpp" #include "xmanipulation.hpp" #include "xoperation.hpp" #include "xreducer.hpp" #include "xslice.hpp" #include "xstrided_view.hpp" #include "xtensor_config.hpp" namespace xt { template struct numeric_constants { static constexpr T PI = 3.141592653589793238463; static constexpr T PI_2 = 1.57079632679489661923; static constexpr T PI_4 = 0.785398163397448309616; static constexpr T D_1_PI = 0.318309886183790671538; static constexpr T D_2_PI = 0.636619772367581343076; static constexpr T D_2_SQRTPI = 1.12837916709551257390; static constexpr T SQRT2 = 1.41421356237309504880; static constexpr T SQRT1_2 = 0.707106781186547524401; static constexpr T E = 2.71828182845904523536; static constexpr T LOG2E = 1.44269504088896340736; static constexpr T LOG10E = 0.434294481903251827651; static constexpr T LN2 = 0.693147180559945309417; }; /*********** * Helpers * ***********/ #define XTENSOR_UNSIGNED_ABS_FUNC(T) \ constexpr inline T abs(const T& x) \ { \ return x; \ } #define XTENSOR_INT_SPECIALIZATION_IMPL(FUNC_NAME, RETURN_VAL, T) \ constexpr inline bool FUNC_NAME(const T& /*x*/) noexcept \ { \ return RETURN_VAL; \ } #define XTENSOR_INT_SPECIALIZATION(FUNC_NAME, RETURN_VAL) \ XTENSOR_INT_SPECIALIZATION_IMPL(FUNC_NAME, RETURN_VAL, char); \ XTENSOR_INT_SPECIALIZATION_IMPL(FUNC_NAME, RETURN_VAL, short); \ XTENSOR_INT_SPECIALIZATION_IMPL(FUNC_NAME, RETURN_VAL, int); \ XTENSOR_INT_SPECIALIZATION_IMPL(FUNC_NAME, RETURN_VAL, long); \ XTENSOR_INT_SPECIALIZATION_IMPL(FUNC_NAME, RETURN_VAL, long long); \ XTENSOR_INT_SPECIALIZATION_IMPL(FUNC_NAME, RETURN_VAL, unsigned char); \ XTENSOR_INT_SPECIALIZATION_IMPL(FUNC_NAME, RETURN_VAL, unsigned short); \ XTENSOR_INT_SPECIALIZATION_IMPL(FUNC_NAME, RETURN_VAL, unsigned int); \ XTENSOR_INT_SPECIALIZATION_IMPL(FUNC_NAME, RETURN_VAL, unsigned long); \ XTENSOR_INT_SPECIALIZATION_IMPL(FUNC_NAME, RETURN_VAL, unsigned long long); #define XTENSOR_UNARY_MATH_FUNCTOR(NAME) \ struct NAME##_fun \ { \ template \ constexpr auto operator()(const T& arg) const \ { \ using math::NAME; \ return NAME(arg); \ } \ template \ constexpr auto simd_apply(const B& arg) const \ { \ using math::NAME; \ return NAME(arg); \ } \ } #define XTENSOR_UNARY_MATH_FUNCTOR_COMPLEX_REDUCING(NAME) \ struct NAME##_fun \ { \ template \ constexpr auto operator()(const T& arg) const \ { \ using math::NAME; \ return NAME(arg); \ } \ template \ constexpr auto simd_apply(const B& arg) const \ { \ using math::NAME; \ return NAME(arg); \ } \ } #define XTENSOR_BINARY_MATH_FUNCTOR(NAME) \ struct NAME##_fun \ { \ template \ constexpr auto operator()(const T1& arg1, const T2& arg2) const \ { \ using math::NAME; \ return NAME(arg1, arg2); \ } \ template \ constexpr auto simd_apply(const B& arg1, const B& arg2) const \ { \ using math::NAME; \ return NAME(arg1, arg2); \ } \ } #define XTENSOR_TERNARY_MATH_FUNCTOR(NAME) \ struct NAME##_fun \ { \ template \ constexpr auto operator()(const T1& arg1, const T2& arg2, const T3& arg3) const \ { \ using math::NAME; \ return NAME(arg1, arg2, arg3); \ } \ template \ auto simd_apply(const B& arg1, const B& arg2, const B& arg3) const \ { \ using math::NAME; \ return NAME(arg1, arg2, arg3); \ } \ } namespace math { using std::abs; using std::fabs; using std::acos; using std::asin; using std::atan; using std::cos; using std::sin; using std::tan; using std::acosh; using std::asinh; using std::atanh; using std::cosh; using std::sinh; using std::tanh; using std::cbrt; using std::sqrt; using std::exp; using std::exp2; using std::expm1; using std::ilogb; using std::log; using std::log10; using std::log1p; using std::log2; using std::logb; using std::ceil; using std::floor; using std::llround; using std::lround; using std::nearbyint; using std::remainder; using std::rint; using std::round; using std::trunc; using std::erf; using std::erfc; using std::lgamma; using std::tgamma; using std::arg; using std::conj; using std::imag; using std::real; using std::atan2; // copysign is not in the std namespace for MSVC #if !defined(_MSC_VER) using std::copysign; #endif using std::fdim; using std::fmax; using std::fmin; using std::fmod; using std::hypot; using std::pow; using std::fma; using std::fpclassify; // Overload isinf, isnan and isfinite because glibc implementation // might return int instead of bool and the SIMD detection requires // bool return type. template inline std::enable_if_t::value, bool> isinf(const T& t) { return bool(std::isinf(t)); } template inline std::enable_if_t::value, bool> isnan(const T& t) { return bool(std::isnan(t)); } template inline std::enable_if_t::value, bool> isfinite(const T& t) { return bool(std::isfinite(t)); } // Overload isinf, isnan and isfinite for complex datatypes, // following the Python specification: template inline bool isinf(const std::complex& c) { return std::isinf(std::real(c)) || std::isinf(std::imag(c)); } template inline bool isnan(const std::complex& c) { return std::isnan(std::real(c)) || std::isnan(std::imag(c)); } template inline bool isfinite(const std::complex& c) { return !isinf(c) && !isnan(c); } // VS2015 STL defines isnan, isinf and isfinite as template // functions, breaking ADL. #if defined(_WIN32) && defined(XTENSOR_USE_XSIMD) /*template inline xsimd::batch_bool isinf(const xsimd::batch& b) { return xsimd::isinf(b); } template inline xsimd::batch_bool isnan(const xsimd::batch& b) { return xsimd::isnan(b); } template inline xsimd::batch_bool isfinite(const xsimd::batch& b) { return xsimd::isfinite(b); }*/ #endif // The following specializations are needed to avoid 'ambiguous overload' errors, // whereas 'unsigned char' and 'unsigned short' are automatically converted to 'int'. // we're still adding those functions to silence warnings XTENSOR_UNSIGNED_ABS_FUNC(unsigned char) XTENSOR_UNSIGNED_ABS_FUNC(unsigned short) XTENSOR_UNSIGNED_ABS_FUNC(unsigned int) XTENSOR_UNSIGNED_ABS_FUNC(unsigned long) XTENSOR_UNSIGNED_ABS_FUNC(unsigned long long) #ifdef _WIN32 XTENSOR_INT_SPECIALIZATION(isinf, false); XTENSOR_INT_SPECIALIZATION(isnan, false); XTENSOR_INT_SPECIALIZATION(isfinite, true); #endif XTENSOR_UNARY_MATH_FUNCTOR_COMPLEX_REDUCING(abs); XTENSOR_UNARY_MATH_FUNCTOR(fabs); XTENSOR_BINARY_MATH_FUNCTOR(fmod); XTENSOR_BINARY_MATH_FUNCTOR(remainder); XTENSOR_TERNARY_MATH_FUNCTOR(fma); XTENSOR_BINARY_MATH_FUNCTOR(fmax); XTENSOR_BINARY_MATH_FUNCTOR(fmin); XTENSOR_BINARY_MATH_FUNCTOR(fdim); XTENSOR_UNARY_MATH_FUNCTOR(exp); XTENSOR_UNARY_MATH_FUNCTOR(exp2); XTENSOR_UNARY_MATH_FUNCTOR(expm1); XTENSOR_UNARY_MATH_FUNCTOR(log); XTENSOR_UNARY_MATH_FUNCTOR(log10); XTENSOR_UNARY_MATH_FUNCTOR(log2); XTENSOR_UNARY_MATH_FUNCTOR(log1p); XTENSOR_BINARY_MATH_FUNCTOR(pow); XTENSOR_UNARY_MATH_FUNCTOR(sqrt); XTENSOR_UNARY_MATH_FUNCTOR(cbrt); XTENSOR_BINARY_MATH_FUNCTOR(hypot); XTENSOR_UNARY_MATH_FUNCTOR(sin); XTENSOR_UNARY_MATH_FUNCTOR(cos); XTENSOR_UNARY_MATH_FUNCTOR(tan); XTENSOR_UNARY_MATH_FUNCTOR(asin); XTENSOR_UNARY_MATH_FUNCTOR(acos); XTENSOR_UNARY_MATH_FUNCTOR(atan); XTENSOR_BINARY_MATH_FUNCTOR(atan2); XTENSOR_UNARY_MATH_FUNCTOR(sinh); XTENSOR_UNARY_MATH_FUNCTOR(cosh); XTENSOR_UNARY_MATH_FUNCTOR(tanh); XTENSOR_UNARY_MATH_FUNCTOR(asinh); XTENSOR_UNARY_MATH_FUNCTOR(acosh); XTENSOR_UNARY_MATH_FUNCTOR(atanh); XTENSOR_UNARY_MATH_FUNCTOR(erf); XTENSOR_UNARY_MATH_FUNCTOR(erfc); XTENSOR_UNARY_MATH_FUNCTOR(tgamma); XTENSOR_UNARY_MATH_FUNCTOR(lgamma); XTENSOR_UNARY_MATH_FUNCTOR(ceil); XTENSOR_UNARY_MATH_FUNCTOR(floor); XTENSOR_UNARY_MATH_FUNCTOR(trunc); XTENSOR_UNARY_MATH_FUNCTOR(round); XTENSOR_UNARY_MATH_FUNCTOR(nearbyint); XTENSOR_UNARY_MATH_FUNCTOR(rint); XTENSOR_UNARY_MATH_FUNCTOR(isfinite); XTENSOR_UNARY_MATH_FUNCTOR(isinf); XTENSOR_UNARY_MATH_FUNCTOR(isnan); } #undef XTENSOR_UNARY_MATH_FUNCTOR #undef XTENSOR_BINARY_MATH_FUNCTOR #undef XTENSOR_TERNARY_MATH_FUNCTOR #undef XTENSOR_UNARY_MATH_FUNCTOR_COMPLEX_REDUCING #undef XTENSOR_UNSIGNED_ABS_FUNC namespace detail { template std::enable_if_t::value, R> fill_init(T init) { return R(init); } template std::enable_if_t::value, R> fill_init(T init) { R result; std::fill(std::begin(result), std::end(result), init); return result; } } #define XTENSOR_REDUCER_FUNCTION(NAME, FUNCTOR, INIT_VALUE_TYPE, INIT) \ template < \ class T = void, \ class E, \ class X, \ class EVS = DEFAULT_STRATEGY_REDUCERS, \ XTL_REQUIRES(xtl::negation>, xtl::negation>>)> \ inline auto NAME(E&& e, X&& axes, EVS es = EVS()) \ { \ using init_value_type = std::conditional_t::value, INIT_VALUE_TYPE, T>; \ using functor_type = FUNCTOR; \ using init_value_fct = xt::const_value; \ return xt::reduce( \ make_xreducer_functor(functor_type(), init_value_fct(detail::fill_init(INIT))), \ std::forward(e), \ std::forward(axes), \ es \ ); \ } \ \ template < \ class T = void, \ class E, \ class X, \ class EVS = DEFAULT_STRATEGY_REDUCERS, \ XTL_REQUIRES(xtl::negation>, xtl::is_integral>)> \ inline auto NAME(E&& e, X axis, EVS es = EVS()) \ { \ return NAME(std::forward(e), {axis}, es); \ } \ \ template )> \ inline auto NAME(E&& e, EVS es = EVS()) \ { \ using init_value_type = std::conditional_t::value, INIT_VALUE_TYPE, T>; \ using functor_type = FUNCTOR; \ using init_value_fct = xt::const_value; \ return xt::reduce( \ make_xreducer_functor(functor_type(), init_value_fct(detail::fill_init(INIT))), \ std::forward(e), \ es \ ); \ } \ \ template \ inline auto NAME(E&& e, const I(&axes)[N], EVS es = EVS()) \ { \ using init_value_type = std::conditional_t::value, INIT_VALUE_TYPE, T>; \ using functor_type = FUNCTOR; \ using init_value_fct = xt::const_value; \ return xt::reduce( \ make_xreducer_functor(functor_type(), init_value_fct(detail::fill_init(INIT))), \ std::forward(e), \ axes, \ es \ ); \ } /******************* * basic functions * *******************/ /** * @defgroup basic_functions Basic functions */ /** * @ingroup basic_functions * @brief Absolute value function. * * Returns an \ref xfunction for the element-wise absolute value * of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto abs(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup basic_functions * @brief Absolute value function. * * Returns an \ref xfunction for the element-wise absolute value * of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto fabs(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup basic_functions * @brief Remainder of the floating point division operation. * * Returns an \ref xfunction for the element-wise remainder of * the floating point division operation e1 / e2. * @param e1 an \ref xexpression or a scalar * @param e2 an \ref xexpression or a scalar * @return an \ref xfunction * @note e1 and e2 can't be both scalars. */ template inline auto fmod(E1&& e1, E2&& e2) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e1), std::forward(e2)); } /** * @ingroup basic_functions * @brief Signed remainder of the division operation. * * Returns an \ref xfunction for the element-wise signed remainder * of the floating point division operation e1 / e2. * @param e1 an \ref xexpression or a scalar * @param e2 an \ref xexpression or a scalar * @return an \ref xfunction * @note e1 and e2 can't be both scalars. */ template inline auto remainder(E1&& e1, E2&& e2) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e1), std::forward(e2)); } /** * @ingroup basic_functions * @brief Fused multiply-add operation. * * Returns an \ref xfunction for e1 * e2 + e3 as if * to infinite precision and rounded only once to fit the result type. * @param e1 an \ref xfunction or a scalar * @param e2 an \ref xfunction or a scalar * @param e3 an \ref xfunction or a scalar * @return an \ref xfunction * @note e1, e2 and e3 can't be scalars every three. */ template inline auto fma(E1&& e1, E2&& e2, E3&& e3) noexcept -> detail::xfunction_type_t { return detail::make_xfunction( std::forward(e1), std::forward(e2), std::forward(e3) ); } /** * @ingroup basic_functions * @brief Maximum function. * * Returns an \ref xfunction for the element-wise maximum * of \a e1 and \a e2. * @param e1 an \ref xexpression or a scalar * @param e2 an \ref xexpression or a scalar * @return an \ref xfunction * @note e1 and e2 can't be both scalars. */ template inline auto fmax(E1&& e1, E2&& e2) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e1), std::forward(e2)); } /** * @ingroup basic_functions * @brief Minimum function. * * Returns an \ref xfunction for the element-wise minimum * of \a e1 and \a e2. * @param e1 an \ref xexpression or a scalar * @param e2 an \ref xexpression or a scalar * @return an \ref xfunction * @note e1 and e2 can't be both scalars. */ template inline auto fmin(E1&& e1, E2&& e2) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e1), std::forward(e2)); } /** * @ingroup basic_functions * @brief Positive difference function. * * Returns an \ref xfunction for the element-wise positive * difference of \a e1 and \a e2. * @param e1 an \ref xexpression or a scalar * @param e2 an \ref xexpression or a scalar * @return an \ref xfunction * @note e1 and e2 can't be both scalars. */ template inline auto fdim(E1&& e1, E2&& e2) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e1), std::forward(e2)); } namespace math { template struct minimum { template constexpr auto operator()(const A1& t1, const A2& t2) const noexcept { return xtl::select(t1 < t2, t1, t2); } template constexpr auto simd_apply(const A1& t1, const A2& t2) const noexcept { return xt_simd::select(t1 < t2, t1, t2); } }; template struct maximum { template constexpr auto operator()(const A1& t1, const A2& t2) const noexcept { return xtl::select(t1 > t2, t1, t2); } template constexpr auto simd_apply(const A1& t1, const A2& t2) const noexcept { return xt_simd::select(t1 > t2, t1, t2); } }; struct clamp_fun { template constexpr auto operator()(const A1& v, const A2& lo, const A3& hi) const { return xtl::select(v < lo, lo, xtl::select(hi < v, hi, v)); } template constexpr auto simd_apply(const A1& v, const A2& lo, const A3& hi) const { return xt_simd::select(v < lo, lo, xt_simd::select(hi < v, hi, v)); } }; struct deg2rad { template ::value, int> = 0> constexpr double operator()(const A& a) const noexcept { return a * xt::numeric_constants::PI / 180.0; } template ::value, int> = 0> constexpr auto operator()(const A& a) const noexcept { return a * xt::numeric_constants::PI / A(180.0); } template ::value, int> = 0> constexpr double simd_apply(const A& a) const noexcept { return a * xt::numeric_constants::PI / 180.0; } template ::value, int> = 0> constexpr auto simd_apply(const A& a) const noexcept { return a * xt::numeric_constants::PI / A(180.0); } }; struct rad2deg { template ::value, int> = 0> constexpr double operator()(const A& a) const noexcept { return a * 180.0 / xt::numeric_constants::PI; } template ::value, int> = 0> constexpr auto operator()(const A& a) const noexcept { return a * A(180.0) / xt::numeric_constants::PI; } template ::value, int> = 0> constexpr double simd_apply(const A& a) const noexcept { return a * 180.0 / xt::numeric_constants::PI; } template ::value, int> = 0> constexpr auto simd_apply(const A& a) const noexcept { return a * A(180.0) / xt::numeric_constants::PI; } }; } /** * @ingroup basic_functions * @brief Convert angles from degrees to radians. * * Returns an \ref xfunction for the element-wise corresponding * angle in radians of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto deg2rad(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup basic_functions * @brief Convert angles from degrees to radians. * * Returns an \ref xfunction for the element-wise corresponding * angle in radians of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto radians(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup basic_functions * @brief Convert angles from radians to degrees. * * Returns an \ref xfunction for the element-wise corresponding * angle in degrees of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto rad2deg(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup basic_functions * @brief Convert angles from radians to degrees. * * Returns an \ref xfunction for the element-wise corresponding * angle in degrees of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto degrees(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup basic_functions * @brief Elementwise maximum * * Returns an \ref xfunction for the element-wise * maximum between e1 and e2. * @param e1 an \ref xexpression * @param e2 an \ref xexpression * @return an \ref xfunction */ template inline auto maximum(E1&& e1, E2&& e2) noexcept -> detail::xfunction_type_t, E1, E2> { return detail::make_xfunction>(std::forward(e1), std::forward(e2)); } /** * @ingroup basic_functions * @brief Elementwise minimum * * Returns an \ref xfunction for the element-wise * minimum between e1 and e2. * @param e1 an \ref xexpression * @param e2 an \ref xexpression * @return an \ref xfunction */ template inline auto minimum(E1&& e1, E2&& e2) noexcept -> detail::xfunction_type_t, E1, E2> { return detail::make_xfunction>(std::forward(e1), std::forward(e2)); } /** * @ingroup basic_functions * @brief Maximum element along given axis. * * Returns an \ref xreducer for the maximum of elements over given * \em axes. * @param e an \ref xexpression * @param axes the axes along which the maximum is found (optional) * @param es evaluation strategy of the reducer * @return an \ref xreducer */ XTENSOR_REDUCER_FUNCTION( amax, math::maximum, typename std::decay_t::value_type, std::numeric_limits>>::lowest() ) /** * @ingroup basic_functions * @brief Minimum element along given axis. * * Returns an \ref xreducer for the minimum of elements over given * \em axes. * @param e an \ref xexpression * @param axes the axes along which the minimum is found (optional) * @param es evaluation strategy of the reducer * @return an \ref xreducer */ XTENSOR_REDUCER_FUNCTION( amin, math::minimum, typename std::decay_t::value_type, std::numeric_limits>>::max() ) /** * @ingroup basic_functions * @brief Clip values between hi and lo * * Returns an \ref xfunction for the element-wise clipped * values between lo and hi * @param e1 an \ref xexpression or a scalar * @param lo a scalar * @param hi a scalar * * @return a \ref xfunction */ template inline auto clip(E1&& e1, E2&& lo, E3&& hi) noexcept -> detail::xfunction_type_t { return detail::make_xfunction( std::forward(e1), std::forward(lo), std::forward(hi) ); } namespace math { template struct sign_impl { template static constexpr std::enable_if_t::value, T> run(T x) { return std::isnan(x) ? std::numeric_limits::quiet_NaN() : x == 0 ? T(copysign(T(0), x)) : T(copysign(T(1), x)); } template static constexpr std::enable_if_t::value, T> run(T x) { return T( sign_impl::run( (x.real() != typename T::value_type(0)) ? x.real() : x.imag() ), 0 ); } template static constexpr std::enable_if_t::value, T> run(T x) { return T(x > T(0)); } }; struct sign_fun { template constexpr auto operator()(const T& x) const { return sign_impl::run(x); } }; } /** * @ingroup basic_functions * @brief Returns an element-wise indication of the sign of a number * * If the number is positive, returns +1. If negative, -1. If the number * is zero, returns 0. * * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto sign(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /************************* * exponential functions * *************************/ /** * @defgroup exp_functions Exponential functions */ /** * @ingroup exp_functions * @brief Natural exponential function. * * Returns an \ref xfunction for the element-wise natural * exponential of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto exp(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup exp_functions * @brief Base 2 exponential function. * * Returns an \ref xfunction for the element-wise base 2 * exponential of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto exp2(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup exp_functions * @brief Natural exponential minus one function. * * Returns an \ref xfunction for the element-wise natural * exponential of \em e, minus 1. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto expm1(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup exp_functions * @brief Natural logarithm function. * * Returns an \ref xfunction for the element-wise natural * logarithm of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto log(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup exp_functions * @brief Base 10 logarithm function. * * Returns an \ref xfunction for the element-wise base 10 * logarithm of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto log10(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup exp_functions * @brief Base 2 logarithm function. * * Returns an \ref xfunction for the element-wise base 2 * logarithm of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto log2(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup exp_functions * @brief Natural logarithm of one plus function. * * Returns an \ref xfunction for the element-wise natural * logarithm of \em e, plus 1. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto log1p(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /******************* * power functions * *******************/ /** * @defgroup pow_functions Power functions */ /** * @ingroup pow_functions * @brief Power function. * * Returns an \ref xfunction for the element-wise value of * of \em e1 raised to the power \em e2. * @param e1 an \ref xexpression or a scalar * @param e2 an \ref xexpression or a scalar * @return an \ref xfunction * @note e1 and e2 can't be both scalars. */ template inline auto pow(E1&& e1, E2&& e2) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e1), std::forward(e2)); } namespace detail { template ()(std::declval()...))> std::true_type supports_test(const F&, const T&...); std::false_type supports_test(...); template struct supports; template struct supports : decltype(supports_test(std::declval(), std::declval()...)) { }; template struct lambda_adapt { explicit lambda_adapt(F&& lmbd) : m_lambda(std::move(lmbd)) { } template auto operator()(T... args) const { return m_lambda(args...); } template )> auto simd_apply(T... args) const { return m_lambda(args...); } F m_lambda; }; } /** * Create a xfunction from a lambda * * This function can be used to easily create performant xfunctions from lambdas: * * @code{cpp} * template * inline auto square(E1&& e1) noexcept * { * auto fnct = [](auto x) -> decltype(x * x) { * return x * x; * }; * return make_lambda_xfunction(std::move(fnct), std::forward(e1)); * } * @endcode * * Lambda function allow the reusal of a single arguments in multiple places (otherwise * only correctly possible when using xshared_expressions). ``auto`` lambda functions are * automatically vectorized with ``xsimd`` if possible (note that the trailing * ``-> decltype(...)`` is mandatory for the feature detection to work). * * @param lambda the lambda to be vectorized * @param args forwarded arguments * * @return lazy xfunction */ template inline auto make_lambda_xfunction(F&& lambda, E&&... args) { using xfunction_type = typename detail::xfunction_type, E...>::type; return xfunction_type(detail::lambda_adapt(std::forward(lambda)), std::forward(args)...); } #define XTENSOR_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) // Workaround for MSVC 2015 & GCC 4.9 #if (defined(_MSC_VER) && _MSC_VER < 1910) || (defined(__GNUC__) && GCC_VERSION < 49999) #define XTENSOR_DISABLE_LAMBDA_FCT #endif #ifdef XTENSOR_DISABLE_LAMBDA_FCT struct square_fct { template auto operator()(T x) const -> decltype(x * x) { return x * x; } }; struct cube_fct { template auto operator()(T x) const -> decltype(x * x * x) { return x * x * x; } }; #endif /** * @ingroup pow_functions * @brief Square power function, equivalent to e1 * e1. * * Returns an \ref xfunction for the element-wise value of * of \em e1 * \em e1. * @param e1 an \ref xexpression or a scalar * @return an \ref xfunction */ template inline auto square(E1&& e1) noexcept { #ifdef XTENSOR_DISABLE_LAMBDA_FCT return make_lambda_xfunction(square_fct{}, std::forward(e1)); #else auto fnct = [](auto x) -> decltype(x * x) { return x * x; }; return make_lambda_xfunction(std::move(fnct), std::forward(e1)); #endif } /** * @ingroup pow_functions * @brief Cube power function, equivalent to e1 * e1 * e1. * * Returns an \ref xfunction for the element-wise value of * of \em e1 * \em e1. * @param e1 an \ref xexpression or a scalar * @return an \ref xfunction */ template inline auto cube(E1&& e1) noexcept { #ifdef XTENSOR_DISABLE_LAMBDA_FCT return make_lambda_xfunction(cube_fct{}, std::forward(e1)); #else auto fnct = [](auto x) -> decltype(x * x * x) { return x * x * x; }; return make_lambda_xfunction(std::move(fnct), std::forward(e1)); #endif } #undef XTENSOR_GCC_VERSION #undef XTENSOR_DISABLE_LAMBDA_FCT namespace detail { // Thanks to Matt Pharr in http://pbrt.org/hair.pdf template struct pow_impl; template struct pow_impl { template auto operator()(T v) const -> decltype(v * v) { T temp = pow_impl{}(v); return temp * temp * pow_impl{}(v); } }; template <> struct pow_impl<1> { template auto operator()(T v) const -> T { return v; } }; template <> struct pow_impl<0> { template auto operator()(T /*v*/) const -> T { return T(1); } }; } /** * @ingroup pow_functions * @brief Integer power function. * * Returns an \ref xfunction for the element-wise power of e1 to * an integral constant. * * Instead of computing the power by using the (expensive) logarithm, this function * computes the power in a number of straight-forward multiplication steps. This function * is therefore much faster (even for high N) than the generic pow-function. * * For example, `e1^20` can be expressed as `(((e1^2)^2)^2)^2*(e1^2)^2`, which is just 5 multiplications. * * @param e an \ref xexpression * @tparam N the exponent (has to be positive integer) * @return an \ref xfunction */ template inline auto pow(E&& e) noexcept { static_assert(N > 0, "integer power cannot be negative"); return make_lambda_xfunction(detail::pow_impl{}, std::forward(e)); } /** * @ingroup pow_functions * @brief Square root function. * * Returns an \ref xfunction for the element-wise square * root of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto sqrt(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup pow_functions * @brief Cubic root function. * * Returns an \ref xfunction for the element-wise cubic * root of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto cbrt(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup pow_functions * @brief Hypotenuse function. * * Returns an \ref xfunction for the element-wise square * root of the sum of the square of \em e1 and \em e2, avoiding * overflow and underflow at intermediate stages of computation. * @param e1 an \ref xexpression or a scalar * @param e2 an \ref xexpression or a scalar * @return an \ref xfunction * @note e1 and e2 can't be both scalars. */ template inline auto hypot(E1&& e1, E2&& e2) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e1), std::forward(e2)); } /*************************** * trigonometric functions * ***************************/ /** * @defgroup trigo_functions Trigonometric function */ /** * @ingroup trigo_functions * @brief Sine function. * * Returns an \ref xfunction for the element-wise sine * of \em e (measured in radians). * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto sin(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup trigo_functions * @brief Cosine function. * * Returns an \ref xfunction for the element-wise cosine * of \em e (measured in radians). * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto cos(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup trigo_functions * @brief Tangent function. * * Returns an \ref xfunction for the element-wise tangent * of \em e (measured in radians). * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto tan(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup trigo_functions * @brief Arcsine function. * * Returns an \ref xfunction for the element-wise arcsine * of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto asin(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup trigo_functions * @brief Arccosine function. * * Returns an \ref xfunction for the element-wise arccosine * of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto acos(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup trigo_functions * @brief Arctangent function. * * Returns an \ref xfunction for the element-wise arctangent * of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto atan(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup trigo_functions * @brief Artangent function, using signs to determine quadrants. * * Returns an \ref xfunction for the element-wise arctangent * of e1 / e2, using the signs of arguments to determine the * correct quadrant. * @param e1 an \ref xexpression or a scalar * @param e2 an \ref xexpression or a scalar * @return an \ref xfunction * @note e1 and e2 can't be both scalars. */ template inline auto atan2(E1&& e1, E2&& e2) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e1), std::forward(e2)); } /************************ * hyperbolic functions * ************************/ /** * @defgroup hyper_functions Hyperbolic functions */ /** * @ingroup hyper_functions * @brief Hyperbolic sine function. * * Returns an \ref xfunction for the element-wise hyperbolic * sine of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto sinh(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup hyper_functions * @brief Hyperbolic cosine function. * * Returns an \ref xfunction for the element-wise hyperbolic * cosine of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto cosh(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup hyper_functions * @brief Hyperbolic tangent function. * * Returns an \ref xfunction for the element-wise hyperbolic * tangent of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto tanh(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup hyper_functions * @brief Inverse hyperbolic sine function. * * Returns an \ref xfunction for the element-wise inverse hyperbolic * sine of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto asinh(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup hyper_functions * @brief Inverse hyperbolic cosine function. * * Returns an \ref xfunction for the element-wise inverse hyperbolic * cosine of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto acosh(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup hyper_functions * @brief Inverse hyperbolic tangent function. * * Returns an \ref xfunction for the element-wise inverse hyperbolic * tangent of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto atanh(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /***************************** * error and gamma functions * *****************************/ /** * @defgroup err_functions Error and gamma functions */ /** * @ingroup err_functions * @brief Error function. * * Returns an \ref xfunction for the element-wise error function * of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto erf(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup err_functions * @brief Complementary error function. * * Returns an \ref xfunction for the element-wise complementary * error function of \em e, whithout loss of precision for large argument. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto erfc(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup err_functions * @brief Gamma function. * * Returns an \ref xfunction for the element-wise gamma function * of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto tgamma(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup err_functions * @brief Natural logarithm of the gamma function. * * Returns an \ref xfunction for the element-wise logarithm of * the asbolute value fo the gamma function of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto lgamma(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /********************************************* * nearest integer floating point operations * *********************************************/ /** * @defgroup nearint_functions Nearest integer floating point operations */ /** * @ingroup nearint_functions * @brief ceil function. * * Returns an \ref xfunction for the element-wise smallest integer value * not less than \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto ceil(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup nearint_functions * @brief floor function. * * Returns an \ref xfunction for the element-wise smallest integer value * not greater than \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto floor(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup nearint_functions * @brief trunc function. * * Returns an \ref xfunction for the element-wise nearest integer not greater * in magnitude than \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto trunc(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup nearint_functions * @brief round function. * * Returns an \ref xfunction for the element-wise nearest integer value * to \em e, rounding halfway cases away from zero, regardless of the * current rounding mode. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto round(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup nearint_functions * @brief nearbyint function. * * Returns an \ref xfunction for the element-wise rounding of \em e to integer * values in floating point format, using the current rounding mode. nearbyint * never raises FE_INEXACT error. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto nearbyint(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup nearint_functions * @brief rint function. * * Returns an \ref xfunction for the element-wise rounding of \em e to integer * values in floating point format, using the current rounding mode. Contrary * to nearbyint, rint may raise FE_INEXACT error. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto rint(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /**************************** * classification functions * ****************************/ /** * @defgroup classif_functions Classification functions */ /** * @ingroup classif_functions * @brief finite value check * * Returns an \ref xfunction for the element-wise finite value check * tangent of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto isfinite(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup classif_functions * @brief infinity check * * Returns an \ref xfunction for the element-wise infinity check * tangent of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto isinf(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } /** * @ingroup classif_functions * @brief NaN check * * Returns an \ref xfunction for the element-wise NaN check * tangent of \em e. * @param e an \ref xexpression * @return an \ref xfunction */ template inline auto isnan(E&& e) noexcept -> detail::xfunction_type_t { return detail::make_xfunction(std::forward(e)); } namespace detail { template inline auto get_functor(T&& args, std::index_sequence) { return FUNCTOR(std::get(args)...); } template inline auto make_xfunction(std::tuple&& f_args, E&&... e) noexcept { using functor_type = F; using expression_tag = xexpression_tag_t; using type = select_xfunction_expression_t...>; auto functor = get_functor( std::forward>(f_args), std::make_index_sequence{} ); return type(std::move(functor), std::forward(e)...); } struct isclose { using result_type = bool; isclose(double rtol, double atol, bool equal_nan) : m_rtol(rtol) , m_atol(atol) , m_equal_nan(equal_nan) { } template bool operator()(const A1& a, const A2& b) const { using internal_type = xtl::promote_type_t; if (math::isnan(a) && math::isnan(b)) { return m_equal_nan; } if (math::isinf(a) && math::isinf(b)) { // check for both infinity signs equal return a == b; } auto d = math::abs(internal_type(a) - internal_type(b)); return d <= m_atol || d <= m_rtol * double((std::max)(math::abs(internal_type(a)), math::abs(internal_type(b))) ); } private: double m_rtol; double m_atol; bool m_equal_nan; }; } /** * @ingroup classif_functions * @brief Element-wise closeness detection * * Returns an \ref xfunction that evaluates to * true if the elements in ``e1`` and ``e2`` are close to each other * according to parameters ``atol`` and ``rtol``. * The equation is: ``std::abs(a - b) <= (m_atol + m_rtol * std::abs(b))``. * @param e1 input array to compare * @param e2 input array to compare * @param rtol the relative tolerance parameter (default 1e-05) * @param atol the absolute tolerance parameter (default 1e-08) * @param equal_nan if true, isclose returns true if both elements of e1 and e2 are NaN * @return an \ref xfunction */ template inline auto isclose(E1&& e1, E2&& e2, double rtol = 1e-05, double atol = 1e-08, bool equal_nan = false) noexcept { return detail::make_xfunction( std::make_tuple(rtol, atol, equal_nan), std::forward(e1), std::forward(e2) ); } /** * @ingroup classif_functions * @brief Check if all elements in \em e1 are close to the * corresponding elements in \em e2. * * Returns true if all elements in ``e1`` and ``e2`` are close to each other * according to parameters ``atol`` and ``rtol``. * @param e1 input array to compare * @param e2 input arrays to compare * @param rtol the relative tolerance parameter (default 1e-05) * @param atol the absolute tolerance parameter (default 1e-08) * @return a boolean */ template inline auto allclose(E1&& e1, E2&& e2, double rtol = 1e-05, double atol = 1e-08) noexcept { return xt::all(isclose(std::forward(e1), std::forward(e2), rtol, atol)); } /********************** * Reducing functions * **********************/ /** * @defgroup red_functions reducing functions */ /** * @ingroup red_functions * @brief Sum of elements over given axes. * * Returns an \ref xreducer for the sum of elements over given * \em axes. * @param e an \ref xexpression * @param axes the axes along which the sum is performed (optional) * @param es evaluation strategy of the reducer * @tparam T the value type used for internal computation. The default is * `E::value_type`. `T` is also used for determining the value type * of the result, which is the type of `T() + E::value_type()`. * You can pass `big_promote_value_type_t` to avoid overflow in computation. * @return an \ref xreducer */ XTENSOR_REDUCER_FUNCTION(sum, detail::plus, typename std::decay_t::value_type, 0) /** * @ingroup red_functions * @brief Product of elements over given axes. * * Returns an \ref xreducer for the product of elements over given * \em axes. * @param e an \ref xexpression * @param axes the axes along which the product is computed (optional) * @param ddof delta degrees of freedom (optional). * The divisor used in calculations is N - ddof, where N represents the number of * elements. By default ddof is zero. * @param es evaluation strategy of the reducer * @tparam T the value type used for internal computation. The default is `E::value_type`. * `T` is also used for determining the value type of the result, which is the type * of `T() * E::value_type()`. * You can pass `big_promote_value_type_t` to avoid overflow in computation. * @return an \ref xreducer */ XTENSOR_REDUCER_FUNCTION(prod, detail::multiplies, typename std::decay_t::value_type, 1) namespace detail { template inline auto mean_division(S&& s, ST e_size) { using value_type = typename std::conditional_t::value, double, T>; // Avoids floating point exception when s.size is 0 value_type div = s.size() != ST(0) ? static_cast(e_size / s.size()) : value_type(0); return std::move(s) / std::move(div); } template < class T, class E, class X, class D, class EVS, XTL_REQUIRES(xtl::negation>, xtl::is_integral)> inline auto mean(E&& e, X&& axes, const D& ddof, EVS es) { // sum cannot always be a double. It could be a complex number which cannot operate on // std::plus. using size_type = typename std::decay_t::size_type; const size_type size = e.size(); XTENSOR_ASSERT(static_cast(ddof) <= size); auto s = sum(std::forward(e), std::forward(axes), es); return mean_division(std::move(s), size - static_cast(ddof)); } template inline auto mean(E&& e, const I (&axes)[N], const D& ddof, EVS es) { using size_type = typename std::decay_t::size_type; const size_type size = e.size(); XTENSOR_ASSERT(static_cast(ddof) <= size); auto s = sum(std::forward(e), axes, es); return mean_division(std::move(s), size - static_cast(ddof)); } template , xtl::is_integral)> inline auto mean_noaxis(E&& e, const D& ddof, EVS es) { using value_type = typename std::conditional_t::value, double, T>; using size_type = typename std::decay_t::size_type; const size_type size = e.size(); XTENSOR_ASSERT(static_cast(ddof) <= size); auto s = sum(std::forward(e), es); return std::move(s) / static_cast((size - static_cast(ddof))); } } /** * @ingroup red_functions * @brief Mean of elements over given axes. * * Returns an \ref xreducer for the mean of elements over given * \em axes. * @param e an \ref xexpression * @param axes the axes along which the mean is computed (optional) * @param es the evaluation strategy (optional) * @tparam T the value type used for internal computation. The default is * `E::value_type`. `T` is also used for determining the value type * of the result, which is the type of `T() + E::value_type()`. * You can pass `big_promote_value_type_t` to avoid overflow in computation. * @return an \ref xexpression */ template < class T = void, class E, class X, class EVS = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(xtl::negation>)> inline auto mean(E&& e, X&& axes, EVS es = EVS()) { return detail::mean(std::forward(e), std::forward(axes), 0u, es); } template )> inline auto mean(E&& e, EVS es = EVS()) { return detail::mean_noaxis(std::forward(e), 0u, es); } template inline auto mean(E&& e, const I (&axes)[N], EVS es = EVS()) { return detail::mean(std::forward(e), axes, 0u, es); } /** * @ingroup red_functions * @brief Average of elements over given axes using weights. * * Returns an \ref xreducer for the mean of elements over given * \em axes. * @param e an \ref xexpression * @param weights \ref xexpression containing weights associated with the values in \ref e * @param axes the axes along which the mean is computed (optional) * @tparam T the value type used for internal computation. The default is * `E::value_type`. `T`is also used for determining the value type of the result, * which is the type of `T() + E::value_type(). * You can pass `big_promote_value_type_t` to avoid overflow in computation. * @return an \ref xexpression * * @sa mean */ template < class T = void, class E, class W, class X, class EVS = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(is_reducer_options, xtl::negation>)> inline auto average(E&& e, W&& weights, X&& axes, EVS ev = EVS()) { xindex_type_t::shape_type> broadcast_shape; xt::resize_container(broadcast_shape, e.dimension()); auto ax = normalize_axis(e, axes); if (weights.dimension() == 1) { if (weights.size() != e.shape()[ax[0]]) { XTENSOR_THROW(std::runtime_error, "Weights need to have the same shape as expression at axes."); } std::fill(broadcast_shape.begin(), broadcast_shape.end(), std::size_t(1)); broadcast_shape[ax[0]] = weights.size(); } else { if (!same_shape(e.shape(), weights.shape())) { XTENSOR_THROW( std::runtime_error, "Weights with dim > 1 need to have the same shape as expression." ); } std::copy(e.shape().begin(), e.shape().end(), broadcast_shape.begin()); } constexpr layout_type L = default_assignable_layout(std::decay_t::static_layout); auto weights_view = reshape_view(std::forward(weights), std::move(broadcast_shape)); auto scl = sum(weights_view, ax, xt::evaluation_strategy::immediate); return sum(std::forward(e) * std::move(weights_view), std::move(ax), ev) / std::move(scl); } template < class T = void, class E, class W, class X, class EVS = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(is_reducer_options, xtl::is_integral)> inline auto average(E&& e, W&& weights, X axis, EVS ev = EVS()) { return average(std::forward(e), std::forward(weights), {axis}, std::forward(ev)); } template inline auto average(E&& e, W&& weights, const X (&axes)[N], EVS ev = EVS()) { // need to select the X&& overload and forward to different type using ax_t = std::array; return average(std::forward(e), std::forward(weights), xt::forward_normalize(e, axes), ev); } template )> inline auto average(E&& e, W&& weights, EVS ev = EVS()) { if (weights.dimension() != e.dimension() || !std::equal(weights.shape().begin(), weights.shape().end(), e.shape().begin())) { XTENSOR_THROW(std::runtime_error, "Weights need to have the same shape as expression."); } auto div = sum(weights, evaluation_strategy::immediate)(); auto s = sum(std::forward(e) * std::forward(weights), ev) / std::move(div); return s; } template )> inline auto average(E&& e, EVS ev = EVS()) { return mean(e, ev); } namespace detail { template std::enable_if_t::value, E> shared_forward(E e) noexcept { return e; } template std::enable_if_t::value, xshared_expression> shared_forward(E e) noexcept { return make_xshared(std::move(e)); } } template < class T = void, class E, class D, class EVS = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(is_reducer_options, xtl::is_integral)> inline auto variance(E&& e, const D& ddof, EVS es = EVS()) { auto cached_mean = mean(e, es)(); return detail::mean_noaxis(square(std::forward(e) - std::move(cached_mean)), ddof, es); } template )> inline auto variance(E&& e, EVS es = EVS()) { return variance(std::forward(e), 0u, es); } template )> inline auto stddev(E&& e, EVS es = EVS()) { return sqrt(variance(std::forward(e), es)); } /** * @ingroup red_functions * @brief Compute the variance along the specified axes * * Returns the variance of the array elements, a measure of the spread of a * distribution. The variance is computed for the flattened array by default, * otherwise over the specified axes. * * Note: this function is not yet specialized for complex numbers. * * @param e an \ref xexpression * @param axes the axes along which the variance is computed (optional) * @param ddof delta degrees of freedom (optional). * The divisor used in calculations is N - ddof, where N represents the number of * elements. By default ddof is zero. * @param es evaluation strategy to use (lazy (default), or immediate) * @tparam T the value type used for internal computation. The default is * `E::value_type`. `T`is also used for determining the value type of the result, * which is the type of `T() + E::value_type()`. * You can pass `big_promote_value_type_t` to avoid overflow in computation. * @return an \ref xexpression * * @sa stddev, mean */ template < class T = void, class E, class X, class D, class EVS = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(xtl::negation>, xtl::is_integral)> inline auto variance(E&& e, X&& axes, const D& ddof, EVS es = EVS()) { decltype(auto) sc = detail::shared_forward(e); // note: forcing copy of first axes argument -- is there a better solution? auto axes_copy = axes; // always eval to prevent repeated evaluations in the next calls auto inner_mean = eval(mean(sc, std::move(axes_copy), evaluation_strategy::immediate)); // fake keep_dims = 1 // Since the inner_shape might have a reference semantic (e.g. xbuffer_adaptor in bindings) // We need to map it to another type before modifying it. // We pragmatically abuse `get_strides_t` using tmp_shape_t = get_strides_t::shape_type>; tmp_shape_t keep_dim_shape = xtl::forward_sequence(e.shape()); for (const auto& el : axes) { keep_dim_shape[el] = 1u; } auto mrv = reshape_view(std::move(inner_mean), std::move(keep_dim_shape)); return detail::mean(square(sc - std::move(mrv)), std::forward(axes), ddof, es); } template < class T = void, class E, class X, class EVS = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(xtl::negation>, xtl::negation>>, is_reducer_options)> inline auto variance(E&& e, X&& axes, EVS es = EVS()) { return variance(std::forward(e), std::forward(axes), 0u, es); } /** * @ingroup red_functions * @brief Compute the standard deviation along the specified axis. * * Returns the standard deviation, a measure of the spread of a distribution, * of the array elements. The standard deviation is computed for the flattened * array by default, otherwise over the specified axis. * * Note: this function is not yet specialized for complex numbers. * * @param e an \ref xexpression * @param axes the axes along which the standard deviation is computed (optional) * @param es evaluation strategy to use (lazy (default), or immediate) * @tparam T the value type used for internal computation. The default is * `E::value_type`. `T`is also used for determining the value type of the result, * which is the type of `T() + E::value_type()`. * You can pass `big_promote_value_type_t` to avoid overflow in computation. * @return an \ref xexpression * * @sa variance, mean */ template < class T = void, class E, class X, class EVS = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(xtl::negation>)> inline auto stddev(E&& e, X&& axes, EVS es = EVS()) { return sqrt(variance(std::forward(e), std::forward(axes), es)); } template inline auto stddev(E&& e, const A (&axes)[N], EVS es = EVS()) { return stddev( std::forward(e), xtl::forward_sequence, decltype(axes)>(axes), es ); } template < class T = void, class E, class A, std::size_t N, class EVS = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(is_reducer_options)> inline auto variance(E&& e, const A (&axes)[N], EVS es = EVS()) { return variance( std::forward(e), xtl::forward_sequence, decltype(axes)>(axes), es ); } template inline auto variance(E&& e, const A (&axes)[N], const D& ddof, EVS es = EVS()) { return variance( std::forward(e), xtl::forward_sequence, decltype(axes)>(axes), ddof, es ); } /** * @ingroup red_functions * @brief Minimum and maximum among the elements of an array or expression. * * Returns an \ref xreducer for the minimum and maximum of an expression's elements. * @param e an \ref xexpression * @param es evaluation strategy to use (lazy (default), or immediate) * @return an \ref xexpression of type ``std::array``, whose first * and second element represent the minimum and maximum respectively */ template )> inline auto minmax(E&& e, EVS es = EVS()) { using std::max; using std::min; using value_type = typename std::decay_t::value_type; using result_type = std::array; using init_value_fct = xt::const_value; auto reduce_func = [](auto r, const auto& v) { r[0] = (min) (r[0], v); r[1] = (max) (r[1], v); return r; }; auto init_func = init_value_fct( result_type{std::numeric_limits::max(), std::numeric_limits::lowest()} ); auto merge_func = [](auto r, const auto& s) { r[0] = (min) (r[0], s[0]); r[1] = (max) (r[1], s[1]); return r; }; return xt::reduce( make_xreducer_functor(std::move(reduce_func), std::move(init_func), std::move(merge_func)), std::forward(e), arange(e.dimension()), es ); } /** * @defgroup acc_functions accumulating functions */ /** * @ingroup acc_functions * @brief Cumulative sum. * * Returns the accumulated sum for the elements over given * \em axis (or flattened). * @param e an \ref xexpression * @param axis the axes along which the cumulative sum is computed (optional) * @tparam T the value type used for internal computation. The default is * `E::value_type`. `T`is also used for determining the value type of the result, * which is the type of `T() + E::value_type()`. * You can pass `big_promote_value_type_t` to avoid overflow in computation. * @return an \ref xarray */ template inline auto cumsum(E&& e, std::ptrdiff_t axis) { using init_value_type = std::conditional_t::value, typename std::decay_t::value_type, T>; return accumulate( make_xaccumulator_functor(detail::plus(), detail::accumulator_identity()), std::forward(e), axis ); } template inline auto cumsum(E&& e) { using init_value_type = std::conditional_t::value, typename std::decay_t::value_type, T>; return accumulate( make_xaccumulator_functor(detail::plus(), detail::accumulator_identity()), std::forward(e) ); } /** * @ingroup acc_functions * @brief Cumulative product. * * Returns the accumulated product for the elements over given * \em axis (or flattened). * @param e an \ref xexpression * @param axis the axes along which the cumulative product is computed (optional) * @tparam T the value type used for internal computation. The default is * `E::value_type`. `T`is also used for determining the value type of the result, * which is the type of `T() * E::value_type()`. * You can pass `big_promote_value_type_t` to avoid overflow in computation. * @return an \ref xarray */ template inline auto cumprod(E&& e, std::ptrdiff_t axis) { using init_value_type = std::conditional_t::value, typename std::decay_t::value_type, T>; return accumulate( make_xaccumulator_functor(detail::multiplies(), detail::accumulator_identity()), std::forward(e), axis ); } template inline auto cumprod(E&& e) { using init_value_type = std::conditional_t::value, typename std::decay_t::value_type, T>; return accumulate( make_xaccumulator_functor(detail::multiplies(), detail::accumulator_identity()), std::forward(e) ); } /***************** * nan functions * *****************/ namespace detail { struct nan_to_num_functor { template inline auto operator()(const A& a) const { if (math::isnan(a)) { return A(0); } if (math::isinf(a)) { if (a < 0) { return std::numeric_limits::lowest(); } else { return (std::numeric_limits::max)(); } } return a; } }; struct nan_min { template constexpr auto operator()(const T lhs, const U rhs) const { // Clunky expression for working with GCC 4.9 return math::isnan(lhs) ? rhs : (math::isnan(rhs) ? lhs : std::common_type_t( detail::make_xfunction>(lhs, rhs) )); } }; struct nan_max { template constexpr auto operator()(const T lhs, const U rhs) const { // Clunky expression for working with GCC 4.9 return math::isnan(lhs) ? rhs : (math::isnan(rhs) ? lhs : std::common_type_t( detail::make_xfunction>(lhs, rhs) )); } }; struct nan_plus { template constexpr auto operator()(const T lhs, const U rhs) const { return !math::isnan(rhs) ? lhs + rhs : lhs; } }; struct nan_multiplies { template constexpr auto operator()(const T lhs, const U rhs) const { return !math::isnan(rhs) ? lhs * rhs : lhs; } }; template struct nan_init { using value_type = T; using result_type = T; constexpr result_type operator()(const value_type lhs) const { return math::isnan(lhs) ? result_type(V) : lhs; } }; } /** * @defgroup nan_functions nan functions */ /** * @ingroup nan_functions * @brief Convert nan or +/- inf to numbers * * This functions converts NaN to 0, and +inf to the highest, -inf to the lowest * floating point value of the same type. * * @param e input \ref xexpression * @return an \ref xexpression */ template inline auto nan_to_num(E&& e) { return detail::make_xfunction(std::forward(e)); } /** * @ingroup nan_functions * @brief Minimum element over given axes, ignoring NaNs. * * Returns an \ref xreducer for the minimum of elements over given * @p axes, ignoring NaNs. * @warning Casting the result to an integer type can cause undefined behavior. * @param e an \ref xexpression * @param axes the axes along which the minimum is found (optional) * @param es evaluation strategy of the reducer (optional) * @tparam T the result type. The default is `E::value_type`. * @return an \ref xreducer */ XTENSOR_REDUCER_FUNCTION(nanmin, detail::nan_min, typename std::decay_t::value_type, std::nan("0")) /** * @ingroup nan_functions * @brief Maximum element along given axes, ignoring NaNs. * * Returns an \ref xreducer for the sum of elements over given * @p axes, ignoring NaN. * @warning Casting the result to an integer type can cause undefined behavior. * @param e an \ref xexpression * @param axes the axes along which the sum is performed (optional) * @param es evaluation strategy of the reducer (optional) * @tparam T the result type. The default is `E::value_type`. * @return an \ref xreducer */ XTENSOR_REDUCER_FUNCTION(nanmax, detail::nan_max, typename std::decay_t::value_type, std::nan("0")) /** * @ingroup nan_functions * @brief Sum of elements over given axes, replacing NaN with 0. * * Returns an \ref xreducer for the sum of elements over given * @p axes, ignoring NaN. * @param e an \ref xexpression * @param axes the axes along which the sum is performed (optional) * @param es evaluation strategy of the reducer (optional) * @tparam T the value type used for internal computation. The default is * `E::value_type`. `T` is also used for determining the value type * of the result, which is the type of `T() + E::value_type()`. * You can pass `big_promote_value_type_t` to avoid overflow in computation. * @return an \ref xreducer */ XTENSOR_REDUCER_FUNCTION(nansum, detail::nan_plus, typename std::decay_t::value_type, 0) /** * @ingroup nan_functions * @brief Product of elements over given axes, replacing NaN with 1. * * Returns an \ref xreducer for the sum of elements over given * @p axes, replacing nan with 1. * @param e an \ref xexpression * @param axes the axes along which the sum is performed (optional) * @param es evaluation strategy of the reducer (optional) * @tparam T the value type used for internal computation. The default is * `E::value_type`. `T` is also used for determining the value type * of the result, which is the type of `T() * E::value_type()`. * You can pass `big_promote_value_type_t` to avoid overflow in computation. * @return an \ref xreducer */ XTENSOR_REDUCER_FUNCTION(nanprod, detail::nan_multiplies, typename std::decay_t::value_type, 1) #define COUNT_NON_ZEROS_CONTENT \ using value_type = typename std::decay_t::value_type; \ using result_type = xt::detail::xreducer_size_type_t; \ using init_value_fct = xt::const_value; \ \ auto init_fct = init_value_fct(0); \ \ auto reduce_fct = [](const auto& lhs, const auto& rhs) \ { \ using value_t = xt::detail::xreducer_temporary_type_t>; \ using result_t = std::decay_t; \ \ return (rhs != value_t(0)) ? lhs + result_t(1) : lhs; \ }; \ auto merge_func = detail::plus(); template )> inline auto count_nonzero(E&& e, EVS es = EVS()) { COUNT_NON_ZEROS_CONTENT; return xt::reduce( make_xreducer_functor(std::move(reduce_fct), std::move(init_fct), std::move(merge_func)), std::forward(e), es ); } template < class E, class X, class EVS = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(xtl::negation>, xtl::negation>)> inline auto count_nonzero(E&& e, X&& axes, EVS es = EVS()) { COUNT_NON_ZEROS_CONTENT; return xt::reduce( make_xreducer_functor(std::move(reduce_fct), std::move(init_fct), std::move(merge_func)), std::forward(e), std::forward(axes), es ); } template < class E, class X, class EVS = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(xtl::negation>, xtl::is_integral)> inline auto count_nonzero(E&& e, X axis, EVS es = EVS()) { return count_nonzero(std::forward(e), {axis}, es); } template inline auto count_nonzero(E&& e, const I (&axes)[N], EVS es = EVS()) { COUNT_NON_ZEROS_CONTENT; return xt::reduce( make_xreducer_functor(std::move(reduce_fct), std::move(init_fct), std::move(merge_func)), std::forward(e), axes, es ); } #undef COUNT_NON_ZEROS_CONTENT template )> inline auto count_nonnan(E&& e, EVS es = EVS()) { return xt::count_nonzero(!xt::isnan(std::forward(e)), es); } template < class E, class X, class EVS = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(xtl::negation>, xtl::negation>)> inline auto count_nonnan(E&& e, X&& axes, EVS es = EVS()) { return xt::count_nonzero(!xt::isnan(std::forward(e)), std::forward(axes), es); } template < class E, class X, class EVS = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(xtl::negation>, xtl::is_integral)> inline auto count_nonnan(E&& e, X&& axes, EVS es = EVS()) { return xt::count_nonzero(!xt::isnan(std::forward(e)), {axes}, es); } template inline auto count_nonnan(E&& e, const I (&axes)[N], EVS es = EVS()) { return xt::count_nonzero(!xt::isnan(std::forward(e)), axes, es); } /** * @ingroup nan_functions * @brief Cumulative sum, replacing nan with 0. * * Returns an xaccumulator for the sum of elements over given * \em axis, replacing nan with 0. * @param e an \ref xexpression * @param axis the axis along which the elements are accumulated (optional) * @tparam T the value type used for internal computation. The default is * `E::value_type`. `T` is also used for determining the value type * of the result, which is the type of `T() + E::value_type()`. * You can pass `big_promote_value_type_t` to avoid overflow in computation. * @return an xaccumulator */ template inline auto nancumsum(E&& e, std::ptrdiff_t axis) { using init_value_type = std::conditional_t::value, typename std::decay_t::value_type, T>; return accumulate( make_xaccumulator_functor(detail::nan_plus(), detail::nan_init()), std::forward(e), axis ); } template inline auto nancumsum(E&& e) { using init_value_type = std::conditional_t::value, typename std::decay_t::value_type, T>; return accumulate( make_xaccumulator_functor(detail::nan_plus(), detail::nan_init()), std::forward(e) ); } /** * @ingroup nan_functions * @brief Cumulative product, replacing nan with 1. * * Returns an xaccumulator for the product of elements over given * \em axis, replacing nan with 1. * @param e an \ref xexpression * @param axis the axis along which the elements are accumulated (optional) * @tparam T the value type used for internal computation. The default is * `E::value_type`. `T` is also used for determining the value type * of the result, which is the type of `T() * E::value_type()`. * You can pass `big_promote_value_type_t` to avoid overflow in computation. * @return an xaccumulator */ template inline auto nancumprod(E&& e, std::ptrdiff_t axis) { using init_value_type = std::conditional_t::value, typename std::decay_t::value_type, T>; return accumulate( make_xaccumulator_functor(detail::nan_multiplies(), detail::nan_init()), std::forward(e), axis ); } template inline auto nancumprod(E&& e) { using init_value_type = std::conditional_t::value, typename std::decay_t::value_type, T>; return accumulate( make_xaccumulator_functor(detail::nan_multiplies(), detail::nan_init()), std::forward(e) ); } namespace detail { template struct diff_impl { template inline void operator()( Arg& ad, const std::size_t& n, xstrided_slice_vector& slice1, xstrided_slice_vector& slice2, std::size_t saxis ) { for (std::size_t i = 0; i < n; ++i) { slice2[saxis] = range(xnone(), ad.shape()[saxis] - 1); ad = strided_view(ad, slice1) - strided_view(ad, slice2); } } }; template <> struct diff_impl { template inline void operator()( Arg& ad, const std::size_t& n, xstrided_slice_vector& slice1, xstrided_slice_vector& slice2, std::size_t saxis ) { for (std::size_t i = 0; i < n; ++i) { slice2[saxis] = range(xnone(), ad.shape()[saxis] - 1); ad = not_equal(strided_view(ad, slice1), strided_view(ad, slice2)); } } }; } /** * @ingroup nan_functions * @brief Mean of elements over given axes, excluding NaNs. * * Returns an \ref xreducer for the mean of elements over given * \em axes, excluding NaNs. * This is not the same as counting NaNs as zero, since excluding NaNs changes the number * of elements considered in the statistic. * @param e an \ref xexpression * @param axes the axes along which the mean is computed (optional) * @param es the evaluation strategy (optional) * @tparam T the result type. The default is `E::value_type`. * You can pass `big_promote_value_type_t` to avoid overflow in computation. * @return an \ref xexpression */ template < class T = void, class E, class X, class EVS = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(xtl::negation>)> inline auto nanmean(E&& e, X&& axes, EVS es = EVS()) { decltype(auto) sc = detail::shared_forward(e); // note: forcing copy of first axes argument -- is there a better solution? auto axes_copy = axes; using value_type = typename std::conditional_t::value, double, T>; using sum_type = typename std::conditional_t< std::is_same::value, typename std::common_type_t::value_type, value_type>, T>; // sum cannot always be a double. It could be a complex number which cannot operate on // std::plus. return nansum(sc, std::forward(axes), es) / xt::cast(count_nonnan(sc, std::move(axes_copy), es)); } template )> inline auto nanmean(E&& e, EVS es = EVS()) { decltype(auto) sc = detail::shared_forward(e); using value_type = typename std::conditional_t::value, double, T>; using sum_type = typename std::conditional_t< std::is_same::value, typename std::common_type_t::value_type, value_type>, T>; return nansum(sc, es) / xt::cast(count_nonnan(sc, es)); } template inline auto nanmean(E&& e, const I (&axes)[N], EVS es = EVS()) { return nanmean( std::forward(e), xtl::forward_sequence, decltype(axes)>(axes), es ); } template )> inline auto nanvar(E&& e, EVS es = EVS()) { decltype(auto) sc = detail::shared_forward(e); return nanmean(square(sc - nanmean(sc)), es); } template )> inline auto nanstd(E&& e, EVS es = EVS()) { return sqrt(nanvar(std::forward(e), es)); } /** * @ingroup nan_functions * @brief Compute the variance along the specified axes, excluding NaNs * * Returns the variance of the array elements, a measure of the spread of a * distribution. The variance is computed for the flattened array by default, * otherwise over the specified axes. * Excluding NaNs changes the number of elements considered in the statistic. * * Note: this function is not yet specialized for complex numbers. * * @param e an \ref xexpression * @param axes the axes along which the variance is computed (optional) * @param es evaluation strategy to use (lazy (default), or immediate) * @tparam T the result type. The default is `E::value_type`. * You can pass `big_promote_value_type_t` to avoid overflow in computation. * @return an \ref xexpression * * @sa nanstd, nanmean */ template < class T = void, class E, class X, class EVS = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(xtl::negation>)> inline auto nanvar(E&& e, X&& axes, EVS es = EVS()) { decltype(auto) sc = detail::shared_forward(e); // note: forcing copy of first axes argument -- is there a better solution? auto axes_copy = axes; using result_type = typename std::conditional_t::value, double, T>; auto inner_mean = nanmean(sc, std::move(axes_copy)); // fake keep_dims = 1 // Since the inner_shape might have a reference semantic (e.g. xbuffer_adaptor in bindings) // We need to map it to another type before modifying it. // We pragmatically abuse `get_strides_t` using tmp_shape_t = get_strides_t::shape_type>; tmp_shape_t keep_dim_shape = xtl::forward_sequence(e.shape()); for (const auto& el : axes) { keep_dim_shape[el] = 1; } auto mrv = reshape_view(std::move(inner_mean), std::move(keep_dim_shape)); return nanmean(square(cast(sc) - std::move(mrv)), std::forward(axes), es); } /** * @ingroup nan_functions * @brief Compute the standard deviation along the specified axis, excluding nans. * * Returns the standard deviation, a measure of the spread of a distribution, * of the array elements. The standard deviation is computed for the flattened * array by default, otherwise over the specified axis. * Excluding NaNs changes the number of elements considered in the statistic. * * Note: this function is not yet specialized for complex numbers. * * @param e an \ref xexpression * @param axes the axes along which the standard deviation is computed (optional) * @param es evaluation strategy to use (lazy (default), or immediate) * @tparam T the result type. The default is `E::value_type`. * You can pass `big_promote_value_type_t` to avoid overflow in computation. * @return an \ref xexpression * * @sa nanvar, nanmean */ template < class T = void, class E, class X, class EVS = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(xtl::negation>)> inline auto nanstd(E&& e, X&& axes, EVS es = EVS()) { return sqrt(nanvar(std::forward(e), std::forward(axes), es)); } template inline auto nanstd(E&& e, const A (&axes)[N], EVS es = EVS()) { return nanstd( std::forward(e), xtl::forward_sequence, decltype(axes)>(axes), es ); } template inline auto nanvar(E&& e, const A (&axes)[N], EVS es = EVS()) { return nanvar( std::forward(e), xtl::forward_sequence, decltype(axes)>(axes), es ); } /** * @ingroup red_functions * @brief Calculate the n-th discrete difference along the given axis. * * Calculate the n-th discrete difference along the given axis. This function is not lazy (might change in * the future). * @param a an \ref xexpression * @param n The number of times values are differenced. If zero, the input is returned as-is. (optional) * @param axis The axis along which the difference is taken, default is the last axis. * @return an xarray */ template auto diff(const xexpression& a, std::size_t n = 1, std::ptrdiff_t axis = -1) { typename std::decay_t::temporary_type ad = a.derived_cast(); std::size_t saxis = normalize_axis(ad.dimension(), axis); if (n <= ad.size()) { if (n != std::size_t(0)) { xstrided_slice_vector slice1(ad.dimension(), all()); xstrided_slice_vector slice2(ad.dimension(), all()); slice1[saxis] = range(1, xnone()); detail::diff_impl impl; impl(ad, n, slice1, slice2, saxis); } } else { auto shape = ad.shape(); shape[saxis] = std::size_t(0); ad.resize(shape); } return ad; } /** * @ingroup red_functions * @brief Integrate along the given axis using the composite trapezoidal rule. * * Returns definite integral as approximated by trapezoidal rule. This function is not lazy (might change * in the future). * @param y an \ref xexpression * @param dx the spacing between sample points (optional) * @param axis the axis along which to integrate. * @return an xarray */ template auto trapz(const xexpression& y, double dx = 1.0, std::ptrdiff_t axis = -1) { auto& yd = y.derived_cast(); std::size_t saxis = normalize_axis(yd.dimension(), axis); xstrided_slice_vector slice1(yd.dimension(), all()); xstrided_slice_vector slice2(yd.dimension(), all()); slice1[saxis] = range(1, xnone()); slice2[saxis] = range(xnone(), yd.shape()[saxis] - 1); auto trap = dx * (strided_view(yd, slice1) + strided_view(yd, slice2)) * 0.5; return eval(sum(trap, {saxis})); } /** * @ingroup red_functions * @brief Integrate along the given axis using the composite trapezoidal rule. * * Returns definite integral as approximated by trapezoidal rule. This function is not lazy (might change * in the future). * @param y an \ref xexpression * @param x an \ref xexpression representing the sample points corresponding to the y values. * @param axis the axis along which to integrate. * @return an xarray */ template auto trapz(const xexpression& y, const xexpression& x, std::ptrdiff_t axis = -1) { auto& yd = y.derived_cast(); auto& xd = x.derived_cast(); decltype(diff(x)) dx; std::size_t saxis = normalize_axis(yd.dimension(), axis); if (xd.dimension() == 1) { dx = diff(x); typename std::decay_t::shape_type shape; resize_container(shape, yd.dimension()); std::fill(shape.begin(), shape.end(), 1); shape[saxis] = dx.shape()[0]; dx.reshape(shape); } else { dx = diff(x, 1, axis); } xstrided_slice_vector slice1(yd.dimension(), all()); xstrided_slice_vector slice2(yd.dimension(), all()); slice1[saxis] = range(1, xnone()); slice2[saxis] = range(xnone(), yd.shape()[saxis] - 1); auto trap = dx * (strided_view(yd, slice1) + strided_view(yd, slice2)) * 0.5; return eval(sum(trap, {saxis})); } /** * @ingroup basic_functions * @brief Returns the one-dimensional piecewise linear interpolant to a function with given discrete data * points (xp, fp), evaluated at x. * * @param x The x-coordinates at which to evaluate the interpolated values (sorted). * @param xp The x-coordinates of the data points (sorted). * @param fp The y-coordinates of the data points, same length as xp. * @param left Value to return for x < xp[0]. * @param right Value to return for x > xp[-1] * @return an one-dimensional xarray, same length as x. */ template inline auto interp(const E1& x, const E2& xp, const E3& fp, T left, T right) { using size_type = common_size_type_t; using value_type = typename E3::value_type; // basic checks XTENSOR_ASSERT(xp.dimension() == 1); XTENSOR_ASSERT(std::is_sorted(x.cbegin(), x.cend())); XTENSOR_ASSERT(std::is_sorted(xp.cbegin(), xp.cend())); // allocate output auto f = xtensor::from_shape(x.shape()); // counter in "x": from left size_type i = 0; // fill f[i] for x[i] <= xp[0] for (; i < x.size(); ++i) { if (x[i] > xp[0]) { break; } f[i] = static_cast(left); } // counter in "x": from right // (index counts one right, to terminate the reverse loop, without risking being negative) size_type imax = x.size(); // fill f[i] for x[-1] >= xp[-1] for (; imax > 0; --imax) { if (x[imax - 1] < xp[xp.size() - 1]) { break; } f[imax - 1] = static_cast(right); } // catch edge case: all entries are "right" if (imax == 0) { return f; } // set "imax" as actual index // (counted one right, see above) --imax; // counter in "xp" size_type ip = 1; // fill f[i] for the interior for (; i <= imax; ++i) { // - search next value in "xp" while (x[i] > xp[ip]) { ++ip; } // - distances as doubles double dfp = static_cast(fp[ip] - fp[ip - 1]); double dxp = static_cast(xp[ip] - xp[ip - 1]); double dx = static_cast(x[i] - xp[ip - 1]); // - interpolate f[i] = fp[ip - 1] + static_cast(dfp / dxp * dx); } return f; } namespace detail { template auto calculate_discontinuity(E1&& discontinuity, E2&&) { return discontinuity; } template auto calculate_discontinuity(xt::placeholders::xtuph, E2&& period) { return 0.5 * period; } template auto calculate_interval(E2&& period, typename std::enable_if::value, E1>::type* = 0) { auto interval_high = 0.5 * period; uint64_t remainder = static_cast(period) % 2; auto boundary_ambiguous = (remainder == 0); return std::make_tuple(interval_high, boundary_ambiguous); } template auto calculate_interval(E2&& period, typename std::enable_if::value, E1>::type* = 0) { auto interval_high = 0.5 * period; auto boundary_ambiguous = true; return std::make_tuple(interval_high, boundary_ambiguous); } } /** * @ingroup basic_functions * @brief Unwrap by taking the complement of large deltas with respect to the period * @details https://numpy.org/doc/stable/reference/generated/numpy.unwrap.html * @param p Input array. * @param discontinuity * Maximum discontinuity between values, default is `period / 2`. * Values below `period / 2` are treated as if they were `period / 2`. * To have an effect different from the default, use `discontinuity > period / 2`. * @param axis Axis along which unwrap will operate, default: the last axis. * @param period Size of the range over which the input wraps. Default: \f$ 2 \pi \f$. */ template inline auto unwrap( E1&& p, E2 discontinuity = xnone(), std::ptrdiff_t axis = -1, E3 period = 2.0 * xt::numeric_constants::PI ) { auto discont = detail::calculate_discontinuity(discontinuity, period); using value_type = typename std::decay_t::value_type; std::size_t saxis = normalize_axis(p.dimension(), axis); auto dd = diff(p, 1, axis); xstrided_slice_vector slice(p.dimension(), all()); slice[saxis] = range(1, xnone()); auto interval_tuple = detail::calculate_interval(period); auto interval_high = std::get<0>(interval_tuple); auto boundary_ambiguous = std::get<1>(interval_tuple); auto interval_low = -interval_high; auto ddmod = xt::eval(xt::fmod(xt::fmod(dd - interval_low, period) + period, period) + interval_low); if (boundary_ambiguous) { // for `mask = (abs(dd) == period/2)`, the above line made //`ddmod[mask] == -period/2`. correct these such that //`ddmod[mask] == sign(dd[mask])*period/2`. auto boolmap = xt::equal(ddmod, interval_low) && (xt::greater(dd, 0.0)); ddmod = xt::where(boolmap, interval_high, ddmod); } auto ph_correct = xt::eval(ddmod - dd); ph_correct = xt::where(xt::abs(dd) < discont, 0.0, ph_correct); E1 up(p); strided_view(up, slice) = strided_view(p, slice) + xt::cumsum(ph_correct, static_cast(saxis)); return up; } /** * @ingroup basic_functions * @brief Returns the one-dimensional piecewise linear interpolant to a function with given discrete data * points (xp, fp), evaluated at x. * * @param x The x-coordinates at which to evaluate the interpolated values (sorted). * @param xp The x-coordinates of the data points (sorted). * @param fp The y-coordinates of the data points, same length as xp. * @return an one-dimensional xarray, same length as x. */ template inline auto interp(const E1& x, const E2& xp, const E3& fp) { return interp(x, xp, fp, fp[0], fp[fp.size() - 1]); } /** * @brief Returns the covariance matrix * * @param x one or two dimensional array * @param y optional one-dimensional array to build covariance to x */ template inline auto cov(const E1& x, const E1& y = E1()) { using value_type = typename E1::value_type; if (y.dimension() == 0) { auto s = x.shape(); using size_type = std::decay_t; if (x.dimension() == 1) { auto covar = eval(zeros({1, 1})); auto x_norm = x - eval(mean(x)); covar(0, 0) = std::inner_product(x_norm.begin(), x_norm.end(), x_norm.begin(), 0.0) / value_type(s[0] - 1); return covar; } XTENSOR_ASSERT(x.dimension() == 2); auto covar = eval(zeros({s[0], s[0]})); auto m = eval(mean(x, {1})); m.reshape({m.shape()[0], 1}); auto x_norm = x - m; for (size_type i = 0; i < s[0]; i++) { auto xi = strided_view(x_norm, {range(i, i + 1), all()}); for (size_type j = i; j < s[0]; j++) { auto xj = strided_view(x_norm, {range(j, j + 1), all()}); covar(j, i) = std::inner_product(xi.begin(), xi.end(), xj.begin(), 0.0) / value_type(s[1] - 1); } } return eval(covar + transpose(covar) - diag(diagonal(covar))); } else { return cov(eval(stack(xtuple(x, y)))); } } /* * convolution mode placeholders for selecting the algorithm * used in computing a 1D convolution. * Same as NumPy's mode parameter. */ namespace convolve_mode { struct valid { }; struct full { }; } namespace detail { template inline auto convolve_impl(E1&& e1, E2&& e2, convolve_mode::valid) { using value_type = typename std::decay::type::value_type; const std::size_t na = e1.size(); const std::size_t nv = e2.size(); const std::size_t n = na - nv + 1; xt::xtensor out = xt::zeros({n}); for (std::size_t i = 0; i < n; i++) { for (std::size_t j = 0; j < nv; j++) { out(i) += e1(j) * e2(j + i); } } return out; } template inline auto convolve_impl(E1&& e1, E2&& e2, convolve_mode::full) { using value_type = typename std::decay::type::value_type; const std::size_t na = e1.size(); const std::size_t nv = e2.size(); const std::size_t n = na + nv - 1; xt::xtensor out = xt::zeros({n}); for (std::size_t i = 0; i < n; i++) { const std::size_t jmn = (i >= nv - 1) ? i - (nv - 1) : 0; const std::size_t jmx = (i < na - 1) ? i : na - 1; for (std::size_t j = jmn; j <= jmx; ++j) { out(i) += e1(j) * e2(i - j); } } return out; } } /* * @brief computes the 1D convolution between two 1D expressions * * @param a 1D expression * @param v 1D expression * @param mode placeholder Select algorithm #convolve_mode * * @detail the algorithm convolves a with v and will incur a copy overhead * should v be longer than a. */ template inline auto convolve(E1&& a, E2&& v, E3 mode) { if (a.dimension() != 1 || v.dimension() != 1) { XTENSOR_THROW(std::runtime_error, "Invalid dimentions convolution arguments must be 1D expressions"); } XTENSOR_ASSERT(a.size() > 0 && v.size() > 0); // swap them so a is always the longest one if (a.size() < v.size()) { return detail::convolve_impl(std::forward(v), std::forward(a), mode); } else { return detail::convolve_impl(std::forward(a), std::forward(v), mode); } } } #endif