C ++의 Boost.MultiArray에 대한 element_wise_add 함수

Oct 30 2020

이것은 A Summation Function For Boost.MultiArray in C ++에 대한 후속 질문입니다 . 모든 요소의 합산 연산 외에도 여기서는 요소 별 연산에 초점을 맞추려고합니다. 이 질문의 주요 아이디어는 element_wise_addBoost.MultiArray에 대한 함수 를 구현하는 것입니다. 이 element_wise_add함수 의 목적은 두에 대해 요소 별 더하기 연산을 수행 하는 것입니다 boost::multi_array. 이 함수 element_wise_add에는 두 개의 입력 매개 변수가 input1있으며 input2요소 별 추가 작업의 경우 반환 유형은 요소 별 결과입니다.

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;
}

사용 된 is_summable개념 :

template<typename T>
concept is_summable = requires(T x) { x + x; };

element_wise_add기능 의 테스트는 다음과 같습니다.

// 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;

모든 제안을 환영합니다.

  • 어떤 질문에 대한 후속 조치입니까?

    C ++의 Boost.MultiArray에 대한 합계 함수

  • 지난 질문 이후 코드에서 어떤 변화가 있었습니까?

    이전 질문은 Boost.MultiArray에 대한 합산 함수의 구현이며이 질문의 주요 아이디어는 element_wise_addBoost.MultiArray에 대한 함수 를 구현하는 것입니다.

  • 새로운 리뷰가 요청되는 이유는 무엇입니까?

    세 가지 유형의 유사한 사용 기능을 위해 과부하 boost::multi_array, boost::detail::multi_array::sub_array그리고 boost::detail::multi_array::const_sub_array다시 나타납니다. 나는 이것이 좋은 생각이 아니라는 것을 알고 있습니다. 그러나 내 마음에 더 좋은 방법은 없습니다. 또한 "모양이 다릅니다"상황에 대한 예외 처리가 완벽하지 않습니다. boost::detail::multi_array::sub_array및 의 경우 boost::detail::multi_array::const_sub_array반환하기에 적절한 것이 무엇인지 잘 모르겠습니다. 나는 다음과 std::optional같은 것을 시도해 본 적이 있습니다 .

    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;
    }
    

    이:

    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;
    }
    

    그러나 이것은 사용하기가 너무 복잡하고 ( 구조 의 콘텐츠에 액세스하려면 .value()또는 .value_or()기능 의 사용 이 필요합니다 std::optional<>) 약간 끔찍하다고 생각합니다. 제안이나 개선 가능성이 있으면 알려주세요!

답변

2 G.Sliepen Oct 30 2020 at 17:17

이것을 operator+()

요소 별 추가는 매우 일반적이고 자연스러운 작업이기 때문에 (예 : STL은에 대해 지원 std::valarray) operator+()함수를 만드는 대신 오버로드 하는 것이 더 직관적 일 수 있습니다 element_wise_add(). 다른 운영자에게도 쉽게 확장 할 수있는 가능한 구현에 대해서는 이 질문 을 참조하십시오 .

이를 만들기의 또 다른 장점은 operator+()그것이이 만드는 것입니다 boost::multi_array자체가 만족 is_summable그래서 재귀 컨테이너에 대한 명시 적 지원을 추가하지 않고, 다음과 같은 것 다음 작업 :

boost::multi_array<boost::multi_array<double, 2>, 3> array1, array2;
auto array3 = array1 + array2;

일치하지 않는 배열 차원 처리

나는 실제로 std::optional수학 연산에 대한 오류 신호를 사용하지 않을 것 입니다. 두 가지 방법이 있습니다.

  1. 반환 된 배열의 차원이 두 입력 배열의 차원의 최대 값인지 확인합니다. 따라서 결과에 추가 {{1}, {2}}하면 .{{3, 5}}{{4, 5}, {2, 0}}

  2. std::logic_error두 개의 불일치 오류를 추가하는 것이 프로그래밍 오류라고 가정하여를 던집니다 .

반복 피하기

기본적으로 동일한 것을 세 번 작성하고 있으며 유일한 변형은 입력이 일반 boost::multi_arrays, sub_arrays 또는 const_sub_arrays 인지 여부 입니다. 이를 방지하려면 입력 유형을 템플릿으로 만들고 boost::multi_array관련 유형 과 만 일치하는지 확인하려면 개념을 작성하면됩니다. 다시 말하지만 이미 사용하는 표현식을 사용할 수 있습니다.

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

이 유형이 있는지 검사한다 x보유 shape()부재의 기능과 그 boost::multi_array복사본 구성 그로부터 수있다. 그런 다음 다음과 같이 작성하십시오.

template<class T> requires is_multi_array<T>
auto element_wise_add(const T& input1, const T& input2)
{
    if (*input1.shape() != *input2.shape())
    {
        throw std::logic_error("array shape mismatch");
    }

    boost::multi_array output(input1);

    for (decltype(+input1.shape()[0]) i = 0; i < input1.shape()[0]; i++)
    {
        output[i] = element_wise_add(input1[i], input2[i]);
    }

    return output;
}

여기서 단점은와 함께 제공되는 유형 지옥에 들어 가지 않고 새를 더 효율적 으로 구성하는 방법을 볼 수 없다는 점을 제외 하면 전체 input1배열이 output정당한 이유없이 복사 multi_array된다는 것입니다 boost::multi_array. 아마도 이것은 다른 템플릿 기능에 아웃소싱 될 수 있습니다.

유형 i이 결정 되는 방식을 변경 했음을 알 수 있습니다. 나는를 사용 decltype()하지만 const-ness 를 유지하기 때문에 버려야합니다. 이를 수행하는 방법 에는 여러 가지 가 +있습니다. 여기서는 단항 트릭을 사용했습니다 .

더 많은 조합 처리

같은 크기 sub_array의 일반 multi_array에 를 추가하려면 어떻게해야 합니까? 접근 방식으로 가능한 모든 조합을 개별적으로 처리해야하지만 내가 보여준 단일 함수 예제를 사용하면 다음과 같이 작성할 수 있습니다.

template<class T1, class T2> requires (is_multi_array<T1> && is_multi_array<T2>)
auto element_wise_add(const T1& input1, const T2& input2)
{
    ...
}

그러나 정수 배열에 double 배열을 추가 할 수도 있습니다. 다음과 같이 작성하면 작동하도록 만들 수 있습니다.

template<typename T1, typename T2>
concept is_summable = requires(T1 x, T2 y) { x + y; };

template<class T1, class T2> requires is_summable<T1, T2>
auto element_wise_add(const T1& input1, const T2& input2)
{
    return input1 + input2;
}

실제로 유용하게 만들려면의 유형 output이 두 입력 배열의 값 유형을 추가 한 결과와 일치하는 값 유형을 갖기 를 원합니다 .