如何在两个Boost::MULTI_ARRAYS(C++)之间执行数学运算?

如何在两个Boost::MULTI_ARRAYS之间执行数学运算?

值类型为DOUBLE的两个数组相加的示例:

auto array1 = boost::multi_array<double, 2>(boost::extents[10][10]);
auto array2 = boost::multi_array<double, 2>(boost::extents[10][10]);

auto array3 = array1  + array2; //Does not compile

我知道的一种可能性是英特尔IPP库。例如,可以用ippiAdd_来完成两个矩阵的相加。但遗憾的是,英特尔IPP不支持双精度加法。

那么,有没有人知道除了英特尔IPP之外的另一个库,一个克服英特尔IPP中限制值类型缺点的解决方案?


解决方案

您可以&只写它:

namespace ArrayOperators {
    template <typename L, typename R>
    static inline auto operator+(L const& l, R const& r) {
        return ArrayOp {std::plus<>{}} (l, r); }

    template <typename L, typename R>
    static inline auto operator-(L const& l, R const& r) {
        return ArrayOp {std::minus<>{}} (l, r); }

    template <typename L, typename R>
    static inline auto operator/(L const& l, R const& r) {
        return ArrayOp {std::divides<>{}} (l, r); }

    template <typename L, typename R>
    static inline auto operator*(L const& l, R const& r) {
        return ArrayOp {std::multiplies<>{}} (l, r); }
}
当然,这需要我们实际实现ArrayOp可调用的。我冒昧地去

  • 实现异构数组(所以当左右手的元素类型不同时)
  • 在右侧不是数组的情况下实现它,在这种情况下,标量操作数将应用于左侧的每个元素
  • 我不支持
    • 就地作业
    • 数组引用/数组(常量)视图
    • 不同形状或维度的数组

这里:

template <typename Op> struct ArrayOp {
    Op op;
    explicit ArrayOp(Op op) : op(op) {}

    template <typename T, typename Scalar, size_t Dim> auto operator()(
        boost::multi_array<T, Dim> const& l,
        Scalar const& v) const
    {
        std::array<int, Dim> shape;
        std::copy_n(l.shape(), Dim, shape.data());

        using R = boost::multi_array<decltype(op(T{}, v)), Dim>;
        R result(shape);
        
        std::transform(
           l.data(), l.data()+l.num_elements(),
           result.data(),
           [&op=op,v](auto const& el) { return op(el, v); });

        return result;
    }

    template <typename T, typename U, size_t Dim> auto operator()(
        boost::multi_array<T, Dim> const& l,
        boost::multi_array<U, Dim> const& r) const
    {
        std::array<int, Dim> shape;
        std::copy_n(l.shape(), Dim, shape.data());
        assert(std::equal(shape.begin(), shape.end(), r.shape()));

        using R = boost::multi_array<decltype(op(T{}, U{})), Dim>;
        R result(shape);
        
        std::transform(
           l.data(), l.data()+l.num_elements(),
           r.data(), result.data(),
           [&op=op](auto const& v1, auto const& v2) { return op(v1, v2); });

        return result;
    }
};

基本上归结为

  • 推断结果数组元素类型和形状
  • 执行一元或二进制转换(取决于标量/数组RHS)

现在我们可以编写程序了:

Live On Compiler Explorer

int main() {
    using MA = boost::multi_array<int, 2>;

    auto shape = boost::extents[3][3];
    MA array1(shape), array2(shape);

    std::generate_n(array1.data(), array1.num_elements(),
            [n = 0]() mutable { return n+=100; });
    std::generate_n(array2.data(), array2.num_elements(),
            [n = 0]() mutable { return n+=1; });

    fmt::print("array1:
	{}
", fmt::join(array1,"
	"));
    fmt::print("array2:
	{}
", fmt::join(array2,"
	"));

    using namespace ArrayOperators;
    auto array3 = (array1 + array2)/100.0;
    fmt::print("array3:
	{}
", fmt::join(array3,"
	"));
}

并打印

array1:
    {100, 200, 300}
    {400, 500, 600}
    {700, 800, 900}
array2:
    {1, 2, 3}
    {4, 5, 6}
    {7, 8, 9}
array3:
    {1.01, 2.02, 3.03}
    {4.04, 5.05, 6.06}
    {7.07, 8.08, 9.09}

但请稍等,您正在解决什么问题

  • 如果需要矩阵(不是数组)操作,请使用Boost uBlas、Eigen、Armadillo
  • 如果您想获得最佳性能,可以使用SIMD/AVX2/GPU指令,您可以使用Boost Compute

相关文章