3

I am trying to understand how to manipulate Eigen Vector/Matrix. I would like to implement a least-square gauss-newton algorithm (hence why I am learning to use the Eigen library). I have a 1x6 vectors of parameters that I need to update each iteration. Right now, I just want to figure out how a function can take a vector as argument and change its values...

Eigen::VectorXf betas = Eigen::VectorXf::Zero(6);

void SomeFunc(const Eigen::VectorXf& v){  // as per the Eigen guide, one must pass as const
    v(0) = 5;  // error: expression must be a modifiable lvalue
    return;
}

int main()
{
    betas(5) = 5.f;  // works
    SomeFunc(&betas);
    std::cout << "Hello World" << std::endl;
    std::cout << betas(0) << "\t" << betas(5) << std::endl;
}

My question is: How would you make a function take a vector and modify its values?

Edit: Eigen guide

Samito
  • 43
  • 4
  • 2
    Replace `const Eigen::VectorXf& v` with `Eigen::VectorXf& v` – Jason Aug 11 '22 at 12:34
  • `v` is marked `const` in you function so you cannot change it. Your compiler should tell you something similar. To modify it, remove the `const` – perivesta Aug 11 '22 at 12:35
  • I'd like to read that Eigen guide and it's reason that you **must** pass as a const reference. Maybe you have misinterpreted the guide? Can you provide a link. – john Aug 11 '22 at 12:38
  • @JasonLiam, https://eigen.tuxfamily.org/dox/TopicFunctionTakingEigenTypes.html says: `Functions taking writable (non-const) parameters must take const references and cast away constness within the function body.` How does one cast away constness? None of the examples show how to do so (from what I could understand at least). – Samito Aug 11 '22 at 12:40
  • @Samito That seems strange. I mean why not just pass a non const lvalue reference instead of passing a const reference and then casting away constness. – Jason Aug 11 '22 at 12:42
  • @Samito The guide shows you how `const_cast(v)(0) = 5;` Just wrap `v` in the appropriate `const_cast` – john Aug 11 '22 at 12:44
  • @JasonLiam The guide claims this may fail to compile. I've not tested it and have no idea why, but they seem to know what they are talking about. – john Aug 11 '22 at 12:47
  • @john, thanks, I hadn't understood this. It works, I'll be digging why :)! Thank you. – Samito Aug 11 '22 at 12:49
  • 1
    @Samito If you have a lot of these casts to make then you should just be able to do it once, and save the result e.g. `Eigen::VectorXf& vv = const_cast(v); vv(0) = 5; vv(1) = 6; vv(2) = 7;` etc. – john Aug 11 '22 at 12:53
  • The reason to take these Eigen type by const ref is the corner case where you operate on sliced matrix (like in the example `cov(x,y, C.block(0,0,3,3));`). Compiler does not allow to bind the temporary to non-const ref, even though we know that this matrix slice is in fact a shortcut to `MatrixXf C` – pptaszni Aug 11 '22 at 16:35
  • 1
    You can pass `v` as a non-const `VectorXf&` in your case (as long as you only pass `VectorXf` objects -- `matrix.col(0)`, etc would not work). For a more generic solution, I recommend `Ref v` -- see here for details: https://eigen.tuxfamily.org/dox/TopicFunctionTakingEigenTypes.html#title1 – chtz Aug 12 '22 at 08:58

2 Answers2

3

As the documentation says:

Functions taking writable (non-const) parameters must take const references and cast away constness within the function body.


How would you make a function take a vector and modify its values?

You can use const_cast as shown below:

Eigen::VectorXf betas = Eigen::VectorXf::Zero(6);

void SomeFunc(const Eigen::VectorXf& v){  // as per the Eigen guide, one must pass as const
    const_cast<Eigen::VectorXf&>(v)(0) = 5;//cons_cast used here
    return;
}

int main()
{
    betas(5) = 5.f;  // works
    SomeFunc(betas);
    std::cout << "Hello World" << std::endl;
    std::cout << betas(0) << "\t" << betas(5) << std::endl;
}
Jason
  • 36,170
  • 5
  • 26
  • 60
2

I believe the purpose of that section of the documentation is to make you aware of the potential pitfalls that come from the various temporary objects that Eigen creates as it goes through various operations. You can write functions that take writable references such as foo(Eigen::VectorXf &vector) and it will work. One problem that can occur with this is if you pass a slice of a matrix into the function thinking it is vector linked back the parent matrix. What you actually pass into the function is a temporary vector that will be destroyed. If you are 100% certain of what you are passing you can use references directly. Otherwise there are safer things to do as suggested by the documentation. Here are 3 versions of the same function.

You can see this stackoverflow question for a discussion of the correct usage of the Eigen::Ref class.

#include <iostream>
#include <Eigen/Dense>

void SomeFunc(Eigen::Ref<Eigen::VectorXf> v);
void SomeFunc2(Eigen::VectorXf &v);
void SomeFunc3(const Eigen::VectorXf &v);

int main(int argc, char * argv[]) {

    Eigen::VectorXf betas = Eigen::VectorXf::Zero(6);
    
    std::cout << "Original\n";
    std::cout << betas << '\n';
    
    SomeFunc(betas);

    std::cout << "SomeFunc\n";
    std::cout << betas << '\n';

    betas.setZero();
    SomeFunc2(betas);

    std::cout << "SomeFunc2\n";
    std::cout << betas << '\n';

    betas.setZero();
    SomeFunc3(betas);

    std::cout << "SomeFunc3\n";
    std::cout << betas << '\n';

    return 0;
}

void SomeFunc(Eigen::Ref<Eigen::VectorXf> v) {
    v(0) = 5;
}

void SomeFunc2(Eigen::VectorXf &v) {
    v(2) = 5;
}
void SomeFunc3(const Eigen::VectorXf &v) {
    const_cast<Eigen::VectorXf&>(v)(4) = 5;
}

To quote from the linked question above:

So in summary, we could recommend Ref for a writable reference, and const Ref& for a const reference.

Matt
  • 2,554
  • 2
  • 24
  • 45