sympy tensor product of pauli matrices - python

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)))

Related

Cube roots of a complex number in python

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.

Trying to optimize my complex function to excute in a polynomial time

I have this code that generate all the 2**40 possible binary numbers, and from this binary numbers, i will try to get all the vectors that match my objectif function conditions which is:
1- each vector in the matrix must have 20 of ones(1).
2- the sum of s = s + (the index of one +1)* the rank of the one must equal 4970.
i wrote this code but it will take a lot of time maybe months, to give the results. Now, i am looking for an alternative way or an optimization of this code if possible.
import time
from multiprocessing import Process
from multiprocessing import Pool
import numpy as np
import itertools
import numpy
CC = 20
#test if there is 20 numbers of 1
def test1numebers(v,x=1,x_l=CC):
c = 0
for i in range(len(v)):
if(v[i]==x):
c+=1
if c == x_l:
return True
else:
return False
#s = s+ the nth of 1 * (index+1)
def objectif_function(v,x=1):
s = 0
for i in range(len(v)):
if(v[i]==x):
s = s+((i+1)*nthi(v,i))
return s
#calculate the nth of 1 in a vecteur
def nthi(v,i):
c = 0
for j in range(0,i+1):
if(v[j] == 1):
c+=1
return c
#generate 2**40 of all possible binray numbers
def generateMatrix(N):
l = itertools.product([0, 1], repeat=N)
return l
#function that get the number of valide vector that match our objectif function
def main_algo(N=40,S=4970):
#N = 40
m = generateMatrix(N)
#S = 4970
c = 0
ii = 0
for i in m:
ii+=1
print("\n count:",ii)
xx = i
if(test1numebers(xx)):
if(objectif_function(xx)==S):
c+=1
print('found one')
print('\n',xx,'\n')
if ii>=1000000:
break
t_end = time.time()
print('time taken for 10**6 is: ',t_end-t_start)
print(c)
#main_algo()
if __name__ == '__main__':
'''p = Process(target=main_algo, args=(40,4970,))
p.start()
p.join()'''
p = Pool(150)
print(p.map(main_algo, [40,4970]))
While you could make a lot of improvements in readability and make your code more pythonic.
I recommend that you use numpy which is the fastest way of working with matrixes.
Avoid working with matrixes on a "pixel by pixel" loop. With numpy you can make those calculations faster and with all the data at once.
Also numpy has support for generating matrixes really fast. I think that you could make a random [0,1] matrix in less lines of code and quite faster.
Also i recommend that you install OPENBLAS, ATLAS and LAPACK which make linear algebra calculations quite faster.
I hope this helps you.

Sympy solve() gives wrong results

I use Sympy solve() function to solve a large number of equations. All variables in the equations are defined as symbols. Variables can start with the letter P or F. I use solve() to express one specific P variable (the one that I observe) with only F variables, so I use solve() to substitute all other P variables with F variables. The sum of the coefficients before the F variables is ideally 1 or almost 1 (e.g.: 0.99).
This produces good results till a certain point where the number of equations becomes pretty big and also their length. There the Sympy solve() function starts to give me wrong results. The sum of the coefficients becomes negative (e.g. -7,...). It looks like that the solve() function gets problems with substituting any carrying over all variables and their coefficients.
Is there a way to correct this problem?
Dictionary of equations under link: https://drive.google.com/open?id=1VBQucrDU-o1diCd6i4rR3MlRh95qycmK
import json
from sympy import Symbol, Add, Eq, solve
# Get data
# data from link above
with open("C:\\\\Test\\dict.json") as f:
equations = json.load(f)
comp =[]
expressions = []
for p, equation_components in equations.items():
p = Symbol(p)
comp.append(p)
expression = []
for name, multiplier in equation_components.items():
if type(multiplier) == float or type(multiplier) == int:
expression.append(Symbol(name) * multiplier)
else:
expression.append(Symbol(name) * Symbol(multiplier))
expressions.append(Eq(p, Add(*expression)))
# Solution for variable P137807
print("Solving...")
# Works for slice :364 !!!!!
solutions = solve(expressions[:364], comp[:364], simplify=False, rational=False)
# Gives wrong results for slice :366 and above !!!!!
# solutions = solve(expressions[:366], comp[:366], simplify=False, rational=False)
vm_symbol = Symbol("P137807")
solution_1 = solutions[vm_symbol]
print("\n")
print("Solution_1:")
print(solution_1)
print("\n")
#Sum of coefficients
list_sum = []
for i in solution_1.args:
if str(i.args[1]) != "ANaN":
list_sum.append(i.args[0])
coeff_sum = sum(list_sum)
print("Sum:")
print(coeff_sum)
...
I just wanted to mark the problem as solved and provide reference to the solution. Please look at numerical instability when solving n=385 linear equations with Float coefficients #17136.
The solution that worked for me was to use the following solver and not the Sympy solve() function:
def ssolve(eqs, syms):
"""return the solution of linear system of equations
with symbolic coefficients and a unique solution.
Examples
========
>>> eqs=[x-1,x+2*y-z-2,x+z+w-6,2*y+z+x-2]
>>> v=[x,y,z,w]
>>> ssolve(eqs, v)
{x: 1, z: 0, w: 5, y: 1/2}
"""
from sympy.solvers.solveset import linear_coeffs
v = list(syms)
N = len(v)
# convert equations to coefficient dictionaries
print('checking linearity')
d = []
v0 = v + [0]
for e in [i.rewrite(Add) for i in eqs]:
co = linear_coeffs(e, *v)
di = dict([(i, c) for i, c in zip(v0, co) if c or not i])
d.append(di)
print('forward solving')
sol = {}
impl = {}
done = False
while not done:
# check for those that are done
more = set([i for i, di in enumerate(d) if len(di) == 2])
did = 0
while more:
di = d[more.pop()]
c = di.pop(0)
x = list(di)[0]
a = di.pop(x)
K = sol[x] = -c/a
v.remove(x)
changed = True
did += 1
# update everyone else
for j, dj in enumerate(d):
if x not in dj:
continue
dj[0] += dj.pop(x)*K
if len(dj) == 2:
more.add(j)
if did: print('found',did,'definitions')
# solve implicitly for the next variable
dcan = [i for i in d if len(i) > 2]
if not dcan:
done = True
else:
# take shortest first
di = next(ordered(dcan, lambda i: len(i)))
done = False
x = next(ordered(i for i in di if i))
c = di.pop(x)
for k in di:
di[k] /= -c
impl[x] = di.copy()
di.clear()
v.remove(x)
# update everyone else
for j, dj in enumerate(d):
if x not in dj:
continue
done = False
c = dj.pop(x)
for k in impl[x]:
dj[k] = dj.get(k, 0) + impl[x][k]*c
have = set(sol)
sol[0] = 1
while N - len(have):
print(N - len(have), 'to backsub')
for k in impl:
if impl[k] and not set(impl[k]) - have - {0}:
sol[k] = sum(impl[k][vi]*sol[vi] for vi in impl[k])
impl[k].clear()
have.add(k)
sol.pop(0)
return sol

