Error from program using SymPy - python

I would like to use the SymPy packages to find the roots of a fourth-order polynomial equation. Subsequently I would like to plot these roots as a function of the parameters of the polynomial equations. I have written the piece of code below. It seems to calculate everything fine, but I cannot plot the results as I get the error "x and y are not of the same dimension". I think it has something to do with my usage of SymPy, because normally it always works like this.
from sympy import *
from math import *
from numpy import *
import pylab as lab
def RootFunc(root, m, c0, r, En):
A = 2*(m**2 - 0.25 - c0**2)/r**2 + 4
B = 8*En*c0/r
C = -4 - 4*En**2 + ((c0**2 + m**2 -.25)/r**2 + 2)**2
return root.subs([(a,A),(b,B),(c,C)])
# Define necessary symbols
x = symbols('x')
a, b, c = symbols('a b c')
En, r = symbols("En r")
# Fix constants
m = 0
c0 = -2
# Solve equation
eq = x**4 + a*x**2 + b*x + c
sol = solve(eq,x)
root1 = sol[0]
grid = linspace(1,10,10)
sol1 = [RootFunc(root1, m, c0, r, .5) for r in grid]
lab.figure(1)
lab.plot(grid,sol1)
lab.show()

Are you sure that you a running the same script that you've given us here?
I say this because I can copy and paste your example verbatim and it works with absolutely no issue.
Once you've checked, could you post which version of Python, SymPy, NumPy and Matplotlib you're using please?
Edit: I think something got slightly lost in translation when you put up your first minimal working example (MWE). The solution in your MWE was real-valued so it didn't have the same issue as your actual program. However, onto the solution:
Your main issue here is this line
sol1 = [RootFunc(root1, m, c0, help, .5) for help in grid]
RootFunc in this case returns a sympy.core.add.Add which pylab has no concept of and therefore can't plot. In your MWE you recognised that this was the issue and tried calling N() and real() on the return value. Unfortunately this just wraps the sympy.core.add.Add object in a NumPy array. When Pylab tries to plot this array it finds a sympy.core.add.Add object which it has no concept of and therefore just throws an error.
Fortunately SymPy allows you to turn a sympy.core.add.Add object into a number using int(), float() or complex(). Since your roots are complex you should use complex() on the return value and then to get the real component use .real.
So to get it too work you should just change the above line to
sol1 = [complex(RootFunc(root1, m, c0, help, .5)).real for help in grid]
Edit2: Just a quick point about style. You're using a lot of wildcard imports in your code (e.g. from numpy import *), which is fine if you're the only person using the code, it does make it neater after all.
However, if you're going to be posting on a forum like this please could you try to use qualified imports (like you've done for pylab) so that we don't have to go trudging through the documentation for all the modules you've used to try and figure out what you're doing.
One other thing: when you encounter a problem like this it really helps to execute it line by line in the python shell and examine the types (with type()) and values (with print() or repr()) of your variables. For this purpose I would strongly urge you to learn how to use IPython as it can really help.

You might be breaking some things with your imports. Can you try this:
import sympy as sy
import numpy as np
import pylab as lab
def RootFunc(root, A, B):
return root.subs([(a,A),(b,B)])
# Define necessary symbols
x = sy.symbols('x')
a, b = sy.symbols('a b')
# Solve equation
eq = x**4 + a*x**2 + b*x
sol = sy.solve(eq,x)
root1 = sol[1] # first element is trivial solution, so take second one
grid = np.linspace(1,10,10)
sol1 = [np.real(sy.N(RootFunc(root1, 1, x))) for x in grid]
lab.figure(1)
lab.plot(grid,sol1)
lab.show()

Related

Avoiding divergent solutions with odeint? shooting method

I am trying to solve an equation in Python. Basically what I want to do is to solve the equation:
(1/x^2)*d(Gam*dL/dx)/dx)+(a^2*x^2/Gam-(m^2))*L=0
This is the Klein-Gordon equation for a massive scalar field in a Schwarzschild spacetime. It suppose that we know m and Gam=x^2-2*x. The initial/boundary condition that I know are L(2+epsilon)=1 and L(infty)=0. Notice that the asymptotic behavior of the equation is
L(x-->infty)-->Exp[(m^2-a^2)*x]/x and Exp[-(m^2-a^2)*x]/x
Then, if a^2>m^2 we will have oscillatory solutions, while if a^2 < m^2 we will have a divergent and a decay solution.
What I am interested is in the decay solution, however when I am trying to solve the above equation transforming it as a system of first order differential equations and using the shooting method in order to find the "a" that can give me the behavior that I am interested about, I am always having a divergent solution. I suppose that it is happening because odeint is always finding the divergent asymptotic solution. Is there a way to avoid or tell to odeint that I am interested in the decay solution? If not, do you know a way that I could solve this problem? Maybe using another method for solving my system of differential equations? If yes, which method?
Basically what I am doing is to add a new system of equation for "a"
(d^2a/dx^2=0, da/dx(2+epsilon)=0,a(2+epsilon)=a_0)
in order to have "a" as a constant. Then I am considering different values for "a_0" and asking if my boundary conditions are fulfilled.
Thanks for your time. Regards,
Luis P.
I am incorporating the value at infinity considering the assimptotic behavior, it means that I will have a relation between the field and its derivative. I will post the code for you if it is helpful:
from IPython import get_ipython
get_ipython().magic('reset -sf')
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
from math import *
from scipy.integrate import ode
These are initial conditions for Schwarzschild. The field is invariant under reescaling, then I can use $L(2+\epsilon)=1$
def init_sch(u_sch):
om = u_sch[0]
return np.array([1,0,om,0]) #conditions near the horizon, [L_c,dL/dx,a,da/dx]
These are our system of equations
def F_sch(IC,r,rho_c,m,lam,l,j=0,mu=0):
L = IC[0]
ph = IC[1]
om = IC[2]
b = IC[3]
Gam_sch=r**2.-2.*r
dR_dr = ph
dph_dr = (1./Gam_sch)*(2.*(1.-r)*ph+L*(l*(l+1.))-om**2.*r**4.*L/Gam_sch+(m**2.+lam*L**2.)*r**2.*L)
dom_dr = b
db_dr = 0.
return [dR_dr,dph_dr,dom_dr,db_dr]
Then I try for different values of "om" and ask if my boundary conditions are fulfilled. p_sch are the parameters of my model. In general what I want to do is a little more complicated and in general I will need more parameters that in the just massive case. Howeve I need to start with the easiest which is what I am asking here
p_sch = (1,1,0,0) #[rho_c,m,lam,l], lam and l are for a more complicated case
ep = 0.2
ep_r = 0.01
r_end = 500
n_r = 500000
n_omega = 1000
omega = np.linspace(p_sch[1]-ep,p_sch[1],n_omega)
r = np.linspace(2+ep_r,r_end,n_r)
tol = 0.01
a = 0
for j in range(len(omega)):
print('trying with $omega =$',omega[j])
omeg = [omega[j]]
ini = init_sch(omeg)
Y = odeint(F_sch,ini,r,p_sch,mxstep=50000000)
print Y[-1,0]
#Here I ask if my asymptotic behavior is fulfilled or not. This should be basically my value at infinity
if abs(Y[-1,0]*((p_sch[1]**2.-Y[-1,2]**2.)**(1/2.)+1./(r[-1]))+Y[-1,1]) < tol:
print(j,'times iterations in omega')
print("R'(inf)) = ", Y[-1,0])
print("\omega",omega[j])
omega_1 = [omega[j]]
a = 10
break
if a > 1:
break
Basically what I want to do here is to solve the system of equations giving different initial conditions and find a value for "a=" (or "om" in the code) that should be near to my boundary conditions. I need this because after this I can give such initial guest to a secant method and try to fiend a best value for "a". However, always that I am running this code I am having divergent solutions that it is, of course, a behavior that I am not interested. I am trying the same but considering the scipy.integrate.solve_vbp, but when I run the following code:
from IPython import get_ipython
get_ipython().magic('reset -sf')
import numpy as np
import matplotlib.pyplot as plt
from math import *
from scipy.integrate import solve_bvp
def bc(ya,yb,p_sch):
m = p_sch[1]
om = p_sch[4]
tol_s = p_sch[5]
r_end = p_sch[6]
return np.array([ya[0]-1,yb[0]-tol_s,ya[1],yb[1]+((m**2-yb[2]**2)**(1/2)+1/r_end)*yb[0],ya[2]-om,yb[2]-om,ya[3],yb[3]])
def fun(r,y,p_sch):
rho_c = p_sch[0]
m = p_sch[1]
lam = p_sch[2]
l = p_sch[3]
L = y[0]
ph = y[1]
om = y[2]
b = y[3]
Gam_sch=r**2.-2.*r
dR_dr = ph
dph_dr = (1./Gam_sch)*(2.*(1.-r)*ph+L*(l*(l+1.))-om**2.*r**4.*L/Gam_sch+(m**2.+lam*L**2.)*r**2.*L)
dom_dr = b
db_dr = 0.*y[3]
return np.vstack((dR_dr,dph_dr,dom_dr,db_dr))
eps_r=0.01
r_end = 500
n_r = 50000
r = np.linspace(2+eps_r,r_end,n_r)
y = np.zeros((4,r.size))
y[0]=1
tol_s = 0.0001
p_sch= (1,1,0,0,0.8,tol_s,r_end)
sol = solve_bvp(fun,bc, r, y, p_sch)
I am obtaining this error: ValueError: bc return is expected to have shape (11,), but actually has (8,).
ValueError: bc return is expected to have shape (11,), but actually has (8,).

Generating and Solving Simultaneous ODE's in Python

I'm relatively new to Python, and am encountering some issues in writing a piece of code that generates and then solves a system of differential equations.
My approach to doing this was to create a set of variables and coefficients, (x0, x1, ..., xn) and (c0, c1 ,..., cn) repsectively, in a list with the function var(). Then the equations are constructed in EOM1(). The initial conditions, along with the set of equations, are all put together in EOM2() and solved using odeint.
Currently the code below runs, albeit not efficiently the reason for which I believe is because odeint runs through all the code with each interaction (that's something else I need to fix but isn't the main problem!).
import sympy as sy
from scipy.integrate import odeint
n=2
cn0list = [0.01, 0.05]
xn0list = [0.01, 0.01]
def var():
xnlist=[]
cnlist=[]
for i in range(n+1):
xnlist.append('x{0}'.format(i))
cnlist.append('c{0}'.format(i))
return xnlist, cnlist
def EOM1():
drdtlist=[]
for i in range(n):
cn1=sy.Symbol(var()[1][i])
xn0=sy.Symbol(var()[0][i])
xn1=sy.Symbol(var()[0][i+1])
eom=cn1*xn0*(1.0-xn1)-cn1*xn1-xn1
drdtlist.append(eom)
xi=sy.Symbol(var()[0][0])
xf=sy.Symbol(var()[0][n])
drdtlist[n-1]=drdtlist[n-1].subs(xf,xi)
return drdtlist
def EOM2(xn, t, cn):
x0, x1 = xn
c0, c1 = cn
f = EOM1()
output = []
for part in f:
output.append(part.evalf(subs={'x0':x0, 'x1':x1, 'c0':c0, 'c1':c1}))
return output
abserr = 1.0e-6
relerr = 1.0e-4
stoptime = 10.0
numpoints = 20
t = [stoptime * float(i) / (numpoints - 1) for i in range(numpoints)]
wsol = odeint(EOM2, xn0list, t, args=(cn0list,), atol=abserr, rtol=relerr)
My problem is that I had difficulty getting Python to treat the variables generated by Sympy appropriately. I got around this with the line
output.append(part.evalf(subs={'x0':x0, 'x1':x1, 'c0':c0, 'c1':c1}))
in EOM2(). Unfortunately, I do not know how to generalize this line to a list of variables from x0 to xn, and from c0 to cn. The same applies to the earlier line in EOM2(),
x0, x1 = xn
c0, c1 = cn
In other words I set n to an arbitrary number, is there a way for Python to interpret each element as it does with the ones I manually entered above? I have tried the following
output.append(part.evalf(subs={'x{0}'.format(j):var(n)[0][j], 'c{0}'.format(j):var(n)[1][j]}))
yet this yields the error that led me to use evalf in the first place,
TypeError: can't convert expression to float
Is there any way do what I want to, generate a set of n equations which are then solved with odeint?
Instead of using evalf you want to look into using sympy.lambdify to generate a callback for use with SciPy. You will need to create a function with the expected signature of odeint, e.g.:
y, params = sym.symbols('y:3'), sym.symbols('kf kb')
ydot = rhs(y, p=params)
f = sym.lambdify((y, t) + params, ydot)
yout = odeint(f, y0, tout, param_values)
We gave a tutorial on (among other things) how to use lambdify with odeint at the SciPy 2017 conference, the material is available here: http://www.sympy.org/scipy-2017-codegen-tutorial/
If you are open to use an external library to handle the function signatures of external solvers you may be interested in a library I've authored: pyodesys
If I understand correctly, you want to make an arbitrary number of substitutions in a SymPy expression. This is how it can be done:
n = 10
syms = sy.symbols('x0:{}'.format(n)) # an array of n symbols
expr = sum(syms) # some expression with those symbols
floats = [1/(j+1) for j in range(n)] # numbers to put in
expr.subs({symbol: value for symbol, value in zip(syms, floats)})
The result of subs is a float in this case (no evalf needed).
Note that the function symbols can directly create any number of symbols for you, via the colon notation. No need for a loop.

solving 1D Schrödinger equation with Numerov method (python)

Good evening.
I'm currently trying to solve the 1D Schrödinger eq. (time independent) with the Numerov method. The derivation of the method is clear to me but I have some problems with the implementation. I tried to look for solutions on google, and there are some (like this one or this one), but I don't really understand what they are doing in their codes...
The Problem:
With some math you can get the equation to this form:
where . For the beginning I'd like to look at the potential V(x)=1 if -a<x<a.
Since I don't have values for the energy or the first values of Psi (which are needed to start the algorithm) I just guessed some...
The code looks like this:
import numpy as np
import matplotlib.pyplot as plt
from scipy.constants import hbar
m= 1e-27
E= 0.5
def numerov_step(psi_1,psi_2,k1,k2,k3,h):
#k1=k_(n-1), k2=k_n, k3=k_(n+1)
#psi_1 = psi_(n-1) and psi_2=psi_n
m = 2*(1-5/12. * h**2 * k2**2)*psi_2
n = (1+1/12.*h**2*k1**2)*psi_1
o = 1 + 1/12. *h**2 *k3**2
return (m-n)/o
def numerov(N,x0,xE,a):
x,dx = np.linspace(x0,xE,N+1,retstep=True)
def V(x,a):
if (np.abs(x)<a):
return 1
else:
return 0
k = np.zeros(N+1)
for i in range(len(k)):
k[i] = 2*m*(E-V(x[i],a))/hbar**2
psi= np.zeros(N+1)
psi[0]=0
psi[1]=0.1
for j in np.arange(2,N):
psi[j+1]= numerov_step(psi[j],psi[j+1],k[j-1],k[j],k[j+1],dx)
return psi
x0 =-10
xE = 10
N =1000
psi=numerov(N,x0,xE,3)
x = np.linspace(x0,xE,N+1)
plt.figure()
plt.plot(x,psi)
plt.show()
Since the plot doesn't look like a wavefunction at all something has to be wrong, but I'm having trobule to find out what it is.. Would be nice if someone could help a little.
Thanks Sito
Unfortunately I don't quite remember the quantum physics so I don't understand some details. Still I see some bugs in your code:
Why inside numerov_step you square k1, k2 and k3?
In your main cycle
for j in np.arange(2,N):
psi[j+1]= numerov_step(psi[j],psi[j+1],k[j-1],k[j],k[j+1],dx)
you messed up with indices. It looks like this line should be
for j in np.arange(2, N):
psi[j] = numerov_step(psi[j - 2], psi[j - 1], k[j - 2], k[j - 1], k[j], dx)
This is the part I don't really understand. Looking into animation at your first link it looks like this equation has good solutions only for certain combinations of V(x) and E and in other cases it quickly goes wild. It looks like both your V(x) and proportion of E to hbar and V(x) are quite different from the referenced articles and this might be one more reason why the solution goes wild.

Solving nonlinear overdetermined system using python

