/*************************************************************************** * 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_FIXED_HPP #define XTENSOR_FIXED_HPP #include #include #include #include #include #include #include "xcontainer.hpp" #include "xsemantic.hpp" #include "xstorage.hpp" #include "xstrides.hpp" #include "xtensor_config.hpp" namespace xtl { namespace detail { template struct sequence_builder> { using sequence_type = xt::const_array; using value_type = typename sequence_type::value_type; using size_type = typename sequence_type::size_type; inline static sequence_type make(size_type /*size*/, value_type /*v*/) { return sequence_type(); } }; } } namespace xt { /********************** * xfixed declaration * **********************/ template class xfixed_container; namespace detail { /************************************************************************************** The following is something we can currently only dream about -- for when we drop support for a lot of the old compilers (e.g. GCC 4.9, MSVC 2017 ;) template constexpr std::size_t calculate_stride(T& shape, std::size_t idx, layout_type L) { if (shape[idx] == 1) { return std::size_t(0); } std::size_t data_size = 1; std::size_t stride = 1; if (L == layout_type::row_major) { // because we have a integer sequence that counts // from 0 to sz - 1, we need to "invert" idx here idx = shape.size() - idx; for (std::size_t i = idx; i != 0; --i) { stride = data_size; data_size = stride * shape[i - 1]; } } else { for (std::size_t i = 0; i < idx + 1; ++i) { stride = data_size; data_size = stride * shape[i]; } } return stride; } *****************************************************************************************/ template struct calculate_stride; template struct calculate_stride { static constexpr std::ptrdiff_t value = Y * calculate_stride::value; }; template struct calculate_stride { static constexpr std::ptrdiff_t value = 1; }; template struct calculate_stride_row_major { static constexpr std::ptrdiff_t value = at::value * calculate_stride_row_major::value; }; template struct calculate_stride_row_major<0, X...> { static constexpr std::ptrdiff_t value = 1; }; template struct calculate_stride { static constexpr std::ptrdiff_t value = calculate_stride_row_major::value; }; namespace workaround { template struct computed_strides; template struct computed_strides> { static constexpr std::ptrdiff_t value = calculate_stride::value; }; template constexpr std::ptrdiff_t get_computed_strides(bool cond) { return cond ? 0 : computed_strides::value; } } template constexpr R get_strides_impl(const xt::fixed_shape& shape, std::index_sequence) { static_assert( (L == layout_type::row_major) || (L == layout_type::column_major), "Layout not supported for fixed array" ); #if (_MSC_VER >= 1910) using temp_type = std::index_sequence; return R({workaround::get_computed_strides(shape[I] == 1)...}); #else return R({shape[I] == 1 ? 0 : calculate_stride::value...}); #endif } template constexpr T get_backstrides_impl(const S& shape, const T& strides, std::index_sequence) { return T({(strides[I] * std::ptrdiff_t(shape[I] - 1))...}); } template struct fixed_compute_size_impl; template struct fixed_compute_size_impl { static constexpr std::size_t value = Y * fixed_compute_size_impl::value; }; template struct fixed_compute_size_impl { static constexpr std::size_t value = X; }; template <> struct fixed_compute_size_impl<> { // support for 0D xtensor fixed (empty shape = xshape<>) static constexpr std::size_t value = 1; }; // TODO unify with constexpr compute_size when dropping MSVC 2015 template struct fixed_compute_size; template struct fixed_compute_size> { static constexpr std::size_t value = fixed_compute_size_impl::value; }; template struct get_init_type_impl; template struct get_init_type_impl { using type = V[Y]; }; template struct get_init_type_impl { using type = V[1]; }; template struct get_init_type_impl { using tmp_type = typename get_init_type_impl::type; using type = tmp_type[Y]; }; } template constexpr R get_strides(const fixed_shape& shape) noexcept { return detail::get_strides_impl(shape, std::make_index_sequence{}); } template constexpr T get_backstrides(const S& shape, const T& strides) noexcept { return detail::get_backstrides_impl(shape, strides, std::make_index_sequence::value>{}); } template struct get_init_type; template struct get_init_type> { using type = typename detail::get_init_type_impl::type; }; template using get_init_type_t = typename get_init_type::type; template struct xcontainer_inner_types> { using shape_type = S; using inner_shape_type = typename S::cast_type; using strides_type = get_strides_t; using inner_strides_type = strides_type; using backstrides_type = inner_strides_type; using inner_backstrides_type = backstrides_type; // NOTE: 0D (S::size() == 0) results in storage for 1 element (scalar) #if defined(_MSC_VER) && _MSC_VER < 1910 && !defined(_WIN64) // WORKAROUND FOR MSVC 2015 32 bit, fallback to unaligned container for 0D scalar case using storage_type = std::array::value>; #else using storage_type = aligned_array::value>; #endif using reference = typename storage_type::reference; using const_reference = typename storage_type::const_reference; using size_type = typename storage_type::size_type; using temporary_type = xfixed_container; static constexpr layout_type layout = L; }; template struct xiterable_inner_types> : xcontainer_iterable_types> { }; /** * @class xfixed_container * @brief Dense multidimensional container with tensor semantic and fixed * dimension. * * The xfixed_container class implements a dense multidimensional container * with tensor semantic and fixed dimension * * @tparam ET The type of the elements. * @tparam S The xshape template paramter of the container. * @tparam L The layout_type of the tensor. * @tparam SH Wether the tensor can be used as a shared expression. * @tparam Tag The expression tag. * @sa xtensor_fixed */ template class xfixed_container : public xcontainer>, public xcontainer_semantic> { public: using self_type = xfixed_container; using base_type = xcontainer; using semantic_base = xcontainer_semantic; using storage_type = typename base_type::storage_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 N = std::tuple_size::value; static constexpr std::size_t rank = N; xfixed_container() = default; xfixed_container(const value_type& v); explicit xfixed_container(const inner_shape_type& shape, layout_type l = L); explicit xfixed_container(const inner_shape_type& shape, value_type v, layout_type l = L); // remove this enable_if when removing the other value_type constructor template , class EN = std::enable_if_t> xfixed_container(nested_initializer_list_t t); ~xfixed_container() = default; xfixed_container(const xfixed_container&) = default; xfixed_container& operator=(const xfixed_container&) = default; xfixed_container(xfixed_container&&) = default; xfixed_container& operator=(xfixed_container&&) = default; template xfixed_container(const xexpression& e); template xfixed_container& operator=(const xexpression& e); template > static xfixed_container from_shape(ST&& /*s*/); template > void resize(ST&& shape, bool force = false) const; template void resize(ST&& shape, layout_type l) const; template void resize(ST&& shape, const strides_type& strides) const; template > const auto& reshape(ST&& shape, layout_type layout = L) const; template bool broadcast_shape(ST& s, bool reuse_cache = false) const; constexpr layout_type layout() const noexcept; bool is_contiguous() const noexcept; private: storage_type m_storage; XTENSOR_CONSTEXPR_ENHANCED_STATIC inner_shape_type m_shape = S(); XTENSOR_CONSTEXPR_ENHANCED_STATIC inner_strides_type m_strides = get_strides(S()); XTENSOR_CONSTEXPR_ENHANCED_STATIC inner_backstrides_type m_backstrides = get_backstrides(m_shape, m_strides); storage_type& storage_impl() noexcept; const storage_type& storage_impl() const noexcept; XTENSOR_CONSTEXPR_RETURN const inner_shape_type& shape_impl() const noexcept; XTENSOR_CONSTEXPR_RETURN const inner_strides_type& strides_impl() const noexcept; XTENSOR_CONSTEXPR_RETURN const inner_backstrides_type& backstrides_impl() const noexcept; friend class xcontainer>; }; #ifdef XTENSOR_HAS_CONSTEXPR_ENHANCED // Out of line definitions to prevent linker errors prior to C++17 template constexpr typename xfixed_container::inner_shape_type xfixed_container::m_shape; template constexpr typename xfixed_container::inner_strides_type xfixed_container::m_strides; template constexpr typename xfixed_container::inner_backstrides_type xfixed_container::m_backstrides; #endif /**************************************** * xfixed_container_adaptor declaration * ****************************************/ template class xfixed_adaptor; template struct xcontainer_inner_types> { using storage_type = std::remove_reference_t; using reference = typename storage_type::reference; using const_reference = typename storage_type::const_reference; using size_type = typename storage_type::size_type; using shape_type = S; using inner_shape_type = typename S::cast_type; using strides_type = get_strides_t; using backstrides_type = strides_type; using inner_strides_type = strides_type; using inner_backstrides_type = backstrides_type; using temporary_type = xfixed_container; static constexpr layout_type layout = L; }; template struct xiterable_inner_types> : xcontainer_iterable_types> { }; /** * @class xfixed_adaptor * @brief Dense multidimensional container adaptor with tensor semantic * and fixed dimension. * * The xfixed_adaptor class implements a dense multidimensional * container adaptor with tensor semantic 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 S The xshape template parameter for the fixed shape of the adaptor * @tparam L The layout_type of the adaptor. * @tparam SH Wether the adaptor can be used as a shared expression. * @tparam Tag The expression tag. */ template class xfixed_adaptor : public xcontainer>, public xcontainer_semantic> { public: using container_closure_type = EC; using self_type = xfixed_adaptor; using base_type = xcontainer; using semantic_base = xcontainer_semantic; using storage_type = typename base_type::storage_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 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 temporary_type = typename semantic_base::temporary_type; using expression_tag = Tag; static constexpr std::size_t N = S::size(); xfixed_adaptor(storage_type&& data); xfixed_adaptor(const storage_type& data); template xfixed_adaptor(D&& data); ~xfixed_adaptor() = default; xfixed_adaptor(const xfixed_adaptor&) = default; xfixed_adaptor& operator=(const xfixed_adaptor&); xfixed_adaptor(xfixed_adaptor&&) = default; xfixed_adaptor& operator=(xfixed_adaptor&&); xfixed_adaptor& operator=(temporary_type&&); template xfixed_adaptor& operator=(const xexpression& e); template > void resize(ST&& shape, bool force = false) const; template void resize(ST&& shape, layout_type l) const; template void resize(ST&& shape, const strides_type& strides) const; template > const auto& reshape(ST&& shape, layout_type layout = L) const; template bool broadcast_shape(ST& s, bool reuse_cache = false) const; constexpr layout_type layout() const noexcept; bool is_contiguous() const noexcept; private: container_closure_type m_storage; XTENSOR_CONSTEXPR_ENHANCED_STATIC inner_shape_type m_shape = S(); XTENSOR_CONSTEXPR_ENHANCED_STATIC inner_strides_type m_strides = get_strides(S()); XTENSOR_CONSTEXPR_ENHANCED_STATIC inner_backstrides_type m_backstrides = get_backstrides(m_shape, m_strides); storage_type& storage_impl() noexcept; const storage_type& storage_impl() const noexcept; XTENSOR_CONSTEXPR_RETURN const inner_shape_type& shape_impl() const noexcept; XTENSOR_CONSTEXPR_RETURN const inner_strides_type& strides_impl() const noexcept; XTENSOR_CONSTEXPR_RETURN const inner_backstrides_type& backstrides_impl() const noexcept; friend class xcontainer>; }; #ifdef XTENSOR_HAS_CONSTEXPR_ENHANCED // Out of line definitions to prevent linker errors prior to C++17 template constexpr typename xfixed_adaptor::inner_shape_type xfixed_adaptor::m_shape; template constexpr typename xfixed_adaptor::inner_strides_type xfixed_adaptor::m_strides; template constexpr typename xfixed_adaptor::inner_backstrides_type xfixed_adaptor::m_backstrides; #endif /************************************ * xfixed_container implementation * ************************************/ /** * @name Constructors */ //@{ /** * Create an uninitialized xfixed_container. * Note this function is only provided for homogeneity, and the shape & layout argument is * disregarded (the template shape is always used). * * @param shape the shape of the xfixed_container (unused!) * @param l the layout_type of the xfixed_container (unused!) */ template inline xfixed_container::xfixed_container(const inner_shape_type& shape, layout_type l) { (void) (shape); (void) (l); XTENSOR_ASSERT(shape.size() == N && std::equal(shape.begin(), shape.end(), m_shape.begin())); XTENSOR_ASSERT(L == l); } template inline xfixed_container::xfixed_container(const value_type& v) { if (this->size() != 1) { XTENSOR_THROW(std::runtime_error, "wrong shape for scalar assignment (has to be xshape<>)."); } m_storage[0] = v; } /** * Create an xfixed_container, and initialize with the value of v. * Note, the shape argument to this function is only provided for homogeneity, * and the shape argument is disregarded (the template shape is always used). * * @param shape the shape of the xfixed_container (unused!) * @param v the fill value * @param l the layout_type of the xfixed_container (unused!) */ template inline xfixed_container::xfixed_container( const inner_shape_type& shape, value_type v, layout_type l ) { (void) (shape); (void) (l); XTENSOR_ASSERT(shape.size() == N && std::equal(shape.begin(), shape.end(), m_shape.begin())); XTENSOR_ASSERT(L == l); std::fill(m_storage.begin(), m_storage.end(), v); } namespace detail { template struct check_initializer_list_shape { template static bool run(const T& t, const S& shape) { std::size_t IX = shape.size() - X; bool result = (shape[IX] == t.size()); for (std::size_t i = 0; i < shape[IX]; ++i) { result = result && check_initializer_list_shape::run(t.begin()[i], shape); } return result; } }; template <> struct check_initializer_list_shape<0> { template static bool run(const T& /*t*/, const S& /*shape*/) { return true; } }; } template template inline xfixed_container xfixed_container::from_shape(ST&& shape) { (void) shape; self_type tmp; XTENSOR_ASSERT(shape.size() == N && std::equal(shape.begin(), shape.end(), tmp.shape().begin())); return tmp; } /** * Allocates an xfixed_container with shape S with values from a C array. * The type returned by get_init_type_t is raw C array ``value_type[X][Y][Z]`` for * ``xt::xshape``. C arrays can be initialized with the initializer list syntax, * but the size is checked at compile time to prevent errors. * Note: for clang < 3.8 this is an initializer_list and the size is not checked at compile-or runtime. */ template template inline xfixed_container::xfixed_container(nested_initializer_list_t t) { XTENSOR_ASSERT_MSG( detail::check_initializer_list_shape::run(t, this->shape()) == true, "initializer list shape does not match fixed shape" ); constexpr auto tmp = layout_type::row_major; L == tmp ? nested_copy(m_storage.begin(), t) : nested_copy(this->template begin(), t); } //@} /** * @name Extended copy semantic */ //@{ /** * The extended copy constructor. */ template template inline xfixed_container::xfixed_container(const xexpression& e) { semantic_base::assign(e); } /** * The extended assignment operator. */ template template inline auto xfixed_container::operator=(const xexpression& e) -> self_type& { return semantic_base::operator=(e); } //@} /** * Note that the xfixed_container **cannot** be resized. Attempting to resize with a different * size throws an assert in debug mode. */ template template inline void xfixed_container::resize(ST&& shape, bool) const { (void) (shape); // remove unused parameter warning if XTENSOR_ASSERT undefined XTENSOR_ASSERT(std::equal(shape.begin(), shape.end(), m_shape.begin()) && shape.size() == m_shape.size()); } /** * Note that the xfixed_container **cannot** be resized. Attempting to resize with a different * size throws an assert in debug mode. */ template template inline void xfixed_container::resize(ST&& shape, layout_type l) const { (void) (shape); // remove unused parameter warning if XTENSOR_ASSERT undefined (void) (l); XTENSOR_ASSERT( std::equal(shape.begin(), shape.end(), m_shape.begin()) && shape.size() == m_shape.size() && L == l ); } /** * Note that the xfixed_container **cannot** be resized. Attempting to resize with a different * size throws an assert in debug mode. */ template template inline void xfixed_container::resize(ST&& shape, const strides_type& strides) const { (void) (shape); // remove unused parameter warning if XTENSOR_ASSERT undefined (void) (strides); XTENSOR_ASSERT(std::equal(shape.begin(), shape.end(), m_shape.begin()) && shape.size() == m_shape.size()); XTENSOR_ASSERT( std::equal(strides.begin(), strides.end(), m_strides.begin()) && strides.size() == m_strides.size() ); } /** * Note that the xfixed_container **cannot** be reshaped to a shape different from ``S``. */ template template inline const auto& xfixed_container::reshape(ST&& shape, layout_type layout) const { if (!(std::equal(shape.begin(), shape.end(), m_shape.begin()) && shape.size() == m_shape.size() && layout == L)) { XTENSOR_THROW(std::runtime_error, "Trying to reshape xtensor_fixed with different shape or layout."); } return *this; } template template inline bool xfixed_container::broadcast_shape(ST& shape, bool) const { return xt::broadcast_shape(m_shape, shape); } template constexpr layout_type xfixed_container::layout() const noexcept { return base_type::static_layout; } template inline bool xfixed_container::is_contiguous() const noexcept { using str_type = typename inner_strides_type::value_type; return m_strides.empty() || (layout() == layout_type::row_major && m_strides.back() == str_type(1)) || (layout() == layout_type::column_major && m_strides.front() == str_type(1)); } template inline auto xfixed_container::storage_impl() noexcept -> storage_type& { return m_storage; } template inline auto xfixed_container::storage_impl() const noexcept -> const storage_type& { return m_storage; } template XTENSOR_CONSTEXPR_RETURN auto xfixed_container::shape_impl() const noexcept -> const inner_shape_type& { return m_shape; } template XTENSOR_CONSTEXPR_RETURN auto xfixed_container::strides_impl() const noexcept -> const inner_strides_type& { return m_strides; } template XTENSOR_CONSTEXPR_RETURN auto xfixed_container::backstrides_impl() const noexcept -> const inner_backstrides_type& { return m_backstrides; } /******************* * xfixed_adaptor * *******************/ /** * @name Constructors */ //@{ /** * Constructs an xfixed_adaptor of the given stl-like container. * @param data the container to adapt */ template inline xfixed_adaptor::xfixed_adaptor(storage_type&& data) : base_type() , m_storage(std::move(data)) { } /** * Constructs an xfixed_adaptor of the given stl-like container. * @param data the container to adapt */ template inline xfixed_adaptor::xfixed_adaptor(const storage_type& data) : base_type() , m_storage(data) { } /** * Constructs an xfixed_adaptor of the given stl-like container, * with the specified shape and layout_type. * @param data the container to adapt */ template template inline xfixed_adaptor::xfixed_adaptor(D&& data) : base_type() , m_storage(std::forward(data)) { } //@} template inline auto xfixed_adaptor::operator=(const xfixed_adaptor& rhs) -> self_type& { base_type::operator=(rhs); m_storage = rhs.m_storage; return *this; } template inline auto xfixed_adaptor::operator=(xfixed_adaptor&& rhs) -> self_type& { base_type::operator=(std::move(rhs)); m_storage = rhs.m_storage; return *this; } template inline auto xfixed_adaptor::operator=(temporary_type&& rhs) -> self_type& { m_storage.resize(rhs.storage().size()); std::copy(rhs.storage().cbegin(), rhs.storage().cend(), m_storage.begin()); return *this; } /** * @name Extended copy semantic */ //@{ /** * The extended assignment operator. */ template template inline auto xfixed_adaptor::operator=(const xexpression& e) -> self_type& { return semantic_base::operator=(e); } //@} /** * Note that the xfixed_adaptor **cannot** be resized. Attempting to resize with a different * size throws an assert in debug mode. */ template template inline void xfixed_adaptor::resize(ST&& shape, bool) const { (void) (shape); // remove unused parameter warning if XTENSOR_ASSERT undefined XTENSOR_ASSERT(std::equal(shape.begin(), shape.end(), m_shape.begin()) && shape.size() == m_shape.size()); } /** * Note that the xfixed_adaptor **cannot** be resized. Attempting to resize with a different * size throws an assert in debug mode. */ template template inline void xfixed_adaptor::resize(ST&& shape, layout_type l) const { (void) (shape); // remove unused parameter warning if XTENSOR_ASSERT undefined (void) (l); XTENSOR_ASSERT( std::equal(shape.begin(), shape.end(), m_shape.begin()) && shape.size() == m_shape.size() && L == l ); } /** * Note that the xfixed_adaptor **cannot** be resized. Attempting to resize with a different * size throws an assert in debug mode. */ template template inline void xfixed_adaptor::resize(ST&& shape, const strides_type& strides) const { (void) (shape); // remove unused parameter warning if XTENSOR_ASSERT undefined (void) (strides); XTENSOR_ASSERT(std::equal(shape.begin(), shape.end(), m_shape.begin()) && shape.size() == m_shape.size()); XTENSOR_ASSERT( std::equal(strides.begin(), strides.end(), m_strides.begin()) && strides.size() == m_strides.size() ); } /** * Note that the xfixed_container **cannot** be reshaped to a shape different from ``S``. */ template template inline const auto& xfixed_adaptor::reshape(ST&& shape, layout_type layout) const { if (!(std::equal(shape.begin(), shape.end(), m_shape.begin()) && shape.size() == m_shape.size() && layout == L)) { XTENSOR_THROW(std::runtime_error, "Trying to reshape xtensor_fixed with different shape or layout."); } return *this; } template template inline bool xfixed_adaptor::broadcast_shape(ST& shape, bool) const { return xt::broadcast_shape(m_shape, shape); } template inline auto xfixed_adaptor::storage_impl() noexcept -> storage_type& { return m_storage; } template inline auto xfixed_adaptor::storage_impl() const noexcept -> const storage_type& { return m_storage; } template constexpr layout_type xfixed_adaptor::layout() const noexcept { return base_type::static_layout; } template inline bool xfixed_adaptor::is_contiguous() const noexcept { using str_type = typename inner_strides_type::value_type; return m_strides.empty() || (layout() == layout_type::row_major && m_strides.back() == str_type(1)) || (layout() == layout_type::column_major && m_strides.front() == str_type(1)); } template XTENSOR_CONSTEXPR_RETURN auto xfixed_adaptor::shape_impl() const noexcept -> const inner_shape_type& { return m_shape; } template XTENSOR_CONSTEXPR_RETURN auto xfixed_adaptor::strides_impl() const noexcept -> const inner_strides_type& { return m_strides; } template XTENSOR_CONSTEXPR_RETURN auto xfixed_adaptor::backstrides_impl() const noexcept -> const inner_backstrides_type& { return m_backstrides; } } #endif