/*************************************************************************** * 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_VIEW_HPP #define XTENSOR_VIEW_HPP #include #include #include #include #include #include #include #include #include #include #include "xaccessible.hpp" #include "xarray.hpp" #include "xbroadcast.hpp" #include "xcontainer.hpp" #include "xiterable.hpp" #include "xsemantic.hpp" #include "xslice.hpp" #include "xtensor.hpp" #include "xtensor_config.hpp" #include "xtensor_forward.hpp" #include "xview_utils.hpp" namespace xt { /******************* * xview extension * *******************/ namespace extension { template struct xview_base_impl; template struct xview_base_impl { using type = xtensor_empty_base; }; template struct xview_base : xview_base_impl, CT, S...> { }; template using xview_base_t = typename xview_base::type; } /********************* * xview declaration * *********************/ template class xview_stepper; template struct xview_shape_type; namespace detail { template struct is_xrange : std::false_type { }; template struct is_xrange> : std::true_type { }; template struct is_xall_slice : std::false_type { }; template struct is_xall_slice> : std::true_type { }; template struct is_contiguous_view_impl { static constexpr bool value = false; }; template struct static_dimension { static constexpr std::ptrdiff_t value = -1; }; template struct static_dimension> { static constexpr std::ptrdiff_t value = static_cast(N); }; template struct static_dimension> { static constexpr std::ptrdiff_t value = static_cast(N); }; template struct static_dimension> { static constexpr std::ptrdiff_t value = sizeof...(I); }; // if we have the same number of integers as we have static dimensions // this can be interpreted like a xscalar template struct is_xscalar_impl> { static constexpr bool value = static_cast(integral_count() ) == static_dimension::shape_type>::value ? true : false; }; template struct is_strided_slice_impl : std::true_type { }; template struct is_strided_slice_impl> : std::false_type { }; template struct is_strided_slice_impl> : std::false_type { }; // If we have no discontiguous slices, we can calculate strides for this view. template struct is_strided_view : std::integral_constant< bool, xtl::conjunction, is_strided_slice_impl>...>::value> { }; // if row major the view can only be (statically) computed as contiguous if: // any number of integers is followed by either one or no range which // are followed by explicit (or implicit) all's // // e.g. // (i, j, all(), all()) == contiguous // (i, range(0, 2), all()) == contiguous // (i) == contiguous (implicit all slices) // (i, all(), j) == *not* contiguous // (i, range(0, 2), range(0, 2)) == *not* contiguous etc. template struct is_contiguous_view_impl { using slice = xtl::mpl::front_t; static constexpr bool is_range_slice = is_xrange::value; static constexpr bool is_int_slice = xtl::is_integral::value; static constexpr bool is_all_slice = is_xall_slice::value; static constexpr bool have_all_seen = all_seen || is_all_slice; static constexpr bool have_range_seen = is_range_slice; static constexpr bool is_valid = valid && (have_all_seen ? is_all_slice : (!range_seen && (is_int_slice || is_range_slice))); static constexpr bool value = is_contiguous_view_impl < layout_type::row_major, is_valid, have_all_seen, range_seen || is_range_slice, xtl::mpl::pop_front_t < V >> ::value; }; template struct is_contiguous_view_impl> { static constexpr bool value = valid; }; // For column major the *same* but reverse is true -- with the additional // constraint that we have to know the dimension at compile time otherwise // we cannot make the decision as there might be implicit all's following. template struct is_contiguous_view_impl { using slice = xtl::mpl::front_t; static constexpr bool is_range_slice = is_xrange::value; static constexpr bool is_int_slice = xtl::is_integral::value; static constexpr bool is_all_slice = is_xall_slice::value; static constexpr bool have_int_seen = int_seen || is_int_slice; static constexpr bool is_valid = valid && (have_int_seen ? is_int_slice : (!range_seen && (is_all_slice || is_range_slice))); static constexpr bool value = is_contiguous_view_impl < layout_type::column_major, is_valid, have_int_seen, is_range_slice || range_seen, xtl::mpl::pop_front_t < V >> ::value; }; template struct is_contiguous_view_impl> { static constexpr bool value = valid; }; // TODO relax has_data_interface constraint here! template struct is_contiguous_view : std::integral_constant< bool, has_data_interface::value && !( E::static_layout == layout_type::column_major && static_cast(static_dimension::value) != sizeof...(S) ) && is_contiguous_view_impl>::value> { }; template struct unwrap_offset_container { using type = void; }; template struct unwrap_offset_container { using type = sequence_view::value>; }; template struct unwrap_offset_container, offset> { using type = sequence_view; }; template struct unwrap_offset_container { using type = sequence_view::value - offset>; }; template struct unwrap_offset_container, offset> { using type = sequence_view; }; template struct get_contigous_shape_type { // if we have no `range` in the slices we can re-use the shape with an offset using type = std::conditional_t< xtl::disjunction...>::value, typename xview_shape_type::type, // In the false branch we know that we have only integers at the front OR end, and NO range typename unwrap_offset_container()>::type>; }; template struct is_sequence_view : std::integral_constant { }; template struct is_sequence_view> : std::integral_constant { }; } template struct xcontainer_inner_types> { using xexpression_type = std::decay_t; using reference = inner_reference_t; using const_reference = typename xexpression_type::const_reference; using size_type = typename xexpression_type::size_type; using temporary_type = view_temporary_type_t; static constexpr layout_type layout = detail::is_contiguous_view::value ? xexpression_type::static_layout : layout_type::dynamic; static constexpr bool is_const = std::is_const>::value; using extract_storage_type = xtl::mpl::eval_if_t< has_data_interface, detail::expr_storage_type, make_invalid_type<>>; using storage_type = std::conditional_t; }; template struct xiterable_inner_types> { using xexpression_type = std::decay_t; static constexpr bool is_strided_view = detail::is_strided_view::value; static constexpr bool is_contiguous_view = detail::is_contiguous_view::value; using inner_shape_type = std::conditional_t< is_contiguous_view, typename detail::get_contigous_shape_type::type, typename xview_shape_type::type>; using stepper = std::conditional_t< is_strided_view, xstepper>, xview_stepper>::value, CT, S...>>; using const_stepper = std::conditional_t< is_strided_view, xstepper>, xview_stepper, S...>>; }; /** * @class xview * @brief Multidimensional view with tensor semantic. * * The xview class implements a multidimensional view with tensor * semantic. It is used to adapt the shape of an xexpression without * changing it. xview is not meant to be used directly, but * only with the \ref view helper functions. * * @tparam CT the closure type of the \ref xexpression to adapt * @tparam S the slices type describing the shape adaptation * * @sa view, range, all, newaxis, keep, drop */ template class xview : public xview_semantic>, public std::conditional_t< detail::is_contiguous_view, S...>::value, xcontiguous_iterable>, xiterable>>, public xaccessible>, public extension::xview_base_t { public: using self_type = xview; using inner_types = xcontainer_inner_types; using xexpression_type = std::decay_t; using semantic_base = xview_semantic; using temporary_type = typename xcontainer_inner_types::temporary_type; using accessible_base = xaccessible; using extension_base = extension::xview_base_t; using expression_tag = typename extension_base::expression_tag; static constexpr bool is_const = std::is_const>::value; using value_type = typename xexpression_type::value_type; using simd_value_type = xt_simd::simd_type; using bool_load_type = typename xexpression_type::bool_load_type; using reference = typename inner_types::reference; using const_reference = typename inner_types::const_reference; using pointer = std:: conditional_t; using const_pointer = typename xexpression_type::const_pointer; using size_type = typename inner_types::size_type; using difference_type = typename xexpression_type::difference_type; static constexpr layout_type static_layout = inner_types::layout; static constexpr bool contiguous_layout = static_layout != layout_type::dynamic; static constexpr bool is_strided_view = detail::is_strided_view::value; static constexpr bool is_contiguous_view = contiguous_layout; using iterable_base = xiterable; using inner_shape_type = typename iterable_base::inner_shape_type; using shape_type = typename xview_shape_type::type; using xexpression_inner_strides_type = xtl::mpl::eval_if_t< has_strides, detail::expr_inner_strides_type, get_strides_type>; using xexpression_inner_backstrides_type = xtl::mpl::eval_if_t< has_strides, detail::expr_inner_backstrides_type, get_strides_type>; using storage_type = typename inner_types::storage_type; static constexpr bool has_trivial_strides = is_contiguous_view && !xtl::disjunction...>::value; using inner_strides_type = std::conditional_t< has_trivial_strides, typename detail::unwrap_offset_container< xexpression_type::static_layout, xexpression_inner_strides_type, integral_count()>::type, get_strides_t>; using inner_backstrides_type = std::conditional_t< has_trivial_strides, typename detail::unwrap_offset_container< xexpression_type::static_layout, xexpression_inner_backstrides_type, integral_count()>::type, get_strides_t>; using strides_type = get_strides_t; using backstrides_type = strides_type; using slice_type = std::tuple; using stepper = typename iterable_base::stepper; using const_stepper = typename iterable_base::const_stepper; using linear_iterator = std::conditional_t< has_data_interface::value && is_strided_view, std::conditional_t, typename iterable_base::linear_iterator>; using const_linear_iterator = std::conditional_t< has_data_interface::value && is_strided_view, typename xexpression_type::const_linear_iterator, typename iterable_base::const_linear_iterator>; using reverse_linear_iterator = std::reverse_iterator; using const_reverse_linear_iterator = std::reverse_iterator; using container_iterator = pointer; using const_container_iterator = const_pointer; static constexpr std::size_t rank = SIZE_MAX; // The FSL argument prevents the compiler from calling this constructor // instead of the copy constructor when sizeof...(SL) == 0. template explicit xview(CTA&& e, FSL&& first_slice, SL&&... slices) noexcept; xview(const xview&) = default; self_type& operator=(const xview& rhs); template self_type& operator=(const xexpression& e); template disable_xexpression& operator=(const E& e); const inner_shape_type& shape() const noexcept; const slice_type& slices() const noexcept; layout_type layout() const noexcept; bool is_contiguous() const noexcept; using accessible_base::shape; template void fill(const T& value); template reference operator()(Args... args); template reference unchecked(Args... args); template reference element(It first, It last); template const_reference operator()(Args... args) const; template const_reference unchecked(Args... args) const; template const_reference element(It first, It last) const; xexpression_type& expression() noexcept; const xexpression_type& expression() const noexcept; template bool broadcast_shape(ST& shape, bool reuse_cache = false) const; template bool has_linear_assign(const ST& strides) const; template std::enable_if_t stepper_begin(const ST& shape); template std::enable_if_t stepper_end(const ST& shape, layout_type l); template std::enable_if_t stepper_begin(const ST& shape) const; template std::enable_if_t stepper_end(const ST& shape, layout_type l) const; template std::enable_if_t stepper_begin(const ST& shape); template std::enable_if_t stepper_end(const ST& shape, layout_type l); template std::enable_if_t stepper_begin(const ST& shape) const; template std::enable_if_t stepper_end(const ST& shape, layout_type l) const; template std::enable_if_t::value, storage_type&> storage(); template std::enable_if_t::value, const storage_type&> storage() const; template std::enable_if_t::value && is_strided_view, linear_iterator> linear_begin(); template std::enable_if_t::value && is_strided_view, linear_iterator> linear_end(); template std::enable_if_t::value && is_strided_view, const_linear_iterator> linear_begin() const; template std::enable_if_t::value && is_strided_view, const_linear_iterator> linear_end() const; template std::enable_if_t::value && is_strided_view, const_linear_iterator> linear_cbegin() const; template std::enable_if_t::value && is_strided_view, const_linear_iterator> linear_cend() const; template std::enable_if_t::value && is_strided_view, reverse_linear_iterator> linear_rbegin(); template std::enable_if_t::value && is_strided_view, reverse_linear_iterator> linear_rend(); template std::enable_if_t::value && is_strided_view, const_reverse_linear_iterator> linear_rbegin() const; template std::enable_if_t::value && is_strided_view, const_reverse_linear_iterator> linear_rend() const; template std::enable_if_t::value && is_strided_view, const_reverse_linear_iterator> linear_crbegin() const; template std::enable_if_t::value && is_strided_view, const_reverse_linear_iterator> linear_crend() const; template std::enable_if_t::value && is_strided_view, const inner_strides_type&> strides() const; template std::enable_if_t::value && is_strided_view, const inner_strides_type&> backstrides() const; template std::enable_if_t::value && is_strided_view, const_pointer> data() const; template std::enable_if_t::value && is_strided_view, pointer> data(); template std::enable_if_t::value && is_strided_view, std::size_t> data_offset() const noexcept; template inline It data_xbegin_impl(It begin) const noexcept; template inline It data_xend_impl(It begin, layout_type l, size_type offset) const noexcept; inline container_iterator data_xbegin() noexcept; inline const_container_iterator data_xbegin() const noexcept; inline container_iterator data_xend(layout_type l, size_type offset) noexcept; inline const_container_iterator data_xend(layout_type l, size_type offset) const noexcept; // Conversion operator enabled for statically "scalar" views template >::value, int>> operator reference() { return (*this)(); } template >::value, int>> operator const_reference() const { return (*this)(); } size_type underlying_size(size_type dim) const; xtl::xclosure_pointer operator&() &; xtl::xclosure_pointer operator&() const&; xtl::xclosure_pointer operator&() &&; template < class E, class T = xexpression_type, class = std::enable_if_t::value && is_contiguous_view, int>> void assign_to(xexpression& e, bool force_resize) const; template using rebind_t = xview; template rebind_t build_view(E&& e) const; // // SIMD interface // template using simd_return_type = xt_simd::simd_return_type; template using enable_simd_interface = std::enable_if_t::value && is_strided_view, R>; template enable_simd_interface store_simd(size_type i, const simd& e); template < class align, class requested_type = value_type, std::size_t N = xt_simd::simd_traits::size, class T = xexpression_type> enable_simd_interface> load_simd(size_type i) const; template enable_simd_interface data_element(size_type i); template enable_simd_interface data_element(size_type i) const; template enable_simd_interface flat(size_type i); template enable_simd_interface flat(size_type i) const; private: // VS 2015 workaround (yes, really) template struct lesser_condition { static constexpr bool value = (I + newaxis_count_before(I + 1) < sizeof...(S)); }; CT m_e; slice_type m_slices; inner_shape_type m_shape; mutable inner_strides_type m_strides; mutable inner_backstrides_type m_backstrides; mutable std::size_t m_data_offset; mutable bool m_strides_computed; template explicit xview(std::true_type, CTA&& e, FSL&& first_slice, SL&&... slices) noexcept; template explicit xview(std::false_type, CTA&& e, FSL&& first_slice, SL&&... slices) noexcept; template auto make_index_sequence(Args... args) const noexcept; void compute_strides(std::true_type) const; void compute_strides(std::false_type) const; reference access(); template reference access(Arg arg, Args... args); const_reference access() const; template const_reference access(Arg arg, Args... args) const; template ::size_type... I, class... Args> reference unchecked_impl(std::index_sequence, Args... args); template ::size_type... I, class... Args> const_reference unchecked_impl(std::index_sequence, Args... args) const; template ::size_type... I, class... Args> reference access_impl(std::index_sequence, Args... args); template ::size_type... I, class... Args> const_reference access_impl(std::index_sequence, Args... args) const; template ::size_type I, class... Args> std::enable_if_t::value, size_type> index(Args... args) const; template ::size_type I, class... Args> std::enable_if_t::value, size_type> index(Args... args) const; template ::size_type, class T> size_type sliced_access(const xslice& slice) const; template ::size_type I, class T, class Arg, class... Args> size_type sliced_access(const xslice& slice, Arg arg, Args... args) const; template ::size_type I, class T, class... Args> disable_xslice sliced_access(const T& squeeze, Args...) const; using base_index_type = xindex_type_t; template base_index_type make_index(It first, It last) const; void assign_temporary_impl(temporary_type&& tmp); template std::size_t data_offset_impl(std::index_sequence) const noexcept; template auto compute_strides_impl(std::index_sequence) const noexcept; inner_shape_type compute_shape(std::true_type) const; inner_shape_type compute_shape(std::false_type) const; template rebind_t build_view_impl(E&& e, std::index_sequence) const; friend class xview_semantic>; }; template auto view(E&& e, S&&... slices); template auto row(E&& e, std::ptrdiff_t index); template auto col(E&& e, std::ptrdiff_t index); /***************************** * xview_stepper declaration * *****************************/ namespace detail { template struct get_stepper_impl { using xexpression_type = typename V::xexpression_type; using type = typename xexpression_type::stepper; }; template struct get_stepper_impl { using xexpression_type = typename V::xexpression_type; using type = typename xexpression_type::const_stepper; }; } template using get_stepper = typename detail::get_stepper_impl::type; template class xview_stepper { public: using view_type = std::conditional_t, xview>; using substepper_type = get_stepper; using value_type = typename substepper_type::value_type; using reference = typename substepper_type::reference; using pointer = typename substepper_type::pointer; using difference_type = typename substepper_type::difference_type; using size_type = typename view_type::size_type; using shape_type = typename substepper_type::shape_type; xview_stepper() = default; xview_stepper( view_type* view, substepper_type it, size_type offset, bool end = false, layout_type l = XTENSOR_DEFAULT_TRAVERSAL ); reference operator*() const; void step(size_type dim); void step_back(size_type dim); void step(size_type dim, size_type n); void step_back(size_type dim, size_type n); void reset(size_type dim); void reset_back(size_type dim); void to_begin(); void to_end(layout_type l); private: bool is_newaxis_slice(size_type index) const noexcept; void to_end_impl(layout_type l); template void common_step_forward(size_type dim, F f); template void common_step_backward(size_type dim, F f); template void common_step_forward(size_type dim, size_type n, F f); template void common_step_backward(size_type dim, size_type n, F f); template void common_reset(size_type dim, F f, bool backwards); view_type* p_view; substepper_type m_it; size_type m_offset; std::array m_index_keeper; }; // meta-function returning the shape type for an xview template struct xview_shape_type { using type = ST; }; template struct xview_shape_type, S...> { using type = std::array() + newaxis_count()>; }; template struct xview_shape_type, S...> { using type = typename xview_shape_type, S...>::type; }; /************************ * xview implementation * ************************/ /** * @name Constructor */ //@{ /** * Constructs a view on the specified xexpression. * Users should not call directly this constructor but * use the view function instead. * @param e the xexpression to adapt * @param first_slice the first slice describing the view * @param slices the slices list describing the view * @sa view */ template template xview::xview(CTA&& e, FSL&& first_slice, SL&&... slices) noexcept : xview( std::integral_constant{}, std::forward(e), std::forward(first_slice), std::forward(slices)... ) { } // trivial strides initializer template template xview::xview(std::true_type, CTA&& e, FSL&& first_slice, SL&&... slices) noexcept : m_e(std::forward(e)) , m_slices(std::forward(first_slice), std::forward(slices)...) , m_shape(compute_shape(detail::is_sequence_view{})) , m_strides(m_e.strides()) , m_backstrides(m_e.backstrides()) , m_data_offset(data_offset_impl(std::make_index_sequence())) , m_strides_computed(true) { } template template xview::xview(std::false_type, CTA&& e, FSL&& first_slice, SL&&... slices) noexcept : m_e(std::forward(e)) , m_slices(std::forward(first_slice), std::forward(slices)...) , m_shape(compute_shape(std::false_type{})) , m_strides_computed(false) { } //@} template inline auto xview::operator=(const xview& rhs) -> self_type& { temporary_type tmp(rhs); return this->assign_temporary(std::move(tmp)); } /** * @name Extended copy semantic */ //@{ /** * The extended assignment operator. */ template template inline auto xview::operator=(const xexpression& e) -> self_type& { return semantic_base::operator=(e); } //@} template template inline auto xview::operator=(const E& e) -> disable_xexpression& { this->fill(e); return *this; } /** * @name Size and shape */ //@{ /** * Returns the shape of the view. */ template inline auto xview::shape() const noexcept -> const inner_shape_type& { return m_shape; } /** * Returns the slices of the view. */ template inline auto xview::slices() const noexcept -> const slice_type& { return m_slices; } /** * Returns the slices of the view. */ template inline layout_type xview::layout() const noexcept { return xtl::mpl::static_if( [&](auto self) { if (static_layout != layout_type::dynamic) { return static_layout; } else { bool strides_match = do_strides_match( self(this)->shape(), self(this)->strides(), self(this)->m_e.layout(), true ); return strides_match ? self(this)->m_e.layout() : layout_type::dynamic; } }, /* else */ [&](auto /*self*/) { return layout_type::dynamic; } ); } template inline bool xview::is_contiguous() const noexcept { return layout() != layout_type::dynamic; } //@} /** * @name Data */ //@{ /** * Fills the view with the given value. * @param value the value to fill the view with. */ template template inline void xview::fill(const T& value) { xtl::mpl::static_if( [&](auto self) { std::fill(self(this)->linear_begin(), self(this)->linear_end(), value); }, /*else*/ [&](auto self) { std::fill(self(this)->begin(), self(this)->end(), value); } ); } /** * Returns a reference to the element at the specified position in the view. * @param args a list of indices specifying the position in the view. Indices * must be unsigned integers, the number of indices should be equal or greater * than the number of dimensions of the view. */ template template inline auto xview::operator()(Args... args) -> reference { XTENSOR_TRY(check_index(shape(), args...)); XTENSOR_CHECK_DIMENSION(shape(), args...); // The static cast prevents the compiler from instantiating the template methods with signed integers, // leading to warning about signed/unsigned conversions in the deeper layers of the access methods return access(static_cast(args)...); } /** * Returns a reference to the element at the specified position in the view. * @param args a list of indices specifying the position in the view. Indices * must be unsigned integers, the number of indices must be equal to the number of * dimensions of the view, 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.unchecked(0, 1); * @endcode */ template template inline auto xview::unchecked(Args... args) -> reference { return unchecked_impl(make_index_sequence(args...), static_cast(args)...); } template template inline auto xview::element(It first, It last) -> reference { XTENSOR_TRY(check_element_index(shape(), first, last)); // TODO: avoid memory allocation auto index = make_index(first, last); return m_e.element(index.cbegin(), index.cend()); } /** * Returns a constant reference to the element at the specified position in the view. * @param args a list of indices specifying the position in the view. Indices must be * unsigned integers, the number of indices should be equal or greater than the number * of dimensions of the view. */ template template inline auto xview::operator()(Args... args) const -> const_reference { XTENSOR_TRY(check_index(shape(), args...)); XTENSOR_CHECK_DIMENSION(shape(), args...); // The static cast prevents the compiler from instantiating the template methods with signed integers, // leading to warning about signed/unsigned conversions in the deeper layers of the access methods return access(static_cast(args)...); } /** * Returns a constant reference to the element at the specified position in the view. * @param args a list of indices specifying the position in the view. Indices * must be unsigned integers, the number of indices must be equal to the number of * dimensions of the view, 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.unchecked(0, 1); * @endcode */ template template inline auto xview::unchecked(Args... args) const -> const_reference { return unchecked_impl(make_index_sequence(args...), static_cast(args)...); } template template inline auto xview::element(It first, It last) const -> const_reference { // TODO: avoid memory allocation auto index = make_index(first, last); return m_e.element(index.cbegin(), index.cend()); } /** * Returns a reference to the underlying expression of the view. */ template inline auto xview::expression() noexcept -> xexpression_type& { return m_e; } /** * Returns a const reference to the underlying expression of the view. */ template inline auto xview::expression() const noexcept -> const xexpression_type& { return m_e; } /** * Returns the data holder of the underlying container (only if the view is on a realized * container). ``xt::eval`` will make sure that the underlying xexpression is * on a realized container. */ template template inline auto xview::storage() -> std::enable_if_t::value, storage_type&> { return m_e.storage(); } template template inline auto xview::storage() const -> std::enable_if_t::value, const storage_type&> { return m_e.storage(); } template template auto xview::linear_begin() -> std::enable_if_t::value && is_strided_view, linear_iterator> { return m_e.storage().begin() + data_offset(); } template template auto xview::linear_end() -> std::enable_if_t::value && is_strided_view, linear_iterator> { return m_e.storage().begin() + data_offset() + this->size(); } template template auto xview::linear_begin() const -> std::enable_if_t::value && is_strided_view, const_linear_iterator> { return linear_cbegin(); } template template auto xview::linear_end() const -> std::enable_if_t::value && is_strided_view, const_linear_iterator> { return linear_cend(); } template template auto xview::linear_cbegin() const -> std::enable_if_t::value && is_strided_view, const_linear_iterator> { return m_e.storage().cbegin() + data_offset(); } template template auto xview::linear_cend() const -> std::enable_if_t::value && is_strided_view, const_linear_iterator> { return m_e.storage().cbegin() + data_offset() + this->size(); } template template auto xview::linear_rbegin() -> std::enable_if_t::value && is_strided_view, reverse_linear_iterator> { return reverse_linear_iterator(linear_end()); } template template auto xview::linear_rend() -> std::enable_if_t::value && is_strided_view, reverse_linear_iterator> { return reverse_linear_iterator(linear_begin()); } template template auto xview::linear_rbegin() const -> std::enable_if_t::value && is_strided_view, const_reverse_linear_iterator> { return linear_crbegin(); } template template auto xview::linear_rend() const -> std::enable_if_t::value && is_strided_view, const_reverse_linear_iterator> { return linear_crend(); } template template auto xview::linear_crbegin() const -> std::enable_if_t::value && is_strided_view, const_reverse_linear_iterator> { return const_reverse_linear_iterator(linear_end()); } template template auto xview::linear_crend() const -> std::enable_if_t::value && is_strided_view, const_reverse_linear_iterator> { return const_reverse_linear_iterator(linear_begin()); } /** * Return the strides for the underlying container of the view. */ template template inline auto xview::strides() const -> std::enable_if_t::value && is_strided_view, const inner_strides_type&> { if (!m_strides_computed) { compute_strides(std::integral_constant{}); m_strides_computed = true; } return m_strides; } template template inline auto xview::backstrides() const -> std::enable_if_t::value && is_strided_view, const inner_strides_type&> { if (!m_strides_computed) { compute_strides(std::integral_constant{}); m_strides_computed = true; } return m_backstrides; } /** * Return the pointer to the underlying buffer. */ template template inline auto xview::data() const -> std::enable_if_t::value && is_strided_view, const_pointer> { return m_e.data(); } template template inline auto xview::data() -> std::enable_if_t::value && is_strided_view, pointer> { return m_e.data(); } template template inline std::size_t xview::data_offset_impl(std::index_sequence) const noexcept { auto temp = std::array( {(static_cast(xt::value(std::get(m_slices), 0)))...} ); std::ptrdiff_t result = 0; std::size_t i = 0; for (; i < std::min(sizeof...(S), m_e.strides().size()); ++i) { result += temp[i] * m_e.strides()[i - newaxis_count_before(i)]; } for (; i < sizeof...(S); ++i) { result += temp[i]; } return static_cast(result) + m_e.data_offset(); } /** * Return the offset to the first element of the view in the underlying container. */ template template inline auto xview::data_offset() const noexcept -> std::enable_if_t::value && is_strided_view, std::size_t> { if (!m_strides_computed) { compute_strides(std::integral_constant{}); m_strides_computed = true; } return m_data_offset; } //@} template inline auto xview::underlying_size(size_type dim) const -> size_type { return m_e.shape()[dim]; } template inline auto xview::operator&() & -> xtl::xclosure_pointer { return xtl::closure_pointer(*this); } template inline auto xview::operator&() const& -> xtl::xclosure_pointer { return xtl::closure_pointer(*this); } template inline auto xview::operator&() && -> xtl::xclosure_pointer { return xtl::closure_pointer(std::move(*this)); } /** * @name Broadcasting */ //@{ /** * Broadcast the shape of the view 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 xview::broadcast_shape(ST& shape, bool) const { return xt::broadcast_shape(m_shape, shape); } /** * Checks whether the xview 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 xview::has_linear_assign(const ST& str) const { return xtl::mpl::static_if( [&](auto self) { return str.size() == self(this)->strides().size() && std::equal(str.cbegin(), str.cend(), self(this)->strides().begin()); }, /*else*/ [](auto /*self*/) { return false; } ); } //@} template template inline It xview::data_xbegin_impl(It begin) const noexcept { return begin + data_offset(); } template template inline It xview::data_xend_impl(It begin, layout_type l, size_type offset) const noexcept { return strided_data_end(*this, begin, l, offset); } template inline auto xview::data_xbegin() noexcept -> container_iterator { return data_xbegin_impl(data()); } template inline auto xview::data_xbegin() const noexcept -> const_container_iterator { return data_xbegin_impl(data()); } template inline auto xview::data_xend(layout_type l, size_type offset) noexcept -> container_iterator { return data_xend_impl(data() + data_offset(), l, offset); } template inline auto xview::data_xend(layout_type l, size_type offset) const noexcept -> const_container_iterator { return data_xend_impl(data() + data_offset(), l, offset); } // Assign to operator enabled for contigous views template template void xview::assign_to(xexpression& e, bool force_resize) const { auto& de = e.derived_cast(); de.resize(shape(), force_resize); std::copy(data() + data_offset(), data() + data_offset() + de.size(), de.template begin()); } template template inline auto xview::build_view_impl(E&& e, std::index_sequence) const -> rebind_t { return rebind_t(std::forward(e), std::get(m_slices)...); } template template inline auto xview::build_view(E&& e) const -> rebind_t { return build_view_impl(std::forward(e), std::make_index_sequence()); } template template inline auto xview::store_simd(size_type i, const simd& e) -> enable_simd_interface { return m_e.template store_simd(data_offset() + i, e); } template template inline auto xview::load_simd(size_type i) const -> enable_simd_interface> { return m_e.template load_simd(data_offset() + i); } template template inline auto xview::data_element(size_type i) -> enable_simd_interface { return m_e.data_element(data_offset() + i); } template template inline auto xview::data_element(size_type i) const -> enable_simd_interface { return m_e.data_element(data_offset() + i); } template template inline auto xview::flat(size_type i) -> enable_simd_interface { XTENSOR_ASSERT(is_contiguous()); return m_e.flat(data_offset() + i); } template template inline auto xview::flat(size_type i) const -> enable_simd_interface { XTENSOR_ASSERT(is_contiguous()); return m_e.flat(data_offset() + i); } template template inline auto xview::make_index_sequence(Args...) const noexcept { return std::make_index_sequence< (sizeof...(Args) + integral_count() > newaxis_count() ? sizeof...(Args) + integral_count() - newaxis_count() : 0)>(); } template template inline auto xview::compute_strides_impl(std::index_sequence) const noexcept { std::size_t original_dim = m_e.dimension(); return std::array( {(static_cast(xt::step_size(std::get(I)>(m_slices), 1)) * ((integral_skip(I) - newaxis_count_before(integral_skip(I))) < original_dim ? m_e.strides()[integral_skip(I) - newaxis_count_before(integral_skip(I))] : 1))...} ); } template inline void xview::compute_strides(std::false_type) const { m_strides = xtl::make_sequence(this->dimension(), 0); m_backstrides = xtl::make_sequence(this->dimension(), 0); constexpr std::size_t n_strides = sizeof...(S) - integral_count(); auto slice_strides = compute_strides_impl(std::make_index_sequence()); for (std::size_t i = 0; i < n_strides; ++i) { m_strides[i] = slice_strides[i]; // adapt strides for shape[i] == 1 to make consistent with rest of xtensor detail::adapt_strides(shape(), m_strides, &m_backstrides, i); } for (std::size_t i = n_strides; i < this->dimension(); ++i) { m_strides[i] = m_e.strides()[i + integral_count() - newaxis_count()]; detail::adapt_strides(shape(), m_strides, &m_backstrides, i); } m_data_offset = data_offset_impl(std::make_index_sequence()); } template inline void xview::compute_strides(std::true_type) const { } template inline auto xview::access() -> reference { return access_impl(make_index_sequence()); } template template inline auto xview::access(Arg arg, Args... args) -> reference { if (sizeof...(Args) >= this->dimension()) { return access(args...); } return access_impl(make_index_sequence(arg, args...), arg, args...); } template inline auto xview::access() const -> const_reference { return access_impl(make_index_sequence()); } template template inline auto xview::access(Arg arg, Args... args) const -> const_reference { if (sizeof...(Args) >= this->dimension()) { return access(args...); } return access_impl(make_index_sequence(arg, args...), arg, args...); } template template ::size_type... I, class... Args> inline auto xview::unchecked_impl(std::index_sequence, Args... args) -> reference { return m_e.unchecked(index(args...)...); } template template ::size_type... I, class... Args> inline auto xview::unchecked_impl(std::index_sequence, Args... args) const -> const_reference { return m_e.unchecked(index(args...)...); } template template ::size_type... I, class... Args> inline auto xview::access_impl(std::index_sequence, Args... args) -> reference { return m_e(index(args...)...); } template template ::size_type... I, class... Args> inline auto xview::access_impl(std::index_sequence, Args... args) const -> const_reference { return m_e(index(args...)...); } template template ::size_type I, class... Args> inline auto xview::index(Args... args) const -> std::enable_if_t::value, size_type> { return sliced_access(I) + newaxis_count_before(I + 1)>( std::get(I + 1)>(m_slices), args... ); } template template ::size_type I, class... Args> inline auto xview::index(Args... args) const -> std::enable_if_t::value, size_type> { return argument() + newaxis_count()>(args...); } template template ::size_type I, class T> inline auto xview::sliced_access(const xslice& slice) const -> size_type { return static_cast(slice.derived_cast()(0)); } template template ::size_type I, class T, class Arg, class... Args> inline auto xview::sliced_access(const xslice& slice, Arg arg, Args... args) const -> size_type { using ST = typename T::size_type; return static_cast( slice.derived_cast()(argument(static_cast(arg), static_cast(args)...)) ); } template template ::size_type I, class T, class... Args> inline auto xview::sliced_access(const T& squeeze, Args...) const -> disable_xslice { return static_cast(squeeze); } template template inline auto xview::make_index(It first, It last) const -> base_index_type { auto index = xtl::make_sequence(m_e.dimension(), 0); using diff_type = typename std::iterator_traits::difference_type; using ivalue_type = typename base_index_type::value_type; auto func1 = [&first](const auto& s) noexcept { return get_slice_value(s, first); }; auto func2 = [](const auto& s) noexcept { return xt::value(s, 0); }; auto s = static_cast( (std::min)(static_cast(std::distance(first, last)), this->dimension()) ); auto first_copy = last - s; for (size_type i = 0; i != m_e.dimension(); ++i) { size_type k = newaxis_skip(i); // need to advance captured `first` first = first_copy; std::advance(first, static_cast(k - xt::integral_count_before(i))); if (first < last) { index[i] = k < sizeof...(S) ? apply(k, func1, m_slices) : static_cast(*first); } else { index[i] = k < sizeof...(S) ? apply(k, func2, m_slices) : ivalue_type(0); } } return index; } template inline auto xview::compute_shape(std::true_type) const -> inner_shape_type { return inner_shape_type(m_e.shape()); } template inline auto xview::compute_shape(std::false_type) const -> inner_shape_type { std::size_t dim = m_e.dimension() - integral_count() + newaxis_count(); auto shape = xtl::make_sequence(dim, 0); auto func = [](const auto& s) noexcept { return get_size(s); }; for (size_type i = 0; i != dim; ++i) { size_type index = integral_skip(i); shape[i] = index < sizeof...(S) ? apply(index, func, m_slices) : m_e.shape()[index - newaxis_count_before(index)]; } return shape; } namespace xview_detail { template inline void run_assign_temporary_impl(V& v, const T& t, std::true_type /* enable strided assign */) { strided_loop_assigner::run(v, t); } template inline void run_assign_temporary_impl(V& v, const T& t, std::false_type /* fallback to iterator assign */) { std::copy(t.cbegin(), t.cend(), v.begin()); } } template inline void xview::assign_temporary_impl(temporary_type&& tmp) { constexpr bool fast_assign = detail::is_strided_view::value && xassign_traits, temporary_type>::simd_strided_assign(); xview_detail::run_assign_temporary_impl(*this, tmp, std::integral_constant{}); } namespace detail { template inline std::size_t get_underlying_shape_index(std::size_t I) { return I - newaxis_count_before...>(I); } template struct check_slice; template <> struct check_slice<> { using type = void_t<>; }; template struct check_slice { static_assert(!std::is_same::value, "ellipsis not supported vith xview"); using type = typename check_slice::type; }; template inline auto make_view_impl(E&& e, std::index_sequence, S&&... slices) { // Checks that no ellipsis slice is used using view_type = xview, get_slice_type, S>...>; return view_type( std::forward(e), get_slice_implementation( e, std::forward(slices), get_underlying_shape_index, S...>(I) )... ); } } /** * Constructs and returns a view on the specified xexpression. Users * should not directly construct the slices but call helper functions * instead. * @param e the xexpression to adapt * @param slices the slices list describing the view. \c view accepts negative * indices, in that case indexing is done in reverse order. * @sa range, all, newaxis */ template inline auto view(E&& e, S&&... slices) { return detail::make_view_impl( std::forward(e), std::make_index_sequence(), std::forward(slices)... ); } namespace detail { class row_impl { public: template inline static auto make(E&& e, const std::ptrdiff_t index) { const auto shape = e.shape(); check_dimension(shape); return view(e, index, xt::all()); } private: template inline static void check_dimension(const S& shape) { if (shape.size() != 2) { XTENSOR_THROW( std::invalid_argument, "A row can only be accessed on an expression with exact two dimensions" ); } } template inline static void check_dimension(const std::array&) { static_assert(N == 2, "A row can only be accessed on an expression with exact two dimensions"); } }; class column_impl { public: template inline static auto make(E&& e, const std::ptrdiff_t index) { const auto shape = e.shape(); check_dimension(shape); return view(e, xt::all(), index); } private: template inline static void check_dimension(const S& shape) { if (shape.size() != 2) { XTENSOR_THROW( std::invalid_argument, "A column can only be accessed on an expression with exact two dimensions" ); } } template inline static void check_dimension(const std::array&) { static_assert(N == 2, "A column can only be accessed on an expression with exact two dimensions"); } }; } /** * Constructs and returns a row (sliced view) on the specified expression. * Users should not directly construct the slices but call helper functions * instead. This function is only allowed on expressions with two dimensions. * @param e the xexpression to adapt * @param index 0-based index of the row, negative indices will return the * last rows in reverse order. * @throws std::invalid_argument if the expression has more than 2 dimensions. */ template inline auto row(E&& e, std::ptrdiff_t index) { return detail::row_impl::make(e, index); } /** * Constructs and returns a column (sliced view) on the specified expression. * Users should not directly construct the slices but call helper functions * instead. This function is only allowed on expressions with two dimensions. * @param e the xexpression to adapt * @param index 0-based index of the column, negative indices will return the * last columns in reverse order. * @throws std::invalid_argument if the expression has more than 2 dimensions. */ template inline auto col(E&& e, std::ptrdiff_t index) { return detail::column_impl::make(e, index); } /*************** * stepper api * ***************/ template template inline auto xview::stepper_begin(const ST& shape) -> std::enable_if_t { size_type offset = shape.size() - this->dimension(); return stepper(this, m_e.stepper_begin(m_e.shape()), offset); } template template inline auto xview::stepper_end(const ST& shape, layout_type l) -> std::enable_if_t { size_type offset = shape.size() - this->dimension(); return stepper(this, m_e.stepper_end(m_e.shape(), l), offset, true, l); } template template inline auto xview::stepper_begin(const ST& shape) const -> std::enable_if_t { size_type offset = shape.size() - this->dimension(); const xexpression_type& e = m_e; return const_stepper(this, e.stepper_begin(m_e.shape()), offset); } template template inline auto xview::stepper_end(const ST& shape, layout_type l) const -> std::enable_if_t { size_type offset = shape.size() - this->dimension(); const xexpression_type& e = m_e; return const_stepper(this, e.stepper_end(m_e.shape(), l), offset, true, l); } template template inline auto xview::stepper_begin(const ST& shape) -> std::enable_if_t { size_type offset = shape.size() - this->dimension(); return stepper(this, data_xbegin(), offset); } template template inline auto xview::stepper_end(const ST& shape, layout_type l) -> std::enable_if_t { size_type offset = shape.size() - this->dimension(); return stepper(this, data_xend(l, offset), offset); } template template inline auto xview::stepper_begin(const ST& shape) const -> std::enable_if_t { size_type offset = shape.size() - this->dimension(); return const_stepper(this, data_xbegin(), offset); } template template inline auto xview::stepper_end(const ST& shape, layout_type l) const -> std::enable_if_t { size_type offset = shape.size() - this->dimension(); return const_stepper(this, data_xend(l, offset), offset); } /******************************** * xview_stepper implementation * ********************************/ template inline xview_stepper::xview_stepper( view_type* view, substepper_type it, size_type offset, bool end, layout_type l ) : p_view(view) , m_it(it) , m_offset(offset) { if (!end) { std::fill(m_index_keeper.begin(), m_index_keeper.end(), 0); auto func = [](const auto& s) noexcept { return xt::value(s, 0); }; for (size_type i = 0; i < sizeof...(S); ++i) { if (!is_newaxis_slice(i)) { size_type s = apply(i, func, p_view->slices()); size_type index = i - newaxis_count_before(i); m_it.step(index, s); } } } else { to_end_impl(l); } } template inline auto xview_stepper::operator*() const -> reference { return *m_it; } template inline void xview_stepper::step(size_type dim) { auto func = [this](size_type index, size_type offset) { m_it.step(index, offset); }; common_step_forward(dim, func); } template inline void xview_stepper::step_back(size_type dim) { auto func = [this](size_type index, size_type offset) { m_it.step_back(index, offset); }; common_step_backward(dim, func); } template inline void xview_stepper::step(size_type dim, size_type n) { auto func = [this](size_type index, size_type offset) { m_it.step(index, offset); }; common_step_forward(dim, n, func); } template inline void xview_stepper::step_back(size_type dim, size_type n) { auto func = [this](size_type index, size_type offset) { m_it.step_back(index, offset); }; common_step_backward(dim, n, func); } template inline void xview_stepper::reset(size_type dim) { auto func = [this](size_type index, size_type offset) { m_it.step_back(index, offset); }; common_reset(dim, func, false); } template inline void xview_stepper::reset_back(size_type dim) { auto func = [this](size_type index, size_type offset) { m_it.step(index, offset); }; common_reset(dim, func, true); } template inline void xview_stepper::to_begin() { std::fill(m_index_keeper.begin(), m_index_keeper.end(), 0); m_it.to_begin(); } template inline void xview_stepper::to_end(layout_type l) { m_it.to_end(l); to_end_impl(l); } template inline bool xview_stepper::is_newaxis_slice(size_type index) const noexcept { // A bit tricky but avoids a lot of template instantiations return newaxis_count_before(index + 1) != newaxis_count_before(index); } template inline void xview_stepper::to_end_impl(layout_type l) { auto func = [](const auto& s) noexcept { return xt::value(s, get_size(s) - 1); }; auto size_func = [](const auto& s) noexcept { return get_size(s); }; for (size_type i = 0; i < sizeof...(S); ++i) { if (!is_newaxis_slice(i)) { size_type s = apply(i, func, p_view->slices()); size_type ix = apply(i, size_func, p_view->slices()); m_index_keeper[i] = ix - size_type(1); size_type index = i - newaxis_count_before(i); s = p_view->underlying_size(index) - 1 - s; m_it.step_back(index, s); } } if (l == layout_type::row_major) { for (size_type i = sizeof...(S); i > 0; --i) { if (!is_newaxis_slice(i - 1)) { m_index_keeper[i - 1]++; break; } } } else if (l == layout_type::column_major) { for (size_type i = 0; i < sizeof...(S); ++i) { if (!is_newaxis_slice(i)) { m_index_keeper[i]++; break; } } } else { XTENSOR_THROW(std::runtime_error, "Iteration only allowed in row or column major."); } } template template void xview_stepper::common_step_forward(size_type dim, F f) { if (dim >= m_offset) { auto func = [&dim, this](const auto& s) noexcept { return step_size(s, this->m_index_keeper[dim]++, 1); }; size_type index = integral_skip(dim); if (!is_newaxis_slice(index)) { size_type step_size = index < sizeof...(S) ? apply(index, func, p_view->slices()) : 1; index -= newaxis_count_before(index); f(index, step_size); } } } template template void xview_stepper::common_step_forward(size_type dim, size_type n, F f) { if (dim >= m_offset) { auto func = [&dim, &n, this](const auto& s) noexcept { auto st_size = step_size(s, this->m_index_keeper[dim], n); this->m_index_keeper[dim] += n; return size_type(st_size); }; size_type index = integral_skip(dim); if (!is_newaxis_slice(index)) { size_type step_size = index < sizeof...(S) ? apply(index, func, p_view->slices()) : n; index -= newaxis_count_before(index); f(index, step_size); } } } template template void xview_stepper::common_step_backward(size_type dim, F f) { if (dim >= m_offset) { auto func = [&dim, this](const auto& s) noexcept { this->m_index_keeper[dim]--; return step_size(s, this->m_index_keeper[dim], 1); }; size_type index = integral_skip(dim); if (!is_newaxis_slice(index)) { size_type step_size = index < sizeof...(S) ? apply(index, func, p_view->slices()) : 1; index -= newaxis_count_before(index); f(index, step_size); } } } template template void xview_stepper::common_step_backward(size_type dim, size_type n, F f) { if (dim >= m_offset) { auto func = [&dim, &n, this](const auto& s) noexcept { this->m_index_keeper[dim] -= n; return step_size(s, this->m_index_keeper[dim], n); }; size_type index = integral_skip(dim); if (!is_newaxis_slice(index)) { size_type step_size = index < sizeof...(S) ? apply(index, func, p_view->slices()) : n; index -= newaxis_count_before(index); f(index, step_size); } } } template template void xview_stepper::common_reset(size_type dim, F f, bool backwards) { auto size_func = [](const auto& s) noexcept { return get_size(s); }; auto end_func = [](const auto& s) noexcept { return xt::value(s, get_size(s) - 1) - xt::value(s, 0); }; size_type index = integral_skip(dim); if (!is_newaxis_slice(index)) { if (dim < m_index_keeper.size()) { size_type size = index < sizeof...(S) ? apply(index, size_func, p_view->slices()) : p_view->shape()[dim]; m_index_keeper[dim] = backwards ? size - 1 : 0; } size_type reset_n = index < sizeof...(S) ? apply(index, end_func, p_view->slices()) : p_view->shape()[dim] - 1; index -= newaxis_count_before(index); f(index, reset_n); } } } #endif