示例#1
0
def calc_Vsh(A, r_s, sanity_checks=False):
    D = A.shape[2]
    Dm1 = A.shape[1]
    q = A.shape[0]
    
    if q * D - Dm1 <= 0:
        return None
    
    R = sp.zeros((D, q, Dm1), dtype=A.dtype, order='C')

    for s in xrange(q):
        R[:,s,:] = r_s.dot(A[s].conj().T)

    R = R.reshape((q * D, Dm1))
    Vconj = ns.nullspace_qr(R.conj().T).T

    if sanity_checks:
        if not sp.allclose(mm.mmul(Vconj.conj(), R), 0):
            log.warning("Sanity Fail in calc_Vsh!: VR != 0")
        if not sp.allclose(mm.mmul(Vconj, Vconj.conj().T), sp.eye(Vconj.shape[0])):
            log.warning("Sanity Fail in calc_Vsh!: V H(V) != eye")
        
    Vconj = Vconj.reshape((q * D - Dm1, D, q))

    Vsh = Vconj.T
    Vsh = sp.asarray(Vsh, order='C')

    if sanity_checks:
        Vs = sp.transpose(Vsh, axes=(0, 2, 1)).conj()
        M = eps_r_noop(r_s, Vs, A)
        if not sp.allclose(M, 0):
            log.warning("Sanity Fail in calc_Vsh!: Bad Vsh")

    return Vsh
示例#2
0
    def density_2s(self, n1, n2):
        """Returns a reduced density matrix for a pair of sites.
        
        Currently only supports sites in the nonuniform window.

        Parameters
        ----------
        n1 : int
            The site number of the first site.
        n2 : int
            The site number of the second site (must be > n1).
        """
        rho = sp.empty((self.q[n1] * self.q[n2], self.q[n1] * self.q[n2]), dtype=sp.complex128)
        r_n2 = sp.empty_like(self.r[n2 - 1])
        r_n1 = sp.empty_like(self.r[n1 - 1])
        ln1m1 = self.get_l(n1 - 1)

        for s2 in xrange(self.q[n2]):
            for t2 in xrange(self.q[n2]):
                r_n2 = mm.mmul(self.A[n2][t2], self.r[n2], mm.H(self.A[n2][s2]))

                r_n = r_n2
                for n in reversed(xrange(n1 + 1, n2)):
                    r_n = tm.eps_r_noop(r_n, self.A[n], self.A[n])

                for s1 in xrange(self.q[n1]):
                    for t1 in xrange(self.q[n1]):
                        r_n1 = mm.mmul(self.A[n1][t1], r_n, mm.H(self.A[n1][s1]))
                        tmp = mm.adot(ln1m1, r_n1)
                        rho[s1 * self.q[n1] + s2, t1 * self.q[n1] + t2] = tmp
        return rho
示例#3
0
 def density_2s(self, d):
     """Returns a reduced density matrix for a pair of (seperated) sites.
     
     The site number basis is used: rho[s * q + u, t * q + v]
     with 0 <= s, t < q and 0 <= u, v < q.
     
     The state must be up-to-date -- see self.update()!
     
     Parameters
     ----------
     d : int
         The distance between the first and the second sites considered (d = n2 - n1).
         
     Returns
     -------
     rho : ndarray
         Reduced density matrix in the number basis.
     """
     rho = sp.empty((self.q * self.q, self.q * self.q), dtype=sp.complex128)
     
     for s2 in xrange(self.q):
         for t2 in xrange(self.q):
             r_n2 = m.mmul(self.A[t2], self.r, m.H(self.A[s2]))
             
             r_n = r_n2
             for n in xrange(d - 1):
                 r_n = tm.eps_r_noop(r_n, self.A, self.A)
                 
             for s1 in xrange(self.q):
                 for t1 in xrange(self.q):
                     r_n1 = m.mmul(self.A[t1], r_n, m.H(self.A[s1]))
                     tmp = m.adot(self.l, r_n1)
                     rho[s1 * self.q + s2, t1 * self.q + t2] = tmp
     return rho        
示例#4
0
    def density_2s(self, n1, n2):
        """Returns a reduced density matrix for a pair of sites.
        
        Currently only supports sites in the nonuniform window.

        Parameters
        ----------
        n1 : int
            The site number of the first site.
        n2 : int
            The site number of the second site (must be > n1).
        """
        rho = sp.empty((self.q[n1] * self.q[n2], self.q[n1] * self.q[n2]),
                       dtype=sp.complex128)
        r_n2 = sp.empty_like(self.r[n2 - 1])
        r_n1 = sp.empty_like(self.r[n1 - 1])
        ln1m1 = self.get_l(n1 - 1)

        for s2 in xrange(self.q[n2]):
            for t2 in xrange(self.q[n2]):
                r_n2 = mm.mmul(self.A[n2][t2], self.r[n2],
                               mm.H(self.A[n2][s2]))

                r_n = r_n2
                for n in reversed(xrange(n1 + 1, n2)):
                    r_n = tm.eps_r_noop(r_n, self.A[n], self.A[n])

                for s1 in xrange(self.q[n1]):
                    for t1 in xrange(self.q[n1]):
                        r_n1 = mm.mmul(self.A[n1][t1], r_n,
                                       mm.H(self.A[n1][s1]))
                        tmp = mm.adot(ln1m1, r_n1)
                        rho[s1 * self.q[n1] + s2, t1 * self.q[n1] + t2] = tmp
        return rho
示例#5
0
 def density_2s(self, n1, n2):
     """Returns a reduced density matrix for a pair of sites.
     
     Parameters
     ----------
     n1 : int
         The site number of the first site.
     n2 : int
         The site number of the second site (must be > n1).        
     """
     rho = sp.empty((self.q[n1] * self.q[n2], self.q[n1] * self.q[n2]), dtype=sp.complex128)
     r_n2 = sp.empty_like(self.r[n2 - 1])
     r_n1 = sp.empty_like(self.r[n1 - 1])
     
     for s2 in xrange(self.q[n2]):
         for t2 in xrange(self.q[n2]):
             r_n2 = m.mmul(self.A[n2][t2], self.r[n2], m.H(self.A[n2][s2]))
             
             r_n = r_n2
             for n in reversed(xrange(n1 + 1, n2)):
                 r_n = self.eps_r(n, r_n)        
                 
             for s1 in xrange(self.q[n1]):
                 for t1 in xrange(self.q[n1]):
                     r_n1 = m.mmul(self.A[n1][t1], r_n, m.H(self.A[n1][s1]))
                     tmp = m.mmul(self.l[n1 - 1], r_n1)
                     rho[s1 * self.q[n1] + s2, t1 * self.q[n1] + t2] = tmp.trace()
     return rho
示例#6
0
def calc_Vsh(A, r_s, sanity_checks=False):
    D = A.shape[2]
    Dm1 = A.shape[1]
    q = A.shape[0]

    if q * D - Dm1 <= 0:
        return None

    R = sp.zeros((D, q, Dm1), dtype=A.dtype, order='C')

    for s in xrange(q):
        R[:, s, :] = r_s.dot(A[s].conj().T)

    R = R.reshape((q * D, Dm1))
    Vconj = ns.nullspace_qr(R.conj().T).T

    if sanity_checks:
        if not sp.allclose(mm.mmul(Vconj.conj(), R), 0):
            log.warning("Sanity Fail in calc_Vsh!: VR != 0")
        if not sp.allclose(mm.mmul(Vconj,
                                   Vconj.conj().T), sp.eye(Vconj.shape[0])):
            log.warning("Sanity Fail in calc_Vsh!: V H(V) != eye")

    Vconj = Vconj.reshape((q * D - Dm1, D, q))

    Vsh = Vconj.T
    Vsh = sp.asarray(Vsh, order='C')

    if sanity_checks:
        Vs = sp.transpose(Vsh, axes=(0, 2, 1)).conj()
        M = eps_r_noop(r_s, Vs, A)
        if not sp.allclose(M, 0):
            log.warning("Sanity Fail in calc_Vsh!: Bad Vsh")

    return Vsh
示例#7
0
    def restore_ONR_n(self, n, G_n_i):
        """Transforms a single A[n] to obtain right orthonormalization.

        Implements the condition for right-orthonormalization from sub-section
        3.1, theorem 1 of arXiv:quant-ph/0608197v2.

        This function must be called for each n in turn, starting at N + 1,
        passing the gauge transformation matrix from the previous step
        as an argument.

        Finds a G[n-1] such that ON_R is fulfilled for n.

        Eigenvalues = 0 are a problem here... IOW rank-deficient matrices.
        Apparently, they can turn up during a run, but if they do we're screwed.

        The fact that M should be positive definite is used to optimize this.

        Parameters
        ----------
        n : int
            The site number.
        G_n_i : ndarray
            The inverse gauge transform matrix for site n obtained in the previous step (for n + 1).

        Returns
        -------
        G_n_m1_i : ndarray
            The inverse gauge transformation matrix for the site n - 1.
        """
        if G_n_i is None:
            GGh_n_i = self.r[n]
        else:
            GGh_n_i = mm.mmul(G_n_i, self.r[n], mm.H(G_n_i))

        M = self.eps_r(n, GGh_n_i)

        try:
            tu = la.cholesky(M) #Assumes M is pos. def.. It should raise LinAlgError if not.
            G_nm1 = mm.H(mm.invtr(tu)) #G is now lower-triangular
            G_nm1_i = mm.H(tu)
        except sp.linalg.LinAlgError:
            print "Restore_ON_R_%u: Falling back to eigh()!" % n
            e,Gh = la.eigh(M)
            G_nm1 = mm.H(mm.mmul(Gh, sp.diag(1/sp.sqrt(e) + 0.j)))
            G_nm1_i = la.inv(G_nm1)

        if G_n_i is None:
            G_n_i = G_nm1_i

        if self.sanity_checks:
            if not sp.allclose(sp.dot(G_nm1, G_nm1_i), sp.eye(G_nm1.shape[0]), atol=1E-13, rtol=1E-13):
                print "Sanity Fail in restore_ONR_%u!: Bad GT at n=%u" % (n, n)

        for s in xrange(self.q[n]):
            self.A[n][s] = mm.mmul(G_nm1, self.A[n][s], G_n_i)

        return G_nm1_i, G_nm1
