← Back
Editing: numpy_expr.hpp
#ifndef PYTHONIC_INCLUDE_TYPES_NUMPY_EXPR_HPP #define PYTHONIC_INCLUDE_TYPES_NUMPY_EXPR_HPP #include "pythonic/include/utils/meta.hpp" #include "pythonic/include/types/nditerator.hpp" PYTHONIC_NS_BEGIN namespace types { template <size_t I, class Args> bool is_trivial_broadcast() { return std::is_same<typename std::tuple_element< 0, typename std::decay<typename std::tuple_element< I, Args>::type>::type::shape_t>::type, std::integral_constant<long, 1>>::value; } template <class... Tys> struct count_non_integral; template <> struct count_non_integral<long> : std::integral_constant<long, 1> { }; template <class T> struct count_non_integral<T> : std::integral_constant<long, 0> { }; template <class Ty0, class Ty1, class... Tys> struct count_non_integral<Ty0, Ty1, Tys...> : std::integral_constant<long, count_non_integral<Ty0>::value + count_non_integral<Ty1, Tys...>::value> { }; template <class P> struct is_perfect_stepping; template <class... Tys> struct is_perfect_stepping<pshape<Tys...>> : std::integral_constant<bool, count_non_integral<Tys...>::value == 1> { }; template <size_t value, class Args, size_t N, size_t... Is> struct all_valid_indices; template <size_t value, class Args, size_t... Is> struct all_valid_indices<value, Args, 0, Is...> { using type = utils::index_sequence<Is...>; }; template <size_t value, class Args, size_t N, size_t... Is> struct all_valid_indices : std::conditional<(value <= std::remove_reference<typename std::tuple_element< N - 1, Args>::type>::type::value), all_valid_indices<value, Args, N - 1, Is..., N - 1>, all_valid_indices<value, Args, N - 1, Is...>>::type { }; template <size_t value, class Args> using valid_indices = typename all_valid_indices<value, Args, std::tuple_size<Args>::value>::type; template <class Expr> struct is_numexpr_arg; template <class Expr, class... Slice> struct numpy_gexpr; template <class Op> struct Dereferencer { template <class Ts, size_t... I> auto operator()(Ts const &iters, utils::index_sequence<I...>) -> decltype(Op{}(*std::get<I>(iters)...)) { return Op{}(*std::get<I>(iters)...); } }; template <class T> struct step { using type = typename T::step_type; }; namespace details { template <size_t I, class Args, size_t... Is> long init_shape_element(Args const &args, utils::index_sequence<Is...>); } template <class Op, class Steps, class... Iters> struct numpy_expr_iterator : std::iterator<std::random_access_iterator_tag, typename std::remove_reference<decltype(std::declval< Op>()(*std::declval<Iters>()...))>::type> { Steps steps_; std::tuple<Iters...> iters_; numpy_expr_iterator(Steps steps, Iters... iters) : steps_(steps), iters_(iters...) { } numpy_expr_iterator(numpy_expr_iterator const &other) : steps_(other.steps_), iters_(other.iters_) { } numpy_expr_iterator &operator=(numpy_expr_iterator const &other) { iters_ = other.iters_; return *this; } template <size_t... I> auto _dereference(utils::index_sequence<I...> s) const -> decltype(Dereferencer<Op>{}(iters_, s)) { return Dereferencer<Op>{}(iters_, s); } auto operator*() const -> decltype( this->_dereference(utils::make_index_sequence<sizeof...(Iters)>{})) { return _dereference(utils::make_index_sequence<sizeof...(Iters)>{}); } template <size_t I> bool _incr_opt(std::integral_constant<bool, true> long_step) { if (is_perfect_stepping<Steps>::value) ++std::get<I>(iters_); else std::get<I>(iters_) += std::get<I>(steps_); return true; } template <size_t I> bool _incr_opt(std::integral_constant<bool, false> long_step) { if (std::tuple_element<I, Steps>::type::value) ++std::get<I>(iters_); return true; } template <size_t... I> void _incr(utils::index_sequence<I...>) { (void)std::initializer_list<bool>{_incr_opt<I>(std::integral_constant< bool, std::is_same<long, typename std::tuple_element< I, Steps>::type>::value>{})...}; } numpy_expr_iterator &operator++() { _incr(utils::make_index_sequence<sizeof...(Iters)>{}); return *this; } numpy_expr_iterator operator+(long i) const { numpy_expr_iterator other(*this); return other += i; } template <size_t... I> void _update(long i, utils::index_sequence<I...>) { (void)std::initializer_list<bool>{ (std::get<I>(iters_) += std::get<I>(steps_) * i, true)...}; } numpy_expr_iterator &operator+=(long i) { _update(i, utils::make_index_sequence<sizeof...(Iters)>{}); return *this; } template <size_t... I> long _difference(numpy_expr_iterator const &other, utils::index_sequence<I...>) const { std::initializer_list<long> distances{(static_cast<long>( std::get<I>(iters_) - std::get<I>(other.iters_)))...}; return *std::max_element(distances.begin(), distances.end()); } long operator-(numpy_expr_iterator const &other) const { return _difference(other, utils::make_index_sequence<sizeof...(Iters)>{}); } bool _neq(numpy_expr_iterator const &other, utils::int_<0u>) const { return false; } template <size_t I> bool _neq(numpy_expr_iterator const &other, utils::int_<I>) const { return (std::get<I - 1>(steps_) && (std::get<I - 1>(iters_) != std::get<I - 1>(other.iters_))) || _neq(other, utils::int_<I - 1>{}); } bool operator!=(numpy_expr_iterator const &other) const { return _neq(other, utils::int_<sizeof...(Iters)>{}); } bool _eq(numpy_expr_iterator const &other, utils::int_<0u>) const { return true; } template <size_t I> bool _eq(numpy_expr_iterator const &other, utils::int_<I>) const { return (!std::get<I - 1>(steps_) || (std::get<I - 1>(iters_) == std::get<I - 1>(other.iters_))) && _eq(other, utils::int_<I - 1>{}); } bool operator==(numpy_expr_iterator const &other) const { return _eq(other, utils::int_<sizeof...(Iters)>{}); } bool _lt(numpy_expr_iterator const &other, utils::int_<0u>) const { return false; } template <size_t I> bool _lt(numpy_expr_iterator const &other, utils::int_<I>) const { if (!std::get<I - 1>(steps_) || (std::get<I - 1>(iters_) == std::get<I - 1>(other.iters_))) return _lt(other, utils::int_<I - 1>{}); else return std::get<I - 1>(steps_) && (std::get<I - 1>(iters_) < std::get<I - 1>(other.iters_)); } bool operator<(numpy_expr_iterator const &other) const { return _lt(other, utils::int_<sizeof...(Iters)>{}); } }; #ifdef USE_XSIMD template <class E, class Op, class Steps, class SIters, class... Iters> struct numpy_expr_simd_iterator : std::iterator<std::random_access_iterator_tag, typename std::remove_reference<decltype(std::declval< Op>()(*std::declval<Iters>()...))>::type> { Steps steps_; std::tuple<Iters...> iters_; SIters siters_; numpy_expr_simd_iterator(array<long, sizeof...(Iters)> steps, SIters const &siters, Iters... iters) : steps_(steps), iters_(iters...), siters_(siters) { } numpy_expr_simd_iterator(numpy_expr_simd_iterator const &other) : steps_(other.steps_), iters_(other.iters_), siters_(other.siters_) { } numpy_expr_simd_iterator &operator=(numpy_expr_simd_iterator const &other) { iters_ = other.iters_; siters_ = other.siters_; return *this; } template <size_t... I> auto _dereference(utils::index_sequence<I...>) const -> decltype(Op{}(*std::get<I>(iters_)...)) { return Op{}(( (std::get<I>(steps_)) ? (*std::get<I>(iters_)) : (xsimd::batch< typename std::decay<decltype(*std::get<I>(siters_))>::type>( *std::get<I>(siters_))))...); } auto operator*() const -> decltype( this->_dereference(utils::make_index_sequence<sizeof...(Iters)>{})) { return _dereference(utils::make_index_sequence<sizeof...(Iters)>{}); } template <size_t I> bool _incr_opt(std::integral_constant<bool, true> long_step) { if (is_perfect_stepping<Steps>::value) ++std::get<I>(iters_); else std::get<I>(iters_) += std::get<I>(steps_); return true; } template <size_t I> bool _incr_opt(std::integral_constant<bool, false> long_step) { if (std::tuple_element<I, Steps>::type::value) ++std::get<I>(iters_); return true; } template <size_t... I> void _incr(utils::index_sequence<I...>) { (void)std::initializer_list<bool>{_incr_opt<I>(std::integral_constant< bool, std::is_same<long, typename std::tuple_element< I, Steps>::type>::value>{})...}; } numpy_expr_simd_iterator &operator++() { _incr(utils::make_index_sequence<sizeof...(Iters)>{}); return *this; } numpy_expr_simd_iterator operator+(long i) const { numpy_expr_simd_iterator other(*this); return other += i; } template <size_t... I> void _update(long i, utils::index_sequence<I...>) { (void)std::initializer_list<bool>{ (std::get<I>(iters_) += std::get<I>(steps_) * i, true)...}; } numpy_expr_simd_iterator &operator+=(long i) { _update(i, utils::make_index_sequence<sizeof...(Iters)>{}); return *this; } template <size_t... I> long _difference(numpy_expr_simd_iterator const &other, utils::index_sequence<I...>) const { std::initializer_list<long> distances{ (std::get<I>(iters_) - std::get<I>(other.iters_))...}; return *std::max_element(distances.begin(), distances.end()); } long operator-(numpy_expr_simd_iterator const &other) const { return _difference(other, utils::make_index_sequence<sizeof...(Iters)>{}); } bool _neq(numpy_expr_simd_iterator const &other, utils::int_<0u>) const { return false; } template <size_t I> bool _neq(numpy_expr_simd_iterator const &other, utils::int_<I>) const { return (std::get<I - 1>(steps_) && (std::get<I - 1>(iters_) != std::get<I - 1>(other.iters_))) || _neq(other, utils::int_<I - 1>{}); } bool operator!=(numpy_expr_simd_iterator const &other) const { return _neq(other, utils::int_<sizeof...(Iters)>{}); } bool _eq(numpy_expr_simd_iterator const &other, utils::int_<0u>) const { return true; } template <size_t I> bool _eq(numpy_expr_simd_iterator const &other, utils::int_<I>) const { return (std::get<I - 1>(steps_) && (std::get<I - 1>(iters_) == std::get<I - 1>(other.iters_))) && _eq(other, utils::int_<I - 1>{}); } bool operator==(numpy_expr_simd_iterator const &other) const { return _eq(other, utils::int_<sizeof...(Iters)>{}); } bool _lt(numpy_expr_simd_iterator const &other, utils::int_<0u>) const { return false; } template <size_t I> bool _lt(numpy_expr_simd_iterator const &other, utils::int_<I>) const { if (std::get<I - 1>(steps_) && (std::get<I - 1>(iters_) == std::get<I - 1>(other.iters_))) return _lt(other, utils::int_<I - 1>{}); else return std::get<I - 1>(steps_) && (std::get<I - 1>(iters_) < std::get<I - 1>(other.iters_)); } bool operator<(numpy_expr_simd_iterator const &other) const { return _lt(other, utils::int_<sizeof...(Iters)>{}); } }; template <class E, class Op, class... Iters> struct numpy_expr_simd_iterator_nobroadcast : std::iterator<std::random_access_iterator_tag, typename std::remove_reference<decltype(std::declval< Op>()(*std::declval<Iters>()...))>::type> { std::tuple<Iters...> iters_; numpy_expr_simd_iterator_nobroadcast(Iters... iters) : iters_(iters...) { } numpy_expr_simd_iterator_nobroadcast( numpy_expr_simd_iterator_nobroadcast const &other) : iters_(other.iters_) { } numpy_expr_simd_iterator_nobroadcast & operator=(numpy_expr_simd_iterator_nobroadcast const &other) { iters_ = other.iters_; return *this; } template <size_t... I> auto _dereference(utils::index_sequence<I...>) const -> decltype(Op{}(*std::get<I>(iters_)...)) { return Op{}((*std::get<I>(iters_))...); } auto operator*() const -> decltype( this->_dereference(utils::make_index_sequence<sizeof...(Iters)>{})) { return _dereference(utils::make_index_sequence<sizeof...(Iters)>{}); } template <size_t... I> void _incr(utils::index_sequence<I...>) { (void)std::initializer_list<bool>{(++std::get<I>(iters_), true)...}; } numpy_expr_simd_iterator_nobroadcast &operator++() { _incr(utils::make_index_sequence<sizeof...(Iters)>{}); return *this; } template <size_t... I> long _difference(numpy_expr_simd_iterator_nobroadcast const &other, utils::index_sequence<I...>) const { std::initializer_list<long> distances{ (std::get<I>(iters_) - std::get<I>(other.iters_))...}; return *std::max_element(distances.begin(), distances.end()); } long operator-(numpy_expr_simd_iterator_nobroadcast const &other) const { return _difference(other, utils::make_index_sequence<sizeof...(Iters)>{}); } numpy_expr_simd_iterator_nobroadcast operator+(long i) const { numpy_expr_simd_iterator_nobroadcast other(*this); return other += i; } template <size_t... I> void _update(long i, utils::index_sequence<I...>) { (void)std::initializer_list<bool>{(std::get<I>(iters_) += i, true)...}; } numpy_expr_simd_iterator_nobroadcast &operator+=(long i) { _update(i, utils::make_index_sequence<sizeof...(Iters)>{}); return *this; } bool _neq(numpy_expr_simd_iterator_nobroadcast const &other, utils::int_<0u>) const { return false; } template <size_t I> bool _neq(numpy_expr_simd_iterator_nobroadcast const &other, utils::int_<I>) const { return (std::get<I - 1>(iters_) != std::get<I - 1>(other.iters_)) || _neq(other, utils::int_<I - 1>{}); } bool operator!=(numpy_expr_simd_iterator_nobroadcast const &other) const { return _neq(other, utils::int_<sizeof...(Iters)>{}); } bool _eq(numpy_expr_simd_iterator_nobroadcast const &other, utils::int_<0u>) const { return true; } template <size_t I> bool _eq(numpy_expr_simd_iterator_nobroadcast const &other, utils::int_<I>) const { return (std::get<I - 1>(iters_) == std::get<I - 1>(other.iters_)) && _eq(other, utils::int_<I - 1>{}); } bool operator==(numpy_expr_simd_iterator_nobroadcast const &other) const { return _eq(other, utils::int_<sizeof...(Iters)>{}); } bool _lt(numpy_expr_simd_iterator_nobroadcast const &other, utils::int_<0u>) const { return false; } template <size_t I> bool _lt(numpy_expr_simd_iterator_nobroadcast const &other, utils::int_<I>) const { if (std::get<I - 1>(iters_) == std::get<I - 1>(other.iters_)) return _lt(other, utils::int_<I - 1>{}); else return std::get<I - 1>(iters_) < std::get<I - 1>(other.iters_); } bool operator<(numpy_expr_simd_iterator_nobroadcast const &other) const { return _lt(other, utils::int_<sizeof...(Iters)>{}); } }; #endif template <long N0, long N1> std::integral_constant<long, N0 == N1> make_step(std::integral_constant<long, N0>, std::integral_constant<long, N1>) { return {}; } template <class T0, class T1> long make_step(T0 n0, T1 n1) { return (long)n0 == (long)n1; } template <class S> constexpr size_t count_none(size_t I) { return std::is_same<S, none_type>::value; } template <class S, class Sp, class... Ss> constexpr size_t count_none(size_t I) { return std::is_same<S, none_type>::value + (I == 0 ? 0 : count_none<Sp, Ss...>(I - 1)); } template <class BT, class T> using step_type_t = decltype(make_step(std::get<0>(std::declval<BT>()), std::get<0>(std::declval<T>()))); constexpr size_t clamp(size_t i, size_t j) { return i > j ? j : i; } template <size_t... J, class Arg, class Shp, class... S> auto make_subslice(utils::index_sequence<J...>, Arg const &arg, Shp const &shp, std::tuple<S...> const &ss) -> decltype(arg(std::get<J>(ss)...)) { // we need to adapt_slice to take broadcasting into account return arg(adapt_slice(std::get<J>(ss), shp.template shape<J - count_none<S...>(J)>(), arg.template shape<clamp(J - count_none<S...>(J), Arg::value - 1)>())...); } /* Expression template for numpy expressions - binary operators */ template <class Op, class... Args> struct numpy_expr { using first_arg = typename utils::front<Args...>::type; static const bool is_vectorizable = utils::all_of< std::remove_reference<Args>::type::is_vectorizable...>::value && utils::all_of< std::is_same<typename std::remove_cv<typename std::remove_reference< first_arg>::type>::type::dtype, typename std::remove_cv<typename std::remove_reference< Args>::type>::type::dtype>::value...>::value && types::is_vector_op< Op, typename std::remove_reference<Args>::type::dtype...>::value; static const bool is_strided = utils::any_of<std::remove_reference<Args>::type::is_strided...>::value; static constexpr size_t value = utils::max_element<std::remove_reference<Args>::type::value...>::value; using value_type = decltype(Op()(std::declval< typename std::remove_reference<Args>::type::value_type>()...)); using dtype = decltype(Op()( std::declval<typename std::remove_reference<Args>::type::dtype>()...)); #ifdef CYTHON_ABI std::tuple<typename std::remove_reference<Args>::type...> args; #else std::tuple<Args...> args; #endif using shape_t = sutils::merged_shapes_t< value, typename std::remove_reference<Args>::type::shape_t...>; using steps_t = pshape<step_type_t< shape_t, typename std::remove_reference<Args>::type::shape_t>...>; static_assert(value == std::tuple_size<shape_t>::value, "consistent shape and size"); using const_iterator = numpy_expr_iterator< Op, steps_t, typename std::remove_reference<Args>::type::const_iterator...>; using iterator = numpy_expr_iterator< Op, steps_t, typename std::remove_reference<Args>::type::iterator...>; using const_fast_iterator = const_nditerator<numpy_expr>; numpy_expr() = default; numpy_expr(numpy_expr const &) = default; numpy_expr(numpy_expr &&) = default; #ifdef CYTHON_ABI template <class... Argp> numpy_expr(numpy_expr<Op, Argp...> const &other) : args(other.args) { } #endif numpy_expr(Args const &... args); template <size_t... I> const_iterator _begin(utils::index_sequence<I...>) const; const_iterator begin() const; template <size_t... I> const_iterator _end(utils::index_sequence<I...>) const; const_iterator end() const; const_fast_iterator begin(types::fast) const; const_fast_iterator end(types::fast) const; template <size_t... I> iterator _begin(utils::index_sequence<I...>); iterator begin(); template <size_t... I> iterator _end(utils::index_sequence<I...>); iterator end(); template <size_t... I> auto _fast(long i, utils::index_sequence<I...>) const -> decltype(Op()(std::get<I>(args).fast(i)...)) { return Op()(std::get<I>(args).fast(i)...); } auto fast(long i) const -> decltype(this->_fast(i, utils::make_index_sequence<sizeof...(Args)>{})); template <class... Indices, size_t... I> auto _load(utils::index_sequence<I...>, Indices... indices) const -> decltype(Op()(std::get<I>(args).load(indices...)...)) { return Op()(std::get<I>(args).load(indices...)...); } template <class... Indices> auto load(Indices... indices) const -> decltype(this->_load(utils::make_index_sequence<sizeof...(Args)>{}, indices...)) { return this->_load(utils::make_index_sequence<sizeof...(Args)>{}, indices...); } template <size_t... I> auto _map_fast(array<long, sizeof...(I)> const &indices, utils::index_sequence<I...>) const -> decltype(Op()(std::get<I>(args).fast(std::get<I>(indices))...)) { return Op()(std::get<I>(args).fast(std::get<I>(indices))...); } template <class... Indices> auto map_fast(Indices... indices) const -> decltype( this->_map_fast(array<long, sizeof...(Indices)>{{indices...}}, utils::make_index_sequence<sizeof...(Args)>{})); public: template <size_t I> auto shape() const -> decltype(details::init_shape_element<I>( args, valid_indices<value, std::tuple<Args...>>{})) { return details::init_shape_element<I>( args, valid_indices<value, std::tuple<Args...>>{}); } template <size_t... I> bool _no_broadcast(utils::index_sequence<I...>) const; bool no_broadcast() const; template <size_t... I> bool _no_broadcast_vectorize(utils::index_sequence<I...>) const; bool no_broadcast_vectorize() const; template <size_t... I> bool _no_broadcast_ex(utils::index_sequence<I...>) const; bool no_broadcast_ex() const; #ifdef USE_XSIMD using simd_iterator = numpy_expr_simd_iterator< numpy_expr, Op, pshape<step_type_t< shape_t, typename std::remove_reference<Args>::type::shape_t>...>, std::tuple< typename std::remove_reference<Args>::type::const_iterator...>, typename std::remove_reference<Args>::type::simd_iterator...>; using simd_iterator_nobroadcast = numpy_expr_simd_iterator_nobroadcast< numpy_expr, Op, typename std::remove_reference< Args>::type::simd_iterator_nobroadcast...>; template <size_t... I> simd_iterator _vbegin(types::vectorize, utils::index_sequence<I...>) const; simd_iterator vbegin(types::vectorize) const; template <size_t... I> simd_iterator _vend(types::vectorize, utils::index_sequence<I...>) const; simd_iterator vend(types::vectorize) const; template <size_t... I> simd_iterator_nobroadcast _vbegin(types::vectorize_nobroadcast, utils::index_sequence<I...>) const; simd_iterator_nobroadcast vbegin(types::vectorize_nobroadcast) const; template <size_t... I> simd_iterator_nobroadcast _vend(types::vectorize_nobroadcast, utils::index_sequence<I...>) const; simd_iterator_nobroadcast vend(types::vectorize_nobroadcast) const; #endif template <size_t... I, class... S> auto _get(utils::index_sequence<I...> is, S const &... s) const -> decltype( Op{}(make_subslice(utils::make_index_sequence<sizeof...(S)>{}, std::get<I>(args), *this, std::make_tuple(s...))...)) { return Op{}(make_subslice(utils::make_index_sequence<sizeof...(S)>{}, std::get<I>(args), *this, std::make_tuple(s...))...); } template <class... S> auto operator()(S const &... s) const -> decltype(this->_get(utils::make_index_sequence<sizeof...(Args)>{}, s...)); template <class F> typename std::enable_if< is_numexpr_arg<F>::value && std::is_same<bool, typename F::dtype>::value && !is_pod_array<F>::value, numpy_vexpr<numpy_expr, ndarray<long, pshape<long>>>>::type fast(F const &filter) const; template <class F> typename std::enable_if< is_numexpr_arg<F>::value && std::is_same<bool, typename F::dtype>::value && !is_pod_array<F>::value, numpy_vexpr<numpy_expr, ndarray<long, pshape<long>>>>::type operator[](F const &filter) const; template <class F> // indexing through an array of indices -- a view typename std::enable_if<is_numexpr_arg<F>::value && !is_array_index<F>::value && !std::is_same<bool, typename F::dtype>::value && !is_pod_array<F>::value, numpy_vexpr<numpy_expr, F>>::type operator[](F const &filter) const; template <class F> // indexing through an array of indices -- a view typename std::enable_if<is_numexpr_arg<F>::value && !is_array_index<F>::value && !std::is_same<bool, typename F::dtype>::value && !is_pod_array<F>::value, numpy_vexpr<numpy_expr, F>>::type fast(F const &filter) const; // FIXME: this does not take into account bounds and broadcasting auto operator[](long i) const -> decltype(this->fast(i)); template <size_t... I, class S> auto _index(S s, utils::index_sequence<I...>) const -> decltype(Op{}(std::get<I>(args)[s]...)) { return Op{}(std::get<I>(args)[s]...); } template <class S> auto operator[](S s) const -> decltype((*this) ._index((s.lower, s), utils::make_index_sequence<sizeof...(Args)>{})) { return _index(s, utils::make_index_sequence<sizeof...(Args)>{}); } dtype operator[](array<long, value> const &indices) const { return _index(indices, utils::make_index_sequence<sizeof...(Args)>{}); } explicit operator bool() const; long flat_size() const; long size() const; }; } template <class Op, class... Args> struct assignable<types::numpy_expr<Op, Args...>> { using type = types::ndarray< typename pythonic::types::numpy_expr<Op, Args...>::dtype, typename pythonic::types::numpy_expr<Op, Args...>::shape_t>; }; template <class Op, class... Arg> struct lazy<types::numpy_expr<Op, Arg...>> { using type = types::numpy_expr<Op, typename lazy<Arg>::type...>; }; PYTHONIC_NS_END /* type inference stuff {*/ #include "pythonic/include/types/combined.hpp" template <class Op, class K, class... Args> struct __combined<pythonic::types::numpy_expr<Op, Args...>, indexable<K>> { using type = pythonic::types::numpy_expr<Op, Args...>; }; template <class Op, class K, class... Args> struct __combined<indexable<K>, pythonic::types::numpy_expr<Op, Args...>> { using type = pythonic::types::numpy_expr<Op, Args...>; }; template <class Op, class K, class V, class... Args> struct __combined<pythonic::types::numpy_expr<Op, Args...>, indexable_container<K, V>> { using type = pythonic::types::numpy_expr<Op, Args...>; }; template <class Op, class K, class V, class... Args> struct __combined<indexable_container<K, V>, pythonic::types::numpy_expr<Op, Args...>> { using type = pythonic::types::numpy_expr<Op, Args...>; }; template <class Op, class K, class... Args> struct __combined<container<K>, pythonic::types::numpy_expr<Op, Args...>> { using type = pythonic::types::numpy_expr<Op, Args...>; }; template <class Op, class K, class... Args> struct __combined<pythonic::types::numpy_expr<Op, Args...>, container<K>> { using type = pythonic::types::numpy_expr<Op, Args...>; }; template <class Op, class Op2, class... Args, class... Args2> struct __combined<pythonic::types::numpy_expr<Op, Args...>, pythonic::types::numpy_expr<Op2, Args2...>> { using type = pythonic::types::ndarray< typename pythonic::types::numpy_expr<Op, Args...>::dtype, pythonic::types::array<long, pythonic::types::numpy_expr<Op, Args...>::value>>; }; template <class E, class Op, class... Args> struct __combined<pythonic::types::numpy_iexpr<E>, pythonic::types::numpy_expr<Op, Args...>> { using type = pythonic::types::numpy_iexpr<E>; }; template <class E, class Op, class... Args> struct __combined<pythonic::types::numpy_expr<Op, Args...>, pythonic::types::numpy_iexpr<E>> { using type = pythonic::types::numpy_iexpr<E>; }; template <class T, class pS, class Op, class... Args> struct __combined<pythonic::types::numpy_expr<Op, Args...>, pythonic::types::ndarray<T, pS>> { using type = pythonic::types::ndarray<T, pS>; }; template <class T, class Op, class... Args> struct __combined<pythonic::types::numpy_expr<Op, Args...>, pythonic::types::numpy_texpr<T>> { using type = pythonic::types::ndarray< typename pythonic::types::numpy_expr<Op, Args...>::dtype, pythonic::types::array<long, pythonic::types::numpy_expr<Op, Args...>::value>>; }; template <class T, class Op, class... Args> struct __combined<pythonic::types::numpy_texpr<T>, pythonic::types::numpy_expr<Op, Args...>> { using type = pythonic::types::ndarray< typename pythonic::types::numpy_expr<Op, Args...>::dtype, pythonic::types::array<long, pythonic::types::numpy_expr<Op, Args...>::value>>; }; /*}*/ #endif
Save File
Cancel