Reputation: 13
I'm trying to perform a double integration on this function
def prob(x,y):
ch = np.sqrt((3-y)**2 + x**2)
hb = np.sqrt((4-x)**2 + y**2)
if np.isclose(4 * y, 12 - 3 * x):
# Without the if statement, any x, y values that satisfy the condition will return nan
return 0.5
else:
return (np.arccos((ch**2 + hb**2 - 25) / (2 * ch * hb)))/(2*np.pi)
from scipy import integrate as integ
integ.dblquad(prob(x,y), 0, 4, lambda x: 0, lambda x: 3)
However I got this error message pointing at the if statement
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Trivially, if i use math.isclose instead of np.isclose in the if statement, I get this error message pointing to the same statement
TypeError: only size-1 arrays can be converted to Python scalars
To my understanding, boolean array has something to do with these errors but checking everything including the if statement yields a scalar boolean.
So are there any way around this?
Upvotes: 1
Views: 1764
Reputation: 231425
Short answer - you need to provide full traceback and calling specification. And you need to call dblquad
correctly.
===
What are x,y
when you call with
integ.dblquad(prob(x,y), 0, 4, lambda x: 0, lambda x: 3)
Depnding on those 2 variables, the error might be rising before dblquad
is even called.
The first argument to dblquad
is supposed to be a function, not an array. Using prob
instead of prob(x,y)
might work.
Given 2 scalars prob
works:
In [674]: prob(1,2)
Out[674]: 0.4685835209054995
Given two arrays we get the ambiguity error:
In [675]: prob(np.arange(3),np.arange(1,4))
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-675-4b16a88f567e> in <module>
----> 1 prob(np.arange(3),np.arange(1,4))
<ipython-input-673-e31785dd54a5> in prob(x, y)
2 ch = np.sqrt((3-y)**2 + x**2)
3 hb = np.sqrt((4-x)**2 + y**2)
----> 4 if np.isclose(4 * y, 12 - 3 * x):
5 # Without the if statement, any x, y values that satisfy the condition will return nan
6 return 0.5
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
And the full traceback (which you should have given us) shows the problem is with the isclose
call:
In [676]: np.isclose(np.arange(3),np.arange(1,4))
Out[676]: array([False, False, False])
It produces a boolean array, which can't be used in an if
clause.
Using math.isclose
raises error because math
functions only accept scalars. They don't work with arrays (multivalued ones).
Using dblquad
correctly, it works:
In [678]: integ.dblquad(prob, 0,4,lambda x:0, lambda x:3)
Out[678]: (4.42854383700761, 1.8525461432365937e-08)
dblquad
passes scalar values to your function, so the use of isclose
(either numpy or math) isn't problem
Using math
functions with scalar inputs is faster:
def probm(x,y):
ch = math.sqrt((3-y)**2 + x**2)
hb = math.sqrt((4-x)**2 + y**2)
if math.isclose(4 * y, 12 - 3 * x):
# Without the if statement, any x, y values that satisfy the condition will return nan
return 0.5
else:
return (math.acos((ch**2 + hb**2 - 25) / (2 * ch * hb)))/(2*math.pi)
In [683]: integ.dblquad(probm, 0,4,lambda x:0, lambda x:3)
Out[683]: (4.428543836134556, 1.8890715880459652e-08)
much faster:
In [685]: timeit integ.dblquad(prob, 0,4,lambda x:0, lambda x:3)
11.7 s ± 24.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [686]: timeit integ.dblquad(probm, 0,4,lambda x:0, lambda x:3)
272 ms ± 1.12 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Upvotes: 0
Reputation: 658
Well, the errors you get are sufficiently helpful in terms of diagnostic. The problem is that np.isclose
will return an array of booleans, and trying to coerce such an array into a single value (which the if
clause does) raises an error. (1)
How to fix it is more dependent on what you want to achieve. Assuming that prob(x,y)
should return a numpy array of the same dimensions as x/y, where values are replaced by 0.5 at locations where the condition is true, you can use:
output = (np.arccos((ch**2 + hb**2 - 25) / (2 * ch * hb)))/(2*np.pi)
output[np.isclose(4 * y, 12 - 3 * x)] = .5
return output
Notice that this may not be the best way to solve your actual problem. It often makes more sense to keep NaN in positions where the calculation fails (that's what NaNs are for). If you want to replace NaNs by 0.5, you might be better off testing for NaN rather than by your math condition:
output = (np.arccos((ch**2 + hb**2 - 25) / (2 * ch * hb)))/(2*np.pi)
output[np.isnan(output)] = .5
return output
(1) One could argue that any non-empty array should evaluate to True
similarly to what lists do in non-numpy Python, and regardless of values (i.e. np.asarray([False])
evaluates as True
). I for one am happy that numpy will raise an exception rather than try to guess what I want in that situation (which could be all
, any
, len(array)>0
, or as here element-wise operation that needs to be rewritten).
Upvotes: 0
Reputation: 16079
Perhaps this is a typographical error.
From the edit I made it looks like you were calling:
integ.dblquad(prob(x, y), 0, 4, lambda x: 0, lambda x: 3)
When in fact you should be calling:
integ.dblquad(prob, 0, 4, lambda x: 0, lambda x: 3)
I believe you likely have and array x
and array y
that are causing the error when calling dblquad
the first way.
E.g.
x = np.array([1,2,3])
y = np.array([3,4,5])
integ.dblquad(prob(x, y), 0, 4, lambda x: 0, lambda x: 3)
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
integ.dblquad(prob, 0, 4, lambda x: 0, lambda x: 3)
(4.42854383700761, 1.8525461432365937e-08)
Upvotes: 1