示例#8
0
    def restore_ONR_n(self, n, G_n_i):
        """Transforms a single A[n] to obtain right orthonormalization.
        
        Implements the condition for right-orthonormalization from sub-section
        3.1, theorem 1 of arXiv:quant-ph/0608197v2.
        
        This function must be called for each n in turn, starting at N + 1,
        passing the gauge transformation matrix from the previous step
        as an argument.
        
        Finds a G[n-1] such that ON_R is fulfilled for n.
        
        Eigenvalues = 0 are a problem here... IOW rank-deficient matrices. 
        Apparently, they can turn up during a run, but if they do we're screwed.    
        
        The fact that M should be positive definite is used to optimize this.
        
        Parameters
        ----------
        n : int
            The site number.
        G_n_i : ndarray
            The inverse gauge transform matrix for site n obtained in the previous step (for n + 1).
    
        Returns
        -------
        G_n_m1_i : ndarray
            The inverse gauge transformation matrix for the site n - 1.
        """
        GGh_n_i = m.mmul(
            G_n_i, m.H(G_n_i)
        )  #r[n] does not belong here. The condition is for sum(AA). r[n] = 1 is a consequence.

        M = self.eps_r(n, GGh_n_i)

        #The following should be more efficient than eigh():
        try:
            tu = la.cholesky(
                M
            )  #Assumes M is pos. def.. It should raise LinAlgError if not.
            G_nm1 = m.H(m.invtr(tu))  #G is now lower-triangular
            G_nm1_i = m.H(tu)
        except sp.linalg.LinAlgError:
            print "restore_ONR_n: Falling back to eigh()!"
            e, Gh = la.eigh(M)
            G_nm1 = m.H(m.mmul(Gh, sp.diag(1 / sp.sqrt(e) + 0.j)))
            G_nm1_i = la.inv(G_nm1)

        for s in xrange(self.q[n]):
            self.A[n][s] = m.mmul(G_nm1, self.A[n][s], G_n_i)
            #It's ok to use the same matrix as out and as an operand here
            #since there are > 2 matrices in the chain and it is not the last argument.

        return G_nm1_i
示例#9
0
 def calc_x(self, n, Vsh, sqrt_l, sqrt_r, sqrt_l_inv, sqrt_r_inv):
     """Calculate the parameter matrix x* giving the desired B.
     
     This is equivalent to eqn. (49) of arXiv:1103.0936v2 [cond-mat.str-el] except 
     that, here, norm-preservation is not enforced, such that the optimal 
     parameter matrices x*_n (for the parametrization of B) are given by the 
     derivative w.r.t. x_n of <Phi[B, A]|Ĥ|Psi[A]>, rather than 
     <Phi[B, A]|Ĥ - H|Psi[A]> (with H = <Psi|Ĥ|Psi>).
     
     An additional sum was added for the single-site hamiltonian.
     
     Some multiplications have been pulled outside of the sums for efficiency.
     
     Direct dependencies: 
         - A[n - 1], A[n], A[n + 1]
         - r[n], r[n + 1], l[n - 2], l[n - 1]
         - C[n], C[n - 1]
         - K[n + 1]
         - V[n]
     """
     x = sp.zeros((self.D[n - 1], self.q[n] * self.D[n] - self.D[n - 1]), dtype=self.typ, order=self.odr)
     x_part = sp.empty_like(x)
     x_subpart = sp.empty_like(self.A[n][0])
     x_subsubpart = sp.empty_like(self.A[n][0])
     
     x_part.fill(0)
     for s in xrange(self.q[n]):
         x_subpart.fill(0)    
         
         if n < self.N:
             x_subsubpart.fill(0)
             for t in xrange(self.q[n + 1]):
                 x_subsubpart += m.mmul(self.C[n][s,t], self.r[n + 1], m.H(self.A[n + 1][t])) #~1st line
                 
             x_subsubpart += m.mmul(self.A[n][s], self.K[n + 1]) #~3rd line               
             
             x_subpart += m.mmul(x_subsubpart, sqrt_r_inv)
         
         if not self.h_ext is None:
             x_subsubpart.fill(0)
             for t in xrange(self.q[n]):                         #Extra term to take care of h_ext..
                 x_subsubpart += self.h_ext(n, s, t) * self.A[n][t] #it may be more effecient to squeeze this into the nn term...
             x_subpart += m.mmul(x_subsubpart, sqrt_r)
         
         x_part += m.mmul(x_subpart, Vsh[s])
             
     x += m.mmul(sqrt_l, x_part)
         
     if n > 1:
         x_part.fill(0)
         for s in xrange(self.q[n]):     #~2nd line
             x_subsubpart.fill(0)
             for t in xrange(self.q[n + 1]):
                 x_subsubpart += m.mmul(m.H(self.A[n - 1][t]), self.l[n - 2], self.C[n - 1][t, s])
             x_part += m.mmul(x_subsubpart, sqrt_r, Vsh[s])
         x += m.mmul(sqrt_l_inv, x_part)
             
     return x
示例#10
0
    def restore_RCF_l(self):
        G_nm1 = None
        l_nm1 = self.l[0]
        for n in xrange(self.N + 1):
            if n == 0:
                x = l_nm1
            else:
                x = mm.mmul(mm.H(G_nm1), l_nm1, G_nm1)
            M = self.eps_l(n, x)
            ev, EV = la.eigh(M)

            self.l[n] = mm.simple_diag_matrix(ev, dtype=self.typ)
            G_n_i = EV

            if n == 0:
                G_nm1 = mm.H(EV) #for left uniform case
                l_nm1 = self.l[n] #for sanity check
                self.u_gnd_l.r = mm.mmul(G_nm1, self.u_gnd_l.r, G_n_i) #since r is not eye

            for s in xrange(self.q[n]):
                self.A[n][s] = mm.mmul(G_nm1, self.A[n][s], G_n_i)

            if self.sanity_checks:
                l = self.eps_l(n, l_nm1)
                if not sp.allclose(l, self.l[n], atol=1E-12, rtol=1E-12):
                    print "Sanity Fail in restore_RCF_l!: l_%u is bad" % n
                    print la.norm(l - self.l[n])

            G_nm1 = mm.H(EV)
            l_nm1 = self.l[n]

            if self.sanity_checks:
                if not sp.allclose(sp.dot(G_nm1, G_n_i), sp.eye(G_n_i.shape[0]),
                                   atol=1E-12, rtol=1E-12):
                    print "Sanity Fail in restore_RCF_l!: Bad GT for l_%u" % n

        #Now G_nm1 = G_N
        G_nm1_i = mm.H(G_nm1)
        for s in xrange(self.q[self.N + 1]):
            self.A[self.N + 1][s] = mm.mmul(G_nm1, self.A[self.N + 1][s], G_nm1_i)

        ##This should not be necessary if G_N is really unitary
        #self.r[self.N] = mm.mmul(G_nm1, self.r[self.N], mm.H(G_nm1))
        #self.r[self.N + 1] = self.r[self.N]
        self.u_gnd_r.l[:] = mm.mmul(mm.H(G_nm1_i), self.u_gnd_r.l, G_nm1_i)
        
        self.S_hc = sp.zeros((self.N), dtype=sp.complex128)
        for n in xrange(1, self.N + 1):
            self.S_hc[n-1] = -sp.sum(self.l[n].diag * sp.log2(self.l[n].diag))
