#ifndef PYTHONIC_NUMPY_ARGMINMAX_HPP #define PYTHONIC_NUMPY_ARGMINMAX_HPP #include "pythonic/utils/functor.hpp" #include "pythonic/types/ndarray.hpp" #include "pythonic/numpy/asarray.hpp" #include "pythonic/builtins/ValueError.hpp" PYTHONIC_NS_BEGIN namespace numpy { namespace details { template P iota(utils::index_sequence) { return {static_cast(Is)...}; } template P iota() { return iota

(utils::make_index_sequence()); } } template long _argminmax_seq(E const &elts, T &minmax_elts) { long index = 0; long res = -1; for (auto const &elt : elts) { if (Op::value(elt, minmax_elts)) { minmax_elts = elt; res = index; } ++index; } return res; } template #ifdef USE_XSIMD typename std::enable_if< !E::is_vectorizable || !types::is_vector_op::value || std::is_same::value, long>::type #else long #endif _argminmax(E const &elts, T &minmax_elts, utils::int_<1>) { return _argminmax_seq(elts, minmax_elts); } template std::tuple _argminmax_fast(E const &elts, T &minmax_elts, long current_pos, utils::int_<1>, Indices... indices) { long res = -1; long n = elts.template shape::type::value - 1>(); for (long i = 0; i < n; ++i) { auto elt = elts.load(indices..., i); if (Op::value(elt, minmax_elts)) { minmax_elts = elt; res = current_pos + i; } } return std::make_tuple(res, current_pos + n); } #ifdef USE_XSIMD template struct bool_caster; template <> struct bool_caster { template auto operator()(T const &value) -> decltype(xsimd::bool_cast(value)) { return xsimd::bool_cast(value); } }; template <> struct bool_caster { template T operator()(T const &value) { return value; } }; template typename std::enable_if< E::is_vectorizable && types::is_vector_op::value && !std::is_same::value, long>::type _argminmax(E const &elts, T &minmax_elts, utils::int_<1>) { using vT = xsimd::simd_type; using iT = xsimd::as_integer_t; static const size_t vN = vT::size; const long n = elts.size(); if (n >= std::numeric_limits::max()) { return _argminmax_seq(elts, minmax_elts); } auto viter = types::vectorizer_nobroadcast::vbegin(elts), vend = types::vectorizer_nobroadcast::vend(elts); const long bound = std::distance(viter, vend); long minmax_index = -1; if (bound > 0) { auto vacc = *viter; iT iota[vN] = {0}; for (long i = 0; i < (long)vN; ++i) iota[i] = i; auto curr = xsimd::load_unaligned(iota); xsimd::simd_type indices = curr; xsimd::simd_type step{vN}; for (++viter; viter != vend; ++viter) { curr += step; auto c = *viter; vacc = typename Op::op{}(vacc, c); auto mask = c == vacc; indices = xsimd::select(bool_caster::value>{}(mask), curr, indices); } alignas(sizeof(vT)) T stored[vN]; vacc.store_aligned(&stored[0]); alignas(sizeof(vT)) long indexed[vN]; indices.store_aligned(&indexed[0]); for (size_t j = 0; j < vN; ++j) { if (Op::value(stored[j], minmax_elts)) { minmax_elts = stored[j]; minmax_index = indexed[j]; } } } auto iter = elts.begin() + bound * vN; for (long i = bound * vN; i < n; ++i, ++iter) { if (Op::value(*iter, minmax_elts)) { minmax_elts = *iter; minmax_index = i; } } return minmax_index; } #endif template long _argminmax(E const &elts, T &minmax_elts, utils::int_) { long current_pos = 0; long current_minmaxarg = 0; for (auto &&elt : elts) { long v = _argminmax(elt, minmax_elts, utils::int_()); if (v >= 0) current_minmaxarg = current_pos + v; current_pos += elt.flat_size(); } return current_minmaxarg; } template typename std::enable_if>::type _argminmax_fast(E const &elts, T &minmax_elts, long current_pos, utils::int_, Indices... indices) { long current_minmaxarg = 0; for (long i = 0, n = elts.template shape::type::value - N>(); i < n; ++i) { long v; std::tie(v, current_pos) = _argminmax_fast( elts, minmax_elts, current_pos, utils::int_(), indices..., i); if (v >= 0) current_minmaxarg = v; } return std::make_tuple(current_minmaxarg, current_pos); } template long argminmax(E const &expr) { if (!expr.flat_size()) throw types::ValueError("empty sequence"); using elt_type = typename E::dtype; elt_type argminmax_value = Op::limit(); #ifndef USE_XSIMD if (utils::no_broadcast_ex(expr)) { return std::get<0>(_argminmax_fast(expr, argminmax_value, 0, utils::int_())); } else #endif return _argminmax(expr, argminmax_value, utils::int_()); } template void _argminmax_tail(T &&out, E const &expr, long curr, V &&curr_minmax, std::integral_constant) { if (Op::value(expr, curr_minmax)) { out = curr; curr_minmax = expr; } } template typename std::enable_if::type _argminmax_tail(T &&out, E const &expr, long curr, V &&curr_minmax, std::integral_constant) { static_assert(N >= 1, "specialization ok"); long i = 0; for (auto &&elt : expr) { _argminmax_tail(out.fast(i), elt, curr, curr_minmax.fast(i), std::integral_constant()); ++i; } } template typename std::enable_if::type _argminmax_head(T &&out, E const &expr, std::integral_constant) { typename E::dtype val = Op::limit(); long i = 0; for (auto &&elt : expr) _argminmax_tail(out, elt, i++, val, std::integral_constant()); } template typename std::enable_if::type _argminmax_head(T &&out, E const &expr, std::integral_constant) { static_assert(N > 1, "specialization ok"); types::ndarray> val{ sutils::getshape(out), Op::limit()}; long i = 0; for (auto &&elt : expr) { _argminmax_tail(out, elt, i++, val, std::integral_constant()); } } template typename std::enable_if::type _argminmax_head(T &&out, E const &expr, std::integral_constant) { static_assert(N >= 1, "specialization ok"); auto out_iter = out.begin(); for (auto &&elt : expr) { _argminmax_head(*out_iter, elt, std::integral_constant()); ++out_iter; } } template void _argminmax_pick_axis(long axis, T &&out, E const &expr, utils::index_sequence) { (void)std::initializer_list{ ((Axis == axis) && (_argminmax_head( out, expr, std::integral_constant()), true))...}; } template types::ndarray> argminmax(E const &array, long axis) { if (axis < 0) axis += E::value; if (axis < 0 || size_t(axis) >= E::value) throw types::ValueError("axis out of bounds"); auto shape = sutils::getshape(array); types::array shp; auto next = std::copy(shape.begin(), shape.begin() + axis, shp.begin()); std::copy(shape.begin() + axis + 1, shape.end(), next); types::ndarray> out{shp, builtins::None}; _argminmax_pick_axis(axis, out, array, utils::make_index_sequence()); return out; } } PYTHONIC_NS_END #endif