Функции extents_to_array и array_to_extents для Boost.MultiArray в C ++

Это дополнительный вопрос для вспомогательной функции get_extents для Boost.MultiArray в C ++. Чтобы извлекать, обрабатывать и вычислять информацию о размерах в каждом измерении из Boost.MultiArray некоторыми лучшими способами. Кроме get_extents вспомогательная функция, я пытаюсь реализовать extents_to_array а также array_to_extents вспомогательные функции для преобразования boost::extents в std::array<std::size_t, NumDims>, и наоборот.

Экспериментальная реализация

Экспериментальная реализация extents_to_array а также array_to_extents вспомогательные функции приведены ниже.

template<std::size_t NumDims>
constexpr auto extents_to_array(const boost::detail::multi_array::extent_gen<NumDims>& input)
{
    std::array<std::size_t, NumDims> output;
    for (size_t rank_index = 0; rank_index < NumDims; rank_index++)
    {
        output[rank_index] = filled_multi_array(input, 0.0).shape()[rank_index];
    }
    return output;
}

template<std::size_t NumDims, std::size_t RankIndex = NumDims - 1>
constexpr auto array_to_extents(std::array<std::size_t, NumDims> new_size)
{
    if constexpr (RankIndex == 0)
    {
        return boost::extents[new_size[0]];
    }
    else
    {
        return get_extents(filled_multi_array(array_to_extents<NumDims, RankIndex - 1>(new_size), 0.0))[new_size[RankIndex]];
    }
}

template<class T, std::size_t NumDims>
constexpr auto print_size(const boost::multi_array<T, NumDims>& input)
{
    auto input_size = extents_to_array(get_extents(input));
    //  Checking shape
    for (size_t rank_index = 0; rank_index < NumDims; rank_index++)
    {
        std::cout << "input_size[" << rank_index << "]:" << input_size[rank_index] << std::endl;
    }
    return;
}

Тестовый пример и полный тестовый код

#include <algorithm>
#include <array>
#include <cassert>
#include <chrono>
#include <complex>
#include <concepts>
#include <deque>
#include <execution>
#include <exception>
#include <functional>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <mutex>
#include <numeric>
#include <optional>
#include <ranges>
#include <stdexcept>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>

#define USE_BOOST_MULTIDIMENSIONAL_ARRAY
#ifdef USE_BOOST_MULTIDIMENSIONAL_ARRAY
#include <boost/multi_array.hpp>
#include <boost/multi_array/algorithm.hpp>
#include <boost/multi_array/base.hpp>
#include <boost/multi_array/collection_concept.hpp>
#endif

template<typename T>
concept is_inserterable = requires(T x)
{
    std::inserter(x, std::ranges::end(x));
};

template<typename T>
concept is_minusable = requires(T x) { x - x; };

template<typename T1, typename T2>
concept is_minusable2 = requires(T1 x1, T2 x2) { x1 - x2; };


#ifdef USE_BOOST_MULTIDIMENSIONAL_ARRAY
template<typename T>
concept is_multi_array = requires(T x)
{
    x.num_dimensions();
    x.shape();
    boost::multi_array(x);
};

//  Add operator
template<is_multi_array T1, is_multi_array T2>
auto operator+(const T1& input1, const T2& input2)
{
    if (input1.num_dimensions() != input2.num_dimensions())     //  Dimensions are different, unable to perform element-wise add operation
    {
        throw std::logic_error("Array dimensions are different");
    }
    if (*input1.shape() != *input2.shape())                     //  Shapes are different, unable to perform element-wise add operation
    {
        throw std::logic_error("Array shapes are different");
    }
    boost::multi_array output(input1);
    for (decltype(+input1.shape()[0]) i{}; i != input1.shape()[0]; ++i)
    {
        output[i] = input1[i] + input2[i];
    }
    return output;
}

//  Minus operator
template<is_multi_array T1, is_multi_array T2>
auto operator-(const T1& input1, const T2& input2)
{
    if (input1.num_dimensions() != input2.num_dimensions())     //  Dimensions are different, unable to perform element-wise add operation
    {
        throw std::logic_error("Array dimensions are different");
    }
    if (*input1.shape() != *input2.shape())                     //  Shapes are different, unable to perform element-wise add operation
    {
        throw std::logic_error("Array shapes are different");
    }
    boost::multi_array output(input1);
    for (decltype(+input1.shape()[0]) i{}; i != input1.shape()[0]; ++i)
    {
        output[i] = input1[i] - input2[i];
    }
    return output;
}


//  Element Increment Operator
//  Define nonmember prefix increment operator
template<is_multi_array T>
auto operator++(T& input)
{
    input = recursive_transform(input, [](auto& x) {return x + 1; });
    return input;
}