示例#11
0
    def restore_SCF(self):
        X = la.cholesky(self.r, lower=True)
        Y = la.cholesky(self.l, lower=False)
        
        U, sv, Vh = la.svd(Y.dot(X))
        
        #s contains the Schmidt coefficients,
        lam = sv**2
        self.S_hc = - np.sum(lam * sp.log2(lam))
        
        S = m.simple_diag_matrix(sv, dtype=self.typ)
        Srt = S.sqrt()
        
        g = m.mmul(Srt, Vh, m.invtr(X, lower=True))
        
        g_i = m.mmul(m.invtr(Y, lower=False), U, Srt)
        
        for s in xrange(self.q):
            self.A[s] = m.mmul(g, self.A[s], g_i)
                
        if self.sanity_checks:
            Sfull = np.asarray(S)
            
            if not np.allclose(g.dot(g_i), np.eye(self.D)):
                print "Sanity check failed! Restore_SCF, bad GT!"
            
            l = m.mmul(m.H(g_i), self.l, g_i)
            r = m.mmul(g, self.r, m.H(g))
            
            if not np.allclose(Sfull, l):
                print "Sanity check failed: Restorce_SCF, left failed!"
                
            if not np.allclose(Sfull, r):
                print "Sanity check failed: Restorce_SCF, right failed!"
                
            l = self.eps_l(Sfull)
            r = self.eps_r(Sfull)
            
            if not np.allclose(Sfull, l, rtol=self.itr_rtol*self.check_fac, 
                               atol=self.itr_atol*self.check_fac):
                print "Sanity check failed: Restorce_SCF, left bad!"
                
            if not np.allclose(Sfull, r, rtol=self.itr_rtol*self.check_fac, 
                               atol=self.itr_atol*self.check_fac):
                print "Sanity check failed: Restorce_SCF, right bad!"

        self.l = S
        self.r = S
示例#12
0
 def density_1s(self, n):
     """Returns a reduced density matrix for a single site.
     
     The site number basis is used: rho[s, t] 
     with 0 <= s, t < q[n].
     
     The state must be up-to-date -- see self.update()!
     
     Parameters
     ----------
     n1 : int
         The site number.
         
     Returns
     -------
     rho : ndarray
         Reduced density matrix in the number basis.
     """
     rho = sp.empty((self.q[n], self.q[n]), dtype=sp.complex128)
                 
     r_n = self.r[n]
     r_nm1 = sp.empty_like(self.r[n - 1])
     for s in xrange(self.q[n]):
         for t in xrange(self.q[n]):
             r_nm1 = m.mmul(self.A[n][t], r_n, m.H(self.A[n][s]))                
             rho[s, t] = m.adot(self.l[n - 1], r_nm1)
     return rho
示例#13
0
    def eps_l(self, n, x):
        """Implements the left epsilon map

        Parameters
        ----------
        res : ndarray
            A matrix to hold the result (with the same dimensions as l[n]). May be None.
        n : int
            The site number.
        x : ndarray
            The argument matrix. For example, using l[n - 1] gives a result l[n]

        Returns
        -------
        res : ndarray
            The resulting matrix.
        """
        if n > self.N + 1:
            n = self.N + 1
        elif n < 0:
            n = 0

        res = sp.zeros_like(self.l[n])

        for s in xrange(self.q[n]):
            res += mm.mmul(mm.H(self.A[n][s]), x, self.A[n][s])
        return res
示例#14
0
    def density_1s(self, n):
        """Returns a reduced density matrix for a single site.
        
        The site number basis is used: rho[s, t] 
        with 0 <= s, t < q[n].
        
        The state must be up-to-date -- see self.update()!
        
        Parameters
        ----------
        n1 : int
            The site number.
            
        Returns
        -------
        rho : ndarray
            Reduced density matrix in the number basis.
        """
        rho = sp.empty((self.q[n], self.q[n]), dtype=sp.complex128)

        r_n = self.r[n]
        r_nm1 = sp.empty_like(self.r[n - 1])
        for s in xrange(self.q[n]):
            for t in xrange(self.q[n]):
                r_nm1 = m.mmul(self.A[n][t], r_n, m.H(self.A[n][s]))
                rho[s, t] = m.adot(self.l[n - 1], r_nm1)
        return rho
示例#15
0
    def calc_C(self, n_low=-1, n_high=-1):
        """Generates the C matrices used to calculate the K's and ultimately the B's
        
        These are to be used on one side of the super-operator when applying the
        nearest-neighbour Hamiltonian, similarly to C in eqn. (44) of 
        arXiv:1103.0936v2 [cond-mat.str-el], except being for the non-norm-preserving case.

        Makes use only of the nearest-neighbour hamiltonian, and of the A's.
        
        C[n] depends on A[n] and A[n + 1].
        
        """
        if self.h_nn is None:
            return 0
        
        if n_low < 1:
            n_low = 1
        if n_high < 1:
            n_high = self.N
        
        for n in xrange(n_low, n_high):
            self.C[n].fill(0)
            for u in xrange(self.q[n]):
                for v in xrange(self.q[n + 1]):
                    AA = m.mmul(self.A[n][u], self.A[n + 1][v]) #only do this once for each 
                    for s in xrange(self.q[n]):
                        for t in xrange(self.q[n + 1]):                
                            h_nn_stuv = self.h_nn(n, s, t, u, v)
                            if h_nn_stuv != 0:
                                self.C[n][s, t] += h_nn_stuv * AA
示例#16
0
    def calc_Vsh(self, n, sqrt_r):
        """Generates m.H(V[n][s]) for a given n, used for generating B[n][s]
        
        This is described on p. 14 of arXiv:1103.0936v2 [cond-mat.str-el] for left 
        gauge fixing. Here, we are using right gauge fixing.
        
        Array slicing and reshaping is used to manipulate the indices as necessary.
        
        Each V[n] directly depends only on A[n] and r[n].
        
        We return the conjugate m.H(V) because we use it in more places than V.
        """
        R = sp.zeros((self.D[n], self.q[n], self.D[n-1]), dtype=self.typ, order='C')
        
        for s in xrange(self.q[n]):
            R[:,s,:] = m.mmul(sqrt_r, m.H(self.A[n][s]))

        R = R.reshape((self.q[n] * self.D[n], self.D[n-1]))
        V = m.H(ns.nullspace_qr(m.H(R)))
        #print (q[n]*D[n] - D[n-1], q[n]*D[n])
        #print V.shape
        #print sp.allclose(mat(V) * mat(V).H, sp.eye(q[n]*D[n] - D[n-1]))
        #print sp.allclose(mat(V) * mat(Rh).H, 0)
        V = V.reshape((self.q[n] * self.D[n] - self.D[n - 1], self.D[n], self.q[n])) #this works with the above form for R
        
        #prepare for using V[s] and already take the adjoint, since we use it more often
        Vsh = sp.empty((self.q[n], self.D[n], self.q[n] * self.D[n] - self.D[n - 1]), dtype=self.typ, order=self.odr)
        for s in xrange(self.q[n]):
            Vsh[s] = m.H(V[:,:,s])
        
        return Vsh
示例#17
0
    def eps_l(self, n, x, out=None):
        """Implements the left epsilon map
        
        FIXME: Ref.
        
        Parameters
        ----------
        n : int
            The site number.
        x : ndarray
            The argument matrix. For example, using l[n - 1] gives a result l[n]
        out : ndarray
            A matrix to hold the result (with the same dimensions as l[n]). May be None.
    
        Returns
        -------
        res : ndarray
            The resulting matrix.
        """
        if out is None:
            out = sp.zeros_like(self.l[n])
        else:
            out.fill(0.)

        for s in xrange(self.q[n]):
            out += m.mmul(m.H(self.A[n][s]), x, self.A[n][s])
        return out
示例#18
0
    def expect_1s_cor(self, o1, o2, n1, n2):
        """Computes the correlation of two single site operators acting on two different sites.
        
        See expect_1s().
        
        n1 must be smaller than n2.
        
        Assumes that the state is normalized.
        
        Parameters
        ----------
        o1 : function
            The first operator, acting on the first site.
        o2 : function
            The second operator, acting on the second site.
        n1 : int
            The site number of the first site.
        n2 : int
            The site number of the second site (must be > n1).
        """        
        r_n = self.eps_r(n2, self.r[n2], o2)

        for n in reversed(xrange(n1 + 1, n2)):
            r_n = self.eps_r(n, r_n)

        r_n = self.eps_r(n1, r_n, o1)   
         
        res = m.mmul(self.l[n1 - 1], r_n)
        return res.trace()
示例#19
0
    def calc_B(self, n, set_eta=True):
        """Generates the B[n] tangent vector corresponding to physical evolution of the state.

        In other words, this returns B[n][x*] (equiv. eqn. (47) of
        arXiv:1103.0936v2 [cond-mat.str-el])
        with x* the parameter matrices satisfying the Euler-Lagrange equations
        as closely as possible.
        
        In the case of Bc, use the general Bc generated in calc_B_centre().
        """
        if n == self.N_centre:
            B, eta_c = self.calc_B_centre()
            if set_eta:
                self.eta[self.N_centre] = eta_c
        else:
            l_sqrt, r_sqrt, l_sqrt_inv, r_sqrt_inv = self.calc_l_r_roots(n)
            
            if n > self.N_centre:
                Vsh = tm.calc_Vsh(self.A[n], r_sqrt, sanity_checks=self.sanity_checks)
                x = self.calc_x(n, Vsh, l_sqrt, r_sqrt, l_sqrt_inv, r_sqrt_inv, right=True)
                
                B = sp.empty_like(self.A[n])
                for s in xrange(self.q[n]):
                    B[s] = mm.mmul(l_sqrt_inv, x, mm.H(Vsh[s]), r_sqrt_inv)
                    
                if self.sanity_checks:
                    M = tm.eps_r_noop(self.r[n], B, self.A[n])
                    if not sp.allclose(M, 0):
                        print "Sanity Fail in calc_B!: B_%u does not satisfy GFC!" % n
            else:
                Vsh = tm.calc_Vsh_l(self.A[n], l_sqrt, sanity_checks=self.sanity_checks)
                x = self.calc_x(n, Vsh, l_sqrt, r_sqrt, l_sqrt_inv, r_sqrt_inv, right=False)
                
                B = sp.empty_like(self.A[n])
                for s in xrange(self.q[n]):
                    B[s] = mm.mmul(l_sqrt_inv, mm.H(Vsh[s]), x, r_sqrt_inv)
                    
                if self.sanity_checks:
                    M = tm.eps_l_noop(self.l[n - 1], B, self.A[n])
                    if not sp.allclose(M, 0):
                        print "Sanity Fail in calc_B!: B_%u does not satisfy GFC!" % n
            
            if set_eta:
                self.eta[n] = sp.sqrt(mm.adot(x, x))


        return B
