T-Rex96
T-Rex96

Reputation: 365

Eigen unaryExpr(): get index of element

I know that in Eigen I can use unaryExpr() to apply a custom function to my Eigen Matrices and Vectors, e.g.

Eigen::VectorXd vec(4);
vec.unaryExpr([](double d) {
    return d * cos(d);
});

to apply custom functions to a Eigen::VectorXd. But is there also a way to get the position of the current element in my Vector? I want to be able to do something like this:

Eigen::VectorXd vec(4);
vec.unaryExpr([](double d, int index) {
     return index * d;
 });

which would for example multiply each entry in the vector by it's position.

Upvotes: 2

Views: 6829

Answers (3)

chtz
chtz

Reputation: 18807

Besides using a nullary expression as suggested by @ggael, you can also use a binary expression together with LinSpaced(size, 0, size-1):

VectorXd v; // some input vector

v = v.binaryExpr(VectorXd::LinSpaced(v.size(), 0, v.size()-1),
                 [](double d, double i){return d*i;});
// or much simpler and more efficient in this case:
v = v.cwiseProduct(VectorXd::LinSpaced(v.size(), 0, v.size()-1));

On sufficiently recent versions of Eigen, LinSpaced should get vectorized (although there are some border cases regarding the last element). binaryExpr only gets vectorized if the passed functor is vectorized, of course.

N.B.: If you are doing mostly element-wise operations, consider using ArrayXd instead of VectorXd.

Upvotes: 2

ggael
ggael

Reputation: 29205

You can workaround using a nullary expression:

VectorXd v;
v = VectorXd::NullaryExpr([&v](Index i) { return v(i)*i; });

You can do almost everything with a nullary expression: https://eigen.tuxfamily.org/dox/TopicCustomizing_NullaryExpr.html

Upvotes: 2

lubgr
lubgr

Reputation: 38287

What you intend to do is nothing but an ordinary for loop:

for (int i = 0; i < vec.size(); ++i)
   vec(i) *= i;

So why not keep things simple? If it's supposed to be usable for creating objects, wrap it in a helper function (template).

Besides, what you can do is relying on Eigen's internal order of evaluation. This seems to work, but I am unsure whether I would rely on it:

struct CountingUnaryFct {
   double operator()(double d) const { return d*index++; }
   mutable int index = 0;
};

vec.unaryExpr(CountingUnaryFct{});

This is a hack of considerable ugliness, as it tricks the unaryExpr template that requires its function object to have a const-qualified member operator()(double) const (not possible with a mutable-lambda, hence the function object) to accept an instance that actually does mutate its state under the hood. But again, it seems to work reliably, at least on one-dimensional matrices.

Upvotes: 0

Related Questions