计算复杂numpy ndarray的abs()**2的最节省内存的方法

问题描述 投票:0回答:5

我正在寻找最节省内存的方法来计算复杂的 numpy ndarray 的绝对平方值

arr = np.empty((250000, 150), dtype='complex128')  # common size

我还没有找到一个 ufunc 可以完全做到

np.abs()**2

由于该大小和类型的数组占用大约半GB,我正在寻找一种主要节省内存的方法。

我还希望它是可移植的,所以最好是 ufunc 的某种组合。

到目前为止我的理解是这应该是最好的

result = np.abs(arr)
result **= 2

它将不必要地计算

(**0.5)**2
,但应该就地计算
**2
。总而言之,峰值内存需求只是原始数组大小 + 结果数组大小,它应该是 1.5 * 原始数组大小,因为结果是真实的。

如果我想摆脱无用的

**2
电话,我必须做这样的事情

result = arr.real**2
result += arr.imag**2

但如果我没有记错的话,这意味着我必须为实部和虚部计算分配内存,因此峰值内存使用量将为 2.0 * 原始数组大小。 arr.real 属性还返回一个不连续的数组(但这不太重要)。


我有什么遗漏的吗?有没有更好的方法可以做到这一点?

编辑1

: 很抱歉没有说清楚,我不想覆盖 arr,所以我不能将其用作 out。

python numpy complex-numbers memory-efficient numpy-ufunc
5个回答
17
投票

numba.vectorize

,为任务创建 numpy 通用函数非常简单:
@numba.vectorize([numba.float64(numba.complex128),numba.float32(numba.complex64)]) def abs2(x): return x.real**2 + x.imag**2

在我的机器上,与创建中间数组的纯 numpy 版本相比,我发现速度提高了三倍:

>>> x = np.random.randn(10000).view('c16') >>> y = abs2(x) >>> np.all(y == x.real**2 + x.imag**2) # exactly equal, being the same operation True >>> %timeit np.abs(x)**2 10000 loops, best of 3: 81.4 µs per loop >>> %timeit x.real**2 + x.imag**2 100000 loops, best of 3: 12.7 µs per loop >>> %timeit abs2(x) 100000 loops, best of 3: 4.6 µs per loop



5
投票

这是一个更快的解决方案,结果存储在

res

中:


import numpy as np res = arr.conjugate() np.multiply(arr,res,out=res)

我们利用了复数的abs属性,即
abs(z) = sqrt(z*z.conjugate)

,所以

abs(z)**2 = z*z.conjugate
    


1
投票
out

参数,可让您将输出定向到您选择的数组。当您想要就地执行操作时,它会很有用。


如果你对第一个方法做了这个小修改,那么你就可以完全就地执行

arr

上的操作:


np.abs(arr, out=arr) arr **= 2


一种仅使用
少量

额外内存的复杂方法可能是就地修改arr,计算新的实际值数组,然后恢复

arr

这意味着存储有关符号的信息(除非您知道您的复数都具有正实部和虚部)。每个实数或虚数的符号只需要一位,因此这会使用

1/16 + 1/16 == 1/8

的内存(除了您创建的新浮点数组之外)。


arr

以存储符号位为代价,

>>> signs_real = np.signbit(arr.real) # store information about the signs >>> signs_imag = np.signbit(arr.imag) >>> arr.real **= 2 # square the real and imaginary values >>> arr.imag **= 2 >>> result = arr.real + arr.imag >>> arr.real **= 0.5 # positive square roots of real and imaginary values >>> arr.imag **= 0.5 >>> arr.real[signs_real] *= -1 # restore the signs of the real and imagary values >>> arr.imag[signs_imag] *= -1
保持不变,
arr

保存我们想要的值。

    

result

0
投票
arr.real

只是复杂数组的视图。所以没有分配额外的内存。

    
如果您不想要 

arr.imag

0
投票
sqrt

如果你不想要双倍内存,那就不行
abs

那么你可以尝试这个(使用索引技巧)

real**2 + imag**2

无论如何,我更喜欢

N0 = 23 np0 = (np.random.randn(N0) + 1j*np.random.randn(N0)).astype(np.complex128) ret_ = np.abs(np0)**2 tmp0 = np0.view(np.float64) ret0 = np.matmul(tmp0.reshape(N0,1,2), tmp0.reshape(N0,2,1)).reshape(N0) assert np.abs(ret_-ret0).max()<1e-7
解决方案


© www.soinside.com 2019 - 2024. All rights reserved.