示例#20
0
 def density_1s(self, n):
     """Returns a reduced density matrix for a single site.
     
     Parameters
     ----------
     n1 : int
         The site number.
     """
     rho = sp.empty((self.q[n], self.q[n]), dtype=sp.complex128)
                 
     r_n = self.r[n]
     r_nm1 = sp.empty_like(self.r[n - 1])
     for s in xrange(self.q[n]):
         for t in xrange(self.q[n]):
             r_nm1 = m.mmul(self.A[n][t], r_n, m.H(self.A[n][s]))                
             rho[s, t] = m.mmul(self.l[n - 1], r_nm1).trace()
     return rho
示例#21
0
 def get_B_from_x(self, x, Vsh, l_sqrt_i, r_sqrt_i, out=None):
     if out is None:
         out = np.zeros_like(self.A)
         
     for s in xrange(self.q):
         out[s] = m.mmul(l_sqrt_i, x, m.H(Vsh[s]), r_sqrt_i)
         
     return out
示例#22
0
 def calc_BHB_prereq(self, tdvp, tdvp2):
     """Calculates prerequisites for the application of the effective Hamiltonian in terms of tangent vectors.
     
     This is called (indirectly) by the self.excite.. functions.
     
     Parameters
     ----------
     tdvp2: EvoMPS_TDVP_Uniform
         Second state (may be the same, or another ground state).
         
     Returns
     -------
     A lot of stuff.
     """
     l = tdvp.l[0]
     r_ = tdvp2.r[0]
     r__sqrt = tdvp2.r_sqrt[0]
     r__sqrt_i = tdvp2.r_sqrt_i[0]
     A = tdvp.A[0]
     A_ = tdvp2.A[0]
         
     #Note: V has ~ D**2 * q**2 elements. We avoid making any copies of it except this one.
     #      This one is only needed because low-level routines force V_[s] to be contiguous.
     #      TODO: Store V instead of Vsh in tdvp_uniform too...
     V_ = sp.transpose(tdvp2.Vsh[0], axes=(0, 2, 1)).conj().copy(order='C')
             
     if self.ham_sites == 2:
         #eyeham = m.eyemat(self.q, dtype=sp.complex128)
         eyeham = sp.eye(self.q, dtype=sp.complex128)
         #diham = m.simple_diag_matrix(sp.repeat([-tdvp.h_expect.real], self.q))
         diham = -tdvp.h_expect.real * sp.eye(self.q, dtype=sp.complex128)
         _ham_tp = self.ham_tp + [[diham, eyeham]]  #subtract norm dof
         
         Ao1 = get_Aop(A, _ham_tp, 2, conj=False)
         
         AhlAo1 = [tm.eps_l_op_1s(l, A, A, o1.conj().T) for o1, o2 in _ham_tp]
         
         A_o2c = get_Aop(A_, _ham_tp, 1, conj=True)
         
         Ao1c = get_Aop(A, _ham_tp, 0, conj=True)
         
         A_Vr_ho2 = [tm.eps_r_op_1s(r__sqrt, A_, V_, o2) for o1, o2 in _ham_tp]
         
         A_A_o12c = get_A_ops(A_, A_, _ham_tp, conj=True)
         
         A_o1 = get_Aop(A_, _ham_tp, 2, conj=False)
         tmp = sp.empty((A_.shape[1], V_.shape[1]), dtype=A.dtype, order='C')
         tmp2 = sp.empty((A_.shape[1], A_o2c[0].shape[1]), dtype=A.dtype, order='C')
         rhs10 = 0
         for al in xrange(len(A_o1)):
             tmp2 = tm.eps_r_noop_inplace(r_, A_, A_o2c[al], tmp2)
             tmp3 = m.mmul(tmp2, r__sqrt_i)
             rhs10 += tm.eps_r_noop_inplace(tmp3, A_o1[al], V_, tmp)
             
         return V_, AhlAo1, A_o2c, Ao1, Ao1c, A_Vr_ho2, A_A_o12c, rhs10, _ham_tp
         
     elif self.ham_sites == 3:
         return
示例#23
0
 def eps_l(self, x, out=None):
     if out is None:
         out = np.zeros_like(self.A[0])
     else:
         out.fill(0.)
         
     for s in xrange(self.q):
         out += m.mmul(m.H(self.A[s]), x, self.A[s])        
         
     return out
示例#24
0
    def calc_C(self, n_low=-1, n_high=-1):
        """Generates the C matrices used to calculate the K's and ultimately the B's

        These are to be used on one side of the super-operator when applying the
        nearest-neighbour Hamiltonian, similarly to C in eqn. (44) of
        arXiv:1103.0936v2 [cond-mat.str-el], except being for the non-norm-preserving case.

        Makes use only of the nearest-neighbour hamiltonian, and of the A's.

        C[n] depends on A[n] and A[n + 1].
        
        This calculation can be significantly faster if a matrix form for h_nn
        is available. See gen_h_matrix().

        """
        if self.h_nn is None:
            return 0

        if n_low < 1:
            n_low = 0
        if n_high < 1:
            n_high = self.N + 1
        
        if self.h_nn_mat is None:
            for n in xrange(n_low, n_high):
                self.C[n].fill(0)
                for u in xrange(self.q[n]):
                    for v in xrange(self.q[n + 1]):
                        AA = mm.mmul(self.A[n][u], self.A[n + 1][v]) #only do this once for each
                        for s in xrange(self.q[n]):
                            for t in xrange(self.q[n + 1]):
                                h_nn_stuv = self.h_nn(n, s, t, u, v)
                                if h_nn_stuv != 0:
                                    self.C[n][s, t] += h_nn_stuv * AA
        else:
            dot = sp.dot
            for n in xrange(n_low, n_high):
                An = self.A[n]
                Anp1 = self.A[n + 1]
                
                AA = sp.empty_like(self.C[n])
                for u in xrange(self.q[n]):
                    for v in xrange(self.q[n + 1]):
                        AA[u, v] = dot(An[u], Anp1[v])
                        
                if n == 0: #FIXME: Temp. hack
                    self.AA0 = AA
                elif n == 1:
                    self.AA1 = AA
                
                res = sp.tensordot(AA, self.h_nn_mat[n], ((0, 1), (2, 3)))
                res = sp.rollaxis(res, 3)
                res = sp.rollaxis(res, 3)
                
                self.C[n][:] = res
示例#25
0
    def calc_l_r_roots(self, n):
        """Returns the matrix square roots (and inverses) needed to calculate B.

        Hermiticity of l[n] and r[n] is used to speed this up.
        If an exception occurs here, it is probably because these matrices
        are no longer Hermitian (enough).
        
        If l[n] or r[n] are diagonal or the identity, further optimizations are
        used.
        """
        try:
            l_sqrt = self.l[n - 1].sqrt()
        except AttributeError:
            l_sqrt, evd = mm.sqrtmh(self.l[n - 1], ret_evd=True)

        try:
            l_sqrt_inv = l_sqrt.inv()
        except AttributeError:
            l_sqrt_inv = mm.invmh(l_sqrt, evd=evd)

        try:
            r_sqrt = self.r[n].sqrt()
        except AttributeError:
            r_sqrt, evd =  mm.sqrtmh(self.r[n], ret_evd=True)

        try:
            r_sqrt_inv = r_sqrt.inv()
        except AttributeError:
            r_sqrt_inv = mm.invmh(r_sqrt, evd=evd)

        if self.sanity_checks:
            if not sp.allclose(mm.mmul(l_sqrt, l_sqrt), self.l[n - 1]):
                print "Sanity Fail in calc_l_r_roots: Bad l_sqrt_%u" % (n - 1)
            if not sp.allclose(mm.mmul(r_sqrt, r_sqrt), self.r[n]):
                print "Sanity Fail in calc_l_r_roots: Bad r_sqrt_%u" % (n)
            if not sp.allclose(mm.mmul(l_sqrt, l_sqrt_inv), sp.eye(l_sqrt.shape[0])):
                print "Sanity Fail in calc_l_r_roots: Bad l_sqrt_inv_%u" % (n - 1)
            if not sp.allclose(mm.mmul(r_sqrt, r_sqrt_inv), sp.eye(r_sqrt.shape[0])):
                print "Sanity Fail in calc_l_r_roots: Bad r_sqrt_inv_%u" % (n)

        return l_sqrt, r_sqrt, l_sqrt_inv, r_sqrt_inv
