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.
Related
I am solving an ODE with Sympy. The equation is
ODE
To solve it, I used this little code, which returns this result.
from sympy import *
from numpy import *
import matplotlib.pyplot as plt
x = symbols('x')
y = Function('y')
f = y(x)
print(f)
edo = Eq(f.diff()+3*x**2*f, 6*x**2)
print(edo)
edoSolve = dsolve(edo, f)
print(edoSolve)
C1*exp(-x**3) + 2
My question is, how can I plot the result with x being a range from 0 to 10?
Firstly it's problematic to combine these two lines:
from sympy import *
from numpy import *
These two libraries define many functions with the same names and mixing those together will lead to problems. For clarity it is better to do something like:
import sympy as sym
import numpy as np
You can only plot a sympy expression if you give numbers for all of the symbols apart from the one that you want to plot against (i.e. x in this example). That means that you need to have a concrete value for the integration constant C1. You can get that by giving an initial conditions (ics) argument to dsolve. Also since dsolve returns an equation you need to choose a side of the equation as the expression that you want to plot. Having done that the sym.plot function will do precisely what you ask for:
In [10]: import sympy as sym
In [11]: sol = sym.dsolve(edo, f, ics={f.subs(x, 0): 1})
In [12]: sol
Out[12]:
3
-x
y(x) = 2 - ℯ
In [13]: sym.plot(sol.rhs, (x, 0, 10))
Out[13]: <sympy.plotting.plot.Plot at 0x7f346de1caf0>
If you want to show solutions for multiple values for C1 together, you could append plots:
from sympy import symbols, Function, Eq, dsolve, plot
x = symbols('x')
y = Function('y')
f = y(x)
edo = Eq(f.diff() + 3 * x ** 2 * f, 6 * x ** 2)
edoSolve = dsolve(edo, f)
plot1 = plot(show=False)
for c1 in range(-5, 6):
plotc1 = plot(edoSolve.subs('C1', c1).rhs, (x, 0, 10), show=False)
plot1.append(plotc1[0])
plot1.show()
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)
I have a really simple issue with my Python program -- which isn't finished at all. Right now it's doing everything I want, but not how I want.
There are three things I've been trying to change:
all functions are being plotted using the same color, and I'd like the program to automatically switch to a new color when a new function is added to the plot (it will be more than 2, all on the same plot).
f(x)'s range is 140. How can I decrease that? Maybe to 20/40.
(most important) My code isn't very efficient. f1 and derivative are not associated at all. I declare the function's model in f1, but I have to set up everything again in derivative. Every time I try to do otherwise I end up having some problem with the main function. I'll eventually add more features like integration and whatnot, and if I'm declaring everything from scratch everytime I want to do something with f1 the program will kinda lose its purpose.
Should I use x = Symbol('x') inside f1?
import numpy as np
import matplotlib.pyplot as plt
from sympy import *
x = Symbol('x')
def f1(a, b, c, d):
y = a*x**3 + b*x**2 + x*c + d
return y
###yprime = y.diff(x)
###return yprime
def derivative(a, b, c, d):
y = a*x**3 + b*x**2 + x*c + d
yprime = y.diff(x)
return yprime
def factorail(n):
if n == 0:
return 1
else:
return n * factorial(n-1)
###colors = iter(cm.rainbow(np.linspace(0, 1, len(ys))))
###for y in ys:
###plt.scatter(x, y, color=next(colors))
def main():
###colors = itertools.cycle(["r", "b", "g"])
y = f1(0,1,2,1)
yp = derivative(0,1,2,1)
print(y)
plot(y, yp)
plot(yp)
plt.show()
if __name__ == '__main__':
main()
Vertical window is set by ylim option. I recommend to also use some explicit limits for x, the default -10 to 10 is not necessarily best for you. And I do recommend reading the page on SymPy plotting.
Color is set by line_color option. Different colors require different calls to plot, but those can be combined. Example:
p = plot(y, (x, -5, 5), ylim=(-20, 20), line_color='b', show=False)
p.extend(plot(yp, (x, -5, 5), ylim=(-20, 20), line_color='r', show=False))
p.show()
results in
The function reuse is easy:
def derivative(a, b, c, d):
y = f1(a, b, c, d)
yprime = y.diff(x)
return yprime
Aside: what happens if we try line_color=['b', 'r'], as in plot(y, yp, ylim=(-20, 20), line_color=['b', 'r'])? Funny stuff happens:
I use the following function to get colors that are "far" from each other in a general way. The 2396745 is pretty arbitrary, and defines how far apart the colors are. It seems to give me good results.
def cmap_ints(i):
return "#"+hex(((int(i)+1)*2396745)%(256**3))[2:].rjust(6,"0")
Usage:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0,1,100)
y1 = 3*x + 4
y2 = 2*x - 5
plt.plot(x,y1,c=cmap_ints(1))
plt.plot(x,y2,c=cmap_ints(2))
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()
I have 3 arrays of data to integrate over and need to return a 3d array. This is my code
import numpy as np
from scipy import integrate
def f(x, y, z):
return x + y + 2*z
a = np.arange(64)
b = np.arange(100)
c = np.arange(100)
result = []
for x in a:
for y in b:
for z in c:
result.append(integrate.quad(f, 0, x, (y, z))[0])
result_1 = np.array(result).reshape(len(a), len(b), len(c))
print(result_1)
It works, but this code is so slow and I need to handle problems bigger than this. Is there any other method, something like broadcasting? And the following is the function I need to handle:
import numpy as np
from scipy import integrate, special
def f(v,r):
alpha = 0.5
chi = 1/2*special.erf((r+1.2)/0.3)-1/2*special.erf((r-1.2)/0.3)
return 4/(np.sqrt(2*np.pi*alpha))*chi*np.exp(-v**2/(2*alpha))
def E_1(r):
def f_1(v,r):
return r*f(v,r)
a = 0
b = r
g = lambda x: -np.inf
h = lambda x: np.inf
return integrate.dblquad(f_1, a, b, g, h)
def E_f(tau, xi_1, xi_2):
return E_1(xi_1*np.cos(tau) + xi_2*np.sin(tau))[0]*(-np.sin(tau))
I need to input tau, xi_1, xi_2 as three arrays, and return to a 3D array. And its arrange like coordinate, every coordinate point corresponds to a result. Just like the first example.