#ifndef PYTHONIC_NUMPY_REDUCE_HPP #define PYTHONIC_NUMPY_REDUCE_HPP #include "pythonic/include/numpy/reduce.hpp" #include "pythonic/types/ndarray.hpp" #include "pythonic/builtins/None.hpp" #include "pythonic/builtins/ValueError.hpp" #include "pythonic/utils/neutral.hpp" #ifdef USE_XSIMD #include #endif #include PYTHONIC_NS_BEGIN namespace numpy { template struct _reduce { template F operator()(E &&e, F acc) { for (auto &&value : std::forward(e)) acc = _reduce{}( std::forward(value), acc); return acc; } }; template struct _reduce { template F operator()(E &&e, F acc) { for (auto value : std::forward(e)) { Op{}(acc, value); } return acc; } }; template struct _reduce { template F operator()(E &&e, F acc, Indices... indices) { for (long i = 0, n = e.template shape::type::value - N>(); i < n; ++i) { acc = _reduce{}( e, acc, indices..., i); } return acc; } }; template struct _reduce { template F operator()(E &&e, F acc, Indices... indices) { for (long i = 0, n = e.template shape::type::value - 1>(); i < n; ++i) { Op{}(acc, e.load(indices..., i)); } return acc; } }; #ifdef USE_XSIMD template F vreduce(E e, F acc) { using T = typename E::dtype; using vT = xsimd::simd_type; static const size_t vN = vT::size; const long n = e.size(); auto viter = vectorizer::vbegin(e), vend = vectorizer::vend(e); const long bound = std::distance(viter, vend); if (bound > 0) { auto vacc = *viter; for (++viter; viter != vend; ++viter) Op{}(vacc, *viter); alignas(sizeof(vT)) T stored[vN]; vacc.store_aligned(&stored[0]); for (size_t j = 0; j < vN; ++j) Op{}(acc, stored[j]); } auto iter = e.begin() + bound * vN; for (long i = bound * vN; i < n; ++i, ++iter) { Op{}(acc, *iter); } return acc; } template struct _reduce { template F operator()(E &&e, F acc) { return vreduce(std::forward(e), acc); } }; template struct _reduce { template F operator()(E &&e, F acc) { return vreduce(std::forward(e), acc); } }; #else template struct _reduce : _reduce { }; template struct _reduce : _reduce { }; #endif template struct reduce_helper; template struct reduce_helper { template reduce_result_type operator()(E const &expr, T p) const { if (utils::no_broadcast_ex(expr)) return _reduce{}(expr, p); else return _reduce{}(expr, p); } }; template struct reduce_helper { template reduce_result_type operator()(E const &expr, T p) const { if (utils::no_broadcast_vectorize(expr)) return _reduce{}(expr, p); else return _reduce{}(expr, p); } }; template typename std::enable_if< std::is_scalar::value || types::is_complex::value, E>::type reduce(E const &expr, types::none_type) { return expr; } template typename std::enable_if< std::is_scalar::value || types::is_complex::value, E>::type reduce(E const &array, long axis) { if (axis != 0) throw types::ValueError("axis out of bounds"); return reduce(array); } template typename std::enable_if::value, reduce_result_type>::type reduce(E const &expr, types::none_type axis, dtype) { using rrt = reduce_result_type; bool constexpr is_vectorizable = E::is_vectorizable && !std::is_same::value && std::is_same::value; rrt p = utils::neutral::value; return reduce_helper{}(expr, p); } template typename std::enable_if>::type reduce(E const &array, long axis, dtype d, types::none_type) { if (axis != 0) throw types::ValueError("axis out of bounds"); return reduce(array, types::none_type{}, d); } template typename std::enable_if>::type reduce(E const &array, long axis, types::none_type, Out &&out) { if (axis != 0) throw types::ValueError("axis out of bounds"); return std::forward(out) = reduce(array); } template struct _reduce_axisb { template void operator()(E &&e, F &&f, long axis, EIndices &&e_indices, FIndices &&f_indices) { for (long i = 0, n = e.template shape::type::value - N>(); i < n; ++i) { _reduce_axisb{}( e, f, axis, std::tuple_cat(e_indices, std::make_tuple(i)), std::tuple_cat(f_indices, std::make_tuple(i))); } } }; template struct _reduce_axisb { template void helper(E &&e, F &&f, EIndices &&e_indices, FIndices &&f_indices, utils::index_sequence, utils::index_sequence) { f.template update(e.load(std::get(e_indices)...), (long)std::get(f_indices)...); } template void operator()(E &&e, F &&f, long axis, EIndices &&e_indices, FIndices &&f_indices) { helper( std::forward(e), std::forward(f), e_indices, f_indices, utils::make_index_sequence< std::tuple_size::type>::value>(), utils::make_index_sequence< std::tuple_size::type>::value>()); } }; template struct _reduce_axis { template void operator()(E &&e, F &&f, long axis, EIndices &&e_indices, FIndices &&f_indices) { if (axis == std::decay::type::value - N) { for (long i = 0, n = e.template shape::type::value - N>(); i < n; ++i) { _reduce_axisb{}( e, f, axis, std::tuple_cat(e_indices, std::make_tuple(i)), std::forward(f_indices)); } } else { for (long i = 0, n = e.template shape::type::value - N>(); i < n; ++i) { _reduce_axis{}( e, f, axis, std::tuple_cat(e_indices, std::make_tuple(i)), std::tuple_cat(f_indices, std::make_tuple(i))); } } } }; template struct _reduce_axis { template void operator()(E &&e, F &&f, long axis, EIndices &&e_indices, FIndices &&f_indices) { } }; template typename std::enable_if>::type reduce(E const &array, long axis, dtype, types::none_type) { if (axis < 0) axis += E::value; if (axis < 0 || size_t(axis) >= E::value) throw types::ValueError("axis out of bounds"); types::array shp; auto tmp = sutils::getshape(array); auto next = std::copy(tmp.begin(), tmp.begin() + axis, shp.begin()); std::copy(tmp.begin() + axis + 1, tmp.end(), next); reduced_type out{shp, builtins::None}; std::fill(out.begin(), out.end(), utils::neutral::value); return reduce(array, axis, types::none_type{}, out); } template typename std::enable_if>::type reduce(E const &array, long axis, types::none_type, Out &&out) { if (axis < 0) axis += E::value; if (axis < 0 || size_t(axis) >= E::value) throw types::ValueError("axis out of bounds"); if (utils::no_broadcast(array)) { std::fill(out.begin(), out.end(), utils::neutral::value); _reduce_axis{}(array, std::forward(out), axis, std::make_tuple(), std::make_tuple()); return std::forward(out); } else { if (axis == 0) { std::fill(out.begin(), out.end(), utils::neutral::value); return _reduce{}( array, std::forward(out)); } else { std::transform(array.begin(), array.end(), out.begin(), [axis](typename E::const_iterator::value_type other) { return reduce(other, axis - 1); }); return std::forward(out); } } } } PYTHONIC_NS_END #endif