3
\$\begingroup\$

This is a follow-up question for An element_wise_add Function For Boost.MultiArray in C++ and An Add/Minus Operator For Boost.MultiArray in C++. Besides the basic element-wise add / minus operations, I am trying to implement an element_wise_multiplication function and an element_wise_division function here for Boost.MultiArray. The new concept is_multiplicable, is_divisible and is_multi_array_dimensionality_equal (as user673679's answer mentioned, considering the dimensions are static, the checking statement has been added in the function requiresments) used here are as below.

template<typename T>
concept is_multiplicable = requires(T x)
{
 x * x;
};
template<typename T>
concept is_divisible = requires(T x)
{
 x / x;
};
template<typename T1, typename T2>
concept is_multi_array_dimensionality_equal =
is_multi_array<T1> && is_multi_array<T2> && requires(T1 x, T2 y)
{
 T1::dimensionality == T2::dimensionality;
};

The another used concept is_multi_array:

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

The implementation of element_wise_multiplication function and element_wise_division function:

// Multiplication
template<is_multiplicable T1, is_multiplicable T2>
auto element_wise_multiplication(const T1& input1, const T2& input2)
{
 return input1 * input2;
}
template<is_multi_array T1, is_multi_array T2>
requires (is_multi_array_dimensionality_equal<T1, T2>)
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); // drawback to be improved: avoiding copying whole input1 array into output, but with appropriate memory allocation
 for (decltype(+input1.shape()[0]) i = 0; 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>
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>
requires (is_multi_array_dimensionality_equal<T1, T2>)
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); // drawback to be improved: avoiding copying whole input1 array into output, but with appropriate memory allocation
 for (decltype(+input1.shape()[0]) i = 0; i < input1.shape()[0]; ++i)
 output[i] = element_wise_division(input1[i], input2[i]);
 return output;
}

The test for the element_wise_multiplication function and the element_wise_division function:

int main()
{
 // 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 = 1;
 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 test_result = A;
 for (size_t i = 0; i < 3; i++)
 {
 test_result = element_wise_multiplication(test_result, A);
 }
 test_result = element_wise_division(test_result, A);
 
 for (index i = 0; i != 3; ++i)
 for (index j = 0; j != 4; ++j)
 for (index k = 0; k != 2; ++k)
 std::cout << test_result[i][j][k] << std::endl;
 return 0;
}

All suggestions are welcome.

  • Which question it is a follow-up to?

    An element_wise_add Function For Boost.MultiArray in C++ and An Add/Minus Operator For Boost.MultiArray in C++

  • What changes has been made in the code since last question?

    The previous questions are the implementation of add / minus operations for Boost.MultiArray and I am trying to implement an element_wise_multiplication function and element_wise_division function here.

  • Why a new review is being asked for?

    There are three new concepts is_multiplicable, is_divisible and is_multi_array_dimensionality_equal have been implemented here. I am not sure is this a good practice although it seems to work fine with the test example mentioned above. On the other hand, the reason for not overloading the operator* and operator/ directly is that the symbol * and / are hard to indicate element-wise multiplication and division clearly. There are some more complex high dimensional matrix multiplication calculations maybe like this one. If there is any further suggestion about this idea, please let me know.

asked Nov 1, 2020 at 15:24
\$\endgroup\$

0

Know someone who can answer? Share a link to this question via email, Twitter, or Facebook.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.