Finding all roots of a complex polynomial with SymPy - python

I'm trying to symbolically solve a polynomial with complex numbers and their conjugates with SymPy. I think I've come a long way, but solve does not give me any solutions although the polynomial is solveable.
from sympy import *
# set up symbols
a, b = symbols("a b", real=True)
t = a+I*b
T = functions.conjugate(t)
# set up polynomial
a1=0.005+I*0.0009
a2=0.9+I*-0.9
a3=0.4+I*0.5
a4=8+I*-80
a5=284+I*-1.5
a6=27100+I*-11500
poly=t**2 * T * a1 + t * T * a2 + t**2 * a3 + T * a4 + t * a5 + a6
# Trying to solve symbolically...
solve([re(poly), im(poly)], a, b)
# Output: []
# Solving numerically works, but only finds one solution...
nsolve((re(poly), im(poly)), (a, b), (0, 0))
# Output: matrix(
# [['-137.962596090596'],
# ['52.6296963395752']])
# verify with two solutions obtained in Maxima
poly.subs({a:-137.9625935162095, b:52.6296992481203}).n()
# Output: 0.000540354631040322 + 0.00054727003909351*I
poly.subs({a:-332.6474382554614+I*-185.9848818313149, b:258.0065640091016+I*-272.3344263478699}).n()
# Output: -6.55448222470652e-12 - 1.41238056784605e-12*I
Any ideas?

One problem could be that your coefficients contain floating point numbers. This often does not work with symbolic software.
Using f=10000*simplify(re(poly)) and g=10000*simplify(im(poly)) and editing the results gives polynomials with integer coefficients. The CAS (Magma in my case) then can produce a triangular representation of the ideal of f and g, which is given as the polynomials
a - 2483798807340123709959247/13545514719183259347425004828125*b^4
+ 66732206412048596386372283/541820588767330373897000193125*b^3
- 3849759933277117021785191063/86691294202772859823520030900*b^2
+ 9245906471290310401430681453/1733825884055457196470400618*b
- 31414499425567273751868164900/866912942027728598235200309,
b^5 - 189465979625/206648369*b^4
+ 330827538698125/826593476*b^3
- 17645868534640625/206648369*b^2
+ 1724106750659765625/206648369*b
- 52548859891484375000/206648369
which tells us that 5 solutions exist. The numerical solutions for the second polynomial are
174.10461010682254746847015187264557067610513554291323564643772
+ 63.402741884833821878468926640811609033267039765104756747285816*i
174.104610106822547468470151872645570676105135542913235646437738
- 63.402741884833821878468926640811609033267039765104756747285804*i
258.006564009101655109715962546854008929462784282347754971379392
+ 272.334426347869856080204881056671278679761260094680345276069337*i
258.006564009101655109715962546854008929462784282347754971379382
- 272.334426347869856080204881056671278679761260094680345276069359*i
52.62969633957523864698147679803879873180829265956656342643376
resulting in exactly one real solution. The numerical result of sympy was correct and complete.

Related

What does r() function mean in the return value of SymPy's dsolve?

I want to evaluate the value of phi(+oo)
where phi(xi) is the solution of ODE
Eq(Derivative(phi(xi), (xi, 2)), (-K + xi**2)*phi(xi))
and K is a known real variable.
By dsolve, I got the solution:
Eq(phi(xi), -K*xi**5*r(3)/20 + C2*(K**2*xi**4/24 - K*xi**2/2 + xi**4/12 + 1) + C1*xi*(xi**4/20 + 1) + O(xi**6))
with an unknown function r() in the first term on the right-hand side.
Here is my code:
import numpy as np
import matplotlib.pyplot as plt
import sympy
from sympy import I, pi, oo
sympy.init_printing()
def apply_ics(sol, ics, x, known_params):
"""
Apply the initial conditions (ics), given as a dictionary on
the form ics = {y(0): y0, y(x).diff(x).subs(x, 0): yp0, ...},
to the solution of the ODE with independent variable x.
The undetermined integration constants C1, C2, ... are extracted
from the free symbols of the ODE solution, excluding symbols in
the known_params list.
"""
free_params = sol.free_symbols - set(known_params)
eqs = [(sol.lhs.diff(x, n) - sol.rhs.diff(x, n)).subs(x, 0).subs(ics)
for n in range(len(ics))]
sol_params = sympy.solve(eqs, free_params)
return sol.subs(sol_params)
K = sympy.Symbol('K', positive = True)
xi = sympy.Symbol('xi',real = True)
phi = sympy.Function('phi')
ode = sympy.Eq( phi(xi).diff(xi, 2), (xi**2-K)*phi(xi))
ode_sol = sympy.dsolve(ode)
ics = { phi(0):1, phi(xi).diff(xi).subs(xi,0): 0}
phi_xi_sol = apply_ics(ode_sol, ics, xi, [K])
Where ode_sol is the solution, phi_xi_sol is the solution after initial conditions are applied.
Since r() is undefined in NumPy I can't evaluate the results by
for g in [0.9, 0.95, 1, 1.05, 1.2]:
phi_xi = sympy.lambdify(xi, phi_xi_sol.rhs.subs({K:g}), 'numpy')
Does anyone know what this function r() mean and how should I deal with it?
As visible in the form of the result, the solver falls back to a power series solution (instead of searching the solution in terms of parabolic cylinder functions as WolframAlpha does).
So let's set phi(xi)=sum a[k]*xi^k leading to the coefficient equations (using a[k]=0 for k<0)
(k+2)(k+1)a[k+2] = -K*a[k] + a[k-2]
a[0] = C2
a[1] = C1
a[2] = -K/2*C2
a[3] = -K/6*C1
a[4] = (K^2/2 + 1)/12*C2
a[5] = (K^2/6 + 1)/20*C1
Inserting that the the power series solution should have been
C2*(1-K/2*xi**2+(K**2/24+1/12)*xi**4) + C1*xi*(1-K/6*xi**2+(K/120+1/20)*xi**4) + O(xi**6)
Comparing with the sympy solution, all terms containing both C1 and K are missing, especially the missing degree 3 term is not explainable. It seems that the solution process was prematurely ended, or some equation transformation was not correctly reversed.
Please note that the ODE solver routines in sympy are experimental and rudimentary. Also, the power series solution gives only valid information for small values of xi, there is no way to derive any exact value for the limit at +oo.
The sol_params is a list containing a single dictionary. Passing that dictionary instead of the list gives the solution phi_xi_sol without the r(3):
Eq(rho(s), (-K*s**2/2 + s**2*xi**2/2 + 1)*(-6*rho(s) + 6*C2*s - C2*K*s**3 + O(s**5))/
(3*(K*s**2 - 2)) + C2*(-K*s**3/6 + s**3*xi**2/6 + s) + O(s**5))

I am newbie in python and doing coding for my physics project which requires to generate a matrix with a variable E

