This forum has been archived. All content is frozen. Please use KDE Discuss instead.

Range support for Eigen::data_types

Tags: None
(comma "," separated)
gnzlbg
Registered Member
Posts
3
Karma
0

Range support for Eigen::data_types

Tue Jun 18, 2013 8:02 am
Eigen provides its own algorithms, and you can iterate over Eigen::Matrix/Array using unaryExpr. However, my application has replaced all C-Arrays and std::arrays with Eigen::Matrix data-types. Using Eigen methods in hot-spots is mandatory. Still, in the remaining 90% of my code base my users shouldn't even know that an Eigen::Matrix vector has different behaviour than a std::vector or a std::array. Explicitly coding everything for Eigen reduces the flexibility of my code.

So what I want is: when a function returns an Eigen vector, my users should be able to just recieve it by value using auto, iterate over it as if it where a std::vector, and use it with at least Boost.Range algorithms (std::algorithms would be nice but is not necessary). I'm not interested in matrices and don't even know if this makes sense for them, but for vectors (point, coordinates, velocities, rotations...) it does.

First attempt: overload std::begin/std::end

Code: Select all
namespace std {
template<typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols>
typename Eigen::Matrix<_Scalar,_Rows,_Cols,_Options,_MaxRows,_MaxCols>::Scalar*
begin(Eigen::Matrix<_Scalar,_Rows,_Cols,_Options,_MaxRows,_MaxCols>& v) {
  return v.data();
}
template<typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols>
typename Eigen::Matrix<_Scalar,_Rows,_Cols,_Options,_MaxRows,_MaxCols>::Scalar*
end(Eigen::Matrix<_Scalar,_Rows,_Cols,_Options,_MaxRows,_MaxCols>& v) {
  return v.data() + v.size();
}
} // std


Then, I need to specialize some traits to be able to use some Boost.Range algorithms:

Code: Select all
namespace boost {
template<typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols>
inline BOOST_DEDUCED_TYPENAME range_difference<
  Eigen::Matrix<_Scalar,_Rows,_Cols,_Options,_MaxRows,_MaxCols>>::type
range_calculate_size(const Eigen::Matrix<_Scalar,_Rows,_Cols,_Options,_MaxRows,_MaxCols>& rng)
{ return rng.size(); }

template<typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols>
struct range_mutable_iterator< Eigen::Matrix<_Scalar,_Rows,_Cols,_Options,_MaxRows,_MaxCols> >
{ typedef _Scalar* type; };

template<typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols>
struct range_const_iterator< Eigen::Matrix<_Scalar,_Rows,_Cols,_Options,_MaxRows,_MaxCols> >
{ typedef _Scalar* type; };
} // boost


This works most of the time, for most of the eigen vectors, and most of the algorithms. This doesn't check if you are using a vector or have a multi-dimensional matrix so it can be dangerous.

It would be nice if Eigen itself would provide a solution to this problem. In particular, if it would:

- conform to the interface of a standard container: this would make things a lot easier, albeit probably very inefficient. Inefficiency would be the user's fault tho.
- specialize Boost.Range for Eigen types: this allows calling Eigen member functions on the Eigen types instead of the std::algorithms using iterators (i.e. efficient).

The first point is required for range-based for loops. The second point would allow using the for_each algorithm (which could call e.g. unaryExpr and potentially be more efficient). Range-based for loops are really nice. Even if they are efficient it is worth it to support them.

I would be interested in implementing this two things. I am familiar with using Eigen3, have some minor experience with expression templates, but haven't really looked much at the Eigen code base yet (most of the code would go into it's own header file tho). So:
- is there interest? If it only interest myself I might just do what I need for my own project.
- if there is interest, would someone be able to mentor me? That is, is there some else that is really interested on this and has the time to review my code (e.g. in a github repository) and to discuss my solution and propose improvements, etc. ?
jitseniesen
Registered Member
Posts
204
Karma
2
There is an issue on our bug tracker on STL-compatible iterators for Eigen type: http://eigen.tuxfamily.org/bz/show_bug.cgi?id=231 . So I'd say there is definitely some interest in this.
gnzlbg
Registered Member
Posts
3
Karma
0
std::begin/std::end is a nice start. It should be important to decide if we want them to be "simple pointers" or if we we want stronger guarantees.

In the bug report the proposed iterators have a pointer to the container and an index. This is allows
- identifying if someone is trying to e.g. compare iterators that belong to different objects.
However, it comes at the expense of:
- twice as much memory per iterator, and
- slightly worse performance.
My experience with this kind of implementation is that the performance drawback and the memory overhead are barely noticeable. One could construct a case in which the memory overhead is important, e.g. if someone stores a vector of iterators. In practice, this would be something really atypical.

Still, the STL doesn't give you this guarantee. Comparing iterators belonging to different containers is undefined behavior.

See: http://stackoverflow.com/questions/8447 ... stablished
From: http://www.open-std.org/jtc1/sc22/wg21/ ... e.html#446 (note: emphasis is mine)
The result of using any iterator operation (24.2.1 [input.iterators], 24.2.2 [output.iterators], 24.2.3 [forward.iterators], 24.2.4 [bidirectional.iterators], 24.2.5 [random.access.iterators]) that uses two iterator values as arguments (footnote) which were obtained from two different ranges r1 and r2 (including their past-the-end values) which are not subranges of one common range is undefined, unless explicitly described otherwise.

footnote) Among others these operations are ==, <, binary -, and copy assignment


Anyways, the fastest way to iterate over an "Eigen sequence" would be to use a range-based for-each algorithm (e.g. an specialization of boost::for_each) that calls unaryExpression for a given function object. I thus wouldn't mind for a barely noticeable slower iterator implementation if it gives me the extra guarantees. Furthermore, there is always the possibility of implementing the extra checks only in debug mode.
User avatar
ggael
Moderator
Posts
3447
Karma
19
OS
It would be nice to put this comment directly in the bugzilla so that it will be read by anyone interested to contribute on this aspect and to continue this discussion there.


Bookmarks



Who is online

Registered users: Bing [Bot], Google [Bot], Sogou [Bot]