/*************************************************************************** * 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_STRIDED_VIEW_HPP #define XTENSOR_STRIDED_VIEW_HPP #include #include #include #include #include #include #include #include "xexpression.hpp" #include "xiterable.hpp" #include "xlayout.hpp" #include "xsemantic.hpp" #include "xstorage.hpp" #include "xstrided_view_base.hpp" #include "xutils.hpp" namespace xt { /*************************** * xstrided_view extension * ***************************/ namespace extension { template struct xstrided_view_base_impl; template struct xstrided_view_base_impl { using type = xtensor_empty_base; }; template struct xstrided_view_base : xstrided_view_base_impl, CT, S, L, FST> { }; template using xstrided_view_base_t = typename xstrided_view_base::type; } template struct select_iterable_base { using type = std::conditional_t, xiterable>; }; template using select_iterable_base_t = typename select_iterable_base::type; template class xstrided_view; template struct xcontainer_inner_types> { using xexpression_type = std::decay_t; using undecay_expression = CT; using reference = inner_reference_t; using const_reference = typename xexpression_type::const_reference; using size_type = typename xexpression_type::size_type; using shape_type = std::decay_t; using undecay_shape = S; using storage_getter = FST; using inner_storage_type = typename storage_getter::type; using temporary_type = typename detail::xtype_for_shape< S>::template type; using storage_type = std::remove_reference_t; static constexpr layout_type layout = L; }; template struct xiterable_inner_types> { using inner_shape_type = std::decay_t; using inner_strides_type = get_strides_t; using inner_backstrides_type_type = inner_strides_type; using const_stepper = std::conditional_t< is_indexed_stepper::stepper>::value, xindexed_stepper, true>, xstepper>>; using stepper = std::conditional_t< is_indexed_stepper::stepper>::value, xindexed_stepper, false>, xstepper>>; }; template struct can_assign, RHS> : can_assign { }; /***************** * xstrided_view * *****************/ /** * @class xstrided_view * @brief View of an xexpression using strides * * The xstrided_view class implements a view utilizing an initial offset * and strides. * * @tparam CT the closure type of the \ref xexpression type underlying this view * @tparam L the layout of the strided view * @tparam S the strides type of the strided view * @tparam FST the flat storage type used for the strided view * * @sa strided_view, transpose */ template > class xstrided_view : public xview_semantic>, public select_iterable_base_t::static_layout, xstrided_view>, private xstrided_view_base>, public extension::xstrided_view_base_t { public: using self_type = xstrided_view; using base_type = xstrided_view_base; using semantic_base = xview_semantic; using extension_base = extension::xstrided_view_base_t; using expression_tag = typename extension_base::expression_tag; using xexpression_type = typename base_type::xexpression_type; using base_type::is_const; using value_type = typename base_type::value_type; using reference = typename base_type::reference; using const_reference = typename base_type::const_reference; using pointer = typename base_type::pointer; using const_pointer = typename base_type::const_pointer; using size_type = typename base_type::size_type; using difference_type = typename base_type::difference_type; using inner_storage_type = typename base_type::inner_storage_type; using storage_type = typename base_type::storage_type; using linear_iterator = typename storage_type::iterator; using const_linear_iterator = typename storage_type::const_iterator; using reverse_linear_iterator = std::reverse_iterator; using const_reverse_linear_iterator = std::reverse_iterator; using iterable_base = select_iterable_base_t; using inner_shape_type = typename base_type::inner_shape_type; using inner_strides_type = typename base_type::inner_strides_type; using inner_backstrides_type = typename base_type::inner_backstrides_type; using shape_type = typename base_type::shape_type; using strides_type = typename base_type::strides_type; using backstrides_type = typename base_type::backstrides_type; using stepper = typename iterable_base::stepper; using const_stepper = typename iterable_base::const_stepper; using base_type::contiguous_layout; using base_type::static_layout; using temporary_type = typename xcontainer_inner_types::temporary_type; using base_index_type = xindex_type_t; using data_alignment = xt_simd::container_alignment_t; using simd_type = xt_simd::simd_type; using simd_value_type = xt_simd::simd_type; using bool_load_type = typename base_type::bool_load_type; template xstrided_view(CTA&& e, SA&& shape, strides_type&& strides, std::size_t offset, layout_type layout) noexcept; xstrided_view(const xstrided_view& rhs) = default; self_type& operator=(const self_type&); template self_type& operator=(const xexpression& e); template disable_xexpression& operator=(const E& e); using base_type::backstrides; using base_type::dimension; using base_type::is_contiguous; using base_type::layout; using base_type::shape; using base_type::size; using base_type::strides; using base_type::operator(); using base_type::at; using base_type::unchecked; using base_type::operator[]; using base_type::data; using base_type::data_offset; using base_type::element; using base_type::expression; using base_type::storage; using base_type::broadcast_shape; using base_type::has_linear_assign; template void fill(const T& value); linear_iterator linear_begin(); linear_iterator linear_end(); const_linear_iterator linear_begin() const; const_linear_iterator linear_end() const; const_linear_iterator linear_cbegin() const; const_linear_iterator linear_cend() const; reverse_linear_iterator linear_rbegin(); reverse_linear_iterator linear_rend(); const_reverse_linear_iterator linear_rbegin() const; const_reverse_linear_iterator linear_rend() const; const_reverse_linear_iterator linear_crbegin() const; const_reverse_linear_iterator linear_crend() const; template disable_indexed_stepper_t stepper_begin(const ST& shape); template disable_indexed_stepper_t stepper_end(const ST& shape, layout_type l); template enable_indexed_stepper_t stepper_begin(const ST& shape); template enable_indexed_stepper_t stepper_end(const ST& shape, layout_type l); template disable_indexed_stepper_t stepper_begin(const ST& shape) const; template disable_indexed_stepper_t stepper_end(const ST& shape, layout_type l) const; template enable_indexed_stepper_t stepper_begin(const ST& shape) const; template enable_indexed_stepper_t stepper_end(const ST& shape, layout_type l) const; template using simd_return_type = xt_simd::simd_return_type; template using enable_simd_interface = std::enable_if_t::value && L != layout_type::dynamic, 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; reference data_element(size_type i); const_reference data_element(size_type i) const; reference flat(size_type i); const_reference flat(size_type i) const; using container_iterator = std:: conditional_t; using const_container_iterator = typename storage_type::const_iterator; template using rebind_t = xstrided_view>; template rebind_t build_view(E&& e) const; private: container_iterator data_xbegin() noexcept; const_container_iterator data_xbegin() const noexcept; container_iterator data_xend(layout_type l, size_type offset) noexcept; const_container_iterator data_xend(layout_type l, size_type offset) const noexcept; template It data_xbegin_impl(It begin) const noexcept; template It data_xend_impl(It end, layout_type l, size_type offset) const noexcept; void assign_temporary_impl(temporary_type&& tmp); using base_type::set_offset; template friend class xstepper; friend class xview_semantic; friend class xaccessible; friend class xconst_accessible; template friend class xaxis_iterator; template friend class xaxis_slice_iterator; }; /************************** * xstrided_view builders * **************************/ template using xstrided_slice = xtl::variant< T, xrange_adaptor, xrange_adaptor, xrange_adaptor, xrange_adaptor, xrange_adaptor, xrange_adaptor, xrange_adaptor, xrange_adaptor, xrange, xstepped_range, xall_tag, xellipsis_tag, xnewaxis_tag>; /** * @typedef xstrided_slice_vector * @brief vector of slices used to build a `xstrided_view` */ using xstrided_slice_vector = std::vector>; template auto strided_view(E&& e, S&& shape, X&& stride, std::size_t offset = 0, layout_type layout = L) noexcept; template auto strided_view(E&& e, const xstrided_slice_vector& slices); /******************************** * xstrided_view implementation * ********************************/ /** * @name Constructor */ //@{ /** * Constructs an xstrided_view * * @param e the underlying xexpression for this view * @param shape the shape of the view * @param strides the strides of the view * @param offset the offset of the first element in the underlying container * @param layout the layout of the view */ template template inline xstrided_view::xstrided_view( CTA&& e, SA&& shape, strides_type&& strides, std::size_t offset, layout_type layout ) noexcept : base_type(std::forward(e), std::forward(shape), std::move(strides), offset, layout) { } //@} template inline auto xstrided_view::operator=(const self_type& 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 xstrided_view::operator=(const xexpression& e) -> self_type& { return semantic_base::operator=(e); } //@} template template inline auto xstrided_view::operator=(const E& e) -> disable_xexpression& { this->fill(e); return *this; } namespace xstrided_view_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 xstrided_view::assign_temporary_impl(temporary_type&& tmp) { constexpr bool fast_assign = xassign_traits, temporary_type>::simd_strided_assign(); xstrided_view_detail::run_assign_temporary_impl(*this, tmp, std::integral_constant{}); } /** * @name Data */ //@{ /** * Fills the view with the given value. * @param value the value to fill the view with. */ template template inline void xstrided_view::fill(const T& value) { if (layout() != layout_type::dynamic) { std::fill(this->linear_begin(), this->linear_end(), value); } else { std::fill(this->begin(), this->end(), value); } } //@} template inline auto xstrided_view::data_element(size_type i) -> reference { return storage()[i]; } template inline auto xstrided_view::data_element(size_type i) const -> const_reference { return storage()[i]; } template inline auto xstrided_view::flat(size_type i) -> reference { return storage()[i]; } template inline auto xstrided_view::flat(size_type i) const -> const_reference { return storage()[i]; } template inline auto xstrided_view::linear_begin() -> linear_iterator { return this->storage().begin() + static_cast(data_offset()); } template inline auto xstrided_view::linear_end() -> linear_iterator { return this->storage().begin() + static_cast(data_offset() + size()); } template inline auto xstrided_view::linear_begin() const -> const_linear_iterator { return this->linear_cbegin(); } template inline auto xstrided_view::linear_end() const -> const_linear_iterator { return this->linear_cend(); } template inline auto xstrided_view::linear_cbegin() const -> const_linear_iterator { return this->storage().cbegin() + static_cast(data_offset()); } template inline auto xstrided_view::linear_cend() const -> const_linear_iterator { return this->storage().cbegin() + static_cast(data_offset() + size()); } template inline auto xstrided_view::linear_rbegin() -> reverse_linear_iterator { return reverse_linear_iterator(this->linear_begin()); } template inline auto xstrided_view::linear_rend() -> reverse_linear_iterator { return reverse_linear_iterator(this->linear_end()); } template inline auto xstrided_view::linear_rbegin() const -> const_reverse_linear_iterator { return this->linear_crbegin(); } template inline auto xstrided_view::linear_rend() const -> const_reverse_linear_iterator { return this->linear_crend(); } template inline auto xstrided_view::linear_crbegin() const -> const_reverse_linear_iterator { return const_reverse_linear_iterator(this->linear_cbegin()); } template inline auto xstrided_view::linear_crend() const -> const_reverse_linear_iterator { return const_reverse_linear_iterator(this->linear_cend()); } /*************** * stepper api * ***************/ template template inline auto xstrided_view::stepper_begin(const ST& shape) -> disable_indexed_stepper_t { size_type offset = shape.size() - dimension(); return stepper(this, data_xbegin(), offset); } template template inline auto xstrided_view::stepper_end(const ST& shape, layout_type l) -> disable_indexed_stepper_t { size_type offset = shape.size() - dimension(); return stepper(this, data_xend(l, offset), offset); } template template inline auto xstrided_view::stepper_begin(const ST& shape) -> enable_indexed_stepper_t { size_type offset = shape.size() - dimension(); return stepper(this, offset); } template template inline auto xstrided_view::stepper_end(const ST& shape, layout_type /*l*/) -> enable_indexed_stepper_t { size_type offset = shape.size() - dimension(); return stepper(this, offset, true); } template template inline auto xstrided_view::stepper_begin(const ST& shape) const -> disable_indexed_stepper_t { size_type offset = shape.size() - dimension(); return const_stepper(this, data_xbegin(), offset); } template template inline auto xstrided_view::stepper_end(const ST& shape, layout_type l) const -> disable_indexed_stepper_t { size_type offset = shape.size() - dimension(); return const_stepper(this, data_xend(l, offset), offset); } template template inline auto xstrided_view::stepper_begin(const ST& shape) const -> enable_indexed_stepper_t { size_type offset = shape.size() - dimension(); return const_stepper(this, offset); } template template inline auto xstrided_view::stepper_end(const ST& shape, layout_type /*l*/) const -> enable_indexed_stepper_t { size_type offset = shape.size() - dimension(); return const_stepper(this, offset, true); } template template inline It xstrided_view::data_xbegin_impl(It begin) const noexcept { return begin + static_cast(this->data_offset()); } template template inline It xstrided_view::data_xend_impl(It begin, layout_type l, size_type offset) const noexcept { return strided_data_end(*this, begin + std::ptrdiff_t(this->data_offset()), l, offset); } template inline auto xstrided_view::data_xbegin() noexcept -> container_iterator { return data_xbegin_impl(this->storage().begin()); } template inline auto xstrided_view::data_xbegin() const noexcept -> const_container_iterator { return data_xbegin_impl(this->storage().cbegin()); } template inline auto xstrided_view::data_xend(layout_type l, size_type offset) noexcept -> container_iterator { return data_xend_impl(this->storage().begin(), l, offset); } template inline auto xstrided_view::data_xend(layout_type l, size_type offset) const noexcept -> const_container_iterator { return data_xend_impl(this->storage().cbegin(), l, offset); } template template inline auto xstrided_view::store_simd(size_type i, const simd& e) -> enable_simd_interface { using align_mode = driven_align_mode_t; xt_simd::store_as(&(storage()[i]), e, align_mode()); } template template inline auto xstrided_view::load_simd(size_type i) const -> enable_simd_interface> { using align_mode = driven_align_mode_t; return xt_simd::load_as(&(storage()[i]), align_mode()); } template template inline auto xstrided_view::build_view(E&& e) const -> rebind_t { inner_shape_type sh(this->shape()); inner_strides_type str(this->strides()); return rebind_t( std::forward(e), std::move(sh), std::move(str), base_type::data_offset(), this->layout() ); } /***************************************** * xstrided_view builders implementation * *****************************************/ /** * Construct a strided view from an xexpression, shape, strides and offset. * * @param e xexpression * @param shape the shape of the view * @param strides the new strides of the view * @param offset the offset of the first element in the underlying container * @param layout the new layout of the expression * * @tparam L the static layout type of the view (default: dynamic) * @tparam E type of xexpression * @tparam S strides type * @tparam X strides type * * @return the view */ template inline auto strided_view(E&& e, S&& shape, X&& strides, std::size_t offset, layout_type layout) noexcept { using view_type = xstrided_view, S, L>; return view_type(std::forward(e), std::forward(shape), std::forward(strides), offset, layout); } namespace detail { struct no_adj_strides_policy { protected: inline void resize(std::size_t) { } inline void set_fake_slice(std::size_t) { } template bool fill_args( const xstrided_slice_vector& /*slices*/, std::size_t /*sl_idx*/, std::size_t /*i*/, std::size_t /*old_shape*/, const ST& /*old_stride*/, S& /*shape*/, get_strides_t& /*strides*/ ) { return false; } }; } /** * Function to create a dynamic view from * an xexpression and an xstrided_slice_vector. * * @param e xexpression * @param slices the slice vector * * @return initialized strided_view according to slices * * @code{.cpp} * xt::xarray a = {{1, 2, 3}, {4, 5, 6}}; * xt::xstrided_slice_vector sv({xt::range(0, 1)}); * sv.push_back(xt::range(0, 3, 2)); * auto v = xt::strided_view(a, sv); * // ==> {{1, 3}} * @endcode * * You can also achieve the same with the following short-hand syntax: * * @code{.cpp} * xt::xarray a = {{1, 2, 3}, {4, 5, 6}}; * auto v = xt::strided_view(a, {xt::range(0, 1), xt::range(0, 3, 2)}); * // ==> {{1, 3}} * @endcode */ template inline auto strided_view(E&& e, const xstrided_slice_vector& slices) { detail::strided_view_args args; args.fill_args( e.shape(), detail::get_strides(e), detail::get_offset(e), e.layout(), slices ); using view_type = xstrided_view, decltype(args.new_shape)>; return view_type( std::forward(e), std::move(args.new_shape), std::move(args.new_strides), args.new_offset, args.new_layout ); } namespace detail { template struct rebind_shape; template struct rebind_shape> { using type = xt::fixed_shape; }; template struct rebind_shape { using type = rebind_container_t; }; template < class S, std::enable_if_t::type>>::value, bool> = true> inline void recalculate_shape_impl(S& shape, size_t size) { using value_type = get_value_type_t>; XTENSOR_ASSERT(std::count(shape.cbegin(), shape.cend(), -1) <= 1); auto iter = std::find(shape.begin(), shape.end(), -1); if (iter != std::end(shape)) { const auto total = std::accumulate(shape.cbegin(), shape.cend(), -1, std::multiplies{}); const auto missing_dimension = size / total; (*iter) = static_cast(missing_dimension); } } template < class S, std::enable_if_t::type>>::value, bool> = true> inline void recalculate_shape_impl(S&, size_t) { } template inline auto recalculate_shape(S&& shape, size_t size) { return recalculate_shape_impl(shape, size); } } template inline auto reshape_view(E&& e, S&& shape) { static_assert( L == layout_type::row_major || L == layout_type::column_major, "traversal has to be row or column major" ); using shape_type = std::decay_t; using unsigned_shape_type = typename detail::rebind_shape::type; get_strides_t strides; detail::recalculate_shape(shape, e.size()); xt::resize_container(strides, shape.size()); compute_strides(shape, L, strides); constexpr auto computed_layout = std::decay_t::static_layout == L ? L : layout_type::dynamic; using view_type = xstrided_view< xclosure_t, unsigned_shape_type, computed_layout, detail::flat_adaptor_getter, L>>; return view_type( std::forward(e), xtl::forward_sequence(shape), std::move(strides), 0, e.layout() ); } /** * @deprecated * @brief Return a view on a container with a new shape * * Note: if you resize the underlying container, this view becomes * invalidated. * * @param e xexpression to reshape * @param shape new shape * @param order traversal order (optional) * * @return view on xexpression with new shape */ template inline auto reshape_view(E&& e, S&& shape, layout_type /*order*/) { return reshape_view(std::forward(e), std::forward(shape)); } template inline auto reshape_view(E&& e, const I (&shape)[N], layout_type order) { using shape_type = std::array; return reshape_view(std::forward(e), xtl::forward_sequence(shape), order); } template inline auto reshape_view(E&& e, const I (&shape)[N]) { using shape_type = std::array; return reshape_view(std::forward(e), xtl::forward_sequence(shape)); } } #endif