根据惰性评估,在不应该 运行 的代码中引发错误

Error raised in code that shouldn't run according to lazy evaluation

我有以下代码作为函数的一部分:

px = x2 - x1
py = y2 - y1
pz = z2 - z1

div = px*px + py*py    

u = ((x0 - x1) * px + (y0 - y1) * py) / div

u= 行 returns RuntimeWarning: invalid value encountered in divide 当 运行。这是因为偶尔 div= 行 returns 为零。

但是,如果我将 u= 行重写为:

u = np.where(div != 0, ((x0 - x1) * px + (y0 - y1) * py) / div, 0)

它仍然是 returns 相同的 运行 时间警告。

此代码输出所需的数字,但我认为 np.where 函数是惰性的。如果不是这种情况,那么我编写的其他代码可能会加速(因此我要问这个问题)。 我错过了什么? np.where 函数是否同时计算 'True' 和 'False 输入,然后根据布尔值计算 select 一个输入?

请注意,这是我最终得到的解决方案:

np.seterr(invalid='ignore')
u = np.where(div != 0, ((x0 - x1) * px + (y0 - y1) * py) / div, 0)
np.seterr(invalid='warn')

虽然这也很好用:

u = np.zeros_like(div)
divb = div != 0
u[divb] = ((x0[divb] - x1[divb]) * px[divb] + 
        (y0[divb] - y1[divb]) * py[divb]) / div[divb]

(这就是我认为 np.where 所做的...)

这两种解决方案的速度大致相同,但都比 np.where 函数本身慢。

任何 explanations/suggestions 欢迎! 谢谢。

这是自最早的矢量化语言(例如 APL、MATLAB)以来程序员一直在解决的除以 0 的问题。

我过去使用的一个解决方案是(有条件地)将除数加 1:

u = ((x0 - x1) * px + (y0 - y1) * py) / (div + (div==0))

并非在所有情况下都有效,但在这种情况下可能有效,因为看起来 div 只有在 pxpy 都为 0 时才会为 0。在在这种情况下,分子也为 0。0/1 = 0.

或用较小的值裁剪 div(这可能是最快的解决方案):

..../np.maximum(div,1e-16)

在 SO 上快速搜索 numpy divide by zero 出现了其他问题。例如:

建议使用 errstate 上下文来关闭警告:

with numpy.errstate(divide='ignore'):
    u = .../div

divide 适用于 1/0 等情况,而 invalid 适用于 0/0 等情况。但是 ignore 最终将 infnan 放入 return 数组中。所以你仍然需要测试 (div==0) 以获得 0 值。

虽然我比较喜欢这种形式的外观:

with np.errstate(invalid='ignore'):
    u = np.where(div==0, 0, .../div)

Warren 的评论解释了为什么 where 不起作用。参数在传递给函数之前进行评估。惰性求值需要 Python 解释器的配合。它通常是语法的一部分(例如 if|&)。