def trace(op): """ Return the trace of an MPO without approximation. """ L = op.L s = op.s idMPO = MPO.MPO(L, 1, s) idMPO.setProductOperator(np.identity(s)) opMPS = MPO.MPSfromMPO(op) idMPS = MPO.MPSfromMPO(idMPO) return contractMPS(opMPS, idMPS)
def getSumNMPO(L, Nmax): """ Input: system size L, maximum occupation number Nmax. Return: the MPO of sum of occupation numbers at all sites. """ d = Nmax + 1 Z = np.zeros((2, d, d), dtype=complex) Z[0, :, :] = np.identity(d, dtype=complex) # Id Z[1, :, :] = np.diag(np.arange(d) + 0j) # n sumN = MPO.MPO(L, 2, d) opL = np.zeros((1, 2, 2), dtype=complex) opL[0, 0, 0] = 1 opL[0, 1, 1] = 1 sumN.setA(0, np.einsum('ijk,kmn->mnij', opL, Z)) opR = np.zeros((2, 1, 2), dtype=complex) opR[0, 0, 1] = 1 opR[1, 0, 0] = 1 sumN.setA(L - 1, np.einsum('ijk,kmn->mnij', opR, Z)) for i in range(1, L - 1): opM = np.zeros((2, 2, 2), dtype=complex) opM[0, 0, 0] = 1 opM[0, 1, 1] = 1 opM[1, 1, 0] = 1 sumN.setA(i, np.einsum('ijk,kmn->mnij', opM, Z)) return sumN
def __init__(self, L, Nmax, t, U, mu, V, Vint, offset): """ Initialization. Input: system size L (int), maximum occupation number N (int), t, U, mu, V, Vint (all are either float or numpy float array of length L) and offset (float). Will generate the MPO of Hamiltonian in self.hamil. """ self.L = L self.d = Nmax + 1 self.t = Common.toArray(L, t) self.U = Common.toArray(L, U) self.mu = Common.toArray(L, mu) self.V = Common.toArray(L, V) self.Vint = Common.toArray(L, Vint) self.hamil = MPO.MPO(L, 5, self.d) self.offset = offset delta = offset / L self.Z = np.zeros((5, self.d, self.d), dtype=complex) self.Z[0, :, :] = np.identity(self.d, dtype=complex) # Id self.Z[1, :, :] = np.diag( np.arange(self.d) * (np.arange(self.d) - 1) + 0j) # n(n-1) self.Z[2, :, :] = np.diag(np.sqrt(np.arange(self.d - 1) + 1) + 0j, -1) # b^dag self.Z[3, :, :] = np.diag(np.sqrt(np.arange(self.d - 1) + 1) + 0j, 1) # b self.Z[4, :, :] = np.diag(np.arange(self.d) + 0j) # n opL = np.zeros((1, 5, 5), dtype=complex) opL[0, 0, 0] = 1 opL[0, 1, 2] = 1 opL[0, 2, 3] = 1 opL[0, 3, 4] = 1 opL[0, 4, 4] = self.V[0] - self.mu[0] opL[0, 4, 1] = self.U[0] / 2 opL[0, 4, 0] = delta self.hamil.setA(0, np.einsum('ijk,kmn->mnij', opL, self.Z)) opR = np.zeros((5, 1, 5), dtype=complex) opR[0, 0, 4] = self.V[self.L - 1] - self.mu[self.L - 1] opR[0, 0, 1] = self.U[self.L - 1] / 2 opR[0, 0, 0] = delta opR[1, 0, 3] = -self.t[self.L - 1] opR[2, 0, 2] = -self.t[self.L - 1] opR[3, 0, 4] = self.Vint[self.L - 2] opR[4, 0, 0] = 1 self.hamil.setA(L - 1, np.einsum('ijk,kmn->mnij', opR, self.Z)) for i in range(1, L - 1): opM = np.zeros((5, 5, 5), dtype=complex) opM[0, 0, 0] = 1 opM[0, 1, 2] = 1 opM[0, 2, 3] = 1 opM[0, 3, 4] = 1 opM[0, 4, 4] = self.V[i] - self.mu[i] opM[0, 4, 1] = self.U[i] / 2 opM[0, 4, 0] = delta opM[1, 4, 3] = -self.t[i] opM[2, 4, 2] = -self.t[i] opM[3, 4, 4] = self.Vint[i - 1] opM[4, 4, 0] = 1 self.hamil.setA(i, np.einsum('ijk,kmn->mnij', opM, self.Z))
def __init__(self, L, Jx, Jy, Jz, g, h, offset): """ Initialization. Input: system size L (int), interaction Jx, Jy and Jz, longitudinal field g, transverse field h (Jx, Jy, Jz, g and h are either float or numpy float array of length L) and offset (float). Will generate the MPO of Hamiltonian in self.hamil. """ self.L = L self.Jx = Common.toArray(L, Jx) self.Jy = Common.toArray(L, Jy) self.Jz = Common.toArray(L, Jz) self.g = Common.toArray(L, g) self.h = Common.toArray(L, h) self.offset = offset self.hamil = MPO.MPO(L, 5, 2) opL = np.array([[[1, 0, 0, 0], [0, self.Jx[0], 0, 0], [0, 0, self.Jy[0], 0], [0, 0, 0, self.Jz[0]], [offset / L, self.g[0], 0, self.h[0]]]]) opR = np.array([[[offset / L, self.g[L - 1], 0, self.h[L - 1]]], [[0, 1, 0, 0]], [[0, 0, 1, 0]], [[0, 0, 0, 1]], [[1, 0, 0, 0]]]) self.hamil.ops[0].A = np.einsum('ijk,kml->mlij', opL, PauliSigma) self.hamil.ops[L - 1].A = np.einsum('ijk,kml->mlij', opR, PauliSigma) for i in range(1, L - 1): opM = np.array([[[1, 0, 0, 0], [0, self.Jx[i], 0, 0], [0, 0, self.Jy[i], 0], [0, 0, 0, self.Jz[i]], [offset / L, self.g[i], 0, self.h[i]]], [[0, 0, 0, 0], [0, 0, 0, 0], [ 0, 0, 0, 0], [0, 0, 0, 0], [0, 1, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [ 0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 1, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [ 0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 1]], [[0, 0, 0, 0], [0, 0, 0, 0], [ 0, 0, 0, 0], [0, 0, 0, 0], [1, 0, 0, 0]] ]) self.hamil.ops[i].A = np.einsum('ijk,kml->mlij', opM, PauliSigma)
def getSumSzProjector(L, S): """ Return the projector of total spin S. S is twice the physical spin (so that it is an integer). L and S have to be simultaneously even or odd. """ if (L % 2 != S % 2): print("Error: impossible total spin") exit(2) Z = np.zeros((2, 2, 2), dtype=complex) Z[0, 0, 0] = 1 Z[1, 1, 1] = 1 P = MPO.MPO(L, 1, 2) Smin = 0 Smax = 0 for i in range(L): Dl = (Smax - Smin) // 2 + 1 tmpA = np.zeros((Dl, Dl + 1, 2)) np.fill_diagonal(tmpA[:, :, 0], 1) np.fill_diagonal(tmpA[:, 1:, 1], 1) if (Smax + 2 - L + i <= S): Smax += 1 else: Smax -= 1 tmpA = tmpA[:, 1:] if (Smin - 2 + L - i >= S): Smin -= 1 else: Smin += 1 tmpA = tmpA[:, :-1] tmpA = np.einsum('ijk,mnk->mnij', tmpA, Z) P.setA(i, tmpA) return P
def getUMPOIsing(self, t, imag=True, cutD=0): """ Return the time-evolution unitary MPO of Ising model. Works only when the model is translational invariant! """ hi = self.J[0] * np.kron(PauliSigma[3, :, :], PauliSigma[3, :, :]) + \ self.h[0] * np.kron(PauliSigma[3, :, :], PauliSigma[0, :, :]) + \ self.g[0] * np.kron(PauliSigma[1, :, :], PauliSigma[0, :, :]) hL = self.h[0] * PauliSigma[3, :, :] + self.g[0] * PauliSigma[1, :, :] hR = self.h[0] * PauliSigma[3, :, :] + self.g[0] * PauliSigma[1, :, :] return MPO.getUMPO(self.L, 2, hi, hL, hR, t, imag=imag, cutD=cutD)
def getSumSzMPO(L): """ Return \sum_{i=1}^L sigma_i^z. """ sumSz = MPO.MPO(L, 2, 2) opL = np.array([[[1, 0, 0, 0], [0, 0, 0, 1]]]) opR = np.array([[[0, 0, 0, 1]], [[1, 0, 0, 0]]]) sumSz.ops[0].A = np.einsum('ijk,kml->mlij', opL, PauliSigma) sumSz.ops[L - 1].A = np.einsum('ijk,kml->mlij', opR, PauliSigma) for i in range(1, L - 1): opM = np.array([[[1, 0, 0, 0], [0, 0, 0, 1]], [[0, 0, 0, 0], [1, 0, 0, 0]]]) sumSz.ops[i].A = np.einsum('ijk,kml->mlij', opM, PauliSigma) return sumSz
def joinMPO(opA, opB, cutD=0): """ Return the product of two MPOs. Will make approximation if cutD is set nonzero. """ if (opA.L != opB.L or opA.s != opB.s): print("Error: inconsistent length / physical dimension!") exit(2) else: L = opA.L s = opA.s newMPO = MPO.MPO(L, 1, s) for i in range(L): tmpA = np.einsum('ijkl,jmpq->imkplq', opA.ops[i].A, opB.ops[i].A) tmpA = tmpA.reshape((s, s, opA.ops[i].Dl * opB.ops[i].Dl, opA.ops[i].Dr * opB.ops[i].Dr)) newMPO.setA(i, tmpA) if (cutD > 0): # Compress MPO newMPS = MPO.MPSfromMPO(newMPO) compressNewMPS = compressMPS(newMPS, cutD, silent=True) newMPO = MPO.MPOfromMPS(compressNewMPS) return newMPO
# Test of DMRG & fitApplyMPO & entanglement entropy & total spin projector #H = IsingModel.hamil H = HeisenbergModel.hamil gs = MPS.MPS(L, D, 2) # gs.setProductState(Sp.Up) #H = BoseHubbardModel.hamil #gs = MPS.MPS(L, D, Nmax+1) gs.setRandomState() Emin = ct.dmrg(H, gs, D) gs.saveMPS("saveGsMPS") H.saveMPO("saveHMPO") applyH = ct.fitApplyMPO(H, gs, D, tol=1e-4, silent=False, maxRound=20) gsFile = MPS.loadMPS("saveGsMPS") HFile = MPO.loadMPO("saveHMPO") print("Emin =", Emin) print("<gsF|HF|gsF> =", np.real(ct.contractMPSMPO(gsFile, HFile, gsFile))) print("<gs|H gs> =", np.real(ct.contractMPS(gs, applyH))) print("Ground state entanglement entropy is", np.exp(gs.getEntanglementEntropy(L // 2))) print() totSzMPO = Sp.getSumSzMPO(L) print("Total spin of ground state if", np.real(ct.contractMPSMPO(gs, totSzMPO, gs))) print() totS = 2 P = Sp.getSumSzProjector(L, totS) gsp = ct.exactApplyMPO(P, gs) print(