I am newbie in python and doing coding for my physics project which requires to generate a matrix with a variable E for which first element of the matrix has to be solved. Please help me. Thanks in advance.
Here is the part of code
import numpy as np
import pylab as pl
import math
import cmath
import sympy as sy
from scipy.optimize import fsolve
#Constants(Values at temp 10K)
hbar = 1.055E-34
m0=9.1095E-31 #free mass of electron
q= 1.602E-19
v = [0.510,0,0.510] # conduction band offset in eV
m1= 0.043 #effective mass in In_0.53Ga_0.47As
m2 = 0.072 #effective mass in Al_0.48In_0.52As
d = [-math.inf,100,math.inf] # dimension of structure in nanometers
'''scaling factor to with units of E in eV, mass in terms of free mass of electron, length in terms
of nanometers '''
s = (2*q*m0*1E-18)/(hbar)**2
#print('scaling factor is ',s)
E = sy.symbols('E') #Suppose energy of incoming particle is 0.3eV
m = [0.043,0.072,0.043] #effective mass of electrons in layers
for i in range(3):
print ('Effective mass of e in layer', i ,'is', m[i])
k=[ ] #Defining an array for wavevectors in different layers
for i in range(3):
k.append(sy.sqrt(s*m[i]*(E-v[i])))
print('Wave vector in layer',i,'is',k[i])
x = []
for i in range(2):
x.append((k[i+1]*m[i])/(k[i]*m[i+1]))
# print(x[i])
#Define Boundary condition matrix for two interfaces.
D0 = (1/2)*sy.Matrix([[1+x[0],1-x[0]], [1-x[0], 1+x[0]]], dtype = complex)
#print(D0)
#A = sy.matrix2numpy(D0,dtype=complex)
D1 = (1/2)*sy.Matrix([[1+x[1],1-x[1]], [1-x[1], 1+x[1]]], dtype = complex)
#print(D1)
#a=eye(3,3)
#print(a)
#Define Propagation matrix for 2nd layer or quantum well
#print(d[1])
#print(k[1])
P1 = 1*sy.Matrix([[sy.exp(-1j*k[1]*d[1]), 0],[0, sy.exp(1j*k[1]*d[1])]], dtype = complex)
#print(P1)
print("abs")
T= D0*P1*D1
#print('Transfer Matrix is given by:',T)
#print('Dimension of tranfer matrix T is' ,T.shape)
#print(T[0,0]
# I want to solve T{0,0} = 0 equation for E
def f(x):
return T[0,0]
x0= 0.5 #intial guess
x = fsolve(f, x0)
print("E is",x)
'''
y=sy.Eq(T[0,0],0)
z=sy.solve(y,E)
print('z',z)
'''
**The main part i guess is the part of the code where i am trying to solve the equation.***Steps I am following:
Defining a symbol E by using sympy
Generating three matrices which involves sum formulae and with variable E
Generating a matrix T my multiplying those 3 matrices,note that elements are complex and involves square roots of negative number.
I need to solve first element of this matrix T[0,0]=0,for variable E and find out value of E. I used fsolve for soving T[0,0]=0.*
Just a note for future questions, please leave out unused imports such as numpy and leave out zombie code like # a = eye(3,3). This helps keep the code as clean and short as possible. Also, the sample code would not run because of indentation problems, so when you copy and paste code, make sure it works before you do so. Always try to make your questions as short and modular as possible.
The expression of T[0,0] is too complex to solve analytically by SymPy so numerical approximation is needed. This leaves 2 options:
using SciPy's solvers which are advanced but require type casting to float values since SciPy does not deal with SymPy objects in any way.
using SymPy's root solvers which are less advanced but are probably simpler to use.
Both of these will only ever produce a single number as output since you can't expect numeric solvers to find every root. If you wanted to find more than one, then I advise that you use a list of points that you want to use as initial values, input each of them into the solvers and keep track of the distinct outputs. This will however never guarantee that you have obtained every root.
Only mix SciPy and SymPy if you are comfortable using both with no problems. SciPy doesn't play at all with SymPy and you should only have list, float, and complex instances when working with SciPy.
import math
import sympy as sy
from scipy.optimize import newton
# Constants(Values at temp 10K)
hbar = 1.055E-34
m0 = 9.1095E-31 # free mass of electron
q = 1.602E-19
v = [0.510, 0, 0.510] # conduction band offset in eV
m1 = 0.043 # effective mass in In_0.53Ga_0.47As
m2 = 0.072 # effective mass in Al_0.48In_0.52As
d = [-math.inf, 100, math.inf] # dimension of structure in nanometers
'''scaling factor to with units of E in eV, mass in terms of free mass of electron, length in terms
of nanometers '''
s = (2 * q * m0 * 1E-18) / hbar ** 2
E = sy.symbols('E') # Suppose energy of incoming particle is 0.3eV
m = [0.043, 0.072, 0.043] # effective mass of electrons in layers
for i in range(3):
print('Effective mass of e in layer', i, 'is', m[i])
k = [] # Defining an array for wavevectors in different layers
for i in range(3):
k.append(sy.sqrt(s * m[i] * (E - v[i])))
print('Wave vector in layer', i, 'is', k[i])
x = []
for i in range(2):
x.append((k[i + 1] * m[i]) / (k[i] * m[i + 1]))
# Define Boundary condition matrix for two interfaces.
D0 = (1 / 2) * sy.Matrix([[1 + x[0], 1 - x[0]], [1 - x[0], 1 + x[0]]], dtype=complex)
D1 = (1 / 2) * sy.Matrix([[1 + x[1], 1 - x[1]], [1 - x[1], 1 + x[1]]], dtype=complex)
# Define Propagation matrix for 2nd layer or quantum well
P1 = 1 * sy.Matrix([[sy.exp(-1j * k[1] * d[1]), 0], [0, sy.exp(1j * k[1] * d[1])]], dtype=complex)
print("abs")
T = D0 * P1 * D1
# did not converge for 0.5
x0 = 0.75
# method 1:
def f(e):
# evaluate T[0,0] at e and remove all sympy related things.
result = complex(T[0, 0].replace(E, e))
return result
solution1 = newton(f, x0)
print(solution1)
# method 2:
solution2 = sy.nsolve(T[0,0], E, x0)
print(solution2)
This prints:
(0.7533104353644469-0.023775286117722193j)
1.00808496181754 - 0.0444042144405285*I
Note that the first line is a native Python complex instance while the second is an instance of SymPy's complex number. One can convert the second simply with print(complex(solution2)).
Now, you'll notice that they produce different numbers but both are correct. This function seems to have a lot of zeros as can be shown from the Geogebra plot:
The red axis is Re(E), green is Im(E) and blue is |T[0,0]|. Each of those "spikes" are probably zeros.

Tricks to speed up solution of a pair of nonlinear symbolic equations