示例#26
0
    def calc_K(self, n_low=-1, n_high=-1):
        """Generates the K matrices used to calculate the B's
        
        K[n] is recursively defined. It depends on C[m] and A[m] for all m >= n.
        
        It directly depends on A[n], A[n + 1], r[n], r[n + 1], C[n] and K[n + 1].
        
        This is equivalent to K on p. 14 of arXiv:1103.0936v2 [cond-mat.str-el], except 
        that it is for the non-gauge-preserving case, and includes a single-site
        Hamiltonian term.
        
        K[1] is, assuming a normalized state, the expectation value H of Ĥ.
        
        Instead of an explicit single-site term here, one could also include the 
        single-site Hamiltonian in the nearest-neighbour term, which may be more 
        efficient.
        """
        if n_low < 1:
            n_low = 1
        if n_high < 1:
            n_high = self.N + 1
            
        for n in reversed(xrange(n_low, n_high)):
            self.K[n].fill(0)

            if n < self.N:
                for s in xrange(self.q[n]): 
                    for t in xrange(self.q[n+1]):
                        self.K[n] += m.mmul(self.C[n][s, t],
                                              self.r[n + 1], m.H(self.A[n+1][t]), 
                                              m.H(self.A[n][s]))
                    self.K[n] += m.mmul(self.A[n][s], self.K[n + 1], 
                                          m.H(self.A[n][s]))
            
            if not self.h_ext is None:
                for s in xrange(self.q[n]):
                    for t in xrange(self.q[n]):
                        h_ext_st = self.h_ext(n, s, t)
                        if h_ext_st != 0:
                            self.K[n] += h_ext_st * m.mmul(self.A[n][t], 
                                                    self.r[n], m.H(self.A[n][s]))
示例#27
0
 def eps_r(self, x, A1=None, A2=None, op=None, out=None):
     """Implements the right epsilon map
     
     FIXME: Ref.
     
     Parameters
     ----------
     op : function
         The single-site operator to use.
     out : ndarray
         A matrix to hold the result (with the same dimensions as r).
     x : ndarray
         The argument matrix.
 
     Returns
     -------
     res : ndarray
         The resulting matrix.
     """
     if out is None:
         out = np.zeros_like(self.A[0])
     else:
         out.fill(0.)
         
     if A1 is None:
         A1 = self.A
     if A2 is None:
         A2 = self.A
         
     if op is None:
         for s in xrange(self.q):
             out += m.mmul(A1[s], x, m.H(A2[s]))
     else:
         for s in xrange(self.q):
             for t in xrange(self.q):
                 o_st = op(s, t)
                 if o_st != 0.:
                     tmp = m.mmul(A1[t], x, m.H(A2[s]))
                     tmp *= o_st
                     out += tmp
     return out
示例#28
0
    def calc_B(self, n, set_eta=True):
        """Generates the B[n] tangent vector corresponding to physical evolution of the state.

        In other words, this returns B[n][x*] (equiv. eqn. (47) of
        arXiv:1103.0936v2 [cond-mat.str-el])
        with x* the parameter matrices satisfying the Euler-Lagrange equations
        as closely as possible.
        
        In the case of B1, use the general B1 generated in calc_B1().
        """
        if self.q[n] * self.D[n] - self.D[n - 1] > 0:
            if n == 1:
                B, eta1 = self.calc_B1()
                if set_eta:
                    self.eta[1] = eta1
            else:
                l_sqrt, r_sqrt, l_sqrt_inv, r_sqrt_inv = self.calc_l_r_roots(n)
    
                Vsh = self.calc_Vsh(n, r_sqrt)
    
                x = self.calc_opt_x(n, Vsh, l_sqrt, r_sqrt, l_sqrt_inv, r_sqrt_inv)
                
                if set_eta:
                    self.eta[n] = sp.sqrt(mm.adot(x, x))
    
                B = sp.empty_like(self.A[n])
                for s in xrange(self.q[n]):
                    B[s] = mm.mmul(l_sqrt_inv, x, mm.H(Vsh[s]), r_sqrt_inv)

            if self.sanity_checks:
                M = sp.zeros_like(self.r[n - 1])
                for s in xrange(self.q[n]):
                    M += mm.mmul(B[s], self.r[n], mm.H(self.A[n][s]))

                if not sp.allclose(M, 0):
                    print "Sanity Fail in calc_B!: B_%u does not satisfy GFC!" % n

            return B
        else:
            return None, 0
示例#29
0
    def calc_Vsh(self, n, sqrt_r):
        """Generates mm.H(V[n][s]) for a given n, used for generating B[n][s]

        This is described on p. 14 of arXiv:1103.0936v2 [cond-mat.str-el] for left
        gauge fixing. Here, we are using right gauge fixing.

        Array slicing and reshaping is used to manipulate the indices as necessary.

        Each V[n] directly depends only on A[n] and r[n].

        We return the conjugate mm.H(V) because we use it in more places than V.
        """
        R = sp.zeros((self.D[n], self.q[n], self.D[n-1]), dtype=self.typ, order='C')

        for s in xrange(self.q[n]):
            R[:,s,:] = mm.mmul(sqrt_r, mm.H(self.A[n][s]))

        R = R.reshape((self.q[n] * self.D[n], self.D[n-1]))
        Vconj = ns.nullspace_qr(mm.H(R)).T

        if self.sanity_checks:
            if not sp.allclose(mm.mmul(Vconj.conj(), R), 0):
                print "Sanity Fail in calc_Vsh!: VR_%u != 0" % (n)
            if not sp.allclose(mm.mmul(Vconj, mm.H(Vconj)), sp.eye(Vconj.shape[0])):
                print "Sanity Fail in calc_Vsh!: V H(V)_%u != eye" % (n)
            
        Vconj = Vconj.reshape((self.q[n] * self.D[n] - self.D[n - 1], self.D[n], self.q[n]))

        Vsh = Vconj.T
        Vsh = sp.asarray(Vsh, order='C')

        if self.sanity_checks:
            M = sp.zeros((self.q[n] * self.D[n] - self.D[n - 1], self.D[n]), dtype=self.typ)
            for s in xrange(self.q[n]):
                M += mm.mmul(mm.H(Vsh[s]), sqrt_r, mm.H(self.A[n][s]))
            if not sp.allclose(M, 0):
                print "Sanity Fail in calc_Vsh!: Bad Vsh_%u" % (n)

        return Vsh
示例#30
0
 def calc_x(self, l_sqrt, l_sqrt_i, r_sqrt, r_sqrt_i, Vsh, out=None):
     if out is None:
         out = np.zeros((self.D, (self.q - 1) * self.D), dtype=self.typ, 
                        order=self.odr)
     
     tmp = np.zeros_like(out)
     for s in xrange(self.q):
         tmp2 = m.mmul(self.A[s], self.K)
         for t in xrange(self.q):
             tmp2 += m.mmul(self.C[s, t], self.r, m.H(self.A[t]))
         tmp += m.mmul(tmp2, r_sqrt_i, Vsh[s])
     out += l_sqrt.dot(tmp)
     
     tmp.fill(0)
     for s in xrange(self.q):
         tmp2.fill(0)
         for t in xrange(self.q):
             tmp2 += m.mmul(m.H(self.A[t]), self.l, self.C[t, s])
         tmp += m.mmul(tmp2, r_sqrt, Vsh[s])
     out += l_sqrt_i.dot(tmp)
     
     return out
示例#31
0
    def eps_r(self, n, x, o=None, out=None):
        """Implements the right epsilon map
        
        FIXME: Ref.
        
        Parameters
        ----------
        n : int
            The site number.
        x : ndarray
            The argument matrix. For example, using r[n] (and o=None) gives a result r[n - 1]
        o : function
            The single-site operator to use. May be None.
        out : ndarray
            A matrix to hold the result (with the same dimensions as r[n - 1]). May be None.
    
        Returns
        -------
        res : ndarray
            The resulting matrix.
        """
        if out is None:
            out = sp.zeros((self.D[n - 1], self.D[n - 1]), dtype=self.typ)
        else:
            out.fill(0)

        if o is None:
            for s in xrange(self.q[n]):
                out += m.mmul(self.A[n][s], x, m.H(self.A[n][s]))            
        else:
            for s in xrange(self.q[n]):
                for t in xrange(self.q[n]):
                    o_st = o(n, s, t)
                    if o_st != 0.:
                        tmp = m.mmul(self.A[n][t], x, m.H(self.A[n][s]))
                        tmp *= o_st
                        out += tmp
        return out