How to simplify matrix expressions in SymPy?

Consider the following example
import sympy as sy
n = sy.symbols('n')
A = sy.MatrixSymbol("A",n,n)
B = sy.MatrixSymbol("B",n,n)
C = sy.MatrixSymbol("C",n,n)
M = A.inverse()*B.inverse() - A.inverse()*C*B.inverse()
B.inverse()*M.inverse()*A.inverse()
The example prints out B^-1*(A^-1*B^-1 - A^-1*C*B^-1)^-1*A^-1.
Can SymPy simplify the expression to (I-C)^-1? If not, how about any of the intermediate results, like collecting common factors in M?
The work around for this is using string converting on expression:
from sympy import *
n = symbols('n')
A = MatrixSymbol("A",n,n)
B = MatrixSymbol("B",n,n)
C = MatrixSymbol("C",n,n)
M = A.inverse()*B.inverse() - A.inverse()*C*B.inverse()
expression = B.inverse()*M.inverse()*A.inverse()
# convert expression to string then simplify
simplify_expression = simplify(str(expression))
pprint(simplify_expression)
Output:
-1
─────
C - 1

Julia: Products of sequences in a vectorized way

Learning to pass from Python to Julia, I am trying to convert an old code that I have, that is calculating a product of sequence of this expression:
I have two versions of the code in Python, one implemented with for loops, and the other using broadcasting. The for loop version is:
import numpy as np
A = np.arange(1.,5.,1)
G = np.array([[1.,2.],[3.,4.]])
def calcF(G,A):
N = A.size
print A
print N
F = []
for l in range(N):
F.append(G/A[l])
print F[l]
for j in range(N):
if j != l:
F[l]*=((G - A[l])/(G + A[j]))*((A[l] - A[j])/(A[l] + A[j]))
return F
F= calcF(G,A)
print F
And the vectorized version I have learned from a response to my question here, is this function:
def calcF_vectorized(G,A):
# Get size of A
N = A.size
# Perform "(G - A[l])/(G + A[j]))" in a vectorized manner
p1 = (G - A[:,None,None,None])/(G + A[:,None,None])
# Perform "((A[l] - A[j])/(A[l] + A[j]))" in a vectorized manner
p2 = ((A[:,None] - A)/(A[:,None] + A))
# Elementwise multiplications between the previously calculated parts
p3 = p1*p2[...,None,None]
# Set the escaped portion "j != l" output as "G/A[l]"
p3[np.eye(N,dtype=bool)] = G/A[:,None,None]
Fout = p3.prod(1)
# If you need separate arrays just like in the question, split it
return np.array_split(Fout,N)
I tried to naively translate the Python for loops code to Julia:
function JuliacalcF(G,A)
F = Array{Float64}[]
for l in eachindex(A)
push!(F,G/A[l])
println(A[i])
for j in eachindex(A)
if j!=l
F[l]*=((G - A[l])/(G + A[j]))*((A[l] - A[j])/(A[l] + A[j]))
end
end
end
#println(alpha)
return F
end
A = collect(1.0:1.0:5.0)
G = Vector{Float64}[[1.,2.],[3.,4.]]
println(JuliacalcF(G,A))
But is there a way to do it in a smart way as in the numpy broadcasting vectorized version?
Also, take a look at More-Dots and Loop Fusion where vectorization is described with examples.

Categories