Beispiel #1
0
    def restore_RCF(self, use_QR=True, update_l=True, diag_l=True):
        """Use a gauge-transformation to restore right canonical form.
        
        Implements the conditions for right canonical form from sub-section
        3.1, theorem 1 of arXiv:quant-ph/0608197v2.
        
        It is possible to skip the diagonalization of the l's, such that
        only the right orthonormalization condition (r_n = eye) is met.
        
        By default, the l's are updated even if diag_l=False.
        
        Parameters
        ----------
        update_l : bool
            Whether to call calc_l() after completion (defaults to True)
        diag_l : bool
            Whether to put l in diagonal form (defaults to True)
        """   
        if use_QR:
            tm.restore_RCF_r_seq(self.A, self.r, sanity_checks=self.sanity_checks,
                                     sc_data="restore_RCF_r")
        else:
            G_n_i = sp.eye(self.D[self.N], dtype=self.typ) #This is actually just the number 1
            for n in xrange(self.N, 0, -1):
                self.r[n - 1], G_n, G_n_i = tm.restore_RCF_r(self.A[n], self.r[n], 
                                                             G_n_i, sc_data=('site', n),
                                                             zero_tol=self.zero_tol,
                                                             sanity_checks=self.sanity_checks)

        if self.sanity_checks:
            if not sp.allclose(self.r[0].A, 1, atol=1E-12, rtol=1E-12):
                log.warning("Sanity Fail in restore_RCF!: r_0 is bad / norm failure")
        
        if diag_l:
            tm.restore_RCF_l_seq(self.A, self.l, sanity_checks=self.sanity_checks,
                                                 sc_data="restore_RCF_l")

            if self.sanity_checks:
                if not sp.allclose(self.l[self.N].A, 1, atol=1E-12, rtol=1E-12):
                    log.warning("Sanity Fail in restore_RCF!: l_N is bad / norm failure")
                    log.warning("l_N = %s", self.l[self.N].squeeze().real)
                
                for n in xrange(1, self.N + 1):
                    r_nm1 = tm.eps_r_noop(self.r[n], self.A[n], self.A[n])
                    #r_nm1 = tm.eps_r_noop(m.eyemat(self.D[n], self.typ), self.A[n], self.A[n])
                    if not sp.allclose(r_nm1, self.r[n - 1], atol=1E-11, rtol=1E-11):
                        log.warning("Sanity Fail in restore_RCF!: r_%u is bad (off by %g)", n, la.norm(r_nm1 - self.r[n - 1]))
        elif update_l:
            self.calc_l()
Beispiel #2
0
    def restore_RCF(self, use_QR=True, update_l=True, diag_l=True):
        """Use a gauge-transformation to restore right canonical form.
        
        Implements the conditions for right canonical form from sub-section
        3.1, theorem 1 of arXiv:quant-ph/0608197v2.
        
        It is possible to skip the diagonalization of the l's, such that
        only the right orthonormalization condition (r_n = eye) is met.
        
        By default, the l's are updated even if diag_l=False.
        
        Parameters
        ----------
        update_l : bool
            Whether to call calc_l() after completion (defaults to True)
        diag_l : bool
            Whether to put l in diagonal form (defaults to True)
        """   
        if use_QR:
            tm.restore_RCF_r_seq(self.A, self.r, sanity_checks=self.sanity_checks,
                                     sc_data="restore_RCF_r")
        else:
            G_n_i = sp.eye(self.D[self.N], dtype=self.typ) #This is actually just the number 1
            for n in xrange(self.N, 0, -1):
                self.r[n - 1], G_n, G_n_i = tm.restore_RCF_r(self.A[n], self.r[n], 
                                                             G_n_i, sc_data=('site', n),
                                                             zero_tol=self.zero_tol,
                                                             sanity_checks=self.sanity_checks)

        if self.sanity_checks:
            if not sp.allclose(self.r[0].A, 1, atol=1E-12, rtol=1E-12):
                log.warning("Sanity Fail in restore_RCF!: r_0 is bad / norm failure")
        
        if diag_l:
            tm.restore_RCF_l_seq(self.A, self.l, sanity_checks=self.sanity_checks,
                                                 sc_data="restore_RCF_l")

            if self.sanity_checks:
                if not sp.allclose(self.l[self.N].A, 1, atol=1E-12, rtol=1E-12):
                    log.warning("Sanity Fail in restore_RCF!: l_N is bad / norm failure")
                    log.warning("l_N = %s", self.l[self.N].squeeze().real)
                
                for n in xrange(1, self.N + 1):
                    r_nm1 = tm.eps_r_noop(self.r[n], self.A[n], self.A[n])
                    #r_nm1 = tm.eps_r_noop(m.eyemat(self.D[n], self.typ), self.A[n], self.A[n])
                    if not sp.allclose(r_nm1, self.r[n - 1], atol=1E-11, rtol=1E-11):
                        log.warning("Sanity Fail in restore_RCF!: r_%u is bad (off by %g)", n, la.norm(r_nm1 - self.r[n - 1]))
        elif update_l:
            self.calc_l()
Beispiel #3
0
    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])
Beispiel #4
0
    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])