Foreach loops over Eigen matrices?
Asked Answered
T

4

12

Is it possible to use the foreach syntax of C++11 with Eigen matrices? For instance, if I wanted to compute the sum of a matrix (I know there's a builtin function for this, I just wanted a simple example) I'd like to do something like

Matrix2d a;
a << 1, 2,
     3, 4;
double sum = 0.0;
for(double d : a) {
  sum += d;
}

However Eigen doesn't seem to allow it. Is there a more natural way to do a foreach loop over elements of an Eigen matrix?

Templetempler answered 20/11, 2014 at 2:10 Comment(0)
R
3

Range-based for loops need the methods .begin() and .end() to be implemented on that type, which they are not for Eigen matrices. However, as a pointer is also a valid random access iterator in C++, the methods .data() and .data() + .size() can be used for the begin and end functions for any of the STL algorithms.

Rincon answered 13/4, 2017 at 11:12 Comment(3)
begin() and end() can be implemented as members, or as free functions (which are found using ADL).Ms
This approach does not take the Matrix stride into account: eigen.tuxfamily.org/dox/…. This may lead to some unwanted side effects like floating point operations with uninitialized values, etc.Fining
Yes, you are right - it does not take into account stride and that could be dangerous when using a base type. I've just done some reading and it seems from v3.4 (not yet stable), Eigen will support iterators - eigen.tuxfamily.org/dox-devel/group__TutorialSTL.htmlRincon
M
3

For your particular case, it's more useful to obtain start and end iterators yourself, and pass both iterators to a standard algorithm:

auto const sum = std::accumulate(a.data(), a.data()+a.size(), 0.0);

If you have another function that really needs range-based for, you need to provide implementations of begin() and end() in the same namespace as the type (for argument-dependent lookup). I'll use C++14 here, to save typing:

namespace Eigen
{
    auto begin(Matrix2d& m) { return m.data(); }
    auto end(Matrix2d& m) { return m.data()+m.size(); }
    auto begin(Matrix2d const& m) { return m.data(); }
    auto end(Matrix2d const& m) { return m.data()+m.size(); }
}
Ms answered 29/11, 2018 at 17:46 Comment(6)
Don’t range-based for loops require unqualified begin(a)/end(a)? So wouldn’t those need to be in the global namespace or in the Eigen namespace (to be found by ADL)?Amylopectin
Yes @Ben, I think you're right (and non-argument-dependent lookup is not performed).Ms
Any idea why Eigen doesn’t provide this?Amylopectin
Probably just because no-one ever wrote it. Perhaps the project would welcome a suitable patch? Or at least a bug report.Ms
This approach does not take the Matrix stride into account: eigen.tuxfamily.org/dox/….Fining
Thanks for that @Fining - I don't know the Eigen library well, so I was ignorant of its strides. It seems that a small iterator class is necessary (but the principle is sound). Would you care to expand with a worked example?Ms
I
2

STL style iterator support has been added to Eigen in version 3.4.

See https://eigen.tuxfamily.org/dox-devel/group__TutorialSTL.html

For OP's question, you can do the following:

Matrix2d A;
A << 1, 2,
     3, 4;
double sum = 0.0;
for(auto x : A.reshaped())
 sum += x;
Illtimed answered 29/10, 2019 at 15:52 Comment(1)
Unfortunately, only from version 3.3.9, which is not considered stable at the moment (currently, I'm depending on the version 3.3.7)Disaffirm
W
0

A pointer to the data array of the matrix can be obtained using the member function .data().

The size of the data array can also be obtained using the member function .size().

Using these two, we now have the pointers to the first element and end of the array as a.data() and a.data()+a.size().

Also, we know that an std::vector can be initialized using iterators (or array pointers in our case).

Thus, we can obtain a vector of doubles that wraps the matrix elements with std::vector<double>(a.data(), a.data()+a.size()).

This vector can be used with the range-based for loop syntax that is included in your code snippet as:

  Matrix2d a;
  a << 1, 2,
       3, 4;
  double sum = 0.0;
  for(double d : std::vector<double>(a.data(), a.data()+a.size())) {
      sum += d;
  }
Wilde answered 2/1, 2015 at 23:38 Comment(1)
This would perform a copy of the matrix, not iterate over the matrix itself, probably not something the asker wants to do.Torey

© 2022 - 2024 — McMap. All rights reserved.