1
\$\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 and is_divisible 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;
};

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<class T1, class T2> requires (is_multiplicable<T1> && is_multiplicable<T2>)
auto element_wise_multiplication(const T1& input1, const T2& input2)
{
    return input1 * input2;
}

template<class T1, class T2> requires (is_multi_array<T1> && is_multi_array<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;
}

//  Division
template<class T1, class T2> requires (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<class T1, class T2> requires (is_multi_array<T1> && is_multi_array<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 two new concepts is_multiplicable and is_divisible have been added here. I am not sure is this a good idea 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\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.