Каков правильный способ работы с комплексными числами в Cython?
Я хотел бы написать чистый цикл C, используя numpy.ndarray из dtype np.complex128. В Cython связанный тип C определен в
Cython/Includes/numpy/__init__.pxd
как
ctypedef double complex complex128_t
поэтому кажется, что это всего лишь простой C-двойной комплекс.
Однако легко получить странное поведение. В частности, с этими определениями
cimport numpy as np
import numpy as np
np.import_array()
cdef extern from "complex.h":
pass
cdef:
np.complex128_t varc128 = 1j
np.float64_t varf64 = 1.
double complex vardc = 1j
double vard = 1.
строка
varc128 = varc128 * varf64
может быть скомпилирован Cython, но gcc не может скомпилировать полученный код C (ошибка: "testcplx.c: 663: 25: ошибка: два или более типов данных в спецификаторах объявлений" и, похоже, связана с линией typedef npy_float64 _Complex __pyx_t_npy_float64_complex;
). Эта ошибка уже сообщалась (например, здесь), но я не нашел никакого объяснения и/или чистого решения.
Без включения complex.h
ошибки нет (думаю, потому что typedef
тогда не включается).
Однако все еще существует проблема, поскольку в html файле, создаваемом cython -a testcplx.pyx
, строка varc128 = varc128 * varf64
является желтой, что означает, что она не была переведена в чистый C. Соответствующий код C:
__pyx_t_2 = __Pyx_c_prod_npy_float64(__pyx_t_npy_float64_complex_from_parts(__Pyx_CREAL(__pyx_v_8testcplx_varc128), __Pyx_CIMAG(__pyx_v_8testcplx_varc128)), __pyx_t_npy_float64_complex_from_parts(__pyx_v_8testcplx_varf64, 0));
__pyx_v_8testcplx_varc128 = __pyx_t_double_complex_from_parts(__Pyx_CREAL(__pyx_t_2), __Pyx_CIMAG(__pyx_t_2));
а __Pyx_CREAL
и __Pyx_CIMAG
- оранжевые (вызовы Python).
Интересно, что линия
vardc = vardc * vard
не вызывает ошибок и переводится в чистый C (просто __pyx_v_8testcplx_vardc = __Pyx_c_prod(__pyx_v_8testcplx_vardc, __pyx_t_double_complex_from_parts(__pyx_v_8testcplx_vard, 0));
), тогда как он очень похож на первый.
Я могу избежать ошибки, используя промежуточные переменные (и это переводит в чистый C):
vardc = varc128
vard = varf64
varc128 = vardc * vard
или просто путем литья (но он не преобразуется в чистый C):
vardc = <double complex>varc128 * <double>varf64
Так что же происходит? В чем смысл ошибки компиляции? Есть ли чистый способ избежать этого? Почему умножение np.complex128_t и np.float64_t, по-видимому, связано с вызовами Python?
Версия
Cython version 0.22 (самая последняя версия в Pypi, когда был задан вопрос) и GCC 4.9.2.
Repository
Я создал крошечный репозиторий с примером (hg clone https://bitbucket.org/paugier/test_cython_complex
) и крошечный Makefile с тремя целями (make clean
, make build
, make html
), поэтому легко проверить что-либо.