我能找到的最简单的解决此问题的方法是简单地改变乘法的顺序。
如果在
testcplx.pyx
中,我更改:
varc128 = varc128 * varf64
至
varc128 = varf64 * varc128
我从之前的失败情况转变为正确工作的情况。这种情况很有用,因为它允许直接对生成的C代码进行比较。
简短概括
乘法运算的顺序会影响翻译,这意味着在失败的版本中,乘法是通过__pyx_t_npy_float64_complex
类型尝试进行的,而在工作的版本中,则是通过__pyx_t_double_complex
类型进行的。这反过来引入了无效的typedef行typedef npy_float64 _Complex __pyx_t_npy_float64_complex;
我相当确定这是一个cython的bug(更新:在此处报告)。虽然这是一个非常古老的gcc bug报告,但回复明确指出(说它实际上不是gcc的bug,而是用户代码错误):
typedef R _Complex C;
This is not valid code; you can't use _Complex together with a typedef,
only together with "float", "double" or "long double" in one of the forms
listed in C99.
他们得出结论,
double _Complex
是一个有效的类型说明符,而
ArbitraryType _Complex
不是。
这份较新的报告 也有相同类型的回应——尝试在非基本类型上使用
_Complex
超出了规范,
GCC 手册 指出
_Complex
只能与
float
、
double
和
long double
一起使用。
因此,我们可以修改 Cython 生成的 C 代码进行测试:将 typedef npy_float64 _Complex __pyx_t_npy_float64_complex;
替换为 typedef double _Complex __pyx_t_npy_float64_complex;
并验证它确实有效,并且可以使输出代码编译通过。
代码简短的远足
交换乘法顺序只是突出了编译器告诉我们的问题。在第一种情况下,有问题的行是这一行:typedef npy_float64 _Complex __pyx_t_npy_float64_complex;
- 它试图将类型npy_float64
和关键字_Complex
分配给类型__pyx_t_npy_float64_complex
。
float _Complex
或double _Complex
是有效的类型,而npy_float64 _Complex
不是。为了看到效果,您可以从该行中删除npy_float64
,或者将其替换为double
或float
,代码可以编译通过。接下来的问题是为什么会产生那一行...
这似乎是由Cython源代码中的this line生成的。
为什么乘法的顺序会显着改变代码 - 以至于引入了类型__pyx_t_npy_float64_complex
,并以一种失败的方式引入?
在失败的实例中,实现乘法的代码将varf64
转换为__pyx_t_npy_float64_complex
类型,对实部和虚部进行乘法,然后重新组装复数。在工作版本中,它通过__Pyx_c_prod
函数直接使用__pyx_t_double_complex
类型进行乘积。
我想这就像是Cython代码从它遇到的第一个变量中获得其乘法类型一样简单。在第一个情况下,它看到一个float64,因此生成基于该类型的(无效)C代码,而在第二个情况下,它看到了(double)complex128类型,并基于此进行翻译。这个解释有点含糊不清,如果时间允许,我希望能回到对它的分析...
关于这一点 -
在这里我们看到npy_float64
的
typedef
是
double
,因此在这种情况下,修复可能包括修改
这里的代码以使用
double _Complex
,其中
type
是
npy_float64
,但这超出了SO答案的范围,也没有提供通用解决方案。
C代码差异结果
工作版本
从行“varc128 = varf64 * varc128”创建此C代码
__pyx_v_8testcplx_varc128 = __Pyx_c_prod(__pyx_t_double_complex_from_parts(__pyx_v_8testcplx_varf64, 0), __pyx_v_8testcplx_varc128);
失败版本
从行 varc128 = varc128 * varf64
生成此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));
这需要额外导入的东西 - 问题所在是这行代码:
typedef npy_float64 _Complex __pyx_t_npy_float64_complex;
,它尝试将类型
npy_float64
和 类型
_Complex
分配给类型
__pyx_t_npy_float64_complex
。
#if CYTHON_CCOMPLEX
#ifdef __cplusplus
typedef ::std::complex< npy_float64 > __pyx_t_npy_float64_complex;
#else
typedef npy_float64 _Complex __pyx_t_npy_float64_complex;
#endif
#else
typedef struct { npy_float64 real, imag; } __pyx_t_npy_float64_complex;
#endif
static CYTHON_INLINE __pyx_t_npy_float64_complex __pyx_t_npy_float64_complex_from_parts(npy_float64, npy_float64);
#if CYTHON_CCOMPLEX
#define __Pyx_c_eq_npy_float64(a, b) ((a)==(b))
#define __Pyx_c_sum_npy_float64(a, b) ((a)+(b))
#define __Pyx_c_diff_npy_float64(a, b) ((a)-(b))
#define __Pyx_c_prod_npy_float64(a, b) ((a)*(b))
#define __Pyx_c_quot_npy_float64(a, b) ((a)/(b))
#define __Pyx_c_neg_npy_float64(a) (-(a))
#ifdef __cplusplus
#define __Pyx_c_is_zero_npy_float64(z) ((z)==(npy_float64)0)
#define __Pyx_c_conj_npy_float64(z) (::std::conj(z))
#if 1
#define __Pyx_c_abs_npy_float64(z) (::std::abs(z))
#define __Pyx_c_pow_npy_float64(a, b) (::std::pow(a, b))
#endif
#else
#define __Pyx_c_is_zero_npy_float64(z) ((z)==0)
#define __Pyx_c_conj_npy_float64(z) (conj_npy_float64(z))
#if 1
#define __Pyx_c_abs_npy_float64(z) (cabs_npy_float64(z))
#define __Pyx_c_pow_npy_float64(a, b) (cpow_npy_float64(a, b))
#endif
#endif
#else
static CYTHON_INLINE int __Pyx_c_eq_npy_float64(__pyx_t_npy_float64_complex, __pyx_t_npy_float64_complex);
static CYTHON_INLINE __pyx_t_npy_float64_complex __Pyx_c_sum_npy_float64(__pyx_t_npy_float64_complex, __pyx_t_npy_float64_complex);
static CYTHON_INLINE __pyx_t_npy_float64_complex __Pyx_c_diff_npy_float64(__pyx_t_npy_float64_complex, __pyx_t_npy_float64_complex);
static CYTHON_INLINE __pyx_t_npy_float64_complex __Pyx_c_prod_npy_float64(__pyx_t_npy_float64_complex, __pyx_t_npy_float64_complex);
static CYTHON_INLINE __pyx_t_npy_float64_complex __Pyx_c_quot_npy_float64(__pyx_t_npy_float64_complex, __pyx_t_npy_float64_complex);
static CYTHON_INLINE __pyx_t_npy_float64_complex __Pyx_c_neg_npy_float64(__pyx_t_npy_float64_complex);
static CYTHON_INLINE int __Pyx_c_is_zero_npy_float64(__pyx_t_npy_float64_complex);
static CYTHON_INLINE __pyx_t_npy_float64_complex __Pyx_c_conj_npy_float64(__pyx_t_npy_float64_complex);
#if 1
static CYTHON_INLINE npy_float64 __Pyx_c_abs_npy_float64(__pyx_t_npy_float64_complex);
static CYTHON_INLINE __pyx_t_npy_float64_complex __Pyx_c_pow_npy_float64(__pyx_t_npy_float64_complex, __pyx_t_npy_float64_complex);
#endif
#endif
typedef _Complex npy_float64 __pyx_t_npy_float64_complex;
(请注意,此语句中首先出现复数)。这可以通过编辑.c文件或(可能)您的Cython/Includes/numpy/__init__.pxd
来交换顺序来解决。 - DavidWnp.complex64_t
和np.complex128_t
无法工作,必须改为写complex或double complex。这在0.11.3中已经修复。Cython 0.11.1及更早版本不支持复数。” - Willvarc128 = varf64 * varc128
,它似乎可以工作,但没有中间变量。您能否确认您的情况是否相同?(显然,这并不能回答更有趣的问题 - 为什么?)。我只是在尝试一些东西时注意到了这个问题,但我完全是新手cython
(即今天设置环境!),虽然之前有一些C和Python的经验。 - J Richard Snape