【问题标题】:Using Eigen::Map<Eigen::MatrixXd> as function argument of type Eigen::MatrixXd使用 Eigen::Map<Eigen::MatrixXd> 作为 Eigen::MatrixXd 类型的函数参数
【发布时间】:2015-04-19 16:57:42
【问题描述】:

简而言之,问题是如何通过一个

Eigen::Map<Eigen::MatrixXd>

对象到一个期望一个函数的函数

Eigen::MatrixXd

对象。


更长的故事:

我有这个 C++ 函数声明

void npMatrix(const Eigen::MatrixXd &data, Eigen::MatrixXd &result);

连同这个实现

void npMatrix(const Eigen::MatrixXd &data, Eigen::MatrixXd &result)
{
//Just do s.th. with arguments
std::cout << data << std::endl;

result(1,1) = -5;
std::cout << result << std::endl;
}

我想使用 numpy.array 作为参数从 python 调用这个函数。为此,我使用了一个用c++编写的包装函数

void pyMatrix(const double* p_data, const int dimData[],
                              double* p_result, const int dimResult[]);

它接受指向数据的指针、数据数组的大小、指向结果的指针以及结果数组的大小。数据指针指向一个 const 内存块,因为在为结果保留的内存块是可写时,数据不会被更改。函数的实现

void pyMatrix(const double *p_data, const int dimData[], double *p_result, const int dimResult[])
{
Eigen::Map<const Eigen::MatrixXd> dataMap(p_data, dimData[0], dimData[1]);
Eigen::Map<Eigen::MatrixXd> resultMap(p_result, dimResult[0], dimResult[1]);

resultMap(0,0) = 100;

npMatrix(dataMap, resultMap);
}

分别为数据和结果定义了一个 Eigen::Map。 Eigen::Map 允许将原始内存作为一种 Eigen::Matrix 访问。 dataMap 的类型是

<const Eigen::MatrixXd>

因为相关的内存是只读的;相比之下,resultMap 的类型是

<Eigen::MatrixXd>

因为它必须是可写的。线

resultMap(0,0) = 100;

表明,resultMap 实际上是可写的。在将 dataMap 传递给预期 const Eigen::MatrixXd 工作的 npMatrix() 时,我找不到以相同方式传递 resultMap 的方法。我敢肯定,麻烦来自这样一个事实,npMatrix 的第一个参数是 const,而第二个不是。我找到的一个可能的解决方案是定义

Eigen::MatrixXd resultMatrix = resultMap;

并将这个resutlMatrix传递给npMatrix()。但是,我想,这会创建一个副本,因此会破坏 Eigen::Map 的良好内存映射。所以我的问题是。

有没有办法将 Eigen:Map 传递给需要非常量 Eigen::MatrixXd 的函数?

附带说明:我可以将 npMatrix 更改为期望 Eigen::Map,但由于在实际项目中,函数已经存在并经过测试,我宁愿不使用它们。

为了完成问题,这里是调用pyMatrix()的python文件

import ctypes as ct
import numpy as np
import matplotlib.pyplot as plt

# Load libfit and define input types
ct.cdll.LoadLibrary("/home/wmader/Methods/fdmb-refactor/build/pyinterface/libpyfit.so")
libfit = ct.CDLL("libpyfit.so")

libfit.pyMatrix.argtypes = [np.ctypeslib.ndpointer(dtype=np.float64, ndim=2),
                                                     np.ctypeslib.ndpointer(dtype=np.int32, ndim=1),
                                                     np.ctypeslib.ndpointer(dtype=np.float64, ndim=2, flags='WRITEABLE'),
                                                     np.ctypeslib.ndpointer(dtype=np.int32, ndim=1)
                                                     ]

data = np.array(np.random.randn(10, 2), dtype=np.float64, order='F')
result = np.zeros_like(data, dtype=np.float64, order='F')

libfit.pyMatrix(data, np.array(data.shape, dtype=np.int32),
                              result, np.array(result.shape, dtype=np.int32))

【问题讨论】:

    标签: c++ numpy eigen3


    【解决方案1】:

    将其作为指向数据的普通指针传递,然后将其 Eigen::Map 放在那里。或者,使用template &lt;typename Derived&gt; 等,在http://eigen.tuxfamily.org/dox/TopicFunctionTakingEigenTypes.html 中找到 我个人的选择是第一个,因为最好有代码,不要暴露你使用过的每个 API 的所有顽固性。此外,您不会失去与 eigen 的兼容性,也不会失去与您(或其他任何人)以后可能使用的任何其他类型的库的兼容性。

    我还发现了另一个技巧,可以在很多场合使用:

    Eigen::MatrixXd a; //lets assume a data pointer like double* DATA that we want to map //Now we do new (&a) Eigen::Map<Eigen::Matrix<Double,Eigen::Dynamic,Eigen::Dynamic>> (DATA,DATA rows,DATA cols);

    这将按照您的要求进行,而不会浪费内存。我认为这是一个很酷的技巧,a 会表现得像一个 matrixXd,但我还没有测试每个场合。 它没有内存副本。 但是,您可能需要在分配之前将a 调整为正确的大小。即便如此,编译器不会在您请求resize 操作时立即分配所有内存,因此也不会有大的无用内存分配!

    小心!调整大小操作可能重新分配特征矩阵使用的内存!因此,如果您 ::Map 内存但随后执行调整矩阵大小的操作,它可能会映射到不同的放在内存中。

    【讨论】:

    • 感谢您的回答。我接受了它,因为我认为总的来说,这是一个很好的解决方案。正如我在问题中所说,我不想更改要调用的函数的接口。因此,我的解决方案是创建我需要的地图。这会导致内存和 cpu 时间的浪费,但与程序的其余部分相比,这并不重要。
    • 谢谢!我想了更多,我想我找到了另一个答案,可以完全按照你的要求做,而不是更改函数声明。我会更新我的答案!
    • 已经 18 个月了,但是 SO 有很多有用的信息。这里new (&amp;a) 是放置版本,因此我相信隐含地从Map 转换为Matrix 对象(如果我错了,请纠正我)。我猜您所指的调整大小是a 不是数据的所有者,因此不能为其分配另一个矩阵,例如分配operator=。我通过跟踪数据的所有权来做到这一点 - 最初 a 可能不拥有数据,但在分配之前,我调整它的大小,当它不是所有者或当前大小不正确时。
    【解决方案2】:

    对于仍在为将Eigen::Map 传递给具有签名Eigen::Matrix 的函数或反之亦然的问题而苦苦挣扎的人,并发现@Aperture Laboratories 建议的Eigen::Matrix 到Eigen::Map 隐式转换技巧不起作用(在我的情况下,这给出了与尝试释放已释放内存相关的运行时错误,[Mismatched delete / Invalid delete errors when running with valgrind]),

    我建议使用Eigen::Ref 类作为函数签名,正如@ggael 在此处给出的答案中所建议的那样: Passing Eigen::Map<ArrayXd> to a function expecting ArrayXd&

    并写在文档中: http://eigen.tuxfamily.org/dox/TopicFunctionTakingEigenTypes.html#TopicUsingRefClass 标题下:

    如何编写通用但非模板化的函数?

    例如对于问题中指定的函数,将签名改为

    void npMatrix(const Eigen::Ref<const Eigen::MatrixXd> & data, Eigen::Ref< Eigen::MatrixXd> result);
    

    意味着将Eigen::Map&lt;Eigen::MatrixXd&gt; 或Eigen::MatrixXd 对象传递给函数将无缝工作(请参阅@ggael 对Correct usage of the Eigen::Ref<> class 的回答,了解在函数签名中使用Eigen::Ref 的不同方法)。

    我很欣赏 OP 说他不想更改函数签名,但就交替使用 Eigen::Maps 和 Eigen::Matrix 而言,我发现这是最简单和最强大的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-07
      相关资源
      最近更新 更多