I'm trying to find a good way to solve a nonlinear overdetermined system with python. I looked into optimization tools here http://docs.scipy.org/doc/scipy/reference/optimize.nonlin.html but I can't figure out how to use them. What I have so far is
#overdetermined nonlinear system that I'll be using
'''
a = cos(x)*cos(y)
b = cos(x)*sin(y)
c = -sin(y)
d = sin(z)*sin(y)*sin(x) + cos(z)*cos(y)
e = cos(x)*sin(z)
f = cos(z)*sin(x)*cos(z) + sin(z)*sin(x)
g = cos(z)*sin(x)*sin(y) - sin(z)*cos(y)
h = cos(x)*cos(z)
a-h will be random int values in the range 0-10 inclusive
'''
import math
from random import randint
import scipy.optimize
def system(p):
x, y, z = p
return(math.cos(x)*math.cos(y)-randint(0,10),
math.cos(x)*math.sin(y)-randint(0,10),
-math.sin(y)-randint(0,10),
math.sin(z)*math.sin(y)*math.sin(x)+math.cos(z)*math.cos(y)-randint(0,10),
math.cos(x)*math.sin(z)-randint(0,10),
math.cos(z)*math.sin(x)*math.cos(z)+math.sin(z)*math.sin(x)-randint(0,10),
math.cos(z)*math.sin(x)*math.sin(y)-math.sin(z)*math.cos(y)-randint(0,10),
math.cos(x)*math.cos(z)-randint(0,10))
x = scipy.optimize.broyden1(system, [1,1,1], f_tol=1e-14)
could you help me out a bit here?
If I understand you right, you want to find an approximate solution to the non-linear system of equations f(x) = b where b is the vector containing the random values b=[a,...,h].
In order to do this you will first need to remove the random values from the system function, because otherwise in each iteration the solver will try to solve a different equation system. Moreover, I think that the basic Broyden method only works for a system with as many unknowns as equations. Alternatively you could use scipy.optimize.leastsq. A possible solution looks like this:
# I am using numpy because it's more convenient for the generation of
# random numbers.
import numpy as np
from numpy.random import randint
import scipy.optimize
# Removed random right-hand side values and changed nomenclature a bit.
def f(x):
x1, x2, x3 = x
return np.asarray((math.cos(x1)*math.cos(x2),
math.cos(x1)*math.sin(x2),
-math.sin(x2),
math.sin(x3)*math.sin(x2)*math.sin(x1)+math.cos(x3)*math.cos(x2),
math.cos(x1)*math.sin(x3),
math.cos(x3)*math.sin(x1)*math.cos(x3)+math.sin(x3)*math.sin(x1),
math.cos(x3)*math.sin(x1)*math.sin(x2)-math.sin(x3)*math.cos(x2),
math.cos(x1)*math.cos(x3)))
# The second parameter is used to set the solution vector using the args
# argument of leastsq.
def system(x,b):
return (f(x)-b)
b = randint(0, 10, size=8)
x = scipy.optimize.leastsq(system, np.asarray((1,1,1)), args=b)[0]
I hope this is of help for you. However, note that it is extremely unlikely that you will find a solution, especially when you generate random integers in the interval [0,10] while the range of f is limited to [-2,2]

Using Python to solve a 5th order polynomial

I think it is pretty clear from the title as to what I am trying to accomplish. So, let us get on with the code:
import math
import numpy as np
import scipy
#from scipy import scipy.optimize.fsolve as sci_solve
x0 = -5
def function(x):
x**5 + x**4 + x**3 + x**2 + x + 1
#print sci_solve(function, x0)
print scipy.optimize.fsolve(function, x0)
Okay, so when I run this code, I get [-5.]. When did it simply print my initial value, and not the roots of this equation? Also, when I ran the code without # before the lines containing the code #from scipy import scipy.optimize.fsolve as sci_solve and print sci_solve(function, x0), it gave me a syntax error.
What am I doing wrong?
As hcwhsa points out, I neglected to relate to the reader the version of python I am using, and I am terribly sorry for this. I am using version 2.7
I'd never actually heard of the scipy module before this question (so thank you for that), but from the example code given at http://folk.uio.no/inf3330/scripting/doc/python/SciPy/tutorial/old/node18.html, it appears that you need to put a return statement before the polynomial given in after the line def function(x):.
One way to do this:
from scipy import *
x0 = -5
p = poly1d([1, 1, 1, 1, 1, 1])
# evaluate for x = x0
p(x0)
# get roots
roots(p)
This gives you all roots, including complex ones. If you want only real roots, you can iterate over roots(p) (it's an array) and check that each item's imag attribute is 0.0.

Categories