将函数应用于所有的Eigen矩阵元素

27

我有一个 Eigen::MatrixXd,我想通过逐个元素地应用函数来修改它的所有元素。例如:

MatrixXd m = ...;

for each m[i][j]:
  m[i][j] = exp(m[i][j]);

有没有办法实现这个结果?

4个回答

33

可以使用Eigen::MatrixBase<>::unaryExpr()成员函数,例如:

#include <cmath>
#include <iostream>

#include <Eigen/Core>

double Exp(double x) // the functor we want to apply
{
    return std::exp(x);
}

int main()
{
    Eigen::MatrixXd m(2, 2);
    m << 0, 1, 2, 3;
    std::cout << m << std::endl << "becomes: ";
    std::cout << std::endl << m.unaryExpr(&Exp) << std::endl;
}

7
不行,因为std::exp有多个重载,而unaryExpr无法仅从std::exp中推断出函数对象的签名(即有多个候选项)。如果您“帮助”编译器指定函数对象类型,例如m.unaryExpr<double(*)(double)>(&std::exp),则可以正常工作。 - vsoftco
根据API参考,必须使用std::ptr_func将函数指针转换为functor,在unaryExpr中作为参数传递。m.unaryExp(std::ptr_func(&Exp)); - Alex Moreno
1
@Alex 感谢您的评论。然而这不是必须的,API 说可以使用 std::ptr_fun ...,例如请参阅 https://eigen.tuxfamily.org/dox/classEigen_1_1MatrixBase.html#title108 在这种情况下,函数名称根据 C++ 的规则自动衰减为指针,并且函数指针的行为就像一个函数对象。此外,std::ptr_fun 在C++11中已经被弃用,请参阅 http://en.cppreference.com/w/cpp/utility/functional/ptr_fun。 - vsoftco
有人知道如何将从 m.unaryExpr(&Expr) 返回的矩阵分配给一个新对象吗? - Shawn Brar

29

vsoftco的回答非常通用,并且对于自定义函数非常好。然而,对于许多常用函数来说,有一种更简单的方法。通过改编他的示例,我们可以使用array,它看起来像这样:

#include <iostream>
#include <Eigen/Core>

int main()
{
    Eigen::MatrixXd m(2, 2);
    m << 0, 1, 2, 3;
    std::cout << m << "\nbecomes:\n";
    std::cout << m.array().exp() << std::endl;
    return 0;
}

1
这也值得注意,因为这种方法似乎会产生更高效(即向量化)的机器代码 https://godbolt.org/z/YE_RJr - o.comp
2
对于未来想了解Eigen支持的其他按系数函数的用户,请参见此处:Eigen系数-wise数学函数目录 - Matt

11

顺便说一下,在C++11及其以后的版本中,这也适用于lambda函数。

#include <cmath>
#include <iostream>

#include <Eigen/Core>

int main()
{
    Eigen::MatrixXd m(2, 2);
    m << 0, 1, 2, 3;
    std::cout << m << std::endl << " ->  " 
    std::cout << m.unaryExpr([](double x){return x + 1}) << std::endl;
}

2
@vsoftco的答案帮助我解决了这个问题的99%,但是由于某种原因将&Exp传递给.unaryExpr()会导致编译错误(g ++,c + 11,Eigen 3.3.5出现与以下相关的错误:base type 'double (*) (double)'无法成为结构或类类型)。
然而,我发现创建一个std :: function对象并传递它可以解决这个问题。复制@vsoftco的示例:
#include <cmath>
#include <iostream>

#include <Eigen/Core>

double Exp(double x) 
{
    return std::exp(x);
}

int main()
{
    Eigen::MatrixXd m(2, 2);
    m << 0, 1, 2, 3;
    std::function<double(double)> exp_wrap = Exp; //added to @vsoftco's answer
    std::cout << m << std::endl << "becomes: ";
    std::cout << std::endl << m.unaryExpr(exp_wrap) << std::endl; //and used here
}

我不确定使用std::function对象(或std::ptr_fun)相对于传递&Exp会增加多少开销,但是如果没有这些替代方法,我无法使其正常工作。

谢谢


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接