【问题标题】:How to execute mathematical operations between two boost::multi_arrays (C++)?如何在两个 boost::multi_arrays (C++) 之间执行数学运算?
【发布时间】:2020-07-05 18:35:57
【问题描述】:

如何在两个 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 中受限值类型的缺点的解决方案?

【问题讨论】:

标签: c++ linear-algebra intel-ipp boost-multi-array


【解决方案1】:

你可以“随便写”:

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 calleable。我冒昧的去

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

这里是:

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:\n\t{}\n", fmt::join(array1,"\n\t"));
    fmt::print("array2:\n\t{}\n", fmt::join(array2,"\n\t"));

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

它会打印出来

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

【讨论】:

    【解决方案2】:

    您必须使用所需的实现为您的对象类型boost::multi_array&lt;double, 2&gt; 重载+ 运算符。

    编辑我只是快速尝试了一下,显然它并不难,但可能需要更多的测试和审查;)

    给你:

    boost::multi_array<double, 2> operator+(boost::multi_array<double, 2> a, boost::multi_array<double, 2> b) {
        boost::multi_array<double, 2> result = a;
        for (size_t i=0; i<a.size(); ++i) {
            for (size_t j=0; j<a[i].size(); ++j) {
                result[i][j] = a[i][j] + b[i][j];
            }
        }
        return result;
    }
    

    【讨论】:

    • 你试过了吗:) 这将是一个更有用的答案,即使我认为question is X/Y
    • 我给你一个解决你的问题的一般思路,而确切的实施可能需要相当长的时间来分析和测试解决方案,很遗憾我没有时间为你做,对不起。
    • 我已经用实现编辑了我的答案,因为我感到内疚:D
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-06-19
    • 2021-05-20
    • 1970-01-01
    • 2021-11-23
    • 1970-01-01
    • 2013-06-10
    • 1970-01-01
    相关资源
    最近更新 更多