How do I calculate the cube roots of a complex number in python? Currently, the only way I've found to do it is according to this answer, but I'm trying to remove any and all import statements from my code.
Current method, reproduced here:
import math
def cuberoot( z ):
z = complex(z)
x = z.real
y = z.imag
mag = abs(z)
arg = math.atan2(y,x)
resMag = mag**(1./3)
resArg = [ (arg+2*math.pi*n)/3. for n in range(1,4) ]
return [ resMag*(math.cos(a) + math.sin(a)*1j) for a in resArg ]
Compute one and multiply with the cube roots of 1?
cuberoots_of_1 = 1, complex(-.5, .75**.5), complex(-.5, -.75**.5)
def cuberoot(z):
cuberoot = complex(z)**(1/3)
return [cuberoot * cr1 for cr1 in cuberoots_of_1]
Test:
>>> cuberoot(-2 + 3j)
[(1.1532283040274223+1.0106429470939737j),
(-1.4518566183526649+0.49340353410400484j),
(0.2986283143252425-1.5040464811979786j)]
>>> for z in cuberoot(-2 + 3j):
print(z**3, abs(z**3 - (-2 + 3j)))
(-1.999999999999999+3j) 1.1102230246251565e-15
(-1.999999999999999+3j) 1.1102230246251565e-15
(-1.9999999999999982+2.9999999999999996j) 1.831026719408895e-15
Doing the same with yours is less accurate:
(-1.999999999999996+3.000000000000002j) 4.572178254219406e-15
(-1.9999999999999933+3.000000000000004j) 7.768388458966724e-15
(-1.9999999999999956+3.0000000000000013j) 4.636427468134552e-15
Here is an approach using rotation by a factor of i^(2/3).
f = 1j**(2/3)
def cube_roots(z):
r = z**(1/3)
return [r, -r*f, r*f**2]
Python's built-in complex can handle finding one root out of the box:
def cube_root(v):
if not isinstance(v, complex):
v = complex(v, 0)
return v ** (1.0 / 3.0)
Examples:
cube_root(-3)
(0.7211247851537043+1.2490247664834064j)
cube_root(complex(1, -2))
(1.2196165079717578-0.47171126778938893j)
The function you reproduced above is one way to get all three roots.
Related
Is there a way to use together sympy's TensorProduct from the sympy.physics.quantum module and the Pauli matrices from sympy.physics.paulialgebra?
My problem is the following:
physically: I have an Hamiltonian written as product of (fermionic) ladder operators and I need to implement the Jordan-Wigner transformation to "translate" it to a spin Hamiltonian
with sympy: I have used sympy's AnnihilateFermion and CreateFermion functions from sympy.physics.secondquant, and I have implemented manually the Jordan-Wigner transformation. The substitution from the ladder operators to the spin operators works fine. Ideally I then would use tensor_product_simp and evaluate_pauli_product to simplify the result. But sympy does not like this mixing. Any suggestions?
Below is the full minimal example. Thanks a lot for any help.
import sympy as sym
import sympy.physics.secondquant as sec
from sympy.physics.quantum import TensorProduct, Dagger, tensor_product_simp, IdentityOperator
from sympy.physics.paulialgebra import Pauli, evaluate_pauli_product
def JW(n, N_sites):
factors = []
for l in range(N_sites):
if l < n-1:
factors.append(-sym.I*Pauli(3))
elif l == n-1:
factors.append((Pauli(1)-sym.I*Pauli(2))/2)
else:
# add identity below
factors.append(Pauli(1)*Pauli(1))
res = factors[0]
for l in range(1,n):
res = TensorProduct(res, factors[l])
return res
if __name__ == '__main__':
N_sites = 4
loci = sym.symbols('l1:{}'.format(N_sites + 1))
chi_s = [sec.AnnihilateFermion(loci[k]) for k in range(N_sites)]
chi_dag_s = [sec.CreateFermion(loci[k]) for k in range(N_sites)]
H = sum((chi_dag_s[n] * chi_s[n + 1] - chi_dag_s[n + 1] * chi_s[n]) for n in range(N_sites - 1))
list_of_subs = []
for n in range(N_sites):
list_of_subs.append((chi_s[n], JW(n, N_sites)))
list_of_subs.append((chi_dag_s[n], Dagger(JW(n, N_sites))))
H_new = H.subs(list_of_subs)
print(H_new)
print(tensor_product_simp(sym.expand(H_new)))
I'm new to python but I been working on a code which can solve an integral equation which range is also changing according the unknown parameter. I tried to use Sympy solve function but it does not return any result. I solved the problem with a for loop, but its really slow, inefficient. I'm sure there must be a better solution, a solver. Maybe there is an other approach? I'am messing up something? I attach also the code.
import sympy as sy
from sympy.solvers import solve
alphasum = 1.707
Lky = 3.078
g = 8
Ep = 195
sigp1 = 1401.927
sigp0 = 1476
e = 2.718282
u = 0.05
k = 0.007
lsl = sy.Symbol('lsl')
def lefthand(g, Ep):
return g * Ep
def rigthhand(sigp1, sigp0, e, u, alphasum, Lky, k, lsl):
return (sigp1 - (-sigp1 + 2 * sigp0 - 2 * sigp0 * (1 - e ** (-u * (((alphasum / Lky) * lsl) + k * lsl)))))
equr = (sy.integrate(rigthhand(sigp1, sigp0, e, u, alphasum, Lky, k, lsl), (lsl, 0, lsl)))
equl = lefthand(g, Ep)
print(equr)
print (equl)
print (equr-equl)
result = solve(equr-equl, lsl,warn =True,check=False,minimal=True,quick=True,simplify=True,quartics=True)
print(result)
You can use nsolve to find numerical solutions:
In [11]: sympy.nsolve(equr-equl, lsl, 8)
Out[11]: 8.60275245116315
In [12]: sympy.nsolve(equr-equl, lsl, -4)
Out[12]: -4.53215114758428
When u is 0 your equation is linear and you can solve if for lsl; let this be an initial guess for the value of lsl at a larger value of u. Increase your value of u to the target value. Let's use capital U for the parameter we will control, replacing it with values closer and closer to u:
>>> U = Symbol('U')
>>> equr = (sy.integrate(rigthhand(sigp1, sigp0, e, U, alphasum, Lky, k, lsl), (lsl, 0, lsl)))
>>> equl = lefthand(g, Ep)
>>> z = equr-equl
>>> u0 = solve(z.subs(U,0),lsl)[0]
>>> for i in range(1,10): # get to u in 9 steps
... u0 = nsolve(z.subs(U,i*u/10.), u0)
>>> print(u0)
-4.71178322070344
Now define a larger value of u
>>> u = 0.3
>>> for i in range(1,10): # get to u in 9 steps
... u0 = nsolve(z.subs(U,i*u/10.), u0)
...
>>> u0
-2.21489271112540
Our intitial guess will (usually) be pretty good since it is exact when u is 0; if it fails then you might need to take more than 9 steps to reach the target value.
I have written a code to compare the solution of sympy and PARI/GP, but when I give a fraction value D=13/12, I get error, TypeError: int expected instead of float.
So I changed p1[i] = pari.stoi(c_long(numbers[i - 1])) to p1[i] = pari.stoi(c_float(numbers[i - 1])), but then nfroots gives no output, note that I have to use fraction in A, B, C, D which might take $10^10$ digits after decimal point.
How can I solve this problem?
The code is given below to download the libpari.dll file, click here -
from ctypes import *
from sympy.solvers import solve
from sympy import Symbol
pari = cdll.LoadLibrary("libpari.dll")
pari.stoi.restype = POINTER(c_long)
pari.cgetg.restype = POINTER(POINTER(c_long))
pari.gtopoly.restype = POINTER(c_long)
pari.nfroots.restype = POINTER(POINTER(c_long))
(t_VEC, t_COL, t_MAT) = (17, 18, 19) # incomplete
pari.pari_init(2 ** 19, 0)
def t_vec(numbers):
l = len(numbers) + 1
p1 = pari.cgetg(c_long(l), c_long(t_VEC))
for i in range(1, l):
#Changed c_long to c_float, but got no output
p1[i] = pari.stoi(c_long(numbers[i - 1]))
return p1
def Quartic_Comparison():
x = Symbol('x')
a=0;A=0;B=1;C=-7;D=13/12 #PROBLEM 1
solution=solve(a*x**4+A*x**3+B*x**2+ C*x + D, x)
print(solution)
V=(A,B,C,D)
P = pari.gtopoly(t_vec(V), c_long(-1))
res = pari.nfroots(None, P)
print("elements as long (only if of type t_INT): ")
for i in range(1, pari.glength(res) + 1):
print(pari.itos(res[i]))
return res #PROBLEM 2
f=Quartic_Comparison()
print(f)
The error is -
[0.158343724039430, 6.84165627596057]
Traceback (most recent call last):
File "C:\Users\Desktop\PARI Function ellisdivisible - Copy.py", line 40, in <module>
f=Quartic_Comparison()
File "C:\Users\Desktop\PARI Function ellisdivisible - Copy.py", line 32, in Quartic_Comparison
P = pari.gtopoly(t_vec(V), c_long(-1))
File "C:\Users\Desktop\PARI Function ellisdivisible - Copy.py", line 20, in t_vec
p1[i] = pari.stoi(c_long(numbers[i - 1]))
TypeError: int expected instead of float
The PARI/C type system is very powerful and can also work with user-defined precision. Therefore PARI/C needs to use its own types system, see e.g. Implementation of the PARI types https://pari.math.u-bordeaux.fr/pub/pari/manuals/2.7.6/libpari.pdf.
All these internal types are handled as pointer to long in the PARI/C world. Don't be fooled by this, but the type has nothing to do with long. It is perhaps best thought of as an index or handle, representing a variable whose internal representation is hidden from the caller.
So whenever switching between PARI/C world and Python you need to convert types.
Conversion are described e.g. in section 4.4.6 in the above mentioned PDF file.
To convert a double to the corresponding PARI type (= t_REAL) one would therefore call the conversion function dbltor.
With the definition of
pari.dbltor.restype = POINTER(c_long)
pari.dbltor.argtypes = (c_double,)
one could get a PARI vector (t_VEC) like this:
def t_vec(numbers):
l = len(numbers) + 1
p1 = pari.cgetg(c_long(l), c_long(t_VEC))
for i in range(1, l):
p1[i] = pari.dbltor(numbers[i - 1])
return p1
User-defined Precision
But the type Python type double has limited precision (search e.g. for floating point precision on stackoverflow).
Therefore if you want to work with defined precision I would recommend to use gdiv.
Define it e.g. like so:
V = (pari.stoi(A), pari.stoi(B), pari.stoi(C), pari.gdiv(pari.stoi(13), pari.stoi(12)))
and adjust t_vec accordingly, to get a vector of these PARI numbers:
def t_vec(numbers):
l = len(numbers) + 1
p1 = pari.cgetg(c_long(l), c_long(t_VEC))
for i in range(1, l):
p1[i] = numbers[i - 1]
return p1
You then need to use realroots to calculate the roots in this case, see https://pari.math.u-bordeaux.fr/dochtml/html-stable/Polynomials_and_power_series.html#polrootsreal.
You could likewise use rtodbl to convert a PARI type t_REAL back to a double. But the same applies, since with using a floating point number you would loose precision. One solution here could be to convert the result to a string and display the list with the strings in the output.
Python Program
A self-contained Python program that considers the above points might look like this:
from ctypes import *
from sympy.solvers import solve
from sympy import Symbol
pari = cdll.LoadLibrary("libpari.so")
pari.stoi.restype = POINTER(c_long)
pari.stoi.argtypes = (c_long,)
pari.cgetg.restype = POINTER(POINTER(c_long))
pari.cgetg.argtypes = (c_long, c_long)
pari.gtopoly.restype = POINTER(c_long)
pari.gtopoly.argtypes = (POINTER(POINTER(c_long)), c_long)
pari.dbltor.restype = POINTER(c_long)
pari.dbltor.argtypes = (c_double,)
pari.rtodbl.restype = c_double
pari.rtodbl.argtypes = (POINTER(c_long),)
pari.realroots.restype = POINTER(POINTER(c_long))
pari.realroots.argtypes = (POINTER(c_long), POINTER(POINTER(c_long)), c_long)
pari.GENtostr.restype = c_char_p
pari.GENtostr.argtypes = (POINTER(c_long),)
pari.gdiv.restype = POINTER(c_long)
pari.gdiv.argtypes = (POINTER(c_long), POINTER(c_long))
(t_VEC, t_COL, t_MAT) = (17, 18, 19) # incomplete
precision = c_long(38)
pari.pari_init(2 ** 19, 0)
def t_vec(numbers):
l = len(numbers) + 1
p1 = pari.cgetg(c_long(l), c_long(t_VEC))
for i in range(1, l):
p1[i] = numbers[i - 1]
return p1
def quartic_comparison():
x = Symbol('x')
a = 0
A = 0
B = 1
C = -7
D = 13 / 12
solution = solve(a * x ** 4 + A * x ** 3 + B * x ** 2 + C * x + D, x)
print(f"sympy: {solution}")
V = (pari.stoi(A), pari.stoi(B), pari.stoi(C), pari.gdiv(pari.stoi(13), pari.stoi(12)))
P = pari.gtopoly(t_vec(V), -1)
roots = pari.realroots(P, None, precision)
res = []
for i in range(1, pari.glength(roots) + 1):
res.append(pari.GENtostr(roots[i]).decode("utf-8")) #res.append(pari.rtodbl(roots[i]))
return res
f = quartic_comparison()
print(f"PARI: {f}")
Test
The output on the console would look like:
sympy: [0.158343724039430, 6.84165627596057]
PARI: ['0.15834372403942977487354358292473161327', '6.8416562759605702251264564170752683867']
Side Note
Not really asked in the question, but just in case you want to avoid 13/12 you could transform your formula from
to
I have an ordinary differential equation like this:
DiffEq = Eq(-ℏ*ℏ*diff(Ψ,x,2)/(2*m) + m*w*w*(x*x)*Ψ/2 - E*Ψ , 0)
I want to perform a variable change :
sp.Eq(u , x*sqrt(m*w/ℏ))
sp.Eq(Ψ, H*exp(-u*u/2))
How can I do this with sympy?
Use the following function:
def variable_change(ODE,dependent_var,
independent_var,
new_dependent_var = None,
new_independent_var= None,
dependent_var_relation = None,
independent_var_relation = None,
order = 2):
if new_dependent_var == None:
new_dependent_var = dependent_var
if new_independent_var == None:
new_independent_var = independent_var
# dependent variable change
if new_independent_var != independent_var:
for i in range(order, -1, -1):
# remplace derivate
a = D(dependent_var , independent_var, i )
ξ = Function("ξ")(independent_var)
b = D( dependent_var.subs(independent_var, ξ), independent_var ,i)
rel = solve(independent_var_relation, new_independent_var)[0]
for j in range(order, 0, -1):
b = b.subs( D(ξ,independent_var,j), D(rel,independent_var,j))
b = b.subs(ξ, new_independent_var)
rel = solve(independent_var_relation, independent_var)[0]
b = b.subs(independent_var, rel)
ODE = ODE.subs(a,b)
ODE = ODE.subs(independent_var, rel)
# change of variables of indpendent variable
if new_dependent_var != dependent_var:
ODE = (ODE.subs(dependent_var.subs(independent_var,new_independent_var) , (solve(dependent_var_relation, dependent_var)[0])))
ODE = ODE.doit().expand()
return ODE.simplify()
For the example posted:
from sympy import *
from sympy import diff as D
E, ℏ ,w,m,x,u = symbols("E, ℏ , w,m,x,u")
Ψ ,H = map(Function, ["Ψ ","H"])
Ψ ,H = Ψ(x), H(u)
DiffEq = Eq(-ℏ*ℏ*D(Ψ,x,2)/(2*m) + m*w*w*(x*x)*Ψ/2 - E*Ψ,0)
display(DiffEq)
display(Eq(u , x*sqrt(m*w/ℏ)))
display(Eq(Ψ, H*exp(-u*u/2)))
newODE = variable_change(ODE = DiffEq,
independent_var = x,
new_independent_var= u,
independent_var_relation = Eq(u , x*sqrt(m*w/ℏ)),
dependent_var = Ψ,
new_dependent_var = H,
dependent_var_relation = Eq(Ψ, H*exp(-u*u/2)),
order = 2)
display(newODE)
Under this substitution the differential equation outputted is then:
Eq((-E*H + u*w*ℏ*D(H, u) + w*ℏ*H/2 - w*ℏ*D(H, (u, 2))/2)*exp(-u**2/2), 0)
If anyone is wondering how they could do it as well on CoCalc notebooks/anywhere where you can mix Sage and Python, here I defined basically the same variables and functions as OP did on his accepted answer, and then after substitution the result is converted back to Sage:
# Sage objects
var("E w m x u")
var("h_bar", latex_name = r'\hbar')
Ψ = function("Ψ")(x)
H = function('H')(u)
DiffEq = (-h_bar*h_bar*Ψ.diff(x, 2)/(2*m) + m*w*w*(x*x)*Ψ/2 - E*Ψ == 0)
display(DiffEq)
display(u == x*sqrt(m*w/h_bar))
display(Ψ == H*exp(-u*u/2))
# Function is purely sympy
newODE = variable_change(
ODE = DiffEq._sympy_(),
independent_var = x._sympy_(),
new_independent_var = u._sympy_(),
independent_var_relation = (u == x*sqrt(m*w/h_bar))._sympy_(),
dependent_var = Ψ._sympy_(),
new_dependent_var = H._sympy_(),
dependent_var_relation = (Ψ == H*exp(-u*u/2))._sympy_(),
order = 2
)
display(newODE._sage_())
Note that the only difference is that here things are converted to SymPy when using as arguments inside OP's function (it'll probably break if you don't!). After you call _sympy_() only once on a variable or expression, every sympy object gets a _sage_() method to convert back.
The result given was:
# Sage object again
1/2*(2*h_bar*u*w*diff(H(u), u) + h_bar*w*H(u) - h_bar*w*diff(H(u), u, u) - 2*E*H(u))*e^(-1/2*u^2) == 0
Which is just OP's result, but Sage handles operands a little bit differently.
Note: in order to avoid overriding stuff on Sage after importing everything from SymPy, you may want to import only diff as D, Function and solve from the main library. You might also want to rename sympy's solve to something else to avoid overriding Sage's own sage.symbolic.relation.solve.
In order to calculate derivatives and other expressions I used the sympy package and said that T = sy.Symbol('T') now that I have calculated the right expression:
E= -T**2*F_deriv_T(T,rho)
where
def F_deriv_rho(T,rho):
ret = 0
for n in range(5):
for m in range(4):
inner= c[n,m]*g_rho_deriv_rho_np*g_T_np
ret += inner
return ret
that looks like this:
F_deriv_rho: [0.0 7.76971e-5*T 0.0001553942*T**2*rho
T*(-5.14488e-5*log(rho) - 5.14488e-5)*log(T) + T*(1.22574e-5*log(rho)+1.22574e-5)*log(T) + T*(1.89488e-5*log(rho) + 1.89488e-5)*log(T) + T(2.29441e-5*log(rho) + 2.29441e-5)*log(T) + T*(7.49956e-5*log(rho) + 7.49956e-5)*log(T)
T**2*(-0.0001028976*rho*log(rho) - 5.14488e-5*rho)*log(T) + T**2*(2.45148e-5*rho*log(rho) + 1.22574e-5*rho)*log(T) + T**2*(3.78976e-5*rho*log(rho) + 1.89488e-5*rho)*log(T) + T**2*(4.58882e-5*rho*log(rho) + 2.29441e-5*rho)*log(T) + T**2*(0.0001499912*rho*log(rho) + 7.49956e 5*rho)*log(T)]
with python I would like to change T (and rho) as a symbol to a value. How could I do that?
So, I would like to create 10 numbers like T_def = np.arange(2000, 10000, 800)and exchange all my sy.symbol(T) by iterating through the 10 values I created in the array.
Thanks for your help
I have found the solution according to this post:
How to substitute multiple symbols in an expression in sympy?
by usings "subs":
>>> from sympy import Symbol
>>> x, y = Symbol('x y')
>>> f = x + y
>>> f.subs({x:10, y: 20})
>>> f
30
There's more for this kinda thing here: http://docs.sympy.org/latest/tutorial/basic_operations.html
EDIT: A faster way would be by using "lamdify" as suggested by #Bjoern Dahlgren