%PDF- %PDF-
Direktori : /lib/python3/dist-packages/pythran/pythonic/numpy/ |
Current File : //lib/python3/dist-packages/pythran/pythonic/numpy/argminmax.hpp |
#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 <class P, size_t... Is> P iota(utils::index_sequence<Is...>) { return {static_cast<typename P::value_type>(Is)...}; } template <class P> P iota() { return iota<P>(utils::make_index_sequence<P::size>()); } } template <class Op, class E, class T> 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 <class Op, class E, class T> #ifdef USE_XSIMD typename std::enable_if< !E::is_vectorizable || !types::is_vector_op<typename Op::op, T, T>::value || std::is_same<typename E::dtype, bool>::value, long>::type #else long #endif _argminmax(E const &elts, T &minmax_elts, utils::int_<1>) { return _argminmax_seq<Op>(elts, minmax_elts); } template <class Op, class E, class T, class... Indices> std::tuple<long, long> _argminmax_fast(E const &elts, T &minmax_elts, long current_pos, utils::int_<1>, Indices... indices) { long res = -1; long n = elts.template shape<std::decay<E>::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 <bool IsInt> struct bool_caster; template <> struct bool_caster<true> { template <class T> auto operator()(T const &value) -> decltype(xsimd::bool_cast(value)) { return xsimd::bool_cast(value); } }; template <> struct bool_caster<false> { template <class T> T operator()(T const &value) { return value; } }; template <class Op, class E, class T> typename std::enable_if< E::is_vectorizable && types::is_vector_op<typename Op::op, T, T>::value && !std::is_same<typename E::dtype, bool>::value, long>::type _argminmax(E const &elts, T &minmax_elts, utils::int_<1>) { using vT = xsimd::simd_type<T>; using iT = xsimd::as_integer_t<T>; static const size_t vN = vT::size; const long n = elts.size(); if (n >= std::numeric_limits<iT>::max()) { return _argminmax_seq<Op>(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<iT> indices = curr; xsimd::simd_type<iT> 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<std::is_floating_point<T>::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 <class Op, class E, size_t N, class T> long _argminmax(E const &elts, T &minmax_elts, utils::int_<N>) { long current_pos = 0; long current_minmaxarg = 0; for (auto &&elt : elts) { long v = _argminmax<Op>(elt, minmax_elts, utils::int_<N - 1>()); if (v >= 0) current_minmaxarg = current_pos + v; current_pos += elt.flat_size(); } return current_minmaxarg; } template <class Op, class E, size_t N, class T, class... Indices> typename std::enable_if<N != 1, std::tuple<long, long>>::type _argminmax_fast(E const &elts, T &minmax_elts, long current_pos, utils::int_<N>, Indices... indices) { long current_minmaxarg = 0; for (long i = 0, n = elts.template shape<std::decay<E>::type::value - N>(); i < n; ++i) { long v; std::tie(v, current_pos) = _argminmax_fast<Op>( elts, minmax_elts, current_pos, utils::int_<N - 1>(), indices..., i); if (v >= 0) current_minmaxarg = v; } return std::make_tuple(current_minmaxarg, current_pos); } template <class Op, class E> 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<Op>(expr, argminmax_value, 0, utils::int_<E::value>())); } else #endif return _argminmax<Op>(expr, argminmax_value, utils::int_<E::value>()); } template <class Op, size_t Dim, size_t Axis, class T, class E, class V> void _argminmax_tail(T &&out, E const &expr, long curr, V &&curr_minmax, std::integral_constant<size_t, 0>) { if (Op::value(expr, curr_minmax)) { out = curr; curr_minmax = expr; } } template <class Op, size_t Dim, size_t Axis, class T, class E, class V, size_t N> typename std::enable_if<Axis != (Dim - N), void>::type _argminmax_tail(T &&out, E const &expr, long curr, V &&curr_minmax, std::integral_constant<size_t, N>) { static_assert(N >= 1, "specialization ok"); long i = 0; for (auto &&elt : expr) { _argminmax_tail<Op, Dim, Axis>(out.fast(i), elt, curr, curr_minmax.fast(i), std::integral_constant<size_t, N - 1>()); ++i; } } template <class Op, size_t Dim, size_t Axis, class T, class E> typename std::enable_if<Axis == (Dim - 1), void>::type _argminmax_head(T &&out, E const &expr, std::integral_constant<size_t, 1>) { typename E::dtype val = Op::limit(); long i = 0; for (auto &&elt : expr) _argminmax_tail<Op, Dim, Axis>(out, elt, i++, val, std::integral_constant<size_t, 0>()); } template <class Op, size_t Dim, size_t Axis, class T, class E, size_t N> typename std::enable_if<Axis == (Dim - N), void>::type _argminmax_head(T &&out, E const &expr, std::integral_constant<size_t, N>) { static_assert(N > 1, "specialization ok"); types::ndarray<typename E::dtype, types::array<long, N - 1>> val{ sutils::getshape(out), Op::limit()}; long i = 0; for (auto &&elt : expr) { _argminmax_tail<Op, Dim, Axis>(out, elt, i++, val, std::integral_constant<size_t, N - 1>()); } } template <class Op, size_t Dim, size_t Axis, class T, class E, size_t N> typename std::enable_if<Axis != (Dim - N), void>::type _argminmax_head(T &&out, E const &expr, std::integral_constant<size_t, N>) { static_assert(N >= 1, "specialization ok"); auto out_iter = out.begin(); for (auto &&elt : expr) { _argminmax_head<Op, Dim, Axis>(*out_iter, elt, std::integral_constant<size_t, N - 1>()); ++out_iter; } } template <class Op, size_t N, class T, class E, size_t... Axis> void _argminmax_pick_axis(long axis, T &&out, E const &expr, utils::index_sequence<Axis...>) { (void)std::initializer_list<bool>{ ((Axis == axis) && (_argminmax_head<Op, N, Axis>( out, expr, std::integral_constant<size_t, N>()), true))...}; } template <class Op, class E> types::ndarray<long, types::array<long, E::value - 1>> 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<long, E::value - 1> shp; auto next = std::copy(shape.begin(), shape.begin() + axis, shp.begin()); std::copy(shape.begin() + axis + 1, shape.end(), next); types::ndarray<long, types::array<long, E::value - 1>> out{shp, builtins::None}; _argminmax_pick_axis<Op, E::value>(axis, out, array, utils::make_index_sequence<E::value>()); return out; } } PYTHONIC_NS_END #endif