= 6 S
import numpy as np
import matplotlib.pyplot as plt
from scipy.linalg import expm
%config InlineBackend.figure_format = "svg"
= lambda u : np.conj(u.T) adj
import tenpy
= np.array([[1,0],[0,1]],dtype=complex)
s0 = np.array([[0,1],[1,0]],dtype=complex)
sx = np.array([[0,-1j],[1j,0]],dtype=complex)
sy = np.array([[1,0],[0,-1]],dtype=complex)
sz
= {"0" : s0,
pauli "x" : sx,
"y" : sy,
"z" : sz,
"+" : (sx+1j*sy)/2,
"-" : (sx-1j*sy)/2}
def spin(polarization,s,S):
if(s==0):
= pauli[polarization]
temp else:
= s0
temp for i in range(1,S,1):
if(i==s):
= np.kron(temp,pauli[polarization])
temp else:
= np.kron(temp,s0)
temp return temp
def vacuum(S):
= np.zeros(2**S,dtype=complex)
vac -1] = 1
vac[return vac
def H_s(S,dir): #open boundaries
= np.zeros((2**S,2**S),dtype=complex)
H for i in range(0,S-1,1):
+= spin(dir,i,S)@spin(dir,i+1,S)
H return H
= H_s(S,"x")
HX = H_s(S,"y")
HY = H_s(S,"z")
HZ
def h_s(S,dir):
#create a dictionary for the Sz operators
= {}
numbers for i in range(0,S,1):
str(i)] = spin(dir,i,S)
numbers[return numbers
= h_s(S,"x")
hX = h_s(S,"y")
hY = h_s(S,"z")
hZ
def Ham(S,Jx=1,Jy=0,Jz=0,hx=0,hy=0,hz=0):
#build the Hamiltonian
= Jx*HX + Jy*HY + Jz*HZ
H for i in range(0,S,1): #we could do a random transverse field here
+= hx*hX[str(i)] + hy*hY[str(i)] + hz*hZ[str(i)]
H return H
= S
L =1,0,0#0.5,1,0.3 #random spin couplings
Jx,Jy,Jz=0,0,0.5#-2,0.2,0.5 #random B fields
hx,hy,hz= 1
G
= 1001
nT = 20
Tmax = np.linspace(0,Tmax,nT)
Ts = Tmax/(nT-1)
dt = ["x","y","z"]
dirs
= vacuum(S) #or make another state
psi0
= Ham(S,Jx=Jx,Jy=Jy,Jz=Jz,hx=hx,hy=hy,hz=hz)
H = spin("+",0,S) #raising operator at left boundary
J1 = spin("-",S-1,S) #lowering operator at right boundary
J2 = G,G G1,G2
Vectorize the density matrix by columns $$ \rho = \begin{pmatrix} \rho_{11}&\rho_{21}\\ \rho_{12}&\rho_{22} \end{pmatrix} \quad \mapsto \quad \vec{\rho} = \begin{pmatrix} \rho_{11}\\ \rho_{12}\\ \rho_{21}\\ \rho_{22} \end{pmatrix} $$
Time evolution is now an eigenvalue operator (not superoperator) equation Lvecρ⃗ = i∂tρ⃗ with
$$ L_\text{vec} = 1\otimes H - H^\top \otimes 1 -i\sum_m \frac{\gamma_m}{2} ((J_m^\dagger J_m)^\top\otimes 1 + 1 \otimes J_m^\dagger J_m - 2(J_m^\dagger)^\top \otimes J_m) $$ where we note the switched order of the dagger term versus the master equation. With U(δt) = e−iδtLvec we have ρ⃗(t+δt) = U(δt)ρ⃗(t) where here δt need not be small.
%%time
= np.identity(2**S)
one
= np.kron(one,H) - np.kron(H.T,one)\
L_vec - 1j*G1/2 * (np.kron((adj(J1)@J1).T,one) + np.kron(one,adj(J1)@J1) - 2*np.kron(adj(J1).T,J1))\
- 1j*G2/2 * (np.kron((adj(J2)@J2).T,one) + np.kron(one,adj(J2)@J2) - 2*np.kron(adj(J2).T,J2))
= np.einsum("a,b->ab",adj(psi0),psi0)
rho = np.zeros(2**S * 2**S,dtype=complex)
rho_vec for c in range(2**S):
*2**S:(c+1)*2**S] = rho[:,c]
rho_vec[c
= np.zeros((nT,3))
mags = np.zeros(nT,dtype=complex)
norm_vec = expm(-1j*dt*L_vec)
U
for t in range(nT):
#expectation values
= np.trace(rho)
norm_vec[t] for s in range(S):
for d in range(3):
+= np.real(np.trace(rho@spin(dirs[d],s,S))) / S
mags[t,d] = U@rho_vec
rho_vec for c in range(2**S):
= rho_vec[c*2**S:(c+1)*2**S] rho[:,c]
CPU times: user 5min 55s, sys: 49.7 s, total: 6min 45s
Wall time: 36.8 s
Numerically integrate the master equation in time using the Euler method $$ \rho(t+\delta t) = \rho(t) - i\, \delta t \bigg([H,\rho(t)]-i\sum_m \frac{\gamma_m}{2} (J_m^\dagger J_m\rho + \rho J_m^\dagger J_m - 2J_m\rho J_m^\dagger)\bigg) $$
Making the timestep smaller seems to help.
%%time
= np.einsum("a,b->ab",adj(psi0),psi0)
rho = np.zeros((nT,3))
magsNaive = np.zeros(nT)
trace for t in range(nT):
for s in range(S):
for d in range(3):
+= np.real(np.trace(rho@spin(dirs[d],s,S))) / S
magsNaive[t,d] = rho + -1j*dt * ((H@rho-rho@H)\
rho -1j*G1/2*(adj(J1)@J1@rho + rho@adj(J1)@J1 - 2*J1@rho@adj(J1))\
-1j*G2/2*(adj(J2)@J2@rho + rho@adj(J2)@J2 - 2*J2@rho@adj(J2)))
CPU times: user 40 s, sys: 360 ms, total: 40.3 s
Wall time: 3.4 s
class VectorizedSpinHalfSite(tenpy.networks.site.Site):
def __init__(self,conserve="None"):
= 2 #SU(2), local hilbert space dimension before vectorization
dim #conserved quantities
self.conserve = conserve
#basic operators
id = np.array([[1,0],[0,1]],dtype=complex) #no factor of 1/2 here
= np.array([[0,1],[1,0]],dtype=complex)
sx = np.array([[0,-1j],[1j,0]],dtype=complex)
sy = np.array([[1,0],[0,-1]],dtype=complex)
sz = (sx + 1j*sy)/2 #[[0,1],[0,0]]
sp = (sx - 1j*sy)/2 #[[0,0],[1,0]]
sm
#composite operators (with transposes!)
= ["0","x","y","z","p","m"]
names = [id,sx,sy,sz,sp,sm]
operators = {}
my_operators for i in range(0,6,1):
for j in range(0,6,1):
"s"+str(names[i])+str(names[j])] = np.kron(operators[i].T,operators[j])
my_operators[
#dissipative operators
"diss_m"] = np.kron((adj(sm)@sm).T,id) + np.kron(id,adj(sm)@sm) - 2*np.kron(adj(sm).T,sm)
my_operators["diss_p"] = np.kron((adj(sp)@sp).T,id) + np.kron(id,adj(sp)@sp) - 2*np.kron(adj(sp).T,sp)
my_operators[
#make it!
__init__(self, tenpy.linalg.charges.LegCharge.from_trivial(dim**2), **my_operators)
tenpy.networks.site.Site.
#label the states - this needs to be done AFTER Site.__init__() is run
= (dim-1)/2
S = {}
my_spin for i in range(0,dim,1):
str(-S+i)] = i*(dim+1)
my_spin["up"] = 0
my_spin["down"] = dim**2-1
my_spin[self.state_labels = my_spin
class VectorizedXYZChain(tenpy.models.model.CouplingMPOModel,tenpy.models.model.NearestNeighborModel):
def init_sites(self, model_params):
return VectorizedSpinHalfSite(conserve="None")
def init_terms(self, model_params):
# read out parameters
= model_params.get("Jx", 0.)
Jx = model_params.get("Jy", 0.)
Jy = model_params.get("Jz", 0.)
Jz = model_params.get("hx", 0.)
hx = model_params.get("hy", 0.)
hy = model_params.get("hz", 0.)
hz = model_params.get("Gp", 0.)
Gp = model_params.get("Gm", 0.)
Gm # add terms
for i in range(len(self.lat.unit_cell)):
self.add_onsite(-hx, i, "sx0")
self.add_onsite(-hy, i, "sy0")
self.add_onsite(-hz, i, "sz0")
#
self.add_onsite(hx, i, "s0x")
self.add_onsite(hy, i, "s0y")
self.add_onsite(hz, i, "s0z")
for i1, i2, dx in self.lat.pairs["nearest_neighbors"]:
self.add_coupling(-Jx, i1, "sx0", i2, "sx0", dx, plus_hc=False)
self.add_coupling(-Jy, i1, "sy0", i2, "sy0", dx, plus_hc=False)
self.add_coupling(-Jz, i1, "sz0", i2, "sz0", dx, plus_hc=False)
#
self.add_coupling(Jx, i1, "s0x", i2, "s0x", dx, plus_hc=False)
self.add_coupling(Jy, i1, "s0y", i2, "s0y", dx, plus_hc=False)
self.add_coupling(Jz, i1, "s0z", i2, "s0z", dx, plus_hc=False)
#add boundary dissipation
self.add_local_term(-1j*Gp/2,[("diss_p",(0,0))])
self.add_local_term(-1j*Gm/2,[("diss_m",(L-1,0))])
#simulation parameters
= {
tebd_params "trunc_params": {
"chi_max": 20, #bond dimension
"svd_min": 1*10**-10
},"N_steps": 1,
"dt": Tmax/(nT-1),
"order": 2,
"preserve_norm": False
}
%%time
= np.zeros(nT)
mzs = dict(L=L, Jx=Jx,Jy=Jy,Jz=Jz, hx=hx,hy=hy,hz=hz,Gp=G1,Gm=G2, bc_MPS="finite")
model_params
= VectorizedXYZChain(model_params)
M = tenpy.networks.mps.MPS.from_lat_product_state(M.lat,[["down"]]) #initial ferromagnetic state
psi = tenpy.algorithms.tebd.TEBDEngine(psi, M, tebd_params)
engine
id = np.array([1,0,0,1]).reshape((4,1,1)) #vectorized
= (id,id) * int(L//2) # only works for even length
my_tuple = tenpy.networks.mps.MPS.from_Bflat(M.lat.mps_sites(), my_tuple)
psi_id
= np.zeros(nT)
norms for step in range(nT):
#norms[step] = np.abs(psi.overlap(psi))
= tenpy.networks.mps.MPSEnvironment(psi_id,psi)
braket = np.real(np.mean(braket.expectation_value("sz0")))
mzs[step] engine.run()
CPU times: user 22min 38s, sys: 37min 28s, total: 1h 7s
Wall time: 5min 5s
2],lw=4,label="Reference: vectorized exp")
plt.plot(Ts,mags[:,#plt.plot(Ts,magsNaive[:,2],label="Reference: euler integrate")
="dashed",label=r"TEBD $\langle 1111|S^{z0}|\rho\rangle$")
plt.plot(Ts,mzs,ls
plt.legend()r"$t$")
plt.xlabel(r"$\langle S^z\rangle$") plt.ylabel(
Text(0, 0.5, '$\\langle S^z\\rangle$')