An error in matplotlib related to numpy.roots - python

I was trying to plot (the modulus of) sum of quadratic roots and it returns me an error illustrated as follow:
import numpy as np
import matplotlib.pyplot as plt
def rooting(a, b, c):
y = [a, b, c]
z = np.roots(y)
return np.absolute(z[0]+z[1])
x = np.linspace(1, 10, 10)
plt.plot(x, rooting(x, 2, 3))
and the error was:
File "C:\Users\user\Anaconda3\lib\site-packages\numpy\core\fromnumeric.py", line 1570, in nonzero
res = nonzero()
SystemError: <built-in method nonzero of numpy.ndarray object at 0x000001422B9BFD00> returned a result with an error set
Can someone tell me what's going on?

The problem arises because you are passing the variable to a vector and then concatenate with b and c are numbers, you must pass to the variable to a scalar, I show my next solution based on the above.
def rooting(a, b, c):
y = [a, b, c]
z = np.roots(y)
return np.absolute(z[0]+z[1])
x = np.linspace(1, 10, 10)
y = [rooting(xi, 2, 3) for xi in x]
plt.plot(x, y)
plt.show()

Using the quadratic formula, we know the roots are (-b ± √(b**2-4ac))/2a.
So the modulus of the sum of the roots is |b/a|.
With this simplification, we can compute the result in a vectorized way (no list comprehesion, looping, or multiple calls of rooting necessary):
import numpy as np
import matplotlib.pyplot as plt
def rooting(a, b, c):
# The roots are (-b ± √(b**2-4ac))/2a
# So the modulus of the sum of the roots is |b/a|
return np.abs(b/a)
x = np.linspace(0, 10, 11)
plt.plot(x, rooting(x, 2, 3))
plt.show()

Related

Scipy curve fit (optimization) - vectorizing a conditional to identify threshold using a custom function

I'm trying to use scipy curve_fit to capture the value of a0 parameter. As of now, it is not changing (always comes out as 1):
X = [[1,2,3],[4,5,6]]
def func(X, a0, c):
x1 = X[0]; x2 = X[1]
a = x1*x2
result = np.where(a(a<a0), -c*(a + np.sqrt(x2)), -c*x1)
return result
Popt, Cov = scipy.curve_fit(func, X, y)
a0, c = Popt
Predicted = func(X, a0, c) # a0 and c are constants
I get the values for c, which is a scalar, without any problem. I can't explain why a0 (also a scalar) is always 1, and I am not sure how to fix it. I did see elsewhere on SO that np.where can be used the way I have used it here, but apparently not for curve_fit function. Maybe I need to use a different method of optimization, and I'd like some pointers to do this using scipy methods.
Edit: I tried the construct suggested by Brad, but that's not it.
Updated!
This should work. note that the a variable is a vector in this example of length 3 because it is computed by the element wise multiplication of the first and second elements of X which is a 2x3 matrix. Therefore a0 can either be a scalar or a vector of length 3 and c can also be a scalar or a vector of length 3.
import numpy as np
X = np.array([[1, 2, 3], [4, 5, 6]])
a0 = np.array([8,25,400])
# a0 = 2
# Code works whether C is scalar or a matrix since it can be broadcast to matrix a below.
# c = 3 # Uncomment this for scalar
c = np.array([8, 12, 2000]) # Element wise
def func(X, a0, c):
x = X[0]
y = X[1]
a = x * y
print(a.shape)
result = np.where(a < a0, c * (a + np.sqrt(y)), c * x)
return result
func(X, a0, c)
This is a minimum amount of code that works. Notice I removed the y>0 and defined a to be the same size as c. Now you get the correct insertions because the first parameter of np.where is now the same size as the second and third parameters. Before (x<a) & (y>0) always evaluated to True or False and that is a scalar in this context. If a was a N dimensional array you would have received a ValueError because the operands could not be broadcast together
import numpy as np
c = np.array([[22,34],[33,480]])
def func(X, a):
x = X[0]; y = X[1]
return np.where(c[(x<a)], -c*(a + np.sqrt(y)), -c*x)
X = [25, 600]
a = np.array([[2,14],[33,22]])
func(X,a)
This also works if c is a constant and a was the array you wanted manipulated
import numpy as np
c = 2
def func(X, a):
x = X[0]; y = X[1]
return np.where(a[(x<a)], -c*(a + np.sqrt(y)), -c*x)
X = [25, 600]
a = np.array([[2,14],[33,22]])
func(X,a)

Integral with variable limits in python

