Python — передать сложный массив NumPy C ++ в Cython

Я хочу Cythonize часть pyx скрипт, который включает в себя работу с массивами с комплексными числами. Соответствующая часть скрипта Python выглядит следующим образом:

M = np.dot(N , Q)

В моей работе, N, Q а также M являются массивами с записями комплексных чисел.

Конкретно хочу перенести матрицы N а также Q к C++ код и сделать матричное умножение в C++,

В то время как я знаю метод для передачи реально значимых массивов с использованием указателей на C++ Сценарий, за которым следует использование Cython, меня немного смущает вопрос о том, как мне подходить к массивным массивам со сложными значениями.

Вот как я пытаюсь передать массив из 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 *'

Я буду благодарен за помощь здесь.

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)

Разве умножение матрицы на кусочки не достаточно для вашего случая использования? 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]]
1

Другие решения

попробуй это

    #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];
}
}
}
0