2

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.

T-Rex96
  • 355
  • 4
  • 15

3 Answers3

2

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

ggael
  • 28,425
  • 2
  • 65
  • 71
2

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.

chtz
  • 17,329
  • 4
  • 26
  • 56
0

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.

lubgr
  • 37,368
  • 3
  • 66
  • 117
  • I'd strongly advice against the second solution. While this likely works at the moment, there is no guarantee on the order in which coefficients are evaluated -- in the future this might very well happen in parallel, for example. – chtz Mar 29 '19 at 13:04