使用numpy数组计算函数返回inf和nan

8

我有以下的numpy数组和函数:

import numpy
import scipy.special

i = numpy.linspace(0, 500, 501, dtype = int)

def func(x, n):
  return scipy.special.eval_hermite(n, x)

我使用两种不同的方法来评估我的numpy数组i中的每个元素的函数:

方法1:

hermites = func(0, i)

方法二:

hermites = [func(0, idx) for idx in i]

这两种方法得到了两个不同的答案。它们之间的区别在于,在元素 50 之后,方法1 开始返回 infnan。虽然方法2 也不能为每个元素 i 给出正确的值,但它可以计算更多的元素。但是,当 i >= 64 时,方法2失败了。
这两种方法用大约相同的时间给出了答案(对于 len(i) = 15000,使用 timeit 确定为 0.7s)。我不明白的是它们得到了不同的结果。这是因为我学习时尽可能避免在 Python 中使用 for 循环。但这次似乎不是这样。
我也考虑过与内存有关的问题。但是,评估单个元素,即 print func(0, 64),也会返回 0.(与方法2 的输出相同)。
发生了什么事?
1个回答

5
这是scipy中的一个错误,由于numpy的“ufuncs”的转换规则有时会令人惊讶。问题在于,在scipy 0.16及更早版本中,当eval_hermite的第一个参数是整数数组,而第二个参数是整数标量时,返回值的数据类型为单精度浮点数(numpy.float32)。当第二个参数是64位浮点值时,返回类型为numpy.float64。用float32表示的最大值远小于float64,所以当第二个参数是整数时,eval_hermite很快就会溢出到无穷大。

例如,这是使用scipy 0.16.0和numpy 1.10.1的情况:

In [26]: from scipy.special import eval_hermite

请注意返回值的数据类型是 float32:
In [27]: eval_hermite([20, 50, 100, 200], 0)
Out[27]: 
array([  6.70442586e+11,             -inf,              inf,
                    inf], dtype=float32)

如果第二个参数是浮点数,返回类型为float64,并且可以表示大的值:
In [28]: eval_hermite([20, 50, 100, 200], 0.0)
Out[28]: 
array([  6.70442573e+011,  -1.96078147e+039,   3.06851876e+093,
         8.45055019e+216])

您的代码解决方法是始终确保传递给eval_hermite的第二个参数为浮点数。例如,
hermites = func(0.0, i)

这个问题已经在scipy开发版本中得到了解决(请参见https://github.com/scipy/scipy/pull/4896),因此当scipy 0.17发布时,不应该出现这个问题。

我们应该编辑标题吗?问题中有趣的部分不是关于“为什么会出现inf和nan”,而是“为什么传递一个数组并使用listcomp按元素迭代不会返回相同的结果?” - DSM
升级到scipy 0.16.0后,获得了更好的结果。然而,现在对于i >= 270,我又获得了infnan。这个错误在scipy开发版本中是否已经完全修复(所有可能的i)?有没有办法获取这个版本? - The Dude
对于 i >= 270,在 x=0 处的 Hermite 多项式值大于可以用 64 位浮点数表示的值,因此应该期望得到 inf。对于 i >= 300,我得到了 nan;我还没有深入研究,但我怀疑某些代码最终会执行 inf - inf - Warren Weckesser

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接