根据惰性评估,在不应该 运行 的代码中引发错误
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
只有在 px
和 py
都为 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
最终将 inf
或 nan
放入 return 数组中。所以你仍然需要测试 (div==0)
以获得 0
值。
虽然我比较喜欢这种形式的外观:
with np.errstate(invalid='ignore'):
u = np.where(div==0, 0, .../div)
Warren 的评论解释了为什么 where
不起作用。参数在传递给函数之前进行评估。惰性求值需要 Python 解释器的配合。它通常是语法的一部分(例如 if
、|
、&
)。
我有以下代码作为函数的一部分:
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
只有在 px
和 py
都为 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
最终将 inf
或 nan
放入 return 数组中。所以你仍然需要测试 (div==0)
以获得 0
值。
虽然我比较喜欢这种形式的外观:
with np.errstate(invalid='ignore'):
u = np.where(div==0, 0, .../div)
Warren 的评论解释了为什么 where
不起作用。参数在传递给函数之前进行评估。惰性求值需要 Python 解释器的配合。它通常是语法的一部分(例如 if
、|
、&
)。