I have two equations f1(b,bb) = 0 and f2(b,bb) = 0 defined in the following code. I am trying to find the set of (b,bb) that satisfy both.
import sympy as sp
from sympy import symbols, simplify, factor
a, b, aa, bb, q, l, h = symbols('a b aa bb q l h')
pb = l*q+h*(1-q)
pb0 = (l*(1-l)*q+h*(1-h)*(1-q))/((1-l)*q+(1-h)*(1-q))
pb1 = (l**2*q+h**2*(1-q))/(l*q+h*(1-q))
a = b*bb/(2*b*bb*l**2 - 3*b*bb*l + b*bb - 2*b*l**2 + 2*b*l - 2*bb*l**2 + 2*bb*l + 2*l**2 - 2)
aa = 2/(l*(2*b*bb*l - b*bb - 2*b*l - 2*bb*l + 2*l + 2))
f1 = 1/pb*((1+(1-aa)/(2*aa*pb1))*q*l*b - (1-(1-aa)/(2*aa*pb1))*(1-q)*h*bb) - 1
f2 = 1/(1-pb)*(1+(1-a)/(2*a*pb0))*(q*(1-l)*b+(1-q)*(1-h)*bb) - 1
sp.solvers.solve((f1,f2), (b,bb))
The solver is taking a prohibitively long time (no solution yet).
I believe the above system is bilinear.
Are there any tricks to speed things up, i.e., python packages that exploit the bilinear structure? Maybe an alternate solver that is more efficient? I am hoping that I don't have to resort to a numerical solution.
If you solve f1 for b and substitute it into f2 you end up with an expression that has a numerator that is cubic in bb and does not factor. Solving a symbolic cubic is not going to be fast. About the best you can do is identify the coefficients and substitute them into the general solutions of the cubic equation.
>>> gen = Tuple(*solve(A*x**3+B*x**2+C*x+D,x))
>>> bbexpr = f2.subs(b, solve(f1, b)[0])
>>> n = bbexpr.as_numer_denom()[0].expand()
Then for each coefficient, something like this (for the first coeff):
>>> eA = n.coeff(bb**3)
>>> gen = gen.subs(A, eA)
Spoiler alert: it's going to be unwieldy.

How do I simplify the sum of sine and cosine in SymPy?

How do I simplify a*sin(wt) + b*cos(wt) into c*sin(wt+theta) using SymPy? For example:
f = sin(t) + 2*cos(t) = 2.236*sin(t + 1.107)
I tried the following:
from sympy import *
t = symbols('t')
f=sin(t)+2*cos(t)
trigsimp(f) #Returns sin(t)+2*cos(t)
simplify(f) #Returns sin(t)+2*cos(t)
f.rewrite(sin) #Returns sin(t)+2*sin(t+Pi/2)
PS.: I dont have direct access to a,b and w. Only to f
Any suggestion?
The general answer can be achieved by noting that you want to have
a * sin(t) + b * cos(t) = A * (cos(c)*sin(t) + sin(c)*cos(t))
This leads to a simultaneous equation a = A * cos(c) and b = A * sin(c).
Dividing the second equation by the second, we can solve for c. Substituting its solution into the first equation, you can solve for A.
I followed the same pattern but just to get it in terms of cos. If you want to get it in terms of sin, you can use Rodrigo's formula.
The following code should be able to take any linear combination of the form x * sin(t - w) or y * cos(t - z). There can be multiple sins and cos'.
from sympy import *
t = symbols('t', real=True)
expr = sin(t)+2*cos(t) # unknown
d = collect(expr.expand(trig=True), [sin(t), cos(t)], evaluate=False)
a = d[sin(t)]
b = d[cos(t)]
cos_phase = atan(a/b)
amplitude = a / sin(cos_phase)
print(amplitude.evalf() * cos(t - cos_phase.evalf()))
Which gives
2.23606797749979*cos(t - 0.463647609000806)
This seems to be a satisfactory match after plotting both graphs.
You could even have something like
expr = 2*sin(t - 3) + cos(t) - 3*cos(t - 2)
and it should work fine.
a * sin(wt) + b * cos(wt) = sqrt(a**2 + b**2) * sin(wt + acos(a / sqrt(a**2 + b**2)))
While the amplitude is the radical sqrt(a**2 + b**2), the phase is given by the arccosine of the ratio a / sqrt(a**2 + b**2), which may not be expressible in terms of arithmetic operations and radicals. Hence, you may be asking SymPy to do the impossible. Better use floating-point values, but you do not need SymPy for that.

numpy - create polynomial by its roots

I'm trying to create a numpy.polynomial by the roots of the polynomial.
I could only find a way to do that by the polynomial's a's
The way it works now, for the polynomial x^2 - 3x + 2 I can create it like that:
poly1d([1, -3, 2])
I want to create it by its roots, which are -1, -2
Numpy has a function that does this: numpy.polynomial.polynomial.polyfromroots
Note that
If a zero has multiplicity n, then it must appear in roots n times.
For that purpose you will need to implement the multiplication of polynomial, that is, you need to make sure your product is able to generate the product of
(am * x^m + ... + a0) * (bn * x^n + ... + b0)
If your product is able to do this, then knowing the roots of
r1, ..., rk
You can write this as
(x - r1) * ... * (x - rk)
and you need to repeatedly calculate the product here.

Categories

Resources