//  Define nonmember postfix increment operator
template<is_multi_array T>
auto operator++(T& input, int i)
{
    auto output = input;
    input = recursive_transform(input, [](auto& x) {return x + 1; });
    return output;
}

//  Element Decrement Operator
//  Define nonmember prefix decrement operator
template<is_multi_array T>
auto operator--(T& input)
{
    input = recursive_transform(input, [](auto& x) {return x - 1; });
    return input;
}

//  Define nonmember postfix decrement operator
template<is_multi_array T>
auto operator--(T& input, int i)
{
    auto output = input;
    input = recursive_transform(input, [](auto& x) {return x - 1; });
    return output;
}


template<typename T>
concept is_multiplicable = requires(T x)
{
    x * x;
};

//  Multiplication
template<is_multiplicable T1, is_multiplicable T2>
constexpr auto element_wise_multiplication(const T1& input1, const T2& input2)
{
    return input1 * input2;
}

template<is_multi_array T1, is_multi_array T2>
constexpr auto element_wise_multiplication(const T1& input1, const T2& input2)
{
    if (input1.num_dimensions() != input2.num_dimensions())     //  Dimensions are different, unable to perform element-wise add operation
    {
        throw std::logic_error("Array dimensions are different");
    }
    if (*input1.shape() != *input2.shape())                     //  Shapes are different, unable to perform element-wise add operation
    {
        throw std::logic_error("Array shapes are different");
    }
    boost::multi_array output(input1);
    for (decltype(+input1.shape()[0]) i{}; i != input1.shape()[0]; ++i)
    {
        output[i] = element_wise_multiplication(input1[i], input2[i]);
    }
    return output;
}

template<typename T>
concept is_divisible = requires(T x)
{
    x / x;
};

//  Division
template<is_divisible T1, is_divisible T2>
constexpr auto element_wise_division(const T1& input1, const T2& input2)
{
    if (input2 == 0)
    {
        throw std::logic_error("Divide by zero exception"); //  Handle the case of dividing by zero exception
    }
    return input1 / input2;
}

template<is_multi_array T1, is_multi_array T2>
constexpr auto element_wise_division(const T1& input1, const T2& input2)
{
    if (input1.num_dimensions() != input2.num_dimensions())     //  Dimensions are different, unable to perform element-wise add operation
    {
        throw std::logic_error("Array dimensions are different");
    }
    if (*input1.shape() != *input2.shape())                     //  Shapes are different, unable to perform element-wise add operation
    {
        throw std::logic_error("Array shapes are different");
    }
    boost::multi_array output(input1);
    for (decltype(+input1.shape()[0]) i{}; i != input1.shape()[0]; ++i)
    {
        output[i] = element_wise_division(input1[i], input2[i]);
    }
    return output;
}

//  ones function
//  Reference: https://stackoverflow.com/q/46595857/6667035
template<class T, std::size_t NumDims>
constexpr auto ones(boost::detail::multi_array::extent_gen<NumDims> size)
{
    boost::multi_array<T, NumDims> output(size);
    return recursive_transform(output, [](auto& x) { return 1; });
}

template<class T, std::size_t NumDims>
constexpr auto filled_multi_array(boost::detail::multi_array::extent_gen<NumDims> size, const T& value)
{
    boost::multi_array<T, NumDims> output(size);
    std::fill_n(output.data(), output.num_elements(), value);
    return output;
}

template<class T, std::size_t NumDims>
auto get_extents(const boost::multi_array<T, NumDims>& input) {
    boost::detail::multi_array::extent_gen<NumDims> output;
    std::vector<std::size_t> shape_list;
    for (size_t i = 0; i < NumDims; i++)
    {
        shape_list.push_back(input.shape()[i]);
    }
    std::copy_n(input.shape(), NumDims, output.ranges_.begin());
    return output;
}

template<std::size_t NumDims>
constexpr auto extents_to_array(const boost::detail::multi_array::extent_gen<NumDims>& input)
{
    std::array<std::size_t, NumDims> output;
    for (size_t rank_index = 0; rank_index < NumDims; rank_index++)
    {
        output[rank_index] = filled_multi_array(input, 0.0).shape()[rank_index];
    }
    return output;
}

template<std::size_t NumDims, std::size_t RankIndex = NumDims - 1>
constexpr auto array_to_extents(std::array<std::size_t, NumDims> new_size)
{
    if constexpr (RankIndex == 0)
    {
        return boost::extents[new_size[0]];
    }
    else
    {
        return get_extents(filled_multi_array(array_to_extents<NumDims, RankIndex - 1>(new_size), 0.0))[new_size[RankIndex]];
    }
}

