def calc_K_l(self, n_low=-1, n_high=-1): """Generates the K matrices used to calculate the B's. For the left gauge-fixing case. """ if n_low < 2: n_low = 2 if n_high < 1: n_high = self.N self.K[1] = sp.zeros((self.D[1], self.D[1]), dtype=self.typ) for n in xrange(n_low, n_high + 1): #if n <= self.N - self.ham_sites + 1: if self.ham_sites == 2: self.K[n], ex = tm.calc_K_l(self.K[n - 1], self.C[n - 1], self.l[n - 2], self.r[n], self.A[n], self.AA[n - 1]) else: assert False, 'left gauge fixing not yet supported for three-site Hamiltonians' self.h_expect[n - 1] = ex #else: # self.K[n].fill(0) if n_high == self.N: self.H_expect = sp.asscalar(self.K[self.N])
def calc_K_l(self, n_low=-1, n_high=-1): """Generates the K matrices used to calculate the B's. For the left gauge-fixing case. """ if n_low < 2: n_low = 2 if n_high < 1: n_high = self.N self.K[1] = sp.zeros((self.D[1], self.D[1]), dtype=self.typ) for n in xrange(n_low, n_high + 1): #if n <= self.N - self.ham_sites + 1: if self.ham_sites == 2: self.K[n], ex = tm.calc_K_l(self.K[n - 1], self.C[n - 1], self.l[n - 2], self.r[n], self.A[n], self.A[n - 1], sanity_checks=self.sanity_checks) else: assert False, 'left gauge fixing not yet supported for three-site Hamiltonians' self.h_expect[n - 1] = ex #else: # self.K[n].fill(0) if n_high == self.N: self.H_expect = sp.asscalar(self.K[self.N])
def calc_K(self): """Generates the right K matrices used to calculate the B's K[n] contains 'column-vectors' such that <l[n]|K[n]> = trace(l[n].dot(K[n])). K_l[n] contains 'bra-vectors' such that <K_l[n]|r[n]> = trace(K_l[n].dot(r[n])). """ self.h_expect = sp.zeros((self.N + 1), dtype=self.typ) self.uni_r.calc_AA() self.uni_r.calc_C() self.uni_r.calc_K() self.K[self.N + 1][:] = self.uni_r.K[0] self.uni_l.calc_AA() self.uni_l.calc_C() K_left, h_left_uni = self.uni_l.calc_K_l() self.K_l[0][:] = K_left[-1] for n in xrange(self.N, self.N_centre - 1, -1): self.K[n], he = tm.calc_K(self.K[n + 1], self.C[n], self.get_l(n - 1), self.r[n + 1], self.A[n], self.get_AA(n)) self.h_expect[n] = he for n in xrange(1, self.N_centre + 1): self.K_l[n], he = tm.calc_K_l(self.K_l[n - 1], self.C[n - 1], self.get_l(n - 2), self.r[n], self.A[n], self.get_AA(n - 1)) self.h_expect[n - 1] = he self.dH_expect = (mm.adot_noconj(self.K_l[self.N_centre], self.r[self.N_centre]) + mm.adot(self.l[self.N_centre - 1], self.K[self.N_centre]) - (self.N + 1) * self.uni_r.h_expect)
def calc_K(self): """Generates the right K matrices used to calculate the B's K[n] contains 'column-vectors' such that <l[n]|K[n]> = trace(l[n].dot(K[n])). K_l[n] contains 'bra-vectors' such that <K_l[n]|r[n]> = trace(K_l[n].dot(r[n])). """ self.h_expect = sp.zeros((self.N + 1), dtype=self.typ) self.uni_r.calc_AA() self.uni_r.calc_C() self.uni_r.calc_K() self.K[self.N + 1][:] = self.uni_r.K self.uni_l.calc_AA() self.uni_l.calc_C() K_left, h_left_uni = self.uni_l.calc_K_l() self.K_l[0][:] = K_left for n in xrange(self.N, self.N_centre - 1, -1): self.K[n], he = tm.calc_K(self.K[n + 1], self.C[n], self.get_l(n - 1), self.r[n + 1], self.A[n], self.A[n + 1], sanity_checks=self.sanity_checks) self.h_expect[n] = he for n in xrange(1, self.N_centre + 1): self.K_l[n], he = tm.calc_K_l(self.K_l[n - 1], self.C[n - 1], self.get_l(n - 2), self.r[n], self.A[n], self.A[n - 1], sanity_checks=self.sanity_checks) self.h_expect[n - 1] = he self.dH_expect = ( mm.adot_noconj(self.K_l[self.N_centre], self.r[self.N_centre]) + mm.adot(self.l[self.N_centre - 1], self.K[self.N_centre]) - (self.N + 1) * self.uni_r.h_expect)
def take_step_split(self, dtau, ham_is_Herm=True): """Take a time-step dtau using the split-step integrator. This is the one-site version of a DMRG-like time integrator described at: http://arxiv.org/abs/1408.5056 It has a fourth-order local error and is symmetric. It requires iteratively computing two matrix exponentials per site, and thus has less predictable CPU time requirements than the Euler or RK4 methods. NOTE: This requires the expokit extension, which is included in evoMPS but must be compiled during, e.g. using setup.py to build all extensions. Parameters ---------- dtau : complex The (imaginary or real) amount of imaginary time (tau) to step. ham_is_Herm : bool Whether the Hamiltonian is really Hermitian. If so, the lanczos method will be used for imaginary time evolution. """ #TODO: Compute eta. self.eta_sq.fill(0) self.eta = 0 assert self.canonical_form == 'right', 'take_step_split only implemented for right canonical form' assert self.ham_sites == 2, 'take_step_split only implemented for nearest neighbour Hamiltonians' dtau *= -1 from expokit_expmv import zexpmv, zexpmvh if sp.iscomplex(dtau) or not ham_is_Herm: expmv = zexpmv fac = 1.j dtau = sp.imag(dtau) else: expmv = zexpmvh fac = 1 norm_est = abs(self.H_expect.real) KL = [None] * (self.N + 1) KL[1] = sp.zeros((self.D[1], self.D[1]), dtype=self.typ) for n in xrange(1, self.N + 1): lop = Vari_Opt_Single_Site_Op(self, n, KL[n - 1], tau=fac) #print "Befor A", n, sp.inner(self.A[n].ravel().conj(), lop.matvec(self.A[n].ravel())).real An = expmv(lop, self.A[n].ravel(), dtau/2., norm_est=norm_est) self.A[n] = An.reshape((self.q[n], self.D[n - 1], self.D[n])) self.l[n] = tm.eps_l_noop(self.l[n - 1], self.A[n], self.A[n]) norm = m.adot(self.l[n], self.r[n]) self.A[n] /= sp.sqrt(norm) #print "After A", n, sp.inner(self.A[n].ravel().conj(), lop.matvec(self.A[n].ravel())).real, norm.real #shift centre matrix right (RCF is like having a centre "matrix" at "1") G = tm.restore_LCF_l_seq(self.A[n - 1:n + 1], self.l[n - 1:n + 1], sanity_checks=self.sanity_checks) if n > 1: self.AA[n - 1] = tm.calc_AA(self.A[n - 1], self.A[n]) self.C[n - 1] = tm.calc_C_mat_op_AA(self.ham[n - 1], self.AA[n - 1]) KL[n], ex = tm.calc_K_l(KL[n - 1], self.C[n - 1], self.l[n - 2], self.r[n], self.A[n], self.AA[n - 1]) if n < self.N: lop2 = Vari_Opt_SC_op(self, n, KL[n], tau=fac) #print "Befor G", n, sp.inner(G.ravel().conj(), lop2.matvec(G.ravel())).real G = expmv(lop2, G.ravel(), -dtau/2., norm_est=norm_est) G = G.reshape((self.D[n], self.D[n])) norm = sp.trace(self.l[n].dot(G).dot(self.r[n].dot(G.conj().T))) G /= sp.sqrt(norm) #print "After G", n, sp.inner(G.ravel().conj(), lop2.matvec(G.ravel())).real, norm.real for s in xrange(self.q[n + 1]): self.A[n + 1][s] = G.dot(self.A[n + 1][s]) self.AA[n] = tm.calc_AA(self.A[n], self.A[n + 1]) self.C[n] = tm.calc_C_mat_op_AA(self.ham[n], self.AA[n]) for n in xrange(self.N, 0, -1): lop = Vari_Opt_Single_Site_Op(self, n, KL[n - 1], tau=fac, sanity_checks=self.sanity_checks) #print "Before A", n, sp.inner(self.A[n].ravel().conj(), lop.matvec(self.A[n].ravel())).real An = expmv(lop, self.A[n].ravel(), dtau/2., norm_est=norm_est) self.A[n] = An.reshape((self.q[n], self.D[n - 1], self.D[n])) self.l[n] = tm.eps_l_noop(self.l[n - 1], self.A[n], self.A[n]) norm = m.adot(self.l[n], self.r[n]) self.A[n] /= sp.sqrt(norm) #print "After A", n, sp.inner(self.A[n].ravel().conj(), lop.matvec(self.A[n].ravel())).real, norm.real #shift centre matrix left (LCF is like having a centre "matrix" at "N") Gi = tm.restore_RCF_r_seq(self.A[n - 1:n + 1], self.r[n - 1:n + 1], sanity_checks=self.sanity_checks) if n < self.N: self.AA[n] = tm.calc_AA(self.A[n], self.A[n + 1]) self.C[n] = tm.calc_C_mat_op_AA(self.ham[n], self.AA[n]) self.calc_K(n_low=n, n_high=n) if n > 1: lop2 = Vari_Opt_SC_op(self, n - 1, KL[n - 1], tau=fac, sanity_checks=self.sanity_checks) Gi = expmv(lop2, Gi.ravel(), -dtau/2., norm_est=norm_est) Gi = Gi.reshape((self.D[n - 1], self.D[n - 1])) norm = sp.trace(self.l[n - 1].dot(Gi).dot(self.r[n - 1].dot(Gi.conj().T))) G /= sp.sqrt(norm) #print "After G", n, sp.inner(Gi.ravel().conj(), lop2.matvec(Gi.ravel())).real, norm.real for s in xrange(self.q[n - 1]): self.A[n - 1][s] = self.A[n - 1][s].dot(Gi) self.AA[n - 1] = tm.calc_AA(self.A[n - 1], self.A[n]) self.C[n - 1] = tm.calc_C_mat_op_AA(self.ham[n - 1], self.AA[n - 1])
def vari_opt_ss_sweep(self, ncv=None): """Perform a DMRG-style optimizing sweep to reduce the energy. This carries out the MPS version of the one-site DMRG algorithm. Combined with imaginary time evolution, this can dramatically improve convergence speed. """ assert self.canonical_form == 'right', 'vari_opt_ss_sweep only implemented for right canonical form' KL = [None] * (self.N + 1) KL[1] = sp.zeros((self.D[1], self.D[1]), dtype=self.typ) for n in xrange(1, self.N + 1): if n > 2: k = n - 1 KL[k], ex = tm.calc_K_l(KL[k - 1], self.C[k - 1], self.l[k - 2], self.r[k], self.A[k], self.AA[k - 1]) lop = Vari_Opt_Single_Site_Op(self, n, KL[n - 1], sanity_checks=self.sanity_checks) evs, eVs = las.eigsh(lop, k=1, which='SA', v0=self.A[n].ravel(), ncv=ncv) self.A[n] = eVs[:, 0].reshape((self.q[n], self.D[n - 1], self.D[n])) #shift centre matrix right (RCF is like having a centre "matrix" at "1") G = tm.restore_LCF_l_seq(self.A[n - 1:n + 1], self.l[n - 1:n + 1], sanity_checks=self.sanity_checks) #This is not strictly necessary, since r[n] is not used again, self.r[n] = G.dot(self.r[n].dot(G.conj().T)) if n < self.N: for s in xrange(self.q[n + 1]): self.A[n + 1][s] = G.dot(self.A[n + 1][s]) #All needed l and r should now be up to date #All needed KR should be valid still #AA and C must be updated if n > 1: self.AA[n - 1] = tm.calc_AA(self.A[n - 1], self.A[n]) self.C[n - 1] = tm.calc_C_mat_op_AA(self.ham[n - 1], self.AA[n - 1]) if n < self.N: self.AA[n] = tm.calc_AA(self.A[n], self.A[n + 1]) self.C[n] = tm.calc_C_mat_op_AA(self.ham[n], self.AA[n]) for n in xrange(self.N, 0, -1): if n < self.N: self.calc_K(n_low=n + 1, n_high=n + 1) lop = Vari_Opt_Single_Site_Op(self, n, KL[n - 1], sanity_checks=self.sanity_checks) evs, eVs = las.eigsh(lop, k=1, which='SA', v0=self.A[n].ravel(), ncv=ncv) self.A[n] = eVs[:, 0].reshape((self.q[n], self.D[n - 1], self.D[n])) #shift centre matrix left (LCF is like having a centre "matrix" at "N") Gi = tm.restore_RCF_r_seq(self.A[n - 1:n + 1], self.r[n - 1:n + 1], sanity_checks=self.sanity_checks) #This is not strictly necessary, since l[n - 1] is not used again self.l[n - 1] = Gi.conj().T.dot(self.l[n - 1].dot(Gi)) if n > 1: for s in xrange(self.q[n - 1]): self.A[n - 1][s] = self.A[n - 1][s].dot(Gi) #All needed l and r should now be up to date #All needed KL should be valid still #AA and C must be updated if n > 1: self.AA[n - 1] = tm.calc_AA(self.A[n - 1], self.A[n]) self.C[n - 1] = tm.calc_C_mat_op_AA(self.ham[n - 1], self.AA[n - 1]) if n < self.N: self.AA[n] = tm.calc_AA(self.A[n], self.A[n + 1]) self.C[n] = tm.calc_C_mat_op_AA(self.ham[n], self.AA[n])