如何在两个 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(

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

实时编译器资源管理器

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:nt{}n", fmt::join(array1,"nt"));
fmt::print("array2:nt{}n", fmt::join(array2,"nt"));
using namespace ArrayOperators;
auto array3 = (array1 + array2)/100.0;
fmt::print("array3:nt{}n", fmt::join(array3,"nt"));
}

它打印

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

您必须为那些对象类型重载+运算符,这些对象类型boost::multi_array<double, 2>所需的实现。

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

给你:

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

最新更新