/*************************************************************************** * 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. * ****************************************************************************/ #ifndef XTENSOR_GENERATOR_HPP #define XTENSOR_GENERATOR_HPP #include #include #include #include #include #include #include #include "xaccessible.hpp" #include "xexpression.hpp" #include "xiterable.hpp" #include "xstrided_view.hpp" #include "xstrides.hpp" #include "xutils.hpp" namespace xt { /************************ * xgenerator extension * ************************/ namespace extension { template struct xgenerator_base_impl; template struct xgenerator_base_impl { using type = xtensor_empty_base; }; template struct xgenerator_base : xgenerator_base_impl, F, R, S> { }; template using xgenerator_base_t = typename xgenerator_base::type; } /************** * xgenerator * **************/ template class xgenerator; template struct xiterable_inner_types> { using inner_shape_type = S; using const_stepper = xindexed_stepper, true>; using stepper = const_stepper; }; template struct xcontainer_inner_types> { using reference = R; using const_reference = R; using size_type = std::size_t; }; /************************************* * overlapping_memory_checker_traits * *************************************/ template struct overlapping_memory_checker_traits< E, std::enable_if_t::value && is_specialization_of::value>> { static bool check_overlap(const E&, const memory_range&) { return false; } }; /** * @class xgenerator * @brief Multidimensional function operating on indices. * * The xgenerator class implements a multidimensional function, * generating a value from the supplied indices. * * @tparam F the function type * @tparam R the return type of the function * @tparam S the shape type of the generator */ template class xgenerator : public xsharable_expression>, public xconst_iterable>, public xconst_accessible>, public extension::xgenerator_base_t { public: using self_type = xgenerator; using functor_type = typename std::remove_reference::type; using accessible_base = xconst_accessible; using extension_base = extension::xgenerator_base_t; using expression_tag = typename extension_base::expression_tag; using inner_types = xcontainer_inner_types; using value_type = R; using reference = typename inner_types::reference; using const_reference = typename inner_types::const_reference; using pointer = value_type*; using const_pointer = const value_type*; using size_type = typename inner_types::size_type; using difference_type = std::ptrdiff_t; using iterable_base = xconst_iterable; using inner_shape_type = typename iterable_base::inner_shape_type; using shape_type = inner_shape_type; using stepper = typename iterable_base::stepper; using const_stepper = typename iterable_base::const_stepper; using bool_load_type = xt::bool_load_type; static constexpr layout_type static_layout = layout_type::dynamic; static constexpr bool contiguous_layout = false; template xgenerator(Func&& f, const S& shape) noexcept; const inner_shape_type& shape() const noexcept; layout_type layout() const noexcept; bool is_contiguous() const noexcept; using accessible_base::shape; template const_reference operator()(Args... args) const; template const_reference unchecked(Args... args) const; template const_reference element(It first, It last) const; template bool broadcast_shape(O& shape, bool reuse_cache = false) const; template bool has_linear_assign(const O& /*strides*/) const noexcept; template const_stepper stepper_begin(const O& shape) const noexcept; template const_stepper stepper_end(const O& shape, layout_type) const noexcept; template ::value>> void assign_to(xexpression& e) const noexcept; const functor_type& functor() const noexcept; template using rebind_t = xgenerator; template rebind_t build_generator(OF&& func) const; template > auto reshape(O&& shape) const&; template > auto reshape(O&& shape) &&; template auto reshape(std::initializer_list shape) const&; template auto reshape(std::initializer_list shape) &&; private: template decltype(auto) compute_shape(O&& shape, std::false_type /*signed*/) const; template auto compute_shape(O&& shape, std::true_type /*signed*/) const; template auto compute_shape(std::initializer_list shape) const; template void adapt_index() const; template void adapt_index(I& arg, Args&... args) const; functor_type m_f; inner_shape_type m_shape; }; /***************************** * xgenerator implementation * *****************************/ /** * @name Constructor */ //@{ /** * Constructs an xgenerator applying the specified function over the * given shape. * @param f the function to apply * @param shape the shape of the xgenerator */ template template inline xgenerator::xgenerator(Func&& f, const S& shape) noexcept : m_f(std::forward(f)) , m_shape(shape) { } //@} /** * @name Size and shape */ //@{ /** * Returns the shape of the xgenerator. */ template inline auto xgenerator::shape() const noexcept -> const inner_shape_type& { return m_shape; } template inline layout_type xgenerator::layout() const noexcept { return static_layout; } template inline bool xgenerator::is_contiguous() const noexcept { return false; } //@} /** * @name Data */ /** * Returns the evaluated element at the specified position in the function. * @param args a list of indices specifying the position in the function. Indices * must be unsigned integers, the number of indices should be equal or greater than * the number of dimensions of the function. */ template template inline auto xgenerator::operator()(Args... args) const -> const_reference { XTENSOR_TRY(check_index(shape(), args...)); adapt_index<0>(args...); return m_f(args...); } /** * Returns a constant reference to the element at the specified position in the expression. * @param args a list of indices specifying the position in the expression. Indices * must be unsigned integers, the number of indices must be equal to the number of * dimensions of the expression, else the behavior is undefined. * * @warning This method is meant for performance, for expressions with a dynamic * number of dimensions (i.e. not known at compile time). Since it may have * undefined behavior (see parameters), operator() should be preferred whenever * it is possible. * @warning This method is NOT compatible with broadcasting, meaning the following * code has undefined behavior: * @code{.cpp} * xt::xarray a = {{0, 1}, {2, 3}}; * xt::xarray b = {0, 1}; * auto fd = a + b; * double res = fd.uncheked(0, 1); * @endcode */ template template inline auto xgenerator::unchecked(Args... args) const -> const_reference { return m_f(args...); } /** * Returns a constant reference to the element at the specified position in the function. * @param first iterator starting the sequence of indices * @param last iterator ending the sequence of indices * The number of indices in the sequence should be equal to or greater * than the number of dimensions of the container. */ template template inline auto xgenerator::element(It first, It last) const -> const_reference { using bounded_iterator = xbounded_iterator; XTENSOR_TRY(check_element_index(shape(), first, last)); return m_f.element(bounded_iterator(first, shape().cbegin()), bounded_iterator(last, shape().cend())); } //@} /** * @name Broadcasting */ //@{ /** * Broadcast the shape of the function to the specified parameter. * @param shape the result shape * @param reuse_cache parameter for internal optimization * @return a boolean indicating whether the broadcasting is trivial */ template template inline bool xgenerator::broadcast_shape(O& shape, bool) const { return xt::broadcast_shape(m_shape, shape); } /** * Checks whether the xgenerator can be linearly assigned to an expression * with the specified strides. * @return a boolean indicating whether a linear assign is possible */ template template inline bool xgenerator::has_linear_assign(const O& /*strides*/) const noexcept { return false; } //@} template template inline auto xgenerator::stepper_begin(const O& shape) const noexcept -> const_stepper { size_type offset = shape.size() - this->dimension(); return const_stepper(this, offset); } template template inline auto xgenerator::stepper_end(const O& shape, layout_type) const noexcept -> const_stepper { size_type offset = shape.size() - this->dimension(); return const_stepper(this, offset, true); } template template inline void xgenerator::assign_to(xexpression& e) const noexcept { e.derived_cast().resize(m_shape); m_f.assign_to(e); } template inline auto xgenerator::functor() const noexcept -> const functor_type& { return m_f; } template template inline auto xgenerator::build_generator(OF&& func) const -> rebind_t { return rebind_t(std::move(func), shape_type(m_shape)); } /** * Reshapes the generator and keeps old elements. The `shape` argument can have one of its value * equal to `-1`, in this case the value is inferred from the number of elements in the generator * and the remaining values in the `shape`. * @code{.cpp} * auto a = xt::arange(50).reshape({-1, 10}); * //a.shape() is {5, 10} * @endcode * @param shape the new shape (has to have same number of elements as the original generator) */ template template inline auto xgenerator::reshape(O&& shape) const& { return reshape_view(*this, compute_shape(shape, xtl::is_signed::value_type>())); } template template inline auto xgenerator::reshape(O&& shape) && { return reshape_view( std::move(*this), compute_shape(shape, xtl::is_signed::value_type>()) ); } template template inline auto xgenerator::reshape(std::initializer_list shape) const& { return reshape_view(*this, compute_shape(shape)); } template template inline auto xgenerator::reshape(std::initializer_list shape) && { return reshape_view(std::move(*this), compute_shape(shape)); } template template inline decltype(auto) xgenerator::compute_shape(O&& shape, std::false_type) const { return xtl::forward_sequence, O>(shape); } template template inline auto xgenerator::compute_shape(O&& shape, std::true_type) const { using vtype = typename shape_type::value_type; xt::dynamic_shape sh(shape.size()); using int_type = typename std::decay_t::value_type; int_type accumulator(1); std::size_t neg_idx = 0; std::size_t i = 0; for (std::size_t j = 0; j != shape.size(); ++j, ++i) { auto dim = shape[j]; if (dim < 0) { XTENSOR_ASSERT(dim == -1 && !neg_idx); neg_idx = i; } else { sh[j] = static_cast(dim); } accumulator *= dim; } if (accumulator < 0) { sh[neg_idx] = this->size() / static_cast(std::make_unsigned_t(std::abs(accumulator))); } return sh; } template template inline auto xgenerator::compute_shape(std::initializer_list shape) const { using sh_type = xt::dynamic_shape; sh_type sh = xtl::make_sequence(shape.size()); std::copy(shape.begin(), shape.end(), sh.begin()); return compute_shape(std::move(sh), xtl::is_signed()); } template template inline void xgenerator::adapt_index() const { } template template inline void xgenerator::adapt_index(I& arg, Args&... args) const { using tmp_value_type = typename decltype(m_shape)::value_type; if (sizeof...(Args) + 1 > m_shape.size()) { adapt_index(args...); } else { if (static_cast(arg) >= m_shape[dim] && m_shape[dim] == 1) { arg = 0; } adapt_index(args...); } } namespace detail { template inline auto make_xgenerator(Functor&& f, const I (&shape)[L]) noexcept { using shape_type = std::array; using type = xgenerator; return type(std::forward(f), xtl::forward_sequence(shape)); } template inline auto make_xgenerator(Functor&& f, S&& shape) noexcept { using type = xgenerator>; return type(std::forward(f), std::forward(shape)); } } } #endif