示例#32
0
    def restore_RCF_r(self):
        G_n_i = None
        for n in reversed(xrange(1, self.N + 2)):
            G_n_i, G_n = self.restore_ONR_n(n, G_n_i)

            self.r[n - 1] = mm.eyemat(self.D[n - 1], self.typ)

            if self.sanity_checks:
                r_n = mm.eyemat(self.D[n], self.typ)

                r_nm1 = self.eps_r(n, r_n)
                if not sp.allclose(r_nm1, self.r[n - 1].A, atol=1E-13, rtol=1E-13):
                    print "Sanity Fail in restore_RCF_r!: r_%u is bad" % (n - 1)
                    print la.norm(r_nm1 - self.r[n - 1])

        #self.r[self.N + 1] = self.r[self.N]

        #Now G_n_i contains g_0_i
        for s in xrange(self.q[0]): #Note: This does not change the scale of A[0]
            self.A[0][s] = mm.mmul(G_n, self.A[0][s], G_n_i)

        self.u_gnd_l.r = mm.mmul(G_n, self.u_gnd_l.r, mm.H(G_n))
        self.l[0] = mm.mmul(mm.H(G_n_i), self.l[0], G_n_i)
示例#33
0
    def calc_l(self, start=-1, finish=-1):
        """Updates the l matrices using the current state.
        Implements step 5 of the TDVP algorithm or, equivalently, eqn. (41).
        (arXiv:1103.0936v2 [cond-mat.str-el])
        """
        if start < 0:
            start = 1
        if finish < 0:
            finish = self.N
        for n in xrange(start, finish + 1):
            self.l[n].fill(0)

            for s in xrange(self.q[n]):
                self.l[n] += m.mmul(m.H(self.A[n][s]), self.l[n - 1], self.A[n][s])
示例#34
0
    def density_2s(self, n1, n2):
        """Returns a reduced density matrix for a pair of (seperated) sites.
        
        The site number basis is used: rho[s * q[n1] + u, t * q[n1] + v]
        with 0 <= s, t < q[n1] and 0 <= u, v < q[n2].
        
        The state must be up-to-date -- see self.update()!
        
        Parameters
        ----------
        n1 : int
            The site number of the first site.
        n2 : int
            The site number of the second site (must be > n1).
            
        Returns
        -------
        rho : ndarray
            Reduced density matrix in the number basis.
        """
        rho = sp.empty((self.q[n1] * self.q[n2], self.q[n1] * self.q[n2]),
                       dtype=sp.complex128)

        for s2 in xrange(self.q[n2]):
            for t2 in xrange(self.q[n2]):
                r_n2 = m.mmul(self.A[n2][t2], self.r[n2], m.H(self.A[n2][s2]))

                r_n = r_n2
                for n in reversed(xrange(n1 + 1, n2)):
                    r_n = tm.eps_r_noop(r_n, self.A[n], self.A[n])

                for s1 in xrange(self.q[n1]):
                    for t1 in xrange(self.q[n1]):
                        r_n1 = m.mmul(self.A[n1][t1], r_n, m.H(self.A[n1][s1]))
                        tmp = m.adot(self.l[n1 - 1], r_n1)
                        rho[s1 * self.q[n1] + s2, t1 * self.q[n1] + t2] = tmp
        return rho
示例#35
0
 def density_1s(self):
     """Returns a reduced density matrix for a single site.
     
     The site number basis is used: rho[s, t] 
     with 0 <= s, t < q.
     
     The state must be up-to-date -- see self.update()!
         
     Returns
     -------
     rho : ndarray
         Reduced density matrix in the number basis.
     """
     rho = np.empty((self.q, self.q), dtype=self.typ)
     for s in xrange(self.q):
         for t in xrange(self.q):                
             rho[s, t] = m.adot(self.l, m.mmul(self.A[t], self.r, m.H(self.A[s])))
     return rho
示例#36
0
    def _calc_B_r_diss(self, op, K, C, n, set_eta=True):
        if self.q[n] * self.D[n] - self.D[n - 1] > 0:
            l_sqrt, l_sqrt_inv, r_sqrt, r_sqrt_inv = tm.calc_l_r_roots(
                self.l[n - 1],
                self.r[n],
                sanity_checks=self.sanity_checks,
                sc_data=("site", n))
            Vsh = tm.calc_Vsh(self.A[n],
                              r_sqrt,
                              sanity_checks=self.sanity_checks)
            x = self.calc_x(n, Vsh, l_sqrt, r_sqrt, l_sqrt_inv, r_sqrt_inv)
            if set_eta:
                self.eta[n] = sp.sqrt(mm.adot(x, x))

            B = sp.empty_like(self.A[n])
            for s in xrange(self.q[n]):
                B[s] = mm.mmul(l_sqrt_inv, x, mm.H(Vsh[s]), r_sqrt_inv)
            return B
        else:
            return None
示例#37
0
    def expect_2s_diss(self, op, LC, LK, n, AA=None):
        """Applies a two-site operator to two sites and returns
        the value after the change. In contrast to
        mps_gen.apply_op_2s, this routine does not change the state itself.
        
        Also, this does not perform self.update().
        
        Parameters
        ----------
        op : ndarray or callable
            The two-site operator. See self.expect_2s().
        n: int
            The site to apply the operator to.
            (It's also applied to n-1.)
        """
        #No neighbors, no fun.

        if n is 1:
            return 0
        if n is N:
            return 0

        A = self.A[n - 1]
        Ap1 = self.A[n]
        if AA is None:
            AA = tm.calc_AA(A, Ap1)

        if callable(op):
            op = sp.vectorize(op, otypes=[sp.complex128])
            op = sp.fromfunction(
                op, (A.shape[0], Ap1.shape[0], A.shape[0], Ap1.shape[0]))

        op = op.reshape(4, 4, 4, 4)
        C = tm.calc_C_mat_op_AA(op, AA)
        res = tm.eps_r_op_2s_C12_AA34(self.r[n + 1], LC, AA)
        operand = self.l[n - 1]
        operand = sp.reshape(operand, (1, 16))
        operand = sp.reshape(operand, (2, 8))

        return mm.mmul(operand, res)
        return mm.adot(self.l[n - 1], res)
示例#38
0
 def _calc_B_l(self, n, set_eta=True):
     if self.q[n] * self.D[n - 1] - self.D[n] > 0:
         l_sqrt, l_sqrt_inv, r_sqrt, r_sqrt_inv = tm.calc_l_r_roots(self.l[n - 1], 
                                                                self.r[n], 
                                                                zero_tol=self.zero_tol,
                                                                sanity_checks=self.sanity_checks,
                                                                sc_data=('site', n))
         
         Vsh = tm.calc_Vsh_l(self.A[n], l_sqrt, sanity_checks=self.sanity_checks)
         
         x = self.calc_x_l(n, Vsh, l_sqrt, r_sqrt, l_sqrt_inv, r_sqrt_inv)
         
         if set_eta:
             self.eta[n] = sp.sqrt(m.adot(x, x))
 
         B = sp.empty_like(self.A[n])
         for s in xrange(self.q[n]):
             B[s] = m.mmul(l_sqrt_inv, m.H(Vsh[s]), x, r_sqrt_inv)
         return B
     else:
         return None
示例#39
0
    def calc_BHB(self,
                 x,
                 p,
                 tdvp,
                 tdvp2,
                 prereq,
                 M_prev=None,
                 y_pi_prev=None,
                 pinv_solver=None):
        if pinv_solver is None:
            pinv_solver = las.gmres

        if self.ham_sites == 3:
            return
        else:
            V_, AhlAo1, A_o2c, Ao1, Ao1c, A_Vr_ho2, A_A_o12c, rhs10, _ham_tp = prereq

        A = tdvp.A[0]
        A_ = tdvp2.A[0]

        l = tdvp.l[0]
        r_ = tdvp2.r[0]

        l_sqrt = tdvp.l_sqrt[0]
        l_sqrt_i = tdvp.l_sqrt_i[0]

        r__sqrt = tdvp2.r_sqrt[0]
        r__sqrt_i = tdvp2.r_sqrt_i[0]

        K__r = tdvp2.K[0]
        K_l = tdvp.K_left[0]

        pseudo = tdvp2 is tdvp

        B = tdvp2.get_B_from_x(x, tdvp2.Vsh[0], l_sqrt_i, r__sqrt_i)

        #Skip zeros due to rank-deficiency
        if la.norm(B) == 0:
            return sp.zeros_like(x), M_prev, y_pi_prev

        if self.sanity_checks:
            tst = tm.eps_r_noop(r_, B, A_)
            if not la.norm(tst) > self.sanity_tol:
                log.warning("Sanity check failed: Gauge-fixing violation! " +
                            str(la.norm(tst)))

        if self.sanity_checks:
            B2 = np.zeros_like(B)
            for s in xrange(self.q):
                B2[s] = l_sqrt_i.dot(x.dot(m.mmul(V_[s], r__sqrt_i)))
            if la.norm(B - B2) / la.norm(B) > self.sanity_tol:
                log.warning("Sanity Fail in calc_BHB! Bad Vri!")

        y = tm.eps_l_noop(l, B, A)

        #        if pseudo:
        #            y = y - m.adot(r_, y) * l #should just = y due to gauge-fixing
        M = pinv_1mE(y, [A_], [A],
                     l,
                     r_,
                     p=-p,
                     left=True,
                     pseudo=pseudo,
                     out=M_prev,
                     tol=self.pinv_tol,
                     solver=pinv_solver,
                     use_CUDA=self.pinv_CUDA,
                     CUDA_use_batch=self.pinv_CUDA_batch,
                     sanity_checks=self.sanity_checks,
                     sc_data='M')

        #print m.adot(r, M)
        if self.sanity_checks:
            y2 = M - sp.exp(+1.j * p) * tm.eps_l_noop(M, A_, A)
            norm = la.norm(y.ravel())
            if norm == 0:
                norm = 1
            tst = la.norm(y - y2) / norm
            if tst > self.sanity_tol:
                log.warning("Sanity Fail in calc_BHB! Bad M. Off by: %g", tst)


#        if pseudo:
#            M = M - l * m.adot(r_, M)
        Mh = M.conj().T.copy(order='C')

        res_ls = 0
        res_lsi = 0

        exp = sp.exp

        if self.ham_sites == 3:
            pass
        else:
            Bo1 = get_Aop(B, _ham_tp, 2, conj=False)
            tmp = sp.empty((B.shape[1], V_.shape[1]), dtype=A.dtype, order='C')
            tmp2 = sp.empty((A_.shape[1], A_o2c[0].shape[1]),
                            dtype=A.dtype,
                            order='C')
            tmp3 = sp.empty_like(tmp2, order='C')
            for al in xrange(len(Bo1)):
                tmp3 = m.dot_inplace(
                    tm.eps_r_noop_inplace(r_, A_, A_o2c[al], tmp2), r__sqrt_i,
                    tmp3)
                res_ls += tm.eps_r_noop_inplace(tmp3, Bo1[al], V_, tmp)  #1

                tmp3 = m.dot_inplace(
                    tm.eps_r_noop_inplace(r_, B, A_o2c[al], tmp2), r__sqrt_i,
                    tmp3)
                tmp = tm.eps_r_noop_inplace(tmp3, Ao1[al], V_, tmp)  #3
                tmp *= exp(+1.j * p)
                res_ls += tmp
            del (tmp)
            del (tmp2)
            del (tmp3)

        res_lsi += sp.exp(-1.j * p) * Mh.dot(rhs10)  #10

        if self.ham_sites == 3:
            pass
        else:
            Bo2 = get_Aop(B, _ham_tp, 3, conj=False)
            for al in xrange(len(AhlAo1)):
                res_lsi += AhlAo1[al].dot(tm.eps_r_noop(r__sqrt, Bo2[al],
                                                        V_))  #2
                res_lsi += exp(-1.j * p) * tm.eps_l_noop(l, Ao1c[al], B).dot(
                    A_Vr_ho2[al])  #4
                res_lsi += exp(-2.j * p) * tm.eps_l_noop(Mh, Ao1c[al], A_).dot(
                    A_Vr_ho2[al])  #12

        K__rri = m.mmul(K__r, r__sqrt_i)
        res_ls += tm.eps_r_noop(K__rri, B, V_)  #5

        res_lsi += K_l.dot(tm.eps_r_noop(r__sqrt, B, V_))  #6

        res_lsi += sp.exp(-1.j * p) * Mh.dot(tm.eps_r_noop(K__rri, A_, V_))  #8

        y1 = sp.exp(+1.j * p) * tm.eps_r_noop(K__r, B, A_)  #7

        if self.ham_sites == 3:
            pass
        elif self.ham_sites == 2:
            tmp = 0
            for al in xrange(len(A_A_o12c)):
                tmp += sp.exp(+1.j * p) * tm.eps_r_noop(
                    tm.eps_r_noop(r_, A_, A_A_o12c[al][1]), B,
                    A_A_o12c[al][0])  #9
                tmp += sp.exp(+2.j * p) * tm.eps_r_noop(
                    tm.eps_r_noop(r_, B, A_A_o12c[al][1]), A,
                    A_A_o12c[al][0])  #11
            y = y1 + tmp  #7, 9, 11
            del (tmp)

        if pseudo:
            y = y - m.adot(l, y) * r_
        y_pi = pinv_1mE(y, [A], [A_],
                        l,
                        r_,
                        p=p,
                        left=False,
                        pseudo=pseudo,
                        out=y_pi_prev,
                        tol=self.pinv_tol,
                        solver=pinv_solver,
                        use_CUDA=self.pinv_CUDA,
                        CUDA_use_batch=self.pinv_CUDA_batch,
                        sanity_checks=self.sanity_checks,
                        sc_data='y_pi')
        #print m.adot(l, y_pi)
        if self.sanity_checks:
            z = y_pi - sp.exp(+1.j * p) * tm.eps_r_noop(y_pi, A, A_)
            tst = la.norm((y - z).ravel()) / la.norm(y.ravel())
            if tst > self.sanity_tol:
                log.warning("Sanity Fail in calc_BHB! Bad x_pi. Off by: %g",
                            tst)

        res_ls += tm.eps_r_noop(m.mmul(y_pi, r__sqrt_i), A, V_)

        res = l_sqrt.dot(res_ls)
        res += l_sqrt_i.dot(res_lsi)

        if self.sanity_checks:
            expval = m.adot(x, res) / m.adot(x, x)
            #print "expval = " + str(expval)
            if expval < -self.sanity_tol:
                log.warning(
                    "Sanity Fail in calc_BHB! H is not pos. semi-definite (%s)",
                    expval)
            if abs(expval.imag) > self.sanity_tol:
                log.warning("Sanity Fail in calc_BHB! H is not Hermitian (%s)",
                            expval)

        return res, M, y_pi
示例#40
0
    def calc_B(self, n, set_eta=True):
        """Generates the B[n] tangent vector corresponding to physical evolution of the state.

        In other words, this returns B[n][x*] (equiv. eqn. (47) of
        arXiv:1103.0936v2 [cond-mat.str-el])
        with x* the parameter matrices satisfying the Euler-Lagrange equations
        as closely as possible.
        
        In the case of Bc, use the general Bc generated in calc_B_centre().
        """
        if n == self.N_centre:
            B, eta_c = self.calc_B_centre()
            if set_eta:
                self.eta[self.N_centre] = eta_c
        else:
            l_sqrt, r_sqrt, l_sqrt_inv, r_sqrt_inv = self.calc_l_r_roots(n)

            if n > self.N_centre:
                Vsh = tm.calc_Vsh(self.A[n],
                                  r_sqrt,
                                  sanity_checks=self.sanity_checks)
                x = self.calc_x(n,
                                Vsh,
                                l_sqrt,
                                r_sqrt,
                                l_sqrt_inv,
                                r_sqrt_inv,
                                right=True)

                B = sp.empty_like(self.A[n])
                for s in xrange(self.q[n]):
                    B[s] = mm.mmul(l_sqrt_inv, x, mm.H(Vsh[s]), r_sqrt_inv)

                if self.sanity_checks:
                    M = tm.eps_r_noop(self.r[n], B, self.A[n])
                    if not sp.allclose(M, 0):
                        print "Sanity Fail in calc_B!: B_%u does not satisfy GFC!" % n
            else:
                Vsh = tm.calc_Vsh_l(self.A[n],
                                    l_sqrt,
                                    sanity_checks=self.sanity_checks)
                x = self.calc_x(n,
                                Vsh,
                                l_sqrt,
                                r_sqrt,
                                l_sqrt_inv,
                                r_sqrt_inv,
                                right=False)

                B = sp.empty_like(self.A[n])
                for s in xrange(self.q[n]):
                    B[s] = mm.mmul(l_sqrt_inv, mm.H(Vsh[s]), x, r_sqrt_inv)

                if self.sanity_checks:
                    M = tm.eps_l_noop(self.l[n - 1], B, self.A[n])
                    if not sp.allclose(M, 0):
                        print "Sanity Fail in calc_B!: B_%u does not satisfy GFC!" % n

            if set_eta:
                self.eta[n] = sp.sqrt(mm.adot(x, x))

        return B
