繁体   English   中英

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

[英]Most memory-efficient way to compute abs()**2 of complex numpy ndarray

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

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

我还没有找到一个可以完全执行np.abs()**2

由于这种大小和类型的数组占用大约半 GB,我正在寻找一种主要是内存高效的方式。

我也希望它是便携的,所以最好是一些 ufuncs 的组合。

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

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,所以我不能使用它。

感谢最近版本的numba中的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

编辑:此解决方案的最低内存要求是两倍,而且速度稍快。 然而,评论中的讨论有助于参考。

这是一个更快的解决方案,结果存储在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

arr.realarr.imag只是复杂数组的视图。 因此没有分配额外的内存。

如果您的主要目标是节省内存,NumPy的ufuncs会选择一个可选的out参数,让您将输出定向到您选择的数组。 当您想要执行操作时,它非常有用。

如果您对第一个方法进行了这个小修改,那么您可以完全对arr执行操作:

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

一种只使用一点额外内存的复杂方式可能是将arr修改到位,计算新的实数值数组然后恢复arr

这意味着存储有关标志的信息(除非您知道您的复数都具有正的实部和虚部)。 每个实数或虚数的符号只需要一个比特,因此这使用了arr的内存的1/16 + 1/16 == 1/8 (除了你创建的新浮点数)。

>>> 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

以存储signbits为代价, arr不变, result保存我们想要的值。

如果你不想要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

无论如何,我更喜欢numba解决方案

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM