How do I in-place modify each element of a 1D Array?

余生颓废 提交于 2019-12-14 03:46:54

问题


I have a 1D eigen array (Eigen::Array<double,Dynamic,Dynamic>) of doubles, and I want to modify each element in the array in place. However, I'm not really sure how to do this. I'm considering this:

Eigen::Array<double,Eigen::Dynamic,Eigen::Dynamic> arr1D;
// ...

// Threshold function:
arr1D.unaryExpr([](double& elem)
{
    elem = elem < 0.0 ? 0.0 : 1.0;
}
);

But this seems like a bit of a hack because the Eigen Reference examples only give examples of .unaryExpr where it is used with a functor that returns a value (and then the whole method just returns a different array). In my case, I was hoping to avoid the need for creating a new array.

I'm new to Eigen so I thought I might be missing something here, input is appreciated.

EDIT: I understand that I can replace the above simply with arr1D = arr1D >= 0.0, but please note that this is just an example


回答1:


.unaryExpr returns "view" to original data transformed by given function. It doesn't do transformation of original data.

You cannot change argument passed to transformation function. Your code is compiled only because you have not triggered template instantiation of appropriate code. If assign result to value then it fails to compile:

#include <Eigen/Dense>

int main()
{
    using namespace Eigen;

    ArrayXd x, y;
    y = x.unaryExpr([](double& elem)
    {
        elem = elem < 0.0 ? 0.0 : 1.0;
    }); // ERROR: cannot convert const double to double&
}

Exact place of error is in Eigen internals:

EIGEN_STRONG_INLINE const Scalar coeff(Index index) const
{
  return derived().functor()( derived().nestedExpression().coeff(index) );
  //     ^^^^^^^^^^^^^^^^^^^ - your lambda
}

I think the easiest way to do in-place with Eigen is:

ArrayXd x = ArrayXd::Random(100);
x = x.unaryExpr([](double elem) // changed type of parameter
{
    return elem < 0.0 ? 0.0 : 1.0; // return instead of assignment
});

unaryExpr does not return full new array/matrix - but it returns special temporary object which acts like it.




回答2:


Evgeny's answer is by far the best if your value types are relatively simple.

However, if you want to recreate real() and complex() (accessing portions of the Scalars structure), you can use const_cast<> to hack it all away, which is what real/complex() actually do (as of v3.3.3): (note: This code was only tested in C++1y, but could be simplified.)

struct Value {
  double a{1.5};
  int b{2};
};

// Savage. Use aforemention hack from scalar_real_ref_op.
struct get_mutable_a_direct {
  double& operator()(const Value& v) const {
    return const_cast<Value&>(v).a;
  }
};
// ...
MatrixX<Value> X(2, 2);
auto X_am_direct = CwiseUnaryView<get_mutable_a_direct, MatrixX<Value>>(
    X, get_mutable_a_direct());
X_am_direct.setConstant(20);

I've also tested out a quick wrapper, to reduce the above to something like:

struct get_a_flex {
  double& operator()(Value& v) const {
    return v.a;
  }
  const double& operator()(const Value& v) const {
    return v.a;
  }
};
// Less? savage.
auto X_am = unaryExprFlex(X, get_a_flex());
X_am *= 10;
cout << X_ac << endl;

// Works.
const auto& Xc = X;
auto Xc_am = unaryExprFlex(Xc, get_a_flex());
// Xc_am.setConstant(20);  // Fails as desired.
cout << Xc_am << endl;

You can see the code snippet here: unary_view_mutable.cc

NOTE: If you want to use labmda, be sure you indicate returning a reference via auto&:

auto X_bm = unaryExprFlex(X, [](Value& v) -> auto& { return v.b; });
cout << X_bm << endl;
X_bm.setConstant(10);
cout << X_bm << endl;


来源:https://stackoverflow.com/questions/19624632/how-do-i-in-place-modify-each-element-of-a-1d-array

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!