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

How to write functions returning Eigen types

Tags: None
(comma "," separated)
quantdev
Registered Member
Posts
17
Karma
0
I have read the eigen documentation on this back to front, and probably the most relevant document is the page on Writing Functions Taking Eigen Types as Parameters, which discusses void functions taking non-const references of Eigen types and modifying them. I am currently trying to write functions which build Eigen types (either expressions or matrices) and returns them by value. They are then received as const references by other functions which perform further operations on them.

This works fine until I try to perform an operation that actually creates a matrix. For example, the following function concatenates two matrices together:

Code: Select all
template <typename DerivedL, typename DerivedR>
Matrix<double, 2, 2> Concatenate(const DenseBase<DerivedL>& left, const DenseBase<DerivedR>& right)
{
  Matrix<double, 2, 2> matrix;
  matrix << left, right; // assuming they are each of dimension 2x1
  return matrix;
}


Other functions then take the output of this function by const reference and perform other operations. However, this is giving me all sorts of errors in valgrind as well as garbage values. I haven't been able to reproduce the problem in a confined example, but I thought I'd ask here first to see if there was anything wrong with this kind of function.

What other considerations are there to be taken into account when returning by value rather than modifying existing Eigen objects?
User avatar
ggael
Moderator
Posts
3447
Karma
19
OS
I don't see anything wrong in this precise example. The typical problem is when returning an expression involving temporary matrices created in the function itself, like:
Code: Select all
Transpose<MatrixXd> foo(const MatrixBase<Derived> &m) {
  MatrixXd tmp = 2*m;
  return tmp.transpose();
}

because the returned Transpose expression stores a reference to tmp which is dead. But in your example you are returning a Matrix, not an expression. So you should be fine.
quantdev
Registered Member
Posts
17
Karma
0
ggael wrote:I don't see anything wrong in this precise example. The typical problem is when returning an expression involving temporary matrices created in the function itself, like:
Code: Select all
Transpose<MatrixXd> foo(const MatrixBase<Derived> &m) {
  MatrixXd tmp = 2*m;
  return tmp.transpose();
}

because the returned Transpose expression stores a reference to tmp which is dead. But in your example you are returning a Matrix, not an expression. So you should be fine.


Thanks ggael. I created the following test program to see what would happen if I pass the output of my Concatenate function into another function (which just passes it straight through). Here is the program:

Code: Select all
#include <type_traits>
#include <iostream>
#include <utility>

#include <Eigen/Dense>

template <typename DerivedL, typename DerivedR>
Eigen::Matrix<double, 2, 2> Concatenate(const Eigen::DenseBase<DerivedL>& left, const Eigen::DenseBase<DerivedR>& right)
{
    Eigen::Matrix<double, 2, 2> matrix;
    matrix << left, right; // assuming they are each of dimension 2x1
    return matrix;
}

template <typename Derived>
Eigen::DenseBase<Derived> Pass(const Eigen::DenseBase<Derived>& matrix)
{
    return matrix;
}

int main()
{
    using Matrix = Eigen::Matrix<double, 2, 1>;

    Matrix mat1;
    mat1 << 1, 1;
    Matrix mat2;
    mat2 << 2, 2;

    auto matrix = Pass(Concatenate(mat1,mat2));

    std:: cout << matrix;
    return 0;
}


I ran this program through valgrind with the --track-origins=yes flag and got a series of errors, namely uninitialised values. Why is this occuring? My understanding is that Pass should keep the output of Concatenate alive because it keeps a const reference, and auto Matrix should store the output by value.

What's wrong with the above code, and how would you go about making this work without creating redundant copies?

The following version of Pass avoids these errors, but I don't really understand why this works and the above doesn't:

Code: Select all
template <typename Derived>
Derived Pass(const Eigen::DenseBase<Derived>& matrix)
{
    return matrix.derived();
}
User avatar
ggael
Moderator
Posts
3447
Karma
19
OS
The problem is the "Pass" function which incorrectly return a DenseBase by value. You cannot create a DenseBase object: DenseBase is an empty abstract base class. Ideally, this example should fail to compile but we don't know how to do so.
quantdev
Registered Member
Posts
17
Karma
0
ggael wrote:The problem is the "Pass" function which incorrectly return a DenseBase by value. You cannot create a DenseBase object: DenseBase is an empty abstract base class. Ideally, this example should fail to compile but we don't know how to do so.


Thanks Ggael.

It sounds like you don't want to permit the object to be copy-constructed:

DenseBase(const DenseBase&) = delete;
User avatar
ggael
Moderator
Posts
3447
Karma
19
OS
Sure, but Eigen has to be C++03 compatible, so we have to emulate this feature by making it private, and manually implement the copy ctor of the numerous derived classes and verify that the compiler is still able to optimize out the numerous copies that occurs when assembling complex expressions...
bouman
Registered Member
Posts
4
Karma
0
Dear Gael,

I have a question about lazy evaluation related to the above question.
My intuition says that even in C++11, returning matrices (or other eigen types) in the following way:

template<typename Derived>
Matrix<Derived::Scalar,Dynamic,Dynamic> myfunc(const MatrixBase<Derived> & inputParam)
{
Matrix<Derived::Scalar,Dynamic,Dynamic> retM;
// assign to retM
return retM;
}

will always trigger evaluation of retM.

I mean, if I want lazy evaluation, then I still need to follow the "c++98-const_cast-hack" approach:

template<typename DerivedA, typename DerivedB>
void myfunc(const MatrixBase<DerivedA> & inputParam, const MatrixBase<DerivedB>& outputParam)
{
MatrixBase<DerivedB> & writableOutput = const_cast<decltype(writableOutput)>(outputParam);

// write here to writableOutput

}

Is my intuition correct?
Or is the first method also compatible with eigen's lazy evaluation system in C++11 because of move semantics?

best regards,
Niek


[ EDIT ]: I've just seen that you've already answered a related question in the "Returning generic Eigen types" post started by alecjacobson
https://forum.kde.org/viewtopic.php?f=74&t=130338
User avatar
ggael
Moderator
Posts
3447
Karma
19
OS
Looks like you are misusing the concept of "lazy-evaluation". Actually what you are looking for is not lazy evaluation, but a way to write functions returning matrix-like objects and avoid any temp/copy if it is directly assigned to an object having some storage like:

Code: Select all
MatrixXd foo1(...);
Matrix4d foo2(...);

MatrixXd A;
A = foo1(...); // already optimized in C++98 thanks to RVO, can be explicitly optimized using move semantic
A = foo2(); // cannot be optimized away using RVO or move semantic
A.block(...) = foo1(); // cannot be optimized away using RVO or move semantic
A = 2 * foo1(); // need to be evaluated in temp anyway

Matrix4d B;
B = foo2(); // already optimized in C++98 thanks to RVO, move semantic cannot help here
B = foo1(); // cannot be optimized away using RVO or move semantic


so as you see, some trivial cases are already optimized away by the compiler. For the others, you indeed can refer the this thread: viewtopic.php?f=74&t=130338


Bookmarks



Who is online

Registered users: bartoloni, Bing [Bot], Evergrowing, Google [Bot], ourcraft