/*************************************************************************** * 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. * ****************************************************************************/ /** * @brief standard mathematical functions for xexpressions */ #ifndef XTENSOR_BUILDER_HPP #define XTENSOR_BUILDER_HPP #include #include #include #include #include #include #include #include #include #include #include "xbroadcast.hpp" #include "xfunction.hpp" #include "xgenerator.hpp" #include "xoperation.hpp" namespace xt { /******** * ones * ********/ /** * Returns an \ref xexpression containing ones of the specified shape. * @tparam shape the shape of the returned expression. */ template inline auto ones(S shape) noexcept { return broadcast(T(1), std::forward(shape)); } template inline auto ones(const I (&shape)[L]) noexcept { return broadcast(T(1), shape); } /********* * zeros * *********/ /** * Returns an \ref xexpression containing zeros of the specified shape. * @tparam shape the shape of the returned expression. */ template inline auto zeros(S shape) noexcept { return broadcast(T(0), std::forward(shape)); } template inline auto zeros(const I (&shape)[L]) noexcept { return broadcast(T(0), shape); } /** * Create a xcontainer (xarray, xtensor or xtensor_fixed) with uninitialized values of * with value_type T and shape. Selects the best container match automatically * from the supplied shape. * * - ``std::vector`` → ``xarray`` * - ``std::array`` or ``initializer_list`` → ``xtensor`` * - ``xshape`` → ``xtensor_fixed>`` * * @param shape shape of the new xcontainer */ template inline xarray empty(const S& shape) { return xarray::from_shape(shape); } template inline xtensor empty(const std::array& shape) { using shape_type = typename xtensor::shape_type; return xtensor(xtl::forward_sequence(shape)); } template inline xtensor empty(const I (&shape)[N]) { using shape_type = typename xtensor::shape_type; return xtensor(xtl::forward_sequence(shape)); } template inline xtensor_fixed, L> empty(const fixed_shape& /*shape*/) { return xtensor_fixed, L>(); } /** * Create a xcontainer (xarray, xtensor or xtensor_fixed) with uninitialized values of * the same shape, value type and layout as the input xexpression *e*. * * @param e the xexpression from which to extract shape, value type and layout. */ template inline auto empty_like(const xexpression& e) { using xtype = temporary_type_t; auto res = xtype::from_shape(e.derived_cast().shape()); return res; } /** * Create a xcontainer (xarray, xtensor or xtensor_fixed), filled with *fill_value* and of * the same shape, value type and layout as the input xexpression *e*. * * @param e the xexpression from which to extract shape, value type and layout. * @param fill_value the value used to set each element of the returned xcontainer. */ template inline auto full_like(const xexpression& e, typename E::value_type fill_value) { using xtype = temporary_type_t; auto res = xtype::from_shape(e.derived_cast().shape()); res.fill(fill_value); return res; } /** * Create a xcontainer (xarray, xtensor or xtensor_fixed), filled with zeros and of * the same shape, value type and layout as the input xexpression *e*. * * Note: contrary to zeros(shape), this function returns a non-lazy, allocated container! * Use ``xt::zeros(e.shape());` for a lazy version. * * @param e the xexpression from which to extract shape, value type and layout. */ template inline auto zeros_like(const xexpression& e) { return full_like(e, typename E::value_type(0)); } /** * Create a xcontainer (xarray, xtensor or xtensor_fixed), filled with ones and of * the same shape, value type and layout as the input xexpression *e*. * * Note: contrary to ones(shape), this function returns a non-lazy, evaluated container! * Use ``xt::ones(e.shape());`` for a lazy version. * * @param e the xexpression from which to extract shape, value type and layout. */ template inline auto ones_like(const xexpression& e) { return full_like(e, typename E::value_type(1)); } namespace detail { template struct get_mult_type_impl { using type = T; }; template struct get_mult_type_impl> { using type = R; }; template using get_mult_type = typename get_mult_type_impl::type; // These methods should be private methods of arange_generator, however thi leads // to ICE on VS2015 template )> inline void arange_assign_to(xexpression& e, U start, U, X step, bool) noexcept { auto& de = e.derived_cast(); U value = start; for (auto&& el : de.storage()) { el = static_cast(value); value += step; } } template >)> inline void arange_assign_to(xexpression& e, U start, U stop, X step, bool endpoint) noexcept { auto& buf = e.derived_cast().storage(); using size_type = decltype(buf.size()); using mult_type = get_mult_type; size_type num = buf.size(); for (size_type i = 0; i < num; ++i) { buf[i] = static_cast(start + step * mult_type(i)); } if (endpoint && num > 1) { buf[num - 1] = static_cast(stop); } } template class arange_generator { public: using value_type = R; using step_type = S; arange_generator(T start, T stop, S step, size_t num_steps, bool endpoint = false) : m_start(start) , m_stop(stop) , m_step(step) , m_num_steps(num_steps) , m_endpoint(endpoint) { } template inline R operator()(Args... args) const { return access_impl(args...); } template inline R element(It first, It) const { return access_impl(*first); } template inline void assign_to(xexpression& e) const noexcept { arange_assign_to(e, m_start, m_stop, m_step, m_endpoint); } private: T m_start; T m_stop; step_type m_step; size_t m_num_steps; bool m_endpoint; // true for setting the last element to m_stop template inline R access_impl(T1 t, Args...) const { if (m_endpoint && m_num_steps > 1 && size_t(t) == m_num_steps - 1) { return static_cast(m_stop); } // Avoids warning when T = char (because char + char => int!) using mult_type = get_mult_type; return static_cast(m_start + m_step * mult_type(t)); } inline R access_impl() const { return static_cast(m_start); } }; template using both_integer = xtl::conjunction, xtl::is_integral>; template using integer_with_signed_integer = xtl::conjunction, xtl::is_signed>; template using integer_with_unsigned_integer = xtl::conjunction, std::is_unsigned>; template >)> inline auto arange_impl(T start, T stop, S step = 1) noexcept { std::size_t shape = static_cast(std::ceil((stop - start) / step)); return detail::make_xgenerator(detail::arange_generator(start, stop, step, shape), {shape}); } template )> inline auto arange_impl(T start, T stop, S step = 1) noexcept { bool empty_cond = (stop - start) / step <= 0; std::size_t shape = 0; if (!empty_cond) { shape = stop > start ? static_cast((stop - start + step - S(1)) / step) : static_cast((start - stop - step - S(1)) / -step); } return detail::make_xgenerator(detail::arange_generator(start, stop, step, shape), {shape}); } template )> inline auto arange_impl(T start, T stop, S step = 1) noexcept { bool empty_cond = stop <= start; std::size_t shape = 0; if (!empty_cond) { shape = static_cast((stop - start + step - S(1)) / step); } return detail::make_xgenerator(detail::arange_generator(start, stop, step, shape), {shape}); } template class fn_impl { public: using value_type = typename F::value_type; using size_type = std::size_t; fn_impl(F&& f) : m_ft(f) { } inline value_type operator()() const { size_type idx[1] = {0ul}; return access_impl(std::begin(idx), std::end(idx)); } template inline value_type operator()(Args... args) const { size_type idx[sizeof...(Args)] = {static_cast(args)...}; return access_impl(std::begin(idx), std::end(idx)); } template inline value_type element(It first, It last) const { return access_impl(first, last); } private: F m_ft; template inline value_type access_impl(const It& begin, const It& end) const { return m_ft(begin, end); } }; template class eye_fn { public: using value_type = T; eye_fn(int k) : m_k(k) { } template inline T operator()(const It& /*begin*/, const It& end) const { using lvalue_type = typename std::iterator_traits::value_type; return *(end - 1) == *(end - 2) + static_cast(m_k) ? T(1) : T(0); } private: std::ptrdiff_t m_k; }; } /** * Generates an array with ones on the diagonal. * @param shape shape of the resulting expression * @param k index of the diagonal. 0 (default) refers to the main diagonal, * a positive value refers to an upper diagonal, and a negative * value to a lower diagonal. * @tparam T value_type of xexpression * @return xgenerator that generates the values on access */ template inline auto eye(const std::vector& shape, int k = 0) { return detail::make_xgenerator(detail::fn_impl>(detail::eye_fn(k)), shape); } /** * Generates a (n x n) array with ones on the diagonal. * @param n length of the diagonal. * @param k index of the diagonal. 0 (default) refers to the main diagonal, * a positive value refers to an upper diagonal, and a negative * value to a lower diagonal. * @tparam T value_type of xexpression * @return xgenerator that generates the values on access */ template inline auto eye(std::size_t n, int k = 0) { return eye({n, n}, k); } /** * Generates numbers evenly spaced within given half-open interval [start, stop). * @param start start of the interval * @param stop stop of the interval * @param step stepsize * @tparam T value_type of xexpression * @return xgenerator that generates the values on access */ template inline auto arange(T start, T stop, S step = 1) noexcept { return detail::arange_impl(start, stop, step); } /** * Generate numbers evenly spaced within given half-open interval [0, stop) * with a step size of 1. * @param stop stop of the interval * @tparam T value_type of xexpression * @return xgenerator that generates the values on access */ template inline auto arange(T stop) noexcept { return arange(T(0), stop, T(1)); } /** * Generates @a num_samples evenly spaced numbers over given interval * @param start start of interval * @param stop stop of interval * @param num_samples number of samples (defaults to 50) * @param endpoint if true, include endpoint (defaults to true) * @tparam T value_type of xexpression * @return xgenerator that generates the values on access */ template inline auto linspace(T start, T stop, std::size_t num_samples = 50, bool endpoint = true) noexcept { using fp_type = std::common_type_t; fp_type step = fp_type(stop - start) / std::fmax(fp_type(1), fp_type(num_samples - (endpoint ? 1 : 0))); return detail::make_xgenerator( detail::arange_generator(fp_type(start), fp_type(stop), step, num_samples, endpoint), {num_samples} ); } /** * Generates @a num_samples numbers evenly spaced on a log scale over given interval * @param start start of interval (pow(base, start) is the first value). * @param stop stop of interval (pow(base, stop) is the final value, except if endpoint = false) * @param num_samples number of samples (defaults to 50) * @param base the base of the log space. * @param endpoint if true, include endpoint (defaults to true) * @tparam T value_type of xexpression * @return xgenerator that generates the values on access */ template inline auto logspace(T start, T stop, std::size_t num_samples, T base = 10, bool endpoint = true) noexcept { return pow(std::move(base), linspace(start, stop, num_samples, endpoint)); } namespace detail { template class concatenate_access { public: using tuple_type = std::tuple; using size_type = std::size_t; using value_type = xtl::promote_type_t::value_type...>; template inline value_type access(const tuple_type& t, size_type axis, It first, It last) const { // trim off extra indices if provided to match behavior of containers auto dim_offset = std::distance(first, last) - std::get<0>(t).dimension(); size_t axis_dim = *(first + axis + dim_offset); auto match = [&](auto& arr) { if (axis_dim >= arr.shape()[axis]) { axis_dim -= arr.shape()[axis]; return false; } return true; }; auto get = [&](auto& arr) { size_t offset = 0; const size_t end = arr.dimension(); for (size_t i = 0; i < end; i++) { const auto& shape = arr.shape(); const size_t stride = std::accumulate( shape.begin() + i + 1, shape.end(), 1, std::multiplies() ); if (i == axis) { offset += axis_dim * stride; } else { const auto len = (*(first + i + dim_offset)); offset += len * stride; } } const auto element = arr.begin() + offset; return *element; }; size_type i = 0; for (; i < sizeof...(CT); ++i) { if (apply(i, match, t)) { break; } } return apply(i, get, t); } }; template class stack_access { public: using tuple_type = std::tuple; using size_type = std::size_t; using value_type = xtl::promote_type_t::value_type...>; template inline value_type access(const tuple_type& t, size_type axis, It first, It) const { auto get_item = [&](auto& arr) { size_t offset = 0; const size_t end = arr.dimension(); size_t after_axis = 0; for (size_t i = 0; i < end; i++) { if (i == axis) { after_axis = 1; } const auto& shape = arr.shape(); const size_t stride = std::accumulate( shape.begin() + i + 1, shape.end(), 1, std::multiplies() ); const auto len = (*(first + i + after_axis)); offset += len * stride; } const auto element = arr.begin() + offset; return *element; }; size_type i = *(first + axis); return apply(i, get_item, t); } }; template class vstack_access { public: using tuple_type = std::tuple; using size_type = std::size_t; using value_type = xtl::promote_type_t::value_type...>; template inline value_type access(const tuple_type& t, size_type axis, It first, It last) const { if (std::get<0>(t).dimension() == 1) { return stack.access(t, axis, first, last); } else { return concatonate.access(t, axis, first, last); } } private: concatenate_access concatonate; stack_access stack; }; template