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 requires
ments) 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 andelement_wise_division
function here.Why a new review is being asked for?
There are three new concepts
is_multiplicable
,is_divisible
andis_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.