I'm trying to compute the following integral
using scipy, with the following program:
def E(z):
result = 1/np.sqrt(Om*(1 + z)**3 + Ode + Ox*(1 + z)**2)
return result
def r(z, E):
result, error = quad(E, 0, z) # integrate E(z) from 0 to z
return result
z being the independent variable, while Om Ode and Ox are simply constants (previously assigned).
When I then try to call the function:
z = np.linspace(1e-3, 4, 300)
plt.plot(z, r(z))
I get the error
flip, a, b = b < a, min(a, b), max(a, b)
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any()
or a.all()
What is the problem? Is scipy.quad unable to integrate up to a variable?
Thank you so much for your help
You could use a combination of Python's map(function, iterable, ...) function,
Return an iterator that applies function to every item of iterable, yielding the results.
and functools partial(func[,*args][, **keywords]) method:
Return a new partial object which when called will behave like func called with the positional arguments args and keyword arguments keywords.
import numpy as np
from scipy.integrate import quad
import matplotlib.pyplot as plt
from functools import partial
def E(z):
Om = 0.32
Ode = 0.68
Ox = 0
result = 1/np.sqrt(Om*(1 + z)**3 + Ode + Ox*(1 + z)**2)
return result
def r(z):
result = np.array(
list(map(partial(quad, E, 0), z))
)[:, 0] # integrate E(z) from 0 to z
return result
z = np.linspace(1e-3, 4, 300)
fig, ax = plt.subplots()
ax.plot(z, r(z))
fig.show()

Rewrite Mathematica output in Python

I want to convert the piecewise-function output of a calculation in Mathematica into Python. Taking inspiration from this page for the Mathematica->Python conversion and this page for writing piecewise functions, I have
from numpy import linspace, vectorize, array
from numpy import arctan, log
import matplotlib.pyplot as plt
from sympy.parsing.mathematica import parse
def fun(x,a,b,c):
# string inside parse('') is my mathematica output
if a == 0:
out = parse('a (-I b + x) ArcTan[(b - a)/c]')
else:
out = parse('4 c a^2 Log[c^2 + (x - a)^2]')
return out
a = 0.17
b = 0.44
c = 0.29
x = linspace(0,50,int(1e3))
vfun = vectorize(fun)
y = vfun(x,a,b,c)
yp = 4*c*a**2*log(c**2 + (x - a)**2)
plt.figure()
plt.plot(x,y,'.-')
plt.title('mathematica -> python conversion')
plt.figure()
plt.plot(x,yp,'.-')
plt.title('expected')
plt.show()
The plot looks like:
whereas it should be:
Have I done something wrong when converting Mathematica to Python? Or is there a problem when assigning numerical values to a, b, and c? (Note that this is a MWE, and the Mathematica code that I want to convert is much longer and complicated than what is shown above.)
A much easier solution is using eval. Now I know eval is very dangerous but most of the time when it takes input from user, but here the input is already defined for us.Now onto the answer.
You are not getting the expected output because your vectorized array does not contain any floats but only contains output from mathematica's parser which return unevaluated string, so we can use .evalf() as answer given by #David, lambdify() which uses eval() internally or we can directly use eval().
Here is the documentation of the methods used : https://docs.sympy.org/latest/tutorial/basic_operations.html
from numpy import linspace, vectorize, array
from numpy import arctan, log
import matplotlib.pyplot as plt
from sympy.parsing.mathematica import MathematicaParser
def fun(x,a,b,c):
obj = MathematicaParser()
if a == 0:
out = obj.parse('a (-I b + x) ArcTan[(b - a)/c]')
else:
out = obj.parse('4 c a^2 Log[c^2 + (x - a)^2]')
return out
a = 0.17
b = 0.44
c = 0.29
x = linspace(0,50,int(1e3))
yp = 4*c*a**2*log(c**2 + (x - a)**2)
vfun = vectorize(fun)
y = vfun(x,a,b,c)
#Here y is a type <class 'numpy.ndarray'> containing 1000 <class 'numpy.str_'>
#['4*c*a**2*log(c**2+(x-a)**2)' '4*c*a**2*log(c**2+(x-a)**2)'
#'4*c*a**2*log(c**2+(x-a)**2)' '4*c*a**2*log(c**2+(x-a)**2)'
#'4*c*a**2*log(c**2+(x-a)**2)' '4*c*a**2*log(c**2+(x-a)**2)'
#'4*c*a**2*log(c**2+(x-a)**2)' '4*c*a**2*log(c**2+(x-a)**2)'
#....
y = eval(y[0]) #y[0] is '4*c*a**2*log(c**2+(x-a)**2)'
#When we evaluate y[0] we again get y as <class 'numpy.ndarray'> conatining 1000 <class 'numpy.float64'>
#Because x is <class 'numpy.ndarray'> it evaluates the first string over
#all the points in x.
#[-0.07309464 -0.07770262 -0.08110382 -0.0828403 -0.08263539 -0.08052339
#-0.07683235 -0.07203573 -0.06659307 -0.06086366 -0.05509179 -0.04942739
#-0.04395413 -0.03871304 -0.03371924 -0.0289728 -0.02446552 -0.02018495
#.....
plt.figure()
plt.plot(x, y,'.-')
plt.title('mathematica -> python conversion')
plt.figure()
plt.plot(x,yp,'.-')
plt.title('expected')
plt.show()
Output :
This is a dirty solution:
import numpy as np
import matplotlib.pyplot as plt
from sympy.parsing.mathematica import mathematica
from sympy import symbols
def fun(x, a, b, c):
# string inside parse('') is my mathematica output
if a == 0:
out = mathematica('a (-I b + x) ArcTan[(b - a)/c]')
else:
out = mathematica('4 c a^2 Log[c^2 + (x - a)^2]')
return out
aa = 0.17
bb = 0.44
cc = 0.29
II = 1
xx = np.linspace(0, 50, int(1e3))
x, a, b, c, I = symbols('x, a, b, c, I')
fun1 = fun(x, a, b, c)
fun2 = fun1.subs({a: aa, c: cc})
print(fun2.evalf())
y_list = []
for item in xx:
y_list.append(fun2.subs({x:item}).evalf())
print(y_list[:10])
plt.figure()
plt.plot(xx, y_list,'.-')
plt.title('mathematica -> python conversion')
plt.show()
I will explain it later how all of this works.
Update
As you may see, when a == 0, the function is 0, you need to check that.
When you use mathematica, the type of fun1 is a sympy function (sympy.core.mul.Mul), that's why you have to use symbols and fun1.subs() and fun2.evalf(), in other words, you need to know who to use the basic functions of sympy.
The way you evaluate a function in order to plot it, well, you use a list:
y_list = []
for item in xx:
y_list.append(fun2.subs({x:item}).evalf())
By the way, I am using sympy version 1.4.

Theano 2D histogram

theano complains x0 is never used when I try to use it to calculate a 2D histogram.
fth is the theano implementation that doesn't work. fnp is the numpy implementation that works as expected.
import theano
import theano.tensor as T
import numpy as np
def fth(a,b,c):
x0 = T.lvector('x0')
x1 = T.lvector('x1')
z = T.lvector('z')
x1 *= x0.size
x0 += x1
T.subtensor.AdvancedIncSubtensor1(z, x0)
f = theano.function([x0, x1, z], z)
return f(a, b, c, on_unused_input='ignore')
def fnp(a, b, c):
np.add.at(c, a+b*a.size, 1)
return c
a = np.array([0, 1, 2, 3], dtype=np.int64)
b = np.array([0, 1, 0, 1], dtype=np.int64)
c = np.zeros(16, dtype=np.int64)
print(fnp(a, b, c))
print(fth(a, b, c))
Error:
theano.compile.function_module.UnusedInputError: theano.function
was asked to create a function computing outputs given certain
inputs, but the provided input variable at index 0 is not part of
the computational graph needed to compute the outputs:
Elemwise{add,no_inplace}.0.
To make this error into a warning, you can pass the parameter
on_unused_input='warn' to theano.function. To disable it
completely, use on_unused_input='ignore'.

How to do curve_fit in python

I need to curve fit a set of data using y = x / (a + x), where a is the parameter that I am required to get from this excercise.
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
x = [1, 2, 7, 10, 20, 70, 200, 1000]
y = [0, 0, 15.3, 34.6, 49.3, 82.6, 100]
def fit(x, a):
return x/(a+x)
par, con = curve_fit(fit, x, y)
plt.plot(x, fit(x, par[0]))
plt.show()
Using this I get some abomination of a fit. Doesn't even remotely fit.
If I try it like this:
def fit(x, a, b):
return b*x/(a+x)
I get a fit but it's without round cornerns. It's just straight lines. What am I doing wrong?
Notice that your x is a list of int, in Python division are by default integer division, which is not what you want here.
Therefore, a few changes will make it work, use the 2nd function as an example, your first function is not going to fit well as it will have a limit of 1 when x->inf:
def fit(x, a, b):
return b*x*1./(a+x)
A, B=curve_fit(fit, x, y)[0]
plt.plot(x, fit(x, A, B))
plt.plot(x, y, 'r+')
plt.savefig('temp.png')
It is a set of straight lines because you only calculate y at those x values, to get a curve: change the plotting call to plt.plot(np.linspace(0,200,100), fit(np.linspace(0,200,100), A, B))

Categories

Resources