簡體   English   中英

如何將 Numpy 矩陣列表映射到 Cython 中的特征矩陣向量

[英]How to map a list of Numpy matrices to a vector of Eigen matrices in Cython

我有一個我想從 Python 運行的 C++ 函數。 為此,我使用 Cython。 我的 C++ 函數在很大程度上依賴於特征矩陣,我使用特征映射到 Python 的 Numpy 矩陣。

在我有一個 Numpy 矩陣列表的情況下,我無法讓它工作。


有什么作用(將普通 Numpy 矩陣映射到特征矩陣):

我有一個 C++ 函數,它在頭文件 (Header.h) 中看起來像:

float MyCppFunction(Eigen::Map<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> &inputMatrix);

在我CythonFile.pyx文件我有(和創建使用Eigency作為解釋地圖在這里):

cdef extern from "Header.h":
    cdef void _MyCppFunction "MyCppFunction"(FlattenedMapWithOrder[Matrix, float, Dynamic, Dynamic, RowMajor] &)

def my_python_function(np.ndarray[ndim=2, dtype=np.float32_t] my_matrix)
    return _MyCppFunction(FlattenedMapWithOrder[Matrix, float, Dynamic, Dynamic, RowMajor](my_matrix))

我可以使用 Cython 構建這個模塊並從 Python 成功調用my_python_function


什么不起作用(將 Numpy 矩陣列表映射到特征矩陣向量):

現在我嘗試做同樣的事情,但是對於一個矩陣列表。 我不能讓它工作。 我擁有的:

頭文件 (Header.h) 中的 C++ 函數如下所示:

float MyCppFunction(std::vector<Eigen::Map<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>> &inputMatrixList);

在我的 CythonFile.pyx 文件中,我有:

cdef extern from "Header.h":
    cdef void _MyCppFunction "MyCppFunction"(vector[FlattenedMapWithOrder[Matrix, float, Dynamic, Dynamic, RowMajor]] &)

def my_python_function(list my_matrix_list)
    cdef vector[FlattenedMapWithOrder[Matrix, float, Dynamic, Dynamic, RowMajor]] map
    
    for matrix in my_matrix_list:
        map.push_back(FlattenedMapWithOrder[Matrix, float, Dynamic, Dynamic, RowMajor](matrix))
        
   return _MyCppFunction(map)

不幸的是,這不會編譯。

例如,當我簡單地使用我想映射到std::vector<int>int list時,這個概念確實可以編譯和運行。 但是,當我將 Numpy 矩陣列表映射到特征矩陣向量時(這是我在上面指出的情況),它不起作用。


我得到的錯誤:

我在編譯過程中得到的錯誤: error C2664: 'float MyCppFunction(std::vector<Eigen::Map<Eigen::Matrix<float,-1,-1,1,-1,-1>,0,Eigen::Stride<0,0>>,std::allocator<Eigen::Map<Eigen::Matrix<float,-1,-1,1,-1,-1>,0,Eigen::Stride<0,0>>>> &)': cannot convert argument 1 from 'std::vector<eigency::FlattenedMap<Eigen::Matrix,float,-1,-1,1,0,0,0,-1,-1>,std::allocator<eigency::FlattenedMap<Eigen::Matrix,float,-1,-1,1,0,0,0,-1,-1>>>' to 'std::vector<Eigen::Map<Eigen::Matrix<float,-1,-1,1,-1,-1>,0,Eigen::Stride<0,0>>,std::allocator<Eigen::Map<Eigen::Matrix<float,-1,-1,1,-1,-1>,0,Eigen::Stride<0,0>>>> &' ./Header.h(21): note: see declaration of 'MyCppFunction'


到目前為止我的分析:

這按預期工作,因此:我可以將int的 python list分配給 C++ std::vector<int>

這也按預期工作,因此:我可以將eigency::FlattenedMap<Eigen::Matrix,float,-1,-1,1,0,0,0,-1,-1>的變量分配給變量類型Eigen::Map<Eigen::Matrix<float,-1,-1,1,-1,-1>,0,Eigen::Stride<0,0>>

當我將后兩個變量包裝在列表/向量中時,我無法分配此變量: std::vector<eigency::FlattenedMap<Eigen::Matrix,float,-1,-1,1,0,0,0,-1,-1>, std::allocator<eigency::FlattenedMap<Eigen::Matrix,float,-1,-1,1,0,0,0,-1,-1>>>到一個變量類型std::vector<Eigen::Map<Eigen::Matrix<float,-1,-1,1,-1,-1>,0,Eigen::Stride<0,0>>, std::allocator<Eigen::Map<Eigen::Matrix<float,-1,-1,1,-1,-1>,0,Eigen::Stride<0,0>>>>

也許它與分配器部分有關,但我不知道,因為我不是真正的 C++ 專家。 有人有將 Numpy 矩陣列表映射到特征矩陣向量的解決方案嗎? 最好遵循與上述相同的模式,但也歡迎其他解決方案。


我要重現的源代碼:

下面是我用來測試的源代碼。 它有一個接受普通特征/Numpy 矩陣的函數,以及一個接受特征/Numpy 矩陣的向量/列表的函數。

如果您注釋掉向量變體的所有段落,代碼就會編譯。 否則,我會收到編譯錯誤。 我在編譯時遇到的(第一個)編譯錯誤是: .\\source_cpp_cython/cpp_source_cpp.h(16): error C2065: 'FlattenedMapWithOrder': undeclared identifier

我在 Windows 上使用 Microsoft Visual Studio 編譯器 (MSVC) 2019。 如果相關,我也使用 Eigen 版本 3.4.0-rc1。

cython_source.pyx

# distutils: language = c++
# distutils: sources = source_cpp_cython/cpp_source_cpp.cpp


from eigency.core cimport *  # Docs: https://pypi.org/project/eigency/1.4/
from libcpp.vector cimport vector
import numpy as np

cimport numpy
cdef extern from "source_cpp_cython/cpp_source_cpp.h":
    cdef float _MyCppFunction "MyCppFunction"(
                FlattenedMapWithOrder[Matrix, float, Dynamic, Dynamic, RowMajor] &
                )

    cdef float _MyCppFunctionVector "MyCppFunctionVector"(
                vector[FlattenedMapWithOrder[Matrix, float, Dynamic, Dynamic, RowMajor]] &
                )


def my_python_function(np.ndarray[ndim=2, dtype=np.float32_t] my_matrix):
    cdef FlattenedMapWithOrder[Matrix, float, Dynamic, Dynamic, RowMajor] my_matrix_cpp

    my_matrix_cpp = FlattenedMapWithOrder[Matrix, float, Dynamic, Dynamic, RowMajor](my_matrix)

    return _MyCppFunction(my_matrix_cpp)


def my_python_function_vector(list my_matrix_list):
    cdef vector[FlattenedMapWithOrder[Matrix, float, Dynamic, Dynamic, RowMajor]] matrix_map_vec
    cdef FlattenedMapWithOrder[Matrix, float, Dynamic, Dynamic, RowMajor] my_matrix_cpp

    for my_matrix in my_matrix_list:
        my_matrix_cpp = FlattenedMapWithOrder[Matrix, float, Dynamic, Dynamic, RowMajor](my_matrix)
        matrix_map_vec.push_back(my_matrix_cpp)

    return _MyCppFunctionVector(matrix_map_vec)

cpp_source_cpp.h

#pragma once
#include <Eigen/Dense>
#include <Eigen/Core>

#include <vector>
#include <numpy/ndarraytypes.h>
#include <complex>
typedef ::std::complex< double > __pyx_t_double_complex;
typedef ::std::complex< float > __pyx_t_float_complex;
#include "eigency_cpp.h"

float MyCppFunction(
    const Eigen::Map<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>& inputMatrix
    );

float MyCppFunctionVector(
    const std::vector<FlattenedMapWithOrder<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>>& inputMatrixList
    );

cpp_source_cpp.cpp

#include "cpp_source_cpp.h"

float MyCppFunction(
    const Eigen::Map<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>& inputMatrix
    )
{
//    std::vector<FlattenedMapWithOrder<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>> test;
    return 5.0;
}

float MyCppFunctionVector(
    const std::vector<FlattenedMapWithOrder<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>>& inputMatrixList
    )
{
    //Convert FlattenedMap to Eigen-Map.
    std::vector<Eigen::Map<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> convertedMatrixList(
                                                                                                            inputMatrixList.begin(), inputMatrixList.end() );
    return 6.0;
}

我的 cython 設置文件setup_cython_module.py中的相關段落是:

# Some constants
SOURCE_FOLDER_NAME = "source_cpp_cython"
OUTPUT_FOLDER_NAME = "cython_module"

# Build extensions list
extensions = [
    Extension(f"{OUTPUT_FOLDER_NAME}.{OUTPUT_FOLDER_NAME}",
              [f"{SOURCE_FOLDER_NAME}/cython_source.pyx"],
              include_dirs=["."] + [f"{SOURCE_FOLDER_NAME}"]
                           + [f"{SOURCE_FOLDER_NAME}\Eigen"] + eigency.get_includes(include_eigen=False)
                           + [numpy.get_include()],
              language='c++',
              # extra_compile_args=['/MT'],  # To let the Microsoft compiler use a specific lib for threading required by OpenCV.
              )
    ]

# Build cython package
dist = setup(
    name=f"{OUTPUT_FOLDER_NAME}",
    version="1.0",
    ext_modules=cythonize(extensions, language_level="3"),  # , gdb_debug=True),
    packages=[f"{OUTPUT_FOLDER_NAME}"]
    )

Cython 的完整日志輸出:

Created output directory:  D:\Default_Folders\Documents\Development\RepoStefan\CythonTest\cython_module

running build_ext
building 'cython_module.cython_module' extension
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\bin\HostX86\x64\cl.exe /c /nologo /Ox /W3 /GL /DNDEBUG /MD -IC:\ProgramData\Miniconda3\envs\cenv38rl\lib\site-packages\eigency -I. -Isource_cpp_cython -Isource_cpp_cython\Eigen -IC:\ProgramData\Miniconda3\envs\cenv38rl\lib\site-packages\numpy\core\include -IC:\ProgramData\Miniconda3\envs\cenv38rl\include -IC:\ProgramData\Miniconda3\envs\cenv38rl\include "-IC:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\ATLMFC\include" "-IC:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include" "-IC:\Program Files (x86)\Windows Kits\NETFXSDK\4.8\include\um" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\ucrt" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\shared" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\um" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\winrt" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\cppwinrt" /EHsc /Tpsource_cpp_cython/cython_source.cpp /Fobuild\temp.win-amd64-3.8\Release\source_cpp_cython/cython_source.obj /MT
cl : Command line warning D9025 : overriding '/MD' with '/MT'
cython_source.cpp
C:\ProgramData\Miniconda3\envs\cenv38rl\lib\site-packages\numpy\core\include\numpy\npy_1_7_deprecated_api.h(14) : Warning Msg: Using deprecated NumPy API, disable it with #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
.\source_cpp_cython/cpp_source_cpp.h(17): error C2065: 'FlattenedMapWithOrder': undeclared identifier
.\source_cpp_cython/cpp_source_cpp.h(17): error C2275: 'Eigen::Matrix<float,-1,-1,1,-1,-1>': illegal use of this type as an expression
.\source_cpp_cython/cpp_source_cpp.h(17): note: see declaration of 'Eigen::Matrix<float,-1,-1,1,-1,-1>'
.\source_cpp_cython/cpp_source_cpp.h(17): error C2974: 'std::vector': invalid template argument for '_Ty', type expected
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\vector(443): note: see declaration of 'std::vector'
.\source_cpp_cython/cpp_source_cpp.h(17): error C2976: 'std::vector': too few template arguments
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\vector(443): note: see declaration of 'std::vector'
.\source_cpp_cython/cpp_source_cpp.h(17): error C2143: syntax error: missing ')' before '>'
.\source_cpp_cython/cpp_source_cpp.h(17): error C2059: syntax error: '>'
.\source_cpp_cython/cpp_source_cpp.h(18): error C2059: syntax error: ')'
source_cpp_cython/cython_source.cpp(1964): error C2664: 'float MyCppFunctionVector(const std::vector)': cannot convert argument 1 from 'std::vector<eigency::FlattenedMap<Eigen::Matrix,float,-1,-1,1,0,0,0,-1,-1>,std::allocator<eigency::FlattenedMap<Eigen::Matrix,float,-1,-1,1,0,0,0,-1,-1>>>' to 'const std::vector'
source_cpp_cython/cython_source.cpp(1964): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
.\source_cpp_cython/cpp_source_cpp.h(16): note: see declaration of 'MyCppFunctionVector'
error: command 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Tools\\MSVC\\14.29.30037\\bin\\HostX86\\x64\\cl.exe' failed with exit status 2

Process finished with exit code 1

非常感謝!

問題是, FlattenedMapWithOrderEigen::Map -這是Eigen::Map加上指針numpy的陣列:

class FlattenedMap: public MapBase<EigencyDenseBase<Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>, _MapOptions, Eigen::Stride<_StrideOuter, _StrideInner> >  {
public:
    ...
    operator Base() const {
        return static_cast<Base>(*this);
    }
...
private:
    PyArrayObject * const object_;
};

雖然由於operator Base可以使用從FlattenedMapWithOrderEigen::Map隱式轉換(順便說一句,這有點“危險”,因為一旦原始FlattenedMapWithOrder對象被銷毀,新的Eigen::Map將具有懸空指針),不可能將std::vector<FlattenedMapWithOrder>std::vector<Eigen::Map> - 這在 C++ 中不允許的

因此,您必須手動執行轉換。

首先,它不會削減它以使用static_cast (就像它為FlattenedMaptWithOrderEigen::Map所做的那樣)這些類具有不同的內存布局(附加object_成員)並且它僅適用於長度為 1 的向量。

這意味着我們需要創建一個正確類型的臨時向量,可以按如下方式完成:

cdef extern from "Header.h":
    """
       void MyCppFunction(std::vector<FlattenedMapWithOrder<Eigen::Matrix,float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>& vec){
           // convert:
           std::vector<Eigen::Map<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> mat_vec(vec.begin(), vec.end());
           // use converted vector:
           MyCppFunction(mat_vec);
    }
    """
    cdef void _MyCppFunction "MyCppFunction"(vector[FlattenedMapWithOrder[Matrix, float, Dynamic, Dynamic, RowMajor]] &)

所以基本上我們引入了一個代理,它會從 Cython 調用,並會創建一個正確類型的臨時向量,然后調用原始函數。

感謝@ead,我找到了解決方案。

FlattenedMapWithOrder具有實現,因此可以將其分配給Eigen::Matrix 然而, std::vector沒有這樣的功能,並且由於std::vector<FlattenedMapWithOrder>std::vector<Eigen::Matrix>是不同的類型,它們不能相互分配。 更多關於這里的信息 上面提到的FlattenedMapWithOrder中的實現在這里

為了解決這個問題,從 Cython 調用的 C++ 代碼中的函數需要簡單地將匹配類型作為輸入參數: std::vector<FlattenedMapWithOrder> 為此,C++ 代碼需要知道FlattenedMapWithOrder類型的定義。

為此,您需要#include "eigency_cpp.h" 不幸的是,這個標題不是自包含的。 因此,(歸功於@ead)我添加了以下幾行:

#include <numpy/ndarraytypes.h>
#include <complex>
typedef ::std::complex< double > __pyx_t_double_complex;
typedef ::std::complex< float > __pyx_t_float_complex;
#include "eigency_cpp.h"

使用這個,我能夠在我的 C++ 代碼中聲明這個函數:

void MyCppFunctionVector(
    const std::vector<eigency::FlattenedMap<Eigen::Matrix, float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>& inputMatrixList,
    std::vector<eigency::FlattenedMap<Eigen::Matrix, float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>& outputMatrixList
    );

我的 *.pxy 文件如下所示:

cdef extern from "source_cpp_cython/cpp_source_cpp.h":
    cdef void _MyCppFunctionVector "MyCppFunctionVector"(
                vector[FlattenedMapWithOrder[Matrix, float, Dynamic, Dynamic, RowMajor]] &,
                vector[FlattenedMapWithOrder[Matrix, float, Dynamic, Dynamic, RowMajor]] &
                )
                
                
def my_python_function_vector(
        list my_matrix_list_input,
        list my_matrix_list_output
    ):
    cdef vector[FlattenedMapWithOrder[Matrix, float, Dynamic, Dynamic, RowMajor]] matrix_map_vec_input
    cdef FlattenedMapWithOrder[Matrix, float, Dynamic, Dynamic, RowMajor] my_matrix_input_cpp
    cdef vector[FlattenedMapWithOrder[Matrix, float, Dynamic, Dynamic, RowMajor]] matrix_map_vec_output
    cdef FlattenedMapWithOrder[Matrix, float, Dynamic, Dynamic, RowMajor] my_matrix_output_cpp

    # Convert input matrix to C++ type.
    for my_matrix_input in my_matrix_list_input:
        my_matrix_input_cpp = FlattenedMapWithOrder[Matrix, float, Dynamic, Dynamic, RowMajor](my_matrix_input)
        matrix_map_vec_input.push_back(my_matrix_input_cpp)

    for my_matrix_output in my_matrix_list_output:
        my_matrix_output_cpp = FlattenedMapWithOrder[Matrix, float, Dynamic, Dynamic, RowMajor](my_matrix_output)
        matrix_map_vec_output.push_back(my_matrix_output_cpp)

    # Call the C++ function.
    _MyCppFunctionVector(matrix_map_vec_input, matrix_map_vec_output)
    return my_matrix_list_output                
    

就是這樣。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM