Это дополнительный вопрос для вспомогательной функции 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
Все предложения приветствуются.
Сводная информация:
На какой вопрос это продолжение?
Вспомогательная функция get_extents для Boost.MultiArray в C ++
Какие изменения были внесены в код с момента последнего вопроса?
extents_to_array
а такжеarray_to_extents
реализованы здесь.Почему запрашивается новый обзор?
Если есть какие-то улучшения, пожалуйста, дайте мне знать.
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;
}