/*************************************************************************** * 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_TENSOR_HPP #define XTENSOR_TENSOR_HPP #include #include #include #include #include #include "xbuffer_adaptor.hpp" #include "xcontainer.hpp" #include "xsemantic.hpp" namespace xt { /*********************** * xtensor declaration * ***********************/ namespace extension { template struct xtensor_container_base; template struct xtensor_container_base { using type = xtensor_empty_base; }; template using xtensor_container_base_t = typename xtensor_container_base::type; } template struct xcontainer_inner_types> { using storage_type = EC; using reference = inner_reference_t; using const_reference = typename storage_type::const_reference; using size_type = typename storage_type::size_type; using shape_type = std::array; using strides_type = get_strides_t; using backstrides_type = get_strides_t; using inner_shape_type = shape_type; using inner_strides_type = strides_type; using inner_backstrides_type = backstrides_type; using temporary_type = xtensor_container; static constexpr layout_type layout = L; }; template struct xiterable_inner_types> : xcontainer_iterable_types> { }; /** * @class xtensor_container * @brief Dense multidimensional container with tensor semantic and fixed * dimension. * * The xtensor_container class implements a dense multidimensional container * with tensor semantics and fixed dimension * * @tparam EC The type of the container holding the elements. * @tparam N The dimension of the container. * @tparam L The layout_type of the tensor. * @tparam Tag The expression tag. * @sa xtensor, xstrided_container, xcontainer */ template class xtensor_container : public xstrided_container>, public xcontainer_semantic>, public extension::xtensor_container_base_t { public: using self_type = xtensor_container; using base_type = xstrided_container; using semantic_base = xcontainer_semantic; using extension_base = extension::xtensor_container_base_t; using storage_type = typename base_type::storage_type; using allocator_type = typename base_type::allocator_type; 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 shape_type = typename base_type::shape_type; using inner_shape_type = typename base_type::inner_shape_type; using strides_type = typename base_type::strides_type; using backstrides_type = typename base_type::backstrides_type; using inner_backstrides_type = typename base_type::inner_backstrides_type; using inner_strides_type = typename base_type::inner_strides_type; using temporary_type = typename semantic_base::temporary_type; using expression_tag = Tag; static constexpr std::size_t rank = N; xtensor_container(); xtensor_container(nested_initializer_list_t t); explicit xtensor_container(const shape_type& shape, layout_type l = L); explicit xtensor_container(const shape_type& shape, const_reference value, layout_type l = L); explicit xtensor_container(const shape_type& shape, const strides_type& strides); explicit xtensor_container(const shape_type& shape, const strides_type& strides, const_reference value); explicit xtensor_container(storage_type&& storage, inner_shape_type&& shape, inner_strides_type&& strides); template static xtensor_container from_shape(S&& s); ~xtensor_container() = default; xtensor_container(const xtensor_container&) = default; xtensor_container& operator=(const xtensor_container&) = default; xtensor_container(xtensor_container&&) = default; xtensor_container& operator=(xtensor_container&&) = default; template explicit xtensor_container(xarray_container&&); template xtensor_container& operator=(xarray_container&&); template xtensor_container(const xexpression& e); template xtensor_container& operator=(const xexpression& e); private: storage_type m_storage; storage_type& storage_impl() noexcept; const storage_type& storage_impl() const noexcept; friend class xcontainer>; }; /***************************************** * xtensor_container_adaptor declaration * *****************************************/ namespace extension { template struct xtensor_adaptor_base; template struct xtensor_adaptor_base { using type = xtensor_empty_base; }; template using xtensor_adaptor_base_t = typename xtensor_adaptor_base::type; } template struct xcontainer_inner_types> { using storage_type = std::remove_reference_t; using reference = inner_reference_t; using const_reference = typename storage_type::const_reference; using size_type = typename storage_type::size_type; using shape_type = std::array; using strides_type = get_strides_t; using backstrides_type = get_strides_t; using inner_shape_type = shape_type; using inner_strides_type = strides_type; using inner_backstrides_type = backstrides_type; using temporary_type = xtensor_container, N, L, Tag>; static constexpr layout_type layout = L; }; template struct xiterable_inner_types> : xcontainer_iterable_types> { }; /** * @class xtensor_adaptor * @brief Dense multidimensional container adaptor with tensor * semantics and fixed dimension. * * The xtensor_adaptor class implements a dense multidimensional * container adaptor with tensor semantics and fixed dimension. It * is used to provide a multidimensional container semantic and a * tensor semantic to stl-like containers. * * @tparam EC The closure for the container type to adapt. * @tparam N The dimension of the adaptor. * @tparam L The layout_type of the adaptor. * @tparam Tag The expression tag. * @sa xstrided_container, xcontainer */ template class xtensor_adaptor : public xstrided_container>, public xcontainer_semantic>, public extension::xtensor_adaptor_base_t { public: using container_closure_type = EC; using self_type = xtensor_adaptor; using base_type = xstrided_container; using semantic_base = xcontainer_semantic; using extension_base = extension::xtensor_adaptor_base_t; using storage_type = typename base_type::storage_type; using allocator_type = typename base_type::allocator_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 temporary_type = typename semantic_base::temporary_type; using expression_tag = Tag; static constexpr std::size_t rank = N; xtensor_adaptor(storage_type&& storage); xtensor_adaptor(const storage_type& storage); template xtensor_adaptor(D&& storage, const shape_type& shape, layout_type l = L); template xtensor_adaptor(D&& storage, const shape_type& shape, const strides_type& strides); ~xtensor_adaptor() = default; xtensor_adaptor(const xtensor_adaptor&) = default; xtensor_adaptor& operator=(const xtensor_adaptor&); xtensor_adaptor(xtensor_adaptor&&) = default; xtensor_adaptor& operator=(xtensor_adaptor&&); xtensor_adaptor& operator=(temporary_type&&); template xtensor_adaptor& operator=(const xexpression& e); template void reset_buffer(P&& pointer, S&& size); private: container_closure_type m_storage; storage_type& storage_impl() noexcept; const storage_type& storage_impl() const noexcept; friend class xcontainer>; }; /**************************** * xtensor_view declaration * ****************************/ template class xtensor_view; namespace extension { template struct xtensor_view_base; template struct xtensor_view_base { using type = xtensor_empty_base; }; template using xtensor_view_base_t = typename xtensor_view_base::type; } template struct xcontainer_inner_types> { using storage_type = std::remove_reference_t; using reference = inner_reference_t; using const_reference = typename storage_type::const_reference; using size_type = typename storage_type::size_type; using shape_type = std::array; using strides_type = get_strides_t; using backstrides_type = get_strides_t; using inner_shape_type = shape_type; using inner_strides_type = strides_type; using inner_backstrides_type = backstrides_type; using temporary_type = xtensor_container, N, L, Tag>; static constexpr layout_type layout = L; }; template struct xiterable_inner_types> : xcontainer_iterable_types> { }; /** * @class xtensor_view * @brief Dense multidimensional container adaptor with view * semantics and fixed dimension. * * The xtensor_view class implements a dense multidimensional * container adaptor with viewsemantics and fixed dimension. It * is used to provide a multidimensional container semantic and a * view semantic to stl-like containers. * * @tparam EC The closure for the container type to adapt. * @tparam N The dimension of the view. * @tparam L The layout_type of the view. * @tparam Tag The expression tag. * @sa xstrided_container, xcontainer */ template class xtensor_view : public xstrided_container>, public xview_semantic>, public extension::xtensor_view_base_t { public: using container_closure_type = EC; using self_type = xtensor_view; using base_type = xstrided_container; using semantic_base = xview_semantic; using extension_base = extension::xtensor_adaptor_base_t; using storage_type = typename base_type::storage_type; using allocator_type = typename base_type::allocator_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 temporary_type = typename semantic_base::temporary_type; using expression_tag = Tag; xtensor_view(storage_type&& storage); xtensor_view(const storage_type& storage); template xtensor_view(D&& storage, const shape_type& shape, layout_type l = L); template xtensor_view(D&& storage, const shape_type& shape, const strides_type& strides); ~xtensor_view() = default; xtensor_view(const xtensor_view&) = default; xtensor_view& operator=(const xtensor_view&); xtensor_view(xtensor_view&&) = default; xtensor_view& operator=(xtensor_view&&); template self_type& operator=(const xexpression& e); template disable_xexpression& operator=(const E& e); private: container_closure_type m_storage; storage_type& storage_impl() noexcept; const storage_type& storage_impl() const noexcept; void assign_temporary_impl(temporary_type&& tmp); friend class xcontainer>; friend class xview_semantic>; }; namespace detail { template struct tensor_view_simd_helper { using valid_return_type = detail::has_simd_interface_impl; using valid_reference = std::is_lvalue_reference; static constexpr bool value = valid_return_type::value && valid_reference::value; using type = std::integral_constant; }; } // xtensor_view can be used on pseudo containers, i.e. containers // whose access operator does not return a reference. Since it // is not possible to take the address f a temporary, the load_simd // method implementation leads to a compilation error. template struct has_simd_interface> : detail::tensor_view_simd_helper>::type { }; /************************************ * xtensor_container implementation * ************************************/ /** * @name Constructors */ //@{ /** * Allocates an uninitialized xtensor_container that holds 0 elements. */ template inline xtensor_container::xtensor_container() : base_type() , m_storage(N == 0 ? 1 : 0, value_type()) { } /** * Allocates an xtensor_container with nested initializer lists. */ template inline xtensor_container::xtensor_container(nested_initializer_list_t t) : base_type() { base_type::resize(xt::shape(t), true); constexpr auto tmp = layout_type::row_major; L == tmp ? nested_copy(m_storage.begin(), t) : nested_copy(this->template begin(), t); } /** * Allocates an uninitialized xtensor_container with the specified shape and * layout_type. * @param shape the shape of the xtensor_container * @param l the layout_type of the xtensor_container */ template inline xtensor_container::xtensor_container(const shape_type& shape, layout_type l) : base_type() { base_type::resize(shape, l); } /** * Allocates an xtensor_container with the specified shape and layout_type. Elements * are initialized to the specified value. * @param shape the shape of the xtensor_container * @param value the value of the elements * @param l the layout_type of the xtensor_container */ template inline xtensor_container::xtensor_container( const shape_type& shape, const_reference value, layout_type l ) : base_type() { base_type::resize(shape, l); std::fill(m_storage.begin(), m_storage.end(), value); } /** * Allocates an uninitialized xtensor_container with the specified shape and strides. * @param shape the shape of the xtensor_container * @param strides the strides of the xtensor_container */ template inline xtensor_container::xtensor_container(const shape_type& shape, const strides_type& strides) : base_type() { base_type::resize(shape, strides); } /** * Allocates an uninitialized xtensor_container with the specified shape and strides. * Elements are initialized to the specified value. * @param shape the shape of the xtensor_container * @param strides the strides of the xtensor_container * @param value the value of the elements */ template inline xtensor_container::xtensor_container( const shape_type& shape, const strides_type& strides, const_reference value ) : base_type() { base_type::resize(shape, strides); std::fill(m_storage.begin(), m_storage.end(), value); } /** * Allocates an xtensor_container by moving specified data, shape and strides * * @param storage the data for the xtensor_container * @param shape the shape of the xtensor_container * @param strides the strides of the xtensor_container */ template inline xtensor_container::xtensor_container( storage_type&& storage, inner_shape_type&& shape, inner_strides_type&& strides ) : base_type(std::move(shape), std::move(strides)) , m_storage(std::move(storage)) { } template template inline xtensor_container::xtensor_container(xarray_container&& rhs) : base_type( xtl::forward_sequence(rhs.shape()), xtl::forward_sequence(rhs.strides()), xtl::forward_sequence(rhs.backstrides()), std::move(rhs.layout()) ) , m_storage(std::move(rhs.storage())) { } template template inline xtensor_container& xtensor_container::operator=(xarray_container&& rhs) { XTENSOR_ASSERT_MSG(N == rhs.dimension(), "Cannot change dimension of xtensor."); std::copy(rhs.shape().begin(), rhs.shape().end(), this->shape_impl().begin()); std::copy(rhs.strides().cbegin(), rhs.strides().cend(), this->strides_impl().begin()); std::copy(rhs.backstrides().cbegin(), rhs.backstrides().cend(), this->backstrides_impl().begin()); this->mutable_layout() = std::move(rhs.layout()); m_storage = std::move(std::move(rhs.storage())); return *this; } template template inline xtensor_container xtensor_container::from_shape(S&& s) { XTENSOR_ASSERT_MSG(s.size() == N, "Cannot change dimension of xtensor."); shape_type shape = xtl::forward_sequence(s); return self_type(shape); } //@} /** * @name Extended copy semantic */ //@{ /** * The extended copy constructor. */ template template inline xtensor_container::xtensor_container(const xexpression& e) : base_type() { XTENSOR_ASSERT_MSG(N == e.derived_cast().dimension(), "Cannot change dimension of xtensor."); // Avoids uninitialized data because of (m_shape == shape) condition // in resize (called by assign), which is always true when dimension() == 0. if (e.derived_cast().dimension() == 0) { detail::resize_data_container(m_storage, std::size_t(1)); } semantic_base::assign(e); } /** * The extended assignment operator. */ template template inline auto xtensor_container::operator=(const xexpression& e) -> self_type& { return semantic_base::operator=(e); } //@} template inline auto xtensor_container::storage_impl() noexcept -> storage_type& { return m_storage; } template inline auto xtensor_container::storage_impl() const noexcept -> const storage_type& { return m_storage; } /********************************** * xtensor_adaptor implementation * **********************************/ /** * @name Constructors */ //@{ /** * Constructs an xtensor_adaptor of the given stl-like container. * @param storage the container to adapt */ template inline xtensor_adaptor::xtensor_adaptor(storage_type&& storage) : base_type() , m_storage(std::move(storage)) { } /** * Constructs an xtensor_adaptor of the given stl-like container. * @param storage the container to adapt */ template inline xtensor_adaptor::xtensor_adaptor(const storage_type& storage) : base_type() , m_storage(storage) { } /** * Constructs an xtensor_adaptor of the given stl-like container, * with the specified shape and layout_type. * @param storage the container to adapt * @param shape the shape of the xtensor_adaptor * @param l the layout_type of the xtensor_adaptor */ template template inline xtensor_adaptor::xtensor_adaptor(D&& storage, const shape_type& shape, layout_type l) : base_type() , m_storage(std::forward(storage)) { base_type::resize(shape, l); } /** * Constructs an xtensor_adaptor of the given stl-like container, * with the specified shape and strides. * @param storage the container to adapt * @param shape the shape of the xtensor_adaptor * @param strides the strides of the xtensor_adaptor */ template template inline xtensor_adaptor::xtensor_adaptor( D&& storage, const shape_type& shape, const strides_type& strides ) : base_type() , m_storage(std::forward(storage)) { base_type::resize(shape, strides); } //@} template inline auto xtensor_adaptor::operator=(const xtensor_adaptor& rhs) -> self_type& { base_type::operator=(rhs); m_storage = rhs.m_storage; return *this; } template inline auto xtensor_adaptor::operator=(xtensor_adaptor&& rhs) -> self_type& { base_type::operator=(std::move(rhs)); m_storage = rhs.m_storage; return *this; } template inline auto xtensor_adaptor::operator=(temporary_type&& rhs) -> self_type& { base_type::shape_impl() = std::move(const_cast(rhs.shape())); base_type::strides_impl() = std::move(const_cast(rhs.strides())); base_type::backstrides_impl() = std::move(const_cast(rhs.backstrides())); m_storage = std::move(rhs.storage()); return *this; } /** * @name Extended copy semantic */ //@{ /** * The extended assignment operator. */ template template inline auto xtensor_adaptor::operator=(const xexpression& e) -> self_type& { return semantic_base::operator=(e); } //@} template inline auto xtensor_adaptor::storage_impl() noexcept -> storage_type& { return m_storage; } template inline auto xtensor_adaptor::storage_impl() const noexcept -> const storage_type& { return m_storage; } template template inline void xtensor_adaptor::reset_buffer(P&& pointer, S&& size) { return m_storage.reset_data(std::forward

(pointer), std::forward(size)); } /******************************* * xtensor_view implementation * *******************************/ /** * @name Constructors */ //@{ /** * Constructs an xtensor_view of the given stl-like container. * @param storage the container to adapt */ template inline xtensor_view::xtensor_view(storage_type&& storage) : base_type() , m_storage(std::move(storage)) { } /** * Constructs an xtensor_view of the given stl-like container. * @param storage the container to adapt */ template inline xtensor_view::xtensor_view(const storage_type& storage) : base_type() , m_storage(storage) { } /** * Constructs an xtensor_view of the given stl-like container, * with the specified shape and layout_type. * @param storage the container to adapt * @param shape the shape of the xtensor_view * @param l the layout_type of the xtensor_view */ template template inline xtensor_view::xtensor_view(D&& storage, const shape_type& shape, layout_type l) : base_type() , m_storage(std::forward(storage)) { base_type::resize(shape, l); } /** * Constructs an xtensor_view of the given stl-like container, * with the specified shape and strides. * @param storage the container to adapt * @param shape the shape of the xtensor_view * @param strides the strides of the xtensor_view */ template template inline xtensor_view::xtensor_view(D&& storage, const shape_type& shape, const strides_type& strides) : base_type() , m_storage(std::forward(storage)) { base_type::resize(shape, strides); } //@} template inline auto xtensor_view::operator=(const xtensor_view& rhs) -> self_type& { base_type::operator=(rhs); m_storage = rhs.m_storage; return *this; } template inline auto xtensor_view::operator=(xtensor_view&& rhs) -> self_type& { base_type::operator=(std::move(rhs)); m_storage = rhs.m_storage; return *this; } /** * @name Extended copy semantic */ //@{ /** * The extended assignment operator. */ template template inline auto xtensor_view::operator=(const xexpression& e) -> self_type& { return semantic_base::operator=(e); } //@} template template inline auto xtensor_view::operator=(const E& e) -> disable_xexpression& { std::fill(m_storage.begin(), m_storage.end(), e); return *this; } template inline auto xtensor_view::storage_impl() noexcept -> storage_type& { return m_storage; } template inline auto xtensor_view::storage_impl() const noexcept -> const storage_type& { return m_storage; } template inline void xtensor_view::assign_temporary_impl(temporary_type&& tmp) { std::copy(tmp.cbegin(), tmp.cend(), m_storage.begin()); } /** * Converts ``std::vector`` (returned e.g. from ``xt::argwhere``) to ``xtensor``. * * @param idx vector of indices * * @return ``xt::xtensor`` (e.g. ``xt::xtensor``) */ template inline auto from_indices(const std::vector& idx) { using return_type = xtensor; using size_type = typename return_type::size_type; if (idx.size() == 0) { return return_type::from_shape({size_type(0), size_type(0)}); } return_type out = return_type::from_shape({idx.size(), idx[0].size()}); for (size_type i = 0; i < out.shape()[0]; ++i) { for (size_type j = 0; j < out.shape()[1]; ++j) { out(i, j) = idx[i][j]; } } return out; } /** * Converts ``std::vector`` (returned e.g. from ``xt::argwhere``) to a flattened * ``xtensor``. * * @param idx a vector of indices * * @return ``xt::xtensor`` (e.g. ``xt::xtensor``) */ template inline auto flatten_indices(const std::vector& idx) { auto n = idx.size(); if (n != 0) { n *= idx[0].size(); } using return_type = xtensor; return_type out = return_type::from_shape({n}); auto iter = out.begin(); for_each( idx.begin(), idx.end(), [&iter](const auto& t) { iter = std::copy(t.cbegin(), t.cend(), iter); } ); return out; } struct ravel_vector_tag; struct ravel_tensor_tag; namespace detail { template struct ravel_return_type; template struct ravel_return_type { using index_type = typename C::value_type; using value_type = typename index_type::value_type; using type = std::vector; template static std::vector init(T n) { return std::vector(n); } }; template struct ravel_return_type { using index_type = typename C::value_type; using value_type = typename index_type::value_type; using type = xt::xtensor; template static xt::xtensor init(T n) { return xtensor::from_shape({n}); } }; } template using ravel_return_type_t = typename detail::ravel_return_type::type; /** * Converts ``std::vector`` (returned e.g. from ``xt::argwhere``) to ``xtensor`` * whereby the indices are ravelled. For 1-d input there is no conversion. * * @param idx vector of indices * @param shape the shape of the original array * @param l the layout type (row-major or column-major) * * @return ``xt::xtensor`` (e.g. ``xt::xtensor``) */ template ravel_return_type_t ravel_indices(const C& idx, const S& shape, layout_type l = layout_type::row_major) { using return_type = typename detail::ravel_return_type::type; using value_type = typename detail::ravel_return_type::value_type; using strides_type = get_strides_t; strides_type strides = xtl::make_sequence(shape.size(), 0); compute_strides(shape, l, strides); return_type out = detail::ravel_return_type::init(idx.size()); auto out_iter = out.begin(); auto idx_iter = idx.begin(); for (; out_iter != out.end(); ++out_iter, ++idx_iter) { *out_iter = element_offset(strides, (*idx_iter).cbegin(), (*idx_iter).cend()); } return out; } } #endif