template<class T, std::size_t NumDims>
constexpr auto print_size(const boost::multi_array<T, NumDims>& input)
{
    auto input_size = extents_to_array(get_extents(input));
    //  Checking shape
    for (size_t rank_index = 0; rank_index < NumDims; rank_index++)
    {
        std::cout << "input_size[" << rank_index << "]:" << input_size[rank_index] << std::endl;
    }
    return;
}
#endif

template<std::size_t dim, class T>
constexpr auto n_dim_vector_generator(T input, std::size_t times)
{
    if constexpr (dim == 0)
    {
        return input;
    }
    else
    {
        auto element = n_dim_vector_generator<dim - 1>(input, times);
        std::vector<decltype(element)> output(times, element);
        return output;
    }
}

template<std::size_t dim, std::size_t times, class T>
constexpr auto n_dim_array_generator(T input)
{
    if constexpr (dim == 0)
    {
        return input;
    }
    else
    {
        auto element = n_dim_array_generator<dim - 1, times>(input);
        std::array<decltype(element), times> output;
        std::fill(std::ranges::begin(output), std::ranges::end(output), element);
        return output;
    }
}

template<std::size_t dim, class T>
constexpr auto n_dim_deque_generator(T input, std::size_t times)
{
    if constexpr (dim == 0)
    {
        return input;
    }
    else
    {
        auto element = n_dim_deque_generator<dim - 1>(input, times);
        std::deque<decltype(element)> output(times, element);
        return output;
    }
}

template<std::size_t dim, class T>
constexpr auto n_dim_list_generator(T input, std::size_t times)
{
    if constexpr (dim == 0)
    {
        return input;
    }
    else
    {
        auto element = n_dim_list_generator<dim - 1>(input, times);
        std::list<decltype(element)> output(times, element);
        return output;
    }
}

template<std::size_t dim, template<class...> class Container = std::vector, class T>
constexpr auto n_dim_container_generator(T input, std::size_t times)
{
    if constexpr (dim == 0)
    {
        return input;
    }
    else
    {
        return Container(times, n_dim_container_generator<dim - 1, Container, T>(input, times));
    }
}

int main()
{
    //  Create one multidimensional std::array with 7 elements, the value of each element is set to 30 in type `std::size_t`.
    auto size_info = n_dim_array_generator<1, 7>(static_cast<std::size_t>(30));
    
    //  Update some values in array.
    size_info[0] = 10;
    size_info[1] = 5;
    size_info[2] = 2;
    

    //  array_to_extents(size_info)
    //  Construct a new Boost.MultiArray with a given one multidimensional std::array
    //  The dimension of this new Boost.MultiArray is determined from the size of the given one multidimensional std::array
    //  The sizes of each dimension are determined from the values in the given one multidimensional std::array
    print_size(filled_multi_array(array_to_extents(size_info), 0.0));
    
    return 0;
}

Результат тестового кода выше:

input_size[0]:10
input_size[1]:5
input_size[2]:2
input_size[3]:30
input_size[4]:30
input_size[5]:30
input_size[6]:30

Ссылка Godbolt здесь.

Все предложения приветствуются.

Сводная информация:

  • На какой вопрос это продолжение?

    Вспомогательная функция get_extents для Boost.MultiArray в C ++

  • Какие изменения были внесены в код с момента последнего вопроса?

    extents_to_array а также array_to_extents реализованы здесь.

  • Почему запрашивается новый обзор?

    Если есть какие-то улучшения, пожалуйста, дайте мне знать.

1 ответ
1

Это создание временного многомерного массива только для получения экстентов? Похоже, что тестовый код создает мульти-массив двойников с 30 * 30 * 30 * 30 * 2 * 5 * 10 элементы … если моя математика верна, выделение памяти ~ 618 МБ для копирования 7 чисел. D:

Если мы уже используем boost::detail пространство имен, мы можем просто скопировать то, что делает boost, чтобы получить форму массива (const_multi_array_ref::init_from_extent_gen):

template<std::size_t NumDims>
constexpr auto extents_to_array(const boost::detail::multi_array::extent_gen<NumDims>& input)
{
    std::array<std::size_t, NumDims> output;
    std::transform(input.ranges_.begin(), input.ranges_.end(), output.begin(),
        [] (auto r) { return r.size(); });

    return output;
}

А другое направление выглядело бы примерно так:

template<std::size_t NumDims>
constexpr auto array_to_extents(std::array<std::size_t, NumDims> input)
{
    boost::detail::multi_array::extent_gen<NumDims> output;

    std::transform(input.begin(), input.end(), output.ranges_.begin(),
        [] (auto s) { return boost::detail::multi_array::extent_range<boost::detail::multi_array::index, boost::detail::multi_array::size_type>(s); });

    return output;
}

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *