【问题标题】:Numpy passing input array as `out` argument to ufuncNumpy 将输入数组作为 `out` 参数传递给 ufunc
【发布时间】:2015-10-28 17:48:17
【问题描述】:

如果类型正确,将输入数组作为可选输出参数提供给 numpy 中的 ufunc 通常是否安全?例如,我已经验证了以下工作:

>>> import numpy as np
>>> arr = np.array([1.2, 3.4, 4.5])
>>> np.floor(arr, arr)
array([ 1.,  3.,  4.])

数组类型必须与输出(numpy.floor() 的浮点数)兼容或相同,否则会发生这种情况:

>>> arr2 = np.array([1, 3, 4], dtype = np.uint8)
>>> np.floor(arr2, arr2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: ufunc 'floor' output (typecode 'e') could not be coerced to provided output parameter (typecode 'B') according to the casting rule ''same_kind''

所以考虑到一个正确类型的数组,就地应用 ufunc 通常是安全的吗?还是floor() 是个例外?文档没有说清楚,以下两个与问题有切线关系的线程也没有说清楚:

  1. Numpy modify array in place?
  2. Numpy Ceil and Floor "out" Argument

编辑:

根据http://docs.scipy.org/doc/numpy/user/c-info.ufunc-tutorial.html 的教程,我会假设它通常是安全的,但并不总是安全的。在计算期间使用输出数组作为中间结果的临时持有者似乎没有任何限制。虽然像 floor()ciel() 这样的东西可能不需要临时存储,但更复杂的功能可能需要。话虽这么说,整个现有的库都可以考虑到这一点。

【问题讨论】:

  • 从技术上讲,它不是一个 ufunc,但是以这种方式将 out 中的 out 参数与 2D 数组一起使用会产生不正确的结果。
  • 这几乎是我正在寻找的反例,但不完全是:)
  • 在“数学运算”下的提示中,ufunc docs 提到使用 add(G, C, G) 作为 G = G + C 的优化。我会说它是安全的。 (另一方面,在输入和输出重叠但不相同的情况下调用 ufunc 会导致问题。)
  • 我之前没有注意到这个评论。我认为你是对的,因为它是安全的。
  • 是的,但这属于重叠但不相同的输入。我只是想知道在满足所有其他条件的情况下是否可以安全地进行所有操作。

标签: python numpy in-place numpy-ufunc


【解决方案1】:

numpy 函数的out 参数是写入结果的数组。使用out 的主要优点是避免在不必要的地方分配新内存。

将函数的输出写入作为输入传递的同一数组上是否安全?没有一般的答案,这取决于函数在做什么。

两个例子

以下是 ufunc 类函数的两个示例:

In [1]: def plus_one(x, out=None):
   ...:     if out is None:
   ...:         out = np.zeros_like(x)
   ...: 
   ...:     for i in range(x.size):
   ...:         out[i] = x[i] + 1
   ...:     return out
   ...: 

In [2]: x = np.arange(5)

In [3]: x
Out[3]: array([0, 1, 2, 3, 4])

In [4]: y = plus_one(x)

In [5]: y
Out[5]: array([1, 2, 3, 4, 5])

In [6]: z = plus_one(x, x)

In [7]: z
Out[7]: array([1, 2, 3, 4, 5])

函数shift_one

In [11]: def shift_one(x, out=None):
    ...:     if out is None:
    ...:         out = np.zeros_like(x)
    ...: 
    ...:     n = x.size
    ...:     for i in range(n):
    ...:         out[(i+1) % n] = x[i]
    ...:     return out
    ...: 

In [12]: x = np.arange(5)

In [13]: x
Out[13]: array([0, 1, 2, 3, 4])

In [14]: y = shift_one(x)

In [15]: y
Out[15]: array([4, 0, 1, 2, 3])

In [16]: z = shift_one(x, x)

In [17]: z
Out[17]: array([0, 0, 0, 0, 0])

对于函数plus_one没有问题:当参数x和out是同一个数组时,得到了预期的结果。但是当参数 x 和 out 是同一个数组时,函数shift_one 给出了一个令人惊讶的结果,因为数组

讨论

对于out[i] := some_operation(x[i])形式的函数,比如上面的plus_one,还有函数floor、ceil、sin、cos、tan、log、conj等,据我所知是安全 使用参数 out 将结果写入输入。

对于采用 ``out[i] := some_operation(x[i], y[i]) 形式的两个输入参数的函数,例如 numpy 函数 add,它也是安全,乘,减。

对于其他功能,则视情况而定。如下图所示,矩阵乘法是不安全的:

In [18]: a = np.arange(4).reshape((2,2))

In [19]: a
Out[19]: 
array([[0, 1],
       [2, 3]])

In [20]: b = (np.arange(4) % 2).reshape((2,2))

In [21]: b
Out[21]: 
array([[0, 1],
       [0, 1]], dtype=int32)

In [22]: c = np.dot(a, b)

In [23]: c
Out[23]: 
array([[0, 1],
       [0, 5]])

In [24]: d = np.dot(a, b, out=a)

In [25]: d
Out[25]: 
array([[0, 1],
       [0, 3]])

最后一句话:如果实现是多线程的,不安全函数的结果甚至可能是不确定的,因为它取决于处理数组元素的顺序。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-22
    • 1970-01-01
    • 2020-12-23
    • 2021-07-16
    • 2016-04-09
    • 1970-01-01
    • 2020-03-06
    • 1970-01-01
    相关资源
    最近更新 更多