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

北战南征 提交于 2019-12-12 05:29:20

问题


I have the following code as part of a function:

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

div = px*px + py*py    

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

the u= line returns RuntimeWarning: invalid value encountered in divide when run. This is because occasionally the div= line returns zero.

However, if I rewrite the u= line as:

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

it still returns the same runtime warning.

This code outputs the desired numbers however I thought that the np.where function was lazy. If this is not the case then there are potential speed ups in other bits of code I have written (hence I'm asking this question). What am I missing? Does the np.where function calculate both the 'True' and 'False inputs and then select one depending on the boolean?

Note, this is the solution I've ended up with:

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

although this works fine too:

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

(which is kind of what I thought np.where did...)

Both these solutions are around the same speed but both are slower than just the np.where function on its own.

Any explanations/suggestions welcome! Thanks.


回答1:


This is a divide by 0 issue that programmers have been working around since the earliest vectorized languages (e.g. APL, MATLAB).

One solution which I've used in the past is to (conditionally) add 1 to the divisor:

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

It doesn't work in all cases, but it might in this, since it appears the div will be 0 only if both px and py are 0. In that case the numerator is also 0. 0/1 = 0.

Or clipping div with a small value (this may be the fastest solution):

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

A quick search on SO for numpy divide by zero turned up other questions. For example:

https://stackoverflow.com/a/26248892/901925 suggests using errstate context to turn off the warning:

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

divide works for cases like 1/0, while invalid is needed for 0/0 cases. But ignore ends up putting either inf or nan in the return array. So you still need to test for (div==0) to get a 0 value.

Though I rather like the appearance of this form:

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

Warren's comment explains why where doesn't work. The arguments are evaluate before being passed to the function. Lazy-evaluation requires the cooperation of the Python interpreter. It is normally part of the syntax (e.g. if,|,&).



来源:https://stackoverflow.com/questions/28836231/error-raised-in-code-that-shouldnt-run-according-to-lazy-evaluation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!