python3/numpy:复数相加时原地相加不一致

python3/numpy: in place addition inconsistency when adding a complex number

我在测试一段代码时遇到了以下行为。虽然我自己找到了解释和解决方案(this 看起来相关但不相同),我想在这里问问你们是否还有更多补充。

a) 我无法就地添加一个复数到一个 numpy 数组 创建为真实的,但我可以使用标量变量

b) 仅在有限数量的情况下发出警告消息, 而且不是很一致

看看这些命令:

>>> from numpy import *
>>> a5=arange(5)
>>> a5[1:5]+=1j
>>> a5
array([0, 1, 2, 3, 4])
>>> a=8
>>> a+=1j
>>> a
(8+1j)
>>> a5+=1j
__main__:1: ComplexWarning: Casting complex values to real discards the imaginary part
>>> a5[3]+=1j
>>> a5[3]
3
>>> 

看到了吗? ComplexWarning 仅在尝试将复数添加到整个向量时发出,但在尝试将相同的位置添加到单个元素或切片时不会发出。

这有什么原因吗? (我几乎无法忍受它没有添加的事实,但肯定不是仅在一种情况下发出警告的事实)。 这是错误还是功能? 怎样才能最好地克服它? 至少,当虚部因不兼容而被丢弃时,在所有情况下出现 ComplexWarning 不是更好吗?

PS。同时我提供一个解决方案: z5=arange(5)+0jc5=arange(5,dtype="complex") 都将按预期运行。但第一个只是部分缓解,因为它表明可以添加一个 numpy 范围的整数并将其分配给一个变量,前提是它是一个复杂的变量,或者是一个正在创建的新变量。

这被认为是一个问题,但由于修复它会破坏兼容性,因此需要时间进行更改。

从 numpy 1.10 开始,就地操作和 ufuncs 的默认转换规则已更改为 "same_kind",因此您的代码将发出 TypeError:

TypeError: Cannot cast ufunc add output from dtype('complex128') to dtype('int64') with casting rule 'same_kind'

来自release notes

Default casting for inplace operations will change to ‘same_kind’ in Numpy 1.10.0. This will certainly break some code that is currently ignoring the warning.

NumPy 次发出警告,但默认情况下 Python 的一般警告机制仅在第一次发出时打印警告。您可以使用 warnings.filterwarningswarnings.simplefilter 更改此行为。有关详细信息,请参阅 warnings module documentation

这是一个示例(使用 Python 2.7 和 NumPy 1.9),显示了针对每个就地操作发出的警告。

Python 2.7.10 |Master 2.1.0.dev1829 (64-bit)| (default, Oct 21 2015, 09:09:19) 
[GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.6)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from numpy import *
>>> import warnings
>>> warnings.simplefilter('always', ComplexWarning)
>>> a5 = arange(5)
>>> a5[1:5] += 1j
__main__:1: ComplexWarning: Casting complex values to real discards the imaginary part
>>> a5
array([0, 1, 2, 3, 4])
>>> a5 += 1j
__main__:1: ComplexWarning: Casting complex values to real discards the imaginary part
>>> a5[3] += 1j
__main__:1: ComplexWarning: Casting complex values to real discards the imaginary part

您展示的标量加法完全不同。

>>> a = 8
>>> a += 1j

在这种情况下,+= 操作没有进行就地操作(它不能,因为在第一行之后,a 是一个 Python int,与 NumPy 数组不同,int 对象是不可变的)。相反,它相当于做 a = a + 1j。那就是构建一个 new Python complex 对象,并重新绑定 a 以引用该新对象。

你说你"barely can stand the fact that it doesn't make the addition"。但请注意,没有办法有效地这样做:原始数组的内容是使用每个项目 8 个字节存储的。总和的内容是(双精度)复数,因此必须使用每项 16 个字节来存储。这使得在这里不可能进行真正的就地操作。

请注意,在 NumPy 1.10 中,这些操作将产生 TypeError 而不是警告。