在 Cython 中将复杂的 numpy 数组传递给 C++
Pass complex numpy array to C++ in Cython
我想对 pyx
脚本的一部分进行 Cythonize,该脚本涉及使用具有复数的 numpy 数组。 python 脚本的相关部分如下所示:
M = np.dot(N , Q)
在我的工作中,N
、Q
和 M
是具有复数条目的 numpy 数组。
具体来说,我想将矩阵 N
和 Q
转换为 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 *'
如果能在这里得到一些帮助,我将不胜感激。
试试这个
#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];
}
}
}
此错误消息告诉您出了什么问题:
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]]
我想对 pyx
脚本的一部分进行 Cythonize,该脚本涉及使用具有复数的 numpy 数组。 python 脚本的相关部分如下所示:
M = np.dot(N , Q)
在我的工作中,N
、Q
和 M
是具有复数条目的 numpy 数组。
具体来说,我想将矩阵 N
和 Q
转换为 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 *'
如果能在这里得到一些帮助,我将不胜感激。
试试这个
#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];
}
}
}
此错误消息告诉您出了什么问题:
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]]