This is a follow-up question for A Summation Function For Boost.MultiArray in C++. Besides the summation operation of all elements, I am trying to focus on the element-wise operation here. The main idea of this question is to implement an element_wise_add
function for Boost.MultiArray. The purpose of this element_wise_add
function is to perform element-wise add operation on two boost::multi_array
s. The function element_wise_add
has two input parameters input1
and input2
for element-wise add operation and the return type is the element-wised result.
template<class T> requires is_summable<T>
auto element_wise_add(const T& input1, const T& input2)
{
return input1 + input2;
}
// Deal with the two input case
template<class T, std::size_t Dims> requires is_summable<T>
auto element_wise_add(const boost::detail::multi_array::const_sub_array<T, Dims>& input1, const boost::detail::multi_array::const_sub_array<T, Dims>& input2)
{
boost::multi_array<T, Dims> output(reinterpret_cast<boost::array<size_t, Dims> const&>(*input1.shape()));
for (typename boost::detail::multi_array::const_sub_array<T, Dims>::index i = 0; i < input1.shape()[0]; i++)
{
output[i] = element_wise_add(input1[i], input2[i]);
}
return output;
}
// Deal with the two input case
template<class T, std::size_t Dims> requires is_summable<T>
auto element_wise_add(const boost::detail::multi_array::sub_array<T, Dims>& input1, const boost::detail::multi_array::sub_array<T, Dims>& input2)
{
boost::multi_array<T, Dims> output(reinterpret_cast<boost::array<size_t, Dims> const&>(*input1.shape()));
for (typename boost::detail::multi_array::sub_array<T, Dims>::index i = 0; i < input1.shape()[0]; i++)
{
output[i] = element_wise_add(input1[i], input2[i]);
}
return output;
}
// Deal with the two input case
template<class T, std::size_t Dims> requires is_summable<T>
auto element_wise_add(const boost::multi_array<T, Dims>& input1, const boost::multi_array<T, Dims>& input2)
{
if (*input1.shape() != *input2.shape()) // if shape is different
{
return input1; // unable to perform element-wise add operation
}
boost::multi_array<T, Dims> output(reinterpret_cast<boost::array<size_t, Dims> const&>(*input1.shape()));
for (typename boost::multi_array<T, Dims>::index i = 0; i < input1.shape()[0]; i++)
{
output[i] = element_wise_add(input1[i], input2[i]);
}
return output;
}
The used is_summable
concept:
template<typename T>
concept is_summable = requires(T x) { x + x; };
The test of this element_wise_add
function is as follows.
// Create a 3D array that is 3 x 4 x 2
typedef boost::multi_array<double, 3> array_type;
typedef array_type::index index;
array_type A(boost::extents[3][4][2]);
// Assign values to the elements
int values = 0;
for (index i = 0; i != 3; ++i)
for (index j = 0; j != 4; ++j)
for (index k = 0; k != 2; ++k)
A[i][j][k] = values++;
for (index i = 0; i != 3; ++i)
for (index j = 0; j != 4; ++j)
for (index k = 0; k != 2; ++k)
std::cout << A[i][j][k] << std::endl;
auto DoubleA = element_wise_add(A, A);
for (index i = 0; i != 3; ++i)
for (index j = 0; j != 4; ++j)
for (index k = 0; k != 2; ++k)
std::cout << DoubleA[i][j][k] << std::endl;
All suggestions are welcome.
Which question it is a follow-up to?
What changes has been made in the code since last question?
The previous question is the implementation of a summation function for Boost.MultiArray and the main idea of this question is to implement an
element_wise_add
function for Boost.MultiArray.Why a new review is being asked for?
The similar usage of three types overload function for
boost::multi_array
,boost::detail::multi_array::sub_array
andboost::detail::multi_array::const_sub_array
appears again. I know maybe this is not a good idea. However, there is no any better way coming up in my mind. Moreover, the exception handling for the "shape is different" situation is not perfect. In the case ofboost::detail::multi_array::sub_array
andboost::detail::multi_array::const_sub_array
, I am not sure what's the appropriate thing to return back. I have ever tried something withstd::optional
like this:template<class T, std::size_t Dims> requires is_summable<T> auto element_wise_add(const boost::detail::multi_array::const_sub_array<T, Dims>& input1, const boost::detail::multi_array::const_sub_array<T, Dims>& input2) { std::optional<boost::detail::multi_array::const_sub_array<T, Dims>> final_output; if (*input1.shape() != *input2.shape()) // if shape is different { final_output = std::nullopt; } else { boost::multi_array<T, Dims> output(reinterpret_cast<boost::array<size_t, Dims> const&>(*input1.shape())); for (typename boost::detail::multi_array::const_sub_array<T, Dims>::index i = 0; i < input1.shape()[0]; i++) { output[i] = element_wise_add(input1[i], input2[i]); } final_output = output; } return final_output; }
And this:
template<class T, std::size_t Dims> requires is_summable<T> auto element_wise_add(const boost::detail::multi_array::sub_array<T, Dims>& input1, const boost::detail::multi_array::sub_array<T, Dims>& input2) { std::optional<boost::detail::multi_array::sub_array<T, Dims>> final_output; if (*input1.shape() != *input2.shape()) // if shape is different { final_output = std::nullopt; } else { boost::multi_array<T, Dims> output(reinterpret_cast<boost::array<size_t, Dims> const&>(*input1.shape())); for (typename boost::detail::multi_array::sub_array<T, Dims>::index i = 0; i < input1.shape()[0]; i++) { output[i] = element_wise_add(input1[i], input2[i]); } final_output = output; } return final_output; }
However, this is so complex to use (the usage of
.value()
or.value_or()
function is needed to access the content instd::optional<>
structure) and a little horrible I think. If there is any suggestion or possible improvement, please tell me!