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.

\$\endgroup\$

0

Your Answer

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