示例#41
0
    def restore_RCF(self, update_l=True, normalize=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.
        
        This performs two 'almost' gauge transformations, where the 'almost'
        means we allow the norm to vary (if "normalize" = True).
        
        The last step (A[1]) is done diffently to the others since G[0],
        the gauge-transf. matrix, is just a number, which can be found more
        efficiently and accurately without using matrix methods.
        
        The last step (A[1]) is important because, if we have successfully made 
        r[1] = 1 in the previous steps, it fully determines the normalization 
        of the state via r[0] ( = l[N]).
        
        Optionally (normalize=False), the function will not attempt to make
        A[1] satisfy the orthonorm. condition, and will take G[0] = 1 = G[N],
        thus performing a pure gauge-transformation, but not ensuring complete
        canonical form.
        
        It is also possible to begin the process from a site n other than N,
        in case the sites > n are known to be in the desired form already.
        
        It is also 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)
        normalize : bool
            Whether to also attempt to normalize the state.
        diag_l : bool
            Whether to put l in diagonal form (defaults to True)
        """
        start = self.N

        G_n_i = sp.eye(self.D[start],
                       dtype=self.typ)  #This is actually just the number 1
        for n in xrange(start, 1, -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)

        #Now do A[1]...
        #Apply the remaining G[1]^-1 from the previous step.
        for s in xrange(self.q[1]):
            self.A[1][s] = m.mmul(self.A[1][s], G_n_i)

        #Now finish off
        tm.eps_r_noop_inplace(self.r[1], self.A[1], self.A[1], out=self.r[0])

        if normalize:
            G0 = 1. / sp.sqrt(self.r[0].squeeze().real)
            self.A[1] *= G0
            self.r[0][:] = 1

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

        if diag_l:
            G_nm1 = sp.eye(self.D[0], dtype=self.typ)
            for n in xrange(1, self.N):
                self.l[n], G_nm1, G_nm1_i = tm.restore_RCF_l(
                    self.A[n], self.l[n - 1], G_nm1, self.sanity_checks)

            #Apply remaining G_Nm1 to A[N]
            n = self.N
            for s in xrange(self.q[n]):
                self.A[n][s] = m.mmul(G_nm1, self.A[n][s])

            #Deal with final, scalar l[N]
            tm.eps_l_noop_inplace(self.l[n - 1],
                                  self.A[n],
                                  self.A[n],
                                  out=self.l[n])

            if self.sanity_checks:
                if not sp.allclose(
                        self.l[self.N].real, 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()
示例#42
0
    def restore_SCF(self, ret_g=False, zero_tol=None):
        """Restores symmetric canonical form.
        
        In this canonical form, self.l == self.r and are diagonal matrices
        with the Schmidt coefficients corresponding to the half-chain
        decomposition form the diagonal entries.
        
        Parameters
        ----------
        ret_g : bool
            Whether to return the gauge-transformation matrices used.
            
        Returns
        -------
        g, g_i : ndarray
            Gauge transformation matrix g and its inverse g_i.
        """
        if zero_tol is None:
            zero_tol = self.zero_tol
        
        X, Xi = tm.herm_fac_with_inv(self.r, lower=True, zero_tol=zero_tol)
        
        Y, Yi = tm.herm_fac_with_inv(self.l, lower=False, zero_tol=zero_tol)          
            
        U, sv, Vh = la.svd(Y.dot(X))
        
        #s contains the Schmidt coefficients,
        lam = sv**2
        self.S_hc = - np.sum(lam * sp.log2(lam))
        
        S = m.simple_diag_matrix(sv, dtype=self.typ)
        Srt = S.sqrt()
        
        g = m.mmul(Srt, Vh, Xi)
        
        g_i = m.mmul(Yi, U, Srt)
        
        for s in xrange(self.q):
            self.A[s] = m.mmul(g, self.A[s], g_i)
                
        if self.sanity_checks:
            Sfull = np.asarray(S)
            
            if not np.allclose(g.dot(g_i), np.eye(self.D)):
                log.warning("Sanity check failed! Restore_SCF, bad GT!")
            
            l = m.mmul(m.H(g_i), self.l, g_i)
            r = m.mmul(g, self.r, m.H(g))
            
            if not np.allclose(Sfull, l):
                log.warning("Sanity check failed: Restorce_SCF, left failed!")
                
            if not np.allclose(Sfull, r):
                log.warning("Sanity check failed: Restorce_SCF, right failed!")
                
            l = tm.eps_l_noop(Sfull, self.A, self.A)
            r = tm.eps_r_noop(Sfull, self.A, self.A)
            
            if not np.allclose(Sfull, l, rtol=self.itr_rtol*self.check_fac, 
                               atol=self.itr_atol*self.check_fac):
                log.warning("Sanity check failed: Restorce_SCF, left bad!")
                
            if not np.allclose(Sfull, r, rtol=self.itr_rtol*self.check_fac, 
                               atol=self.itr_atol*self.check_fac):
                log.warning("Sanity check failed: Restorce_SCF, right bad!")

        self.l = S
        self.r = S
        
        if ret_g:
            return g, g_i
        else:
            return
示例#43
0
    def calc_BHB_prereq(self, tdvp, tdvp2):
        """Calculates prerequisites for the application of the effective Hamiltonian in terms of tangent vectors.
        
        This is called (indirectly) by the self.excite.. functions.
        
        Parameters
        ----------
        tdvp2: EvoMPS_TDVP_Uniform
            Second state (may be the same, or another ground state).
            
        Returns
        -------
        A lot of stuff.
        """
        l = tdvp.l[0]
        r_ = tdvp2.r[0]
        r__sqrt = tdvp2.r_sqrt[0]
        r__sqrt_i = tdvp2.r_sqrt_i[0]
        A = tdvp.A[0]
        A_ = tdvp2.A[0]

        #Note: V has ~ D**2 * q**2 elements. We avoid making any copies of it except this one.
        #      This one is only needed because low-level routines force V_[s] to be contiguous.
        #      TODO: Store V instead of Vsh in tdvp_uniform too...
        V_ = sp.transpose(tdvp2.Vsh[0], axes=(0, 2, 1)).conj().copy(order='C')

        if self.ham_sites == 2:
            #eyeham = m.eyemat(self.q, dtype=sp.complex128)
            eyeham = sp.eye(self.q, dtype=sp.complex128)
            #diham = m.simple_diag_matrix(sp.repeat([-tdvp.h_expect.real], self.q))
            diham = -tdvp.h_expect.real * sp.eye(self.q, dtype=sp.complex128)
            _ham_tp = self.ham_tp + [[diham, eyeham]]  #subtract norm dof

            Ao1 = get_Aop(A, _ham_tp, 2, conj=False)

            AhlAo1 = [
                tm.eps_l_op_1s(l, A, A,
                               o1.conj().T) for o1, o2 in _ham_tp
            ]

            A_o2c = get_Aop(A_, _ham_tp, 1, conj=True)

            Ao1c = get_Aop(A, _ham_tp, 0, conj=True)

            A_Vr_ho2 = [
                tm.eps_r_op_1s(r__sqrt, A_, V_, o2) for o1, o2 in _ham_tp
            ]

            A_A_o12c = get_A_ops(A_, A_, _ham_tp, conj=True)

            A_o1 = get_Aop(A_, _ham_tp, 2, conj=False)
            tmp = sp.empty((A_.shape[1], V_.shape[1]),
                           dtype=A.dtype,
                           order='C')
            tmp2 = sp.empty((A_.shape[1], A_o2c[0].shape[1]),
                            dtype=A.dtype,
                            order='C')
            rhs10 = 0
            for al in xrange(len(A_o1)):
                tmp2 = tm.eps_r_noop_inplace(r_, A_, A_o2c[al], tmp2)
                tmp3 = m.mmul(tmp2, r__sqrt_i)
                rhs10 += tm.eps_r_noop_inplace(tmp3, A_o1[al], V_, tmp)

            return V_, AhlAo1, A_o2c, Ao1, Ao1c, A_Vr_ho2, A_A_o12c, rhs10, _ham_tp

        elif self.ham_sites == 3:
            return
示例#44
0
    def restore_RCF(self, ret_g=False, zero_tol=None):
        """Restores right canonical form.
        
        In this form, self.r = sp.eye(self.D) and self.l is diagonal, with
        the squared Schmidt coefficients corresponding to the half-chain
        decomposition as eigenvalues.
        
        Parameters
        ----------
        ret_g : bool
            Whether to return the gauge-transformation matrices used.
            
        Returns
        -------
        g, g_i : ndarray
            Gauge transformation matrix g and its inverse g_i.
        """
        if zero_tol is None:
            zero_tol = self.zero_tol
        
        #First get G such that r = eye
        G, G_i, rank = tm.herm_fac_with_inv(self.r, lower=True, zero_tol=zero_tol,
                                            return_rank=True)

        self.l = m.mmul(m.H(G), self.l, G)
        
        #Now bring l into diagonal form, trace = 1 (guaranteed by r = eye..?)
        ev, EV = la.eigh(self.l)

        G = G.dot(EV)
        G_i = m.H(EV).dot(G_i)
        
        for s in xrange(self.q):
            self.A[s] = m.mmul(G_i, self.A[s], G)
            
        #ev contains the squares of the Schmidt coefficients,
        self.S_hc = - np.sum(ev * sp.log2(ev))
        
        self.l = m.simple_diag_matrix(ev, dtype=self.typ)
        
        r_old = self.r
        
        if rank == self.D:
            self.r = m.eyemat(self.D, self.typ)
        else:
            self.r = sp.zeros((self.D), dtype=self.typ)
            self.r[-rank:] = 1
            self.r = m.simple_diag_matrix(self.r, dtype=self.typ)

        if self.sanity_checks:            
            r_ = m.mmul(G_i, r_old, m.H(G_i)) 
            
            if not np.allclose(self.r, r_, 
                               rtol=self.itr_rtol*self.check_fac,
                               atol=self.itr_atol*self.check_fac):
                log.warning("Sanity check failed: RestoreRCF, bad r (bad GT).")
            
            l = tm.eps_l_noop(self.l, self.A, self.A)
            r = tm.eps_r_noop(self.r, self.A, self.A)
            
            if not np.allclose(r, self.r,
                               rtol=self.itr_rtol*self.check_fac, 
                               atol=self.itr_atol*self.check_fac):
                log.warning("Sanity check failed: Restore_RCF, r not eigenvector! %s", la.norm(r - self.r))

            if not np.allclose(l, self.l,
                               rtol=self.itr_rtol*self.check_fac, 
                               atol=self.itr_atol*self.check_fac):
                log.warning("Sanity check failed: Restore_RCF, l not eigenvector! %s", la.norm(l - self.l))
        
        if ret_g:
            return G, G_i
        else:
            return