sjr*_*son 5 python windows cython complex-numbers
在我作为开发人员的一个包中,我们广泛使用 Cython 来执行涉及复数的繁重计算。该包需要可从 Unix + Windows 系统构建,因此我们需要一种方法来(重新)定义double complex与两者兼容的类型(假设我们使用 MSVC 在 Windows 上编译 Cython 扩展)。Windows 不支持double complexC99,而是定义了自己的类型_Dcomplex(请参阅https://learn.microsoft.com/en-us/cpp/c-runtime-library/complex-math-support?view=vs-2019)。
由于这种不同的类型,人们不能<complex.h>以这种方式(例如)在.pyx文件中进行外部操作:
cdef extern from "complex.h" nogil:
double cabs(double complex z)
double carg(double complex z)
double complex cexp(double complex z)
double complex conj(double complex z)
double cimag(double complex z)
double creal(double complex z)
double complex csqrt(double complex z)
Run Code Online (Sandbox Code Playgroud)
当尝试编译.c其中包含的扩展的结果文件(使用 MSVC)时,您会收到如下错误:
error C2061: syntax error: identifier '__pyx_t_double_complex'
Run Code Online (Sandbox Code Playgroud)
它来自.c文件中的这个块:
#if CYTHON_CCOMPLEX
#ifdef __cplusplus
typedef ::std::complex< double > __pyx_t_double_complex;
#else
typedef double _Complex __pyx_t_double_complex;
#endif
#else
typedef struct { double real, imag; } __pyx_t_double_complex;
#endif
Run Code Online (Sandbox Code Playgroud)
我想我可以通过以下方法解决这个问题:
IF UNAME_SYSNAME == "Windows":
cdef extern from "complex.h" nogil:
double cabs(_Dcomplex z)
double carg(_Dcomplex z)
_Dcomplex cexp(_Dcomplex z)
_Dcomplex conj(_Dcomplex z)
double cimag(_Dcomplex z)
double creal(_Dcomplex z)
_Dcomplex csqrt(_Dcomplex z)
ELSE:
cdef extern from "complex.h" nogil:
double cabs(double complex z)
double carg(double complex z)
double complex cexp(double complex z)
double complex conj(double complex z)
double cimag(double complex z)
double creal(double complex z)
double complex csqrt(double complex z)
Run Code Online (Sandbox Code Playgroud)
但这只会导致编译 Cython 文件时出现以下错误:
'_Dcomplex' is not a type identifier
Run Code Online (Sandbox Code Playgroud)
关于如何解决这个问题有什么想法吗?或者对 MSVC 替代方案的建议,以便我们可以保持我们良好的double complex基于界面的原样(请注意,所有打包都将通过 Conda 完成,因此可能存在我不知道的替代方案)?
我认为最好的选择是推出这些功能,而不尝试包装 VC 的功能 - 遗憾的是,但这就是 Windows 上现在的状态。
但如果你坚持<complex.h>在 Windows 上使用,这是我能想到的最好的办法:
第一个问题: <complex.h>在 Windows 上包含会导致错误的CYTHON_CCOMPLEX- 值,因此生成的 C 文件无法开箱即用地进行编译,这里是一个重现器:
%%cython
cdef extern from "complex.h":
pass
# activating code generation for complex:
cdef double complex b
Run Code Online (Sandbox Code Playgroud)
因错误而失败'error C2061: syntax error: identifier '__pyx_t_double_complex'- 您所看到的错误。
为了避免这种情况,必须CYTHON_CCOMPLEX=0显式地传递定义,在 IPython 中,可以通过以下方式完成(必须记住:仅在 Windows 上执行此操作):
%%cython
# distutils: define_macros=CYTHON_CCOMPLEX=0
...
Run Code Online (Sandbox Code Playgroud)
现在,我们仍然遇到问题,Cython 将复杂的实用程序放在生成的 C 代码中包含的任何内容(这也意味着 C 逐字代码)之后(这就是#include <complex.h>搞乱一切的原因),因此我们只剩下我们可以使用包含/逐字 C 代码中的技巧的前向声明(这是C 端的__pyx_t_double_complexCython )。double complex
_Dcomplex与 具有相同的内存布局__pyx_t_double_complex,但编译器不会执行隐式转换。
在前向声明的帮助下我能想到的最好的办法是__pyx_t_double_complex:
%%cython
# distutils: define_macros=CYTHON_CCOMPLEX=0
# because at this point in the C-code
# we have only the forward declaration,
# pointer is all we can use:
cdef extern from *:
"""
#include <complex.h>
struct __pyx_t_double_complex;
inline double pyx_cabs(struct __pyx_t_double_complex *z){
return cabs(*(_Dcomplex*)z);
}
"""
double pyx_cabs(double complex *z)
# here we have the whole definition
# at our disposal, so use a wrapper
# to get read of the pointer
cdef cabs(double complex z):
return pyx_cabs(&z)
#test that it works:
cdef double complex b
b.real = 1.0
b.imag = 2.0
print(cabs(b))
print(pyx_cabs(&b)) # can be used via pointer
Run Code Online (Sandbox Code Playgroud)
因此,也许最好按如下方式包装,它没有那么神奇(没有 C 逐字),但仍然需要 cdef 函数来进行显式转换:
%%cython
# distutils: define_macros=CYTHON_CCOMPLEX=0
cdef extern from "<complex.h>":
ctypedef struct _Dcomplex:
pass
# use cname-trick to avoid name-clashes:
double cabs_ "cabs"(_Dcomplex z)
# used for explicit casting:
cdef cabs(double complex z):
return cabs_((<_Dcomplex*>&z)[0])
cdef double complex b
b.real = 1.0
b.imag = 2.0
print(cabs(b))
Run Code Online (Sandbox Code Playgroud)
我没有检查这些函数是否内联在 end-pyd 文件中,因此这仍然可能是一个问题。
| 归档时间: |
|
| 查看次数: |
523 次 |
| 最近记录: |