07-24-2023, 04:27 AM
What is the correct way to work with complex numbers in Cython?
I would like to write a pure C loop using a numpy.ndarray of dtype np.complex128. In Cython, the associated C type is defined in
``Cython/Includes/numpy/__init__.pxd`` as
ctypedef double complex complex128_t
so it seems this is just a simple C double complex.
However, it's easy to obtain strange behaviors. In particular, with these definitions
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.
the line
varc128 = varc128 * varf64
can be compiled by Cython but gcc can not compiled the C code produced (the error is "testcplx.c:663:25: error: two or more data types in declaration specifiers" and seems to be due to the line ``typedef npy_float64 _Complex __pyx_t_npy_float64_complex;``). This error has already been reported (for example [here](
Without inclusion of ``complex.h``, there is no error (I guess because the ``typedef`` is then not included).
However, there is still a problem since in the html file produced by ``cython -a testcplx.pyx``, the line ``varc128 = varc128 * varf64`` is yellow, meaning that it has not been translated into pure C. The corresponding C code is:
__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));
and the ``__Pyx_CREAL`` and ``__Pyx_CIMAG`` are orange (Python calls).
Interestingly, the line
vardc = vardc * vard
does not produce any error and is translated into pure C (just ``__pyx_v_8testcplx_vardc = __Pyx_c_prod(__pyx_v_8testcplx_vardc, __pyx_t_double_complex_from_parts(__pyx_v_8testcplx_vard, 0));``), whereas it is very very similar to the first one.
I can avoid the error by using intermediate variables (and it translates into pure C):
vardc = varc128
vard = varf64
varc128 = vardc * vard
or simply by casting (but it does not translate into pure C):
vardc = <double complex>varc128 * <double>varf64
So what happens? What is the meaning of the compilation error? Is there a clean way to avoid it? Why does the multiplication of a np.complex128_t and np.float64_t seem to involve Python calls?
Versions
--------
Cython version 0.22 (most recent version in Pypi when the question was asked) and GCC 4.9.2.
Repository
----------
I created a tiny repository with the example (`hg clone
I would like to write a pure C loop using a numpy.ndarray of dtype np.complex128. In Cython, the associated C type is defined in
``Cython/Includes/numpy/__init__.pxd`` as
ctypedef double complex complex128_t
so it seems this is just a simple C double complex.
However, it's easy to obtain strange behaviors. In particular, with these definitions
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.
the line
varc128 = varc128 * varf64
can be compiled by Cython but gcc can not compiled the C code produced (the error is "testcplx.c:663:25: error: two or more data types in declaration specifiers" and seems to be due to the line ``typedef npy_float64 _Complex __pyx_t_npy_float64_complex;``). This error has already been reported (for example [here](
[To see links please register here]
)) but I didn't find any good explanation and/or clean solution.Without inclusion of ``complex.h``, there is no error (I guess because the ``typedef`` is then not included).
However, there is still a problem since in the html file produced by ``cython -a testcplx.pyx``, the line ``varc128 = varc128 * varf64`` is yellow, meaning that it has not been translated into pure C. The corresponding C code is:
__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));
and the ``__Pyx_CREAL`` and ``__Pyx_CIMAG`` are orange (Python calls).
Interestingly, the line
vardc = vardc * vard
does not produce any error and is translated into pure C (just ``__pyx_v_8testcplx_vardc = __Pyx_c_prod(__pyx_v_8testcplx_vardc, __pyx_t_double_complex_from_parts(__pyx_v_8testcplx_vard, 0));``), whereas it is very very similar to the first one.
I can avoid the error by using intermediate variables (and it translates into pure C):
vardc = varc128
vard = varf64
varc128 = vardc * vard
or simply by casting (but it does not translate into pure C):
vardc = <double complex>varc128 * <double>varf64
So what happens? What is the meaning of the compilation error? Is there a clean way to avoid it? Why does the multiplication of a np.complex128_t and np.float64_t seem to involve Python calls?
Versions
--------
Cython version 0.22 (most recent version in Pypi when the question was asked) and GCC 4.9.2.
Repository
----------
I created a tiny repository with the example (`hg clone
[To see links please register here]
) and a tiny Makefile with 3 targets (`make clean`, `make build`, `make html`) so it is easy to test anything.