【问题标题】:Pass complex numpy array to C++ in Cython在 Cython 中将复杂的 numpy 数组传递给 C++
【发布时间】:2018-02-27 07:43:40
【问题描述】:

我想对 pyx 脚本的一部分进行 Cythonize,该脚本涉及处理具有复数的 numpy 数组。 python 脚本的相关部分如下所示:

M = np.dot(N , Q)

在我的工作中,NQM 是带有复数条目的 numpy 数组。

具体来说,我想将矩阵NQ 转移到C++ 代码并在C++ 中进行矩阵乘法。

虽然我知道使用指向 C++ 脚本的指针来传输实值 numpy 数组的方法,然后使用 cython,但对于如何处理具有复杂值的 numpy 数组,我有点困惑。

这就是我目前尝试将数组从pyx 转移到C++ 的方式。

import numpy as np
cimport numpy as np

cdef extern from "./matmult.h" nogil:
    void mult(double* M, double* N, double* Q)

def sim():    
    cdef:
        np.ndarray[np.complex128_t,ndim=2] N = np.zeros(( 2 , 2 ), dtype=np.float64)
        np.ndarray[np.complex128_t,ndim=2] Q = np.zeros(( 2 , 2 ), dtype=np.float64)  
        np.ndarray[np.complex128_t,ndim=2] M = np.zeros(( 2 , 2 ), dtype=np.float64)          

    N = np.array([[1.1 + 2j,2.2],[3.3,4.4]])
    Q = np.array([[3.3,4.4+5j],[5.5,6.6]])  

    mult(&M[0,0], &N[0,0], &Q[0,0])
    print M

这是我的 C++ 代码:

#include "matmult.h"
using namespace std;

int main(){}

void mult(double *M, double *N, double *Q)
{
  double P[2][2], A[2][2], B[2][2];

  for (int i=0; i<2; i++)
  {
    for (int j=0; j<2; j++)
    {
      A[i][j] = *( N + ((2*i) + j) );
      B[i][j] = *( Q + ((2*i) + j) );
      P[i][j] = 0;      
    }
  }

  for (int i=0; i<2; i++)
  {
    for (int j=0; j<2; j++)
    {
      for (int k=0; k<2; k++)
      {
         P[i][j] += A[i][k]*B[k][i];  
      }
    }
  }  

  for (int i=0; i<2; i++)
  {
    for (int j=0; j<2; j++)
    {
      *( M + ((2*i) + j) ) = P[i][j];      
    }
  }
}

当我使用 cython 编译时,出现以下错误

mat.pyx:17:27: Cannot assign type 'double complex *' to 'double *'

如果能在这里得到一些帮助,我将不胜感激。

【问题讨论】:

    标签: python c++ numpy cython cythonize


    【解决方案1】:

    此错误消息告诉您出了什么问题:

    mat.pyx:17:27: 无法将类型 'double complex *' 分配给 'double *'

    也就是说,你有一个来自 numpy 的双复数指针(指向 complex128 numpy dtype 的指针),你正试图使用​​双指针将它传递给 C++ 函数。 C++ 需要能够处理复数,所以如果你改变你的 double* -> std::complex 这应该可以解决你的问题

    void mult(double *M, double *N, double *Q)
    

    变成

    #include <complex>
    void mult(std::complex<double> *M, std::complex<double> *N, std::complex<double> *Q)
    

    numpy 矩阵乘法是否不足以满足您的用例? Cython 可能有点矫枉过正。

    编辑:好的,我终于明白了,处理 C++ std::complex 和 C double _Complex 类型有点奇怪。

    cppmul.pyx:

    import numpy as np
    cimport numpy as np
    
    cdef extern from "./matmult.h" nogil:
        void mult(np.complex128_t* M, np.complex128_t* N, np.complex128_t* Q)
    
    def sim():
        cdef:
            np.ndarray[np.complex128_t,ndim=2] N = np.zeros(( 2 , 2 ), dtype=np.complex128)
            np.ndarray[np.complex128_t,ndim=2] Q = np.zeros(( 2 , 2 ), dtype=np.complex128)
            np.ndarray[np.complex128_t,ndim=2] M = np.zeros(( 2 , 2 ), dtype=np.complex128)
    
        N = np.array([[1.1 + 2j,2.2],[3.3,4.4]])
        Q = np.array([[3.3,4.4+5j],[5.5,6.6]])
    
        mult(&M[0,0], &N[0,0], &Q[0,0])
        print M
    

    matmul.c:

    #include "matmult.h"
    
    void mult(complex_t *M, complex_t *N, complex_t *Q)
    {
      complex_t P[2][2], A[2][2], B[2][2];
    
      for (int i=0; i<2; i++)
      {
        for (int j=0; j<2; j++)
        {
          A[i][j] = *( N + ((2*i) + j) );
          B[i][j] = *( Q + ((2*i) + j) );
          P[i][j] = 0;
        }
      }
    
      for (int i=0; i<2; i++)
      {
        for (int j=0; j<2; j++)
        {
          for (int k=0; k<2; k++)
          {
             P[i][j] += A[i][k]*B[k][i];
          }
        }
      }
    
      for (int i=0; i<2; i++)
      {
        for (int j=0; j<2; j++)
        {
          *( M + ((2*i) + j) ) = P[i][j];
        }
      }
    }
    

    matmult.h:

    #include <complex.h>
    
    typedef double _Complex complex_t;
    void mult(complex_t *M, complex_t *N, complex_t *Q);
    

    setup.py:

    from distutils.core import setup
    from Cython.Build import cythonize
    from distutils.extension import Extension
    import numpy as np
    
    sourcefiles = ['cppmul.pyx', 'matmult.c']
    
    extensions = [Extension("cppmul",
                            sourcefiles,
                            include_dirs=[np.get_include()],
                            extra_compile_args=['-O3']
    )]
    
    setup(
        ext_modules = cythonize(extensions)
    )
    

    在运行python setup.py build_ext --inplace 后,它会按预期导入并运行

    import cppmul
    cppmul.sim()
    

    结果:

    [[15.73 +6.6j 15.73 +6.6j]
     [43.56+16.5j 43.56+16.5j]]
    

    【讨论】:

    • evanmicur:实际上,我在这里只给出了完整代码的骨架。在我的实际工作中,我正在处理具有数十万个条目的矩阵。 Numpy 矩阵乘法对此非常慢,尤其是当矩阵条目是复数时。这就是我想在这里使用 Cython 的原因。如果你能提出一个更好的方法来解决这个问题,那对我来说会很棒。另外,我尝试实施您的建议 (std::complex&lt;double&gt; *M)。它可能接近解决问题,但我仍然在编译时收到此错误:error: command 'gcc' failed with exit status 1
    • 只是为了确保:您使用的是 numpy.dot(A, b) 或 (A @ B in 3.4+) 操作?手写循环会很慢。 Numpy 将使用 C/Fortran 后端进行矩阵乘法,我真的不认为你有很好的机会通过编写自己的 C 代码来击败它。但是如果你坚持,编译错误有点难以解决——错误 1 ​​只是意味着它失败了,所以这里没有足够的细节。确保您在整个 C++ 代码中的类型保持一致。如果您确实需要非常快的 matmul,您可能需要考虑使用 petsc4py 之类的并行矩阵库。
    • evanmicur:这是完整的错误:mat.pyx:6:18: Expected an identifier or literal building 'matmult_cy' extension gcc -fno-strict-aliasing -I/Users/anaconda2/include -arch x86_64 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -I/Users/anaconda2/lib/python2.7/site-packages/numpy/core/include -I/Users/anaconda2/include/python2.7 -c mat.cpp -o build/temp.macosx-10.6-x86_64-2.7/mat.o -O3 cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++ ^ error: command 'gcc' failed with exit status 1
    • evanmicur:我一直在使用 numpy(A,b)。即使在那之后它也很慢。我从未使用过petsc4py。你知道我可以从中学习如何做到这一点的有用资源吗?
    • 不幸的是,我发现文档帮助不大,但它可能在 conda 上。它是一个 c 库 petsc 的 python 包装器。给我一分钟,我会测试一些东西并回复你
    【解决方案2】:

    试试这个

        #include "matmult.h"
     using namespace std;
    
    int main(){}
    
    void mult(double *M, double *N, double *Q)
    {
     double P[2][2], A[2][2], B[2][2];
    
     for (int i=0; i<2; i++)
     {
    for (int j=0; j<2; j++)
    {
      A[i][j] = *( N + ((2*i) + j) );
      B[i][j] = *( Q + ((2*i) + j) );
      P[i][j] = 0;      
      }
      }
    
      for (int i=0; i<2; i++)
      {
        for (int j=0; j<2; j++)
        {
        for (int k=0; k<2; k++)
        {
           P[i][j] += A[i][k]*B[k][i];  
         }
     }
    }  
    
    for (int i=0; i<2; i++)
     {
      for (int j=0; j<2; j++)
      {
      *(  ((2*i) + j) )+ M = P[i][j];      
      }
     }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多