def expect_2s(self, op, n): """Computes the expectation value of a nearest-neighbour two-site operator. The operator should be a q[n] x q[n + 1] x q[n] x q[n + 1] array such that op[s, t, u, v] = <st|op|uv> or a function of the form op(s, t, u, v) = <st|op|uv>. The state must be up-to-date -- see self.update()! Parameters ---------- op : ndarray or callable The operator array or function. n : int The leftmost site number (operator acts on n, n + 1). Returns ------- expval : floating point number The expectation value (data type may be complex) """ A = self.A[n] Ap1 = self.A[n + 1] 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])) C = tm.calc_C_mat_op_AA(op, AA) res = tm.eps_r_op_2s_C12_AA34(self.r[n + 1], C, AA) return m.adot(self.l[n - 1], res)
def expect_2s(self, op, n): """Computes the expectation value of a nearest-neighbour two-site operator. The operator should be a q[n] x q[n + 1] x q[n] x q[n + 1] array such that op[s, t, u, v] = <st|op|uv> or a function of the form op(s, t, u, v) = <st|op|uv>. Parameters ---------- o : ndarray or callable The operator array or function. n : int The leftmost site number (operator acts on n, n + 1). """ A = self.get_A(n) Ap1 = self.get_A(n + 1) 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])) C = tm.calc_C_mat_op_AA(op, AA) res = tm.eps_r_op_2s_C12_AA34(self.get_r(n + 1), C, AA) return mm.adot(self.get_l(n - 1), res)
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 h_nn is in array form. """ if self.h_nn is None: return if n_low < 1: n_low = 0 if n_high < 1: n_high = self.N + 1 if callable(self.h_nn): for n in xrange(n_low, n_high): self.C[n] = tm.calc_C_func_op( lambda s, t, u, v: self.h_nn(n, s, t, u, v), self.get_A(n), self.get_A(n + 1)) else: for n in xrange(n_low, n_high): self.AA[n] = tm.calc_AA(self.get_A(n), self.get_A(n + 1)) self.C[n][:] = tm.calc_C_mat_op_AA(self.h_nn[n], self.AA[n])
def expect_2s(self, op, n, AA=None): """Computes the expectation value of a nearest-neighbour two-site operator. The operator should be a q[n] x q[n + 1] x q[n] x q[n + 1] array such that op[s, t, u, v] = <st|op|uv> or a function of the form op(s, t, u, v) = <st|op|uv>. The state must be up-to-date -- see self.update()! Parameters ---------- op : ndarray or callable The operator array or function. n : int The leftmost site number (operator acts on n, n + 1). Returns ------- expval : floating point number The expectation value (data type may be complex) """ A = self.A[n] Ap1 = self.A[n + 1] 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])) C = tm.calc_C_mat_op_AA(op, AA) res = tm.eps_r_op_2s_C12_AA34(self.r[n + 1], C, AA) return m.adot(self.l[n - 1], res)
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 h_nn is in array form. """ if self.h_nn is None: return if n_low < 1: n_low = 0 if n_high < 1: n_high = self.N + 1 if callable(self.h_nn): for n in xrange(n_low, n_high): self.C[n] = tm.calc_C_func_op(lambda s,t,u,v: self.h_nn(n,s,t,u,v), self.get_A(n), self.get_A(n + 1)) else: for n in xrange(n_low, n_high): self.AA[n] = tm.calc_AA(self.get_A(n), self.get_A(n + 1)) self.C[n][:] = tm.calc_C_mat_op_AA(self.h_nn[n], self.AA[n])
def expect_2s(self, op, n): """Computes the expectation value of a nearest-neighbour two-site operator. The operator should be a q[n] x q[n + 1] x q[n] x q[n + 1] array such that op[s, t, u, v] = <st|op|uv> or a function of the form op(s, t, u, v) = <st|op|uv>. Parameters ---------- o : ndarray or callable The operator array or function. n : int The leftmost site number (operator acts on n, n + 1). """ A = self.get_A(n) Ap1 = self.get_A(n + 1) 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])) C = tm.calc_C_mat_op_AA(op, AA) res = tm.eps_r_op_2s_C12_AA34(self.get_r(n + 1), C, AA) return mm.adot(self.get_l(n - 1), res)
def _calc_AAc_AAcm1(self): for n in [self.N_centre - 1, self.N_centre]: AA = tm.calc_AA(self.A[n], self.A[n + 1]) if n == self.N_centre - 1: self.AAcm1 = AA elif n == self.N_centre: self.AAc = AA
def matvec(self, x): self.calls += 1 #print self.calls t = self.tdvp n = self.n An = x.reshape((self.q[n], self.D[n - 1], self.D[n])) if n > 1: AAnm1 = tm.calc_AA(t.A[n - 1], An) Cnm1 = tm.calc_C_mat_op_AA(t.ham[n - 1], AAnm1) else: AAnm1 = None Cnm1 = None if n < t.N: AAn = tm.calc_AA(An, t.A[n + 1]) Cn = tm.calc_C_mat_op_AA(t.ham[n], AAn) else: AAn = None Cn = None res = sp.zeros_like(An) #Assuming RCF if not Cnm1 is None: for s in xrange(t.q[n]): res[s] += tm.eps_l_noop(t.l[n - 2], t.A[n - 1], Cnm1[:, s]) res[s] += self.KLnm1.dot(An[s]) res[s] = self.lm1_i.dot(res[s]) if not Cn is None: for s in xrange(t.q[n]): res[s] += tm.eps_r_noop(t.r[n + 1], Cn[s, :], t.A[n + 1]) res[s] += An[s].dot(t.K[n + 1]) #print "en = ", (sp.inner(An.conj().ravel(), res.ravel()) # / sp.inner(An.conj().ravel(), An.ravel())) return res.reshape(x.shape) * self.tau
def calc_C(self, n_low=-1, n_high=-1): """Generates the C tensors used to calculate the K's and ultimately the B's. This is called automatically by self.update(). C[n] contains a contraction of the Hamiltonian self.ham with the parameter tensors over the local basis indices. This is prerequisite for calculating the tangent vector parameters B, which optimally approximate the exact time evolution. 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], 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] through A[n + self.ham_sites - 1]. """ if self.ham is None: return 0 if n_low < 1: n_low = 1 if n_high < 1: n_high = self.N - self.ham_sites + 1 for n in xrange(1, self.N): self.AA[n] = tm.calc_AA(self.A[n], self.A[n + 1]) if self.ham_sites == 3: for n in xrange(1, self.N - 1): self.AAA[n] = tm.calc_AAA_AA(self.AA[n], self.A[n + 2]) else: self.AAA.fill(None) for n in xrange(n_low, n_high + 1): if callable(self.ham): ham_n = lambda *args: self.ham(n, *args) ham_n = sp.vectorize(ham_n, otypes=[sp.complex128]) ham_n = sp.fromfunction(ham_n, tuple(self.C[n].shape[:-2] * 2)) else: ham_n = self.ham[n] if self.ham_sites == 2: self.C[n] = tm.calc_C_mat_op_AA(ham_n, self.AA[n]) else: self.C[n] = tm.calc_C_3s_mat_op_AAA(ham_n, self.AAA[n])
def calc_C(self, n_low=-1, n_high=-1): """Generates the C tensors used to calculate the K's and ultimately the B's. This is called automatically by self.update(). C[n] contains a contraction of the Hamiltonian self.ham with the parameter tensors over the local basis indices. This is prerequisite for calculating the tangent vector parameters B, which optimally approximate the exact time evolution. 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], 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] through A[n + self.ham_sites - 1]. """ if self.ham is None: return 0 if n_low < 1: n_low = 1 if n_high < 1: n_high = self.N - self.ham_sites + 1 for n in xrange(n_low, n_high + 1): if callable(self.ham): ham_n = lambda *args: self.ham(n, *args) ham_n = sp.vectorize(ham_n, otypes=[sp.complex128]) ham_n = sp.fromfunction(ham_n, tuple(self.C[n].shape[:-2] * 2)) else: ham_n = self.ham[n] if self.ham_sites == 2: AA = tm.calc_AA(self.A[n], self.A[n + 1]) self.C[n] = tm.calc_C_mat_op_AA(ham_n, AA) else: AAA = tm.calc_AAA(self.A[n], self.A[n + 1], self.A[n + 2]) self.C[n] = tm.calc_C_3s_mat_op_AAA(ham_n, AAA)
def matvec(self, x): self.calls += 1 #print self.calls t = self.tdvp n = self.n Gn = x.reshape((self.D[n], self.D[n])) res = self.KLn.dot(Gn) + Gn.dot(t.K[n + 1]) if n < t.N: Ap1 = sp.array([Gn.dot(As) for As in t.A[n + 1]]) AAn = tm.calc_AA(t.A[n], Ap1) Cn = tm.calc_C_mat_op_AA(t.ham[n], AAn) for s in xrange(t.q[n]): sres = tm.eps_r_noop(t.r[n + 1], Cn[s, :], t.A[n + 1]) res += t.A[n][s].conj().T.dot(t.l[n - 1].dot(sres)) return res.reshape(x.shape) * self.tau
def calc_AA(self): """Calculates the products A[s] A[t] for s, t in range(self.q). The result is stored in self.AA. """ self.AA = tm.calc_AA(self.A, self.A)
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: V_, Vr_, Vri_, Vri_A_, C_, C_Vri_AA_, C_AAA_r_Ah_Vrih, \ C_AhAhlAA, C_AA_r_Ah_Vrih_, C_AAA_Vrh_, C_Vri_A_r_Ah_, \ C_AhlAA, C_AhlAA_conj, C_AA_Vrh, rhs10 = prereq else: C_, C_conj, V_, Vr_, Vri_, C_Vri_A_conj, C_AhlA, C_A_Vrh_, rhs10 = prereq A = tdvp.A[0] A_ = tdvp2.A[0] AA = tdvp.AA[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(Vri_[s])) if la.norm(B - B2) / la.norm(B) > self.sanity_tol: log.warning("Sanity Fail in calc_BHB! Bad Vri!") BA_ = tm.calc_AA(B, A_) AB = tm.calc_AA(A, B) if self.ham_sites == 3: BAA_ = tm.calc_AAA_AA(BA_, A_) ABA_ = tm.calc_AAA_AA(AB, A_) AAB = tm.calc_AAA_AA(AA, B) 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') if self.ham_sites == 3: tmp = BAA_ + sp.exp(+1.j * p) * ABA_ + sp.exp(+2.j * p) * AAB res = l_sqrt.dot(tm.eps_r_op_3s_C123_AAA456( r_, tmp, C_Vri_AA_)) #1 1D, #3, #3c else: tmp = BA_ + sp.exp(+1.j * p) * AB res = l_sqrt.dot(tm.eps_r_op_2s_AA12_C34(r_, tmp, C_Vri_A_conj)) #1, #3 OK res += sp.exp(-1.j * p) * l_sqrt_i.dot(Mh.dot(rhs10)) #10 exp = sp.exp subres = sp.zeros_like(res) eye = m.eyemat(C_.shape[2], dtype=tdvp.typ) eye2 = m.eyemat(A.shape[2], dtype=tdvp.typ) if self.ham_sites == 3: subres += exp(-2.j * p) * tm.eps_l_noop(Mh, A, C_AAA_r_Ah_Vrih) #12 subres += exp(-3.j * p) * tm.eps_l_op_2s_AA12_C34( Mh, AA, C_AAA_Vrh_) #12b for s in xrange(self.q): #subres += exp(-2.j * p) * A[s].conj().T.dot(Mh.dot(C_AAA_r_Ah_Vrih[s])) #12 subres += tm.eps_r_noop(B[s], C_AhAhlAA[s, :], Vr_) #2b subres += exp(-1.j * p) * tm.eps_l_noop( l.dot(B[s]), A, C_AA_r_Ah_Vrih_[s, :]) #4 subres += A[s].conj().T.dot( l.dot( tm.eps_r_op_2s_AA12_C34(eye2, AB, C_Vri_A_r_Ah_[ s, :, :]))) #2 -ive of that it should be.... subres += exp(-1.j * p) * tm.eps_l_op_2s_AA12_C34( eye2, C_AhlAA_conj[s, :, :], BA_).dot(Vr_[s].conj().T) #4b subres += exp(-2.j * p) * tm.eps_l_op_2s_AA12_C34( l.dot(B[s]), AA, C_AA_Vrh[s, :, :]) #4c subres += exp(+1.j * p) * tm.eps_r_op_2s_AA12_C34( r_.dot_left(B[s]), C_AhlAA[s, :, :], Vri_A_) #3b #for t in xrange(self.q): #subres += (C_AhAhlAA[t, s].dot(B[s]).dot(Vr_[t].conj().T)) #2b #subres += (exp(-1.j * p) * A[s].conj().T.dot(l.dot(B[t])).dot(C_AA_r_Ah_Vrih_[s, t])) #4 #subres += (exp(-3.j * p) * AA[t, s].conj().T.dot(Mh).dot(C_AAA_Vrh_[t, s])) #12b #for u in xrange(self.q): #subres += A[s].conj().T.dot(l.dot(AB[t, u]).dot(C_A_r_Ah_Vrih[s, t, u])) #2 -ive of that it should be.... #subres += (exp(+1.j * p) * C_AhlAA[t, s, s].dot(B[u]).dot(r_.dot(A_[u].conj().T)).dot(Vri_[t].conj().T)) #3b #subres += (exp(-1.j * p) * C_AhAhlA[s, t, u].dot(BA_[t, u]).dot(Vr_[s].conj().T)) #4b #subres += (exp(-2.j * p) * AA[t, s].conj().T.dot(l.dot(B[u])).dot(C_AA_Vrh[t, s, u])) #4c else: for s in xrange(self.q): #subres += C_AhlA[s, t].dot(B[s]).dot(Vr_[t].conj().T) #2 OK subres += tm.eps_r_noop(B[s], C_AhlA[s, :], Vr_) #2 #+ exp(-1.j * p) * A[t].conj().T.dot(l.dot(B[s])).dot(C_A_Vrh_[t, s]) #4 OK with 3 subres += exp(-1.j * p) * tm.eps_l_noop( l.dot(B[s]), A, C_A_Vrh_[s, :]) #4 #+ exp(-2.j * p) * A[s].conj().T.dot(Mh.dot(C_[s, t])).dot(Vr_[t].conj().T)) #12 subres += exp(-2.j * p) * A[s].conj().T.dot(Mh).dot( tm.eps_r_noop(eye, C_[s, :], Vr_)) #12 res += l_sqrt_i.dot(subres) res += l_sqrt.dot(tm.eps_r_noop(K__r, B, Vri_)) #5 res += l_sqrt_i.dot(K_l.dot(tm.eps_r_noop(r__sqrt, B, V_))) #6 res += sp.exp(-1.j * p) * l_sqrt_i.dot( Mh.dot(tm.eps_r_noop(K__r, A_, Vri_))) #8 y1 = sp.exp(+1.j * p) * tm.eps_r_noop(K__r, B, A_) #7 if self.ham_sites == 3: tmp = sp.exp(+1.j * p) * BAA_ + sp.exp(+2.j * p) * ABA_ + sp.exp( +3.j * p) * AAB #9, #11, #11b y = y1 + tm.eps_r_op_3s_C123_AAA456(r_, tmp, C_) elif self.ham_sites == 2: tmp = sp.exp(+1.j * p) * BA_ + sp.exp(+2.j * p) * AB #9, #11 y = y1 + tm.eps_r_op_2s_AA12_C34(r_, tmp, C_conj) 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 += l_sqrt.dot(tm.eps_r_noop(y_pi, A, Vri_)) 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
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])
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 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] AA = tdvp.AA[0] AA_ = tdvp2.AA[0] AAA_ = tdvp2.AAA[0] eyed = np.eye(self.q**self.ham_sites) eyed = eyed.reshape(tuple([self.q] * self.ham_sites * 2)) ham_ = self.ham - tdvp.h_expect.real * eyed V_ = sp.transpose(tdvp2.Vsh[0], axes=(0, 2, 1)).conj().copy(order='C') Vri_ = sp.zeros_like(V_) try: for s in xrange(self.q): Vri_[s] = r__sqrt_i.dot_left(V_[s]) except AttributeError: for s in xrange(self.q): Vri_[s] = V_[s].dot(r__sqrt_i) Vr_ = sp.zeros_like(V_) try: for s in xrange(self.q): Vr_[s] = r__sqrt.dot_left(V_[s]) except AttributeError: for s in xrange(self.q): Vr_[s] = V_[s].dot(r__sqrt) Vri_A_ = tm.calc_AA(Vri_, A_) if self.ham_sites == 2: _C_AhlA = np.empty((self.q, self.q, A.shape[2], A.shape[2]), dtype=tdvp.typ) for u in xrange(self.q): for s in xrange(self.q): _C_AhlA[u, s] = A[u].conj().T.dot(l.dot(A[s])) C_AhlA = sp.tensordot(ham_, _C_AhlA, ((0, 2), (0, 1))) C_AhlA = sp.transpose(C_AhlA, axes=(1, 0, 2, 3)).copy(order='C') _C_A_Vrh_ = tm.calc_AA(A_, sp.transpose(Vr_, axes=(0, 2, 1)).conj()) C_A_Vrh_ = sp.tensordot(ham_, _C_A_Vrh_, ((3, 1), (0, 1))) C_A_Vrh_ = sp.transpose(C_A_Vrh_, axes=(1, 0, 2, 3)).copy(order='C') C_Vri_A_conj = tm.calc_C_conj_mat_op_AA(ham_, Vri_A_).copy(order='C') C_ = tm.calc_C_mat_op_AA(ham_, AA_).copy(order='C') C_conj = tm.calc_C_conj_mat_op_AA(ham_, AA_).copy(order='C') rhs10 = tm.eps_r_op_2s_AA12_C34(r_, AA_, C_Vri_A_conj) return C_, C_conj, V_, Vr_, Vri_, C_Vri_A_conj, C_AhlA, C_A_Vrh_, rhs10 elif self.ham_sites == 3: C_Vri_AA_ = np.empty((self.q, self.q, self.q, Vri_.shape[1], A_.shape[2]), dtype=tdvp.typ) for s in xrange(self.q): for t in xrange(self.q): for u in xrange(self.q): C_Vri_AA_[s, t, u] = Vri_[s].dot(AA_[t, u]) C_Vri_AA_ = sp.tensordot(ham_, C_Vri_AA_, ((3, 4, 5), (0, 1, 2))).copy(order='C') C_AAA_r_Ah_Vrih = np.empty((self.q, self.q, self.q, self.q, self.q, #FIXME: could be too memory-intensive A_.shape[1], Vri_.shape[1]), dtype=tdvp.typ) for s in xrange(self.q): for t in xrange(self.q): for u in xrange(self.q): for k in xrange(self.q): for j in xrange(self.q): C_AAA_r_Ah_Vrih[s, t, u, k, j] = AAA_[s, t, u].dot(r_.dot(A_[k].conj().T)).dot(Vri_[j].conj().T) C_AAA_r_Ah_Vrih = sp.tensordot(ham_, C_AAA_r_Ah_Vrih, ((3, 4, 5, 2, 1), (0, 1, 2, 3, 4))).copy(order='C') C_AhAhlAA = np.empty((self.q, self.q, self.q, self.q, A_.shape[2], A.shape[2]), dtype=tdvp.typ) for t in xrange(self.q): for j in xrange(self.q): for i in xrange(self.q): for s in xrange(self.q): C_AhAhlAA[j, t, i, s] = AA[i, j].conj().T.dot(l.dot(AA[s, t])) C_AhAhlAA = sp.tensordot(ham_, C_AhAhlAA, ((4, 1, 0, 3), (1, 0, 2, 3))).copy(order='C') C_AA_r_Ah_Vrih_ = np.empty((self.q, self.q, self.q, self.q, A_.shape[1], Vri_.shape[1]), dtype=tdvp.typ) for t in xrange(self.q): for u in xrange(self.q): for k in xrange(self.q): for j in xrange(self.q): C_AA_r_Ah_Vrih_[u, t, k, j] = AA_[t, u].dot(r_.dot(A_[k].conj().T)).dot(Vri_[j].conj().T) C_AA_r_Ah_Vrih_ = sp.tensordot(ham_, C_AA_r_Ah_Vrih_, ((4, 5, 2, 1), (1, 0, 2, 3))).copy(order='C') C_AAA_Vrh_ = np.empty((self.q, self.q, self.q, self.q, A_.shape[1], Vri_.shape[1]), dtype=tdvp.typ) for s in xrange(self.q): for t in xrange(self.q): for u in xrange(self.q): for k in xrange(self.q): C_AAA_Vrh_[s, t, u, k] = AAA_[s, t, u].dot(Vr_[k].conj().T) C_AAA_Vrh_ = sp.tensordot(ham_, C_AAA_Vrh_, ((3, 4, 5, 2), (0, 1, 2, 3))).copy(order='C') C_Vri_A_r_Ah_ = np.empty((self.q, self.q, self.q, A_.shape[2], Vri_.shape[1]), dtype=tdvp.typ) for u in xrange(self.q): for k in xrange(self.q): for j in xrange(self.q): C_Vri_A_r_Ah_[u, k, j] = Vri_[j].dot(A_[k]).dot(r_.dot(A_[u].conj().T)) C_Vri_A_r_Ah_ = sp.tensordot(ham_.conj(), C_Vri_A_r_Ah_, ((5, 2, 1), (0, 1, 2))).copy(order='C') C_AhlAA = np.empty((self.q, self.q, self.q, A_.shape[2], A.shape[2]), dtype=tdvp.typ) for j in xrange(self.q): for i in xrange(self.q): for s in xrange(self.q): C_AhlAA[j, i, s] = A[s].conj().T.dot(l.dot(AA[i, j])) C_AhlAA_conj = sp.tensordot(ham_.conj(), C_AhlAA, ((1, 0, 3), (0, 1, 2))).copy(order='C') C_AhlAA = sp.tensordot(ham_, C_AhlAA, ((4, 3, 0), (0, 1, 2))) C_AhlAA = sp.transpose(C_AhlAA, axes=(2, 0, 1, 3, 4)).copy(order='C') C_AA_Vrh = np.empty((self.q, self.q, self.q, A_.shape[2], Vr_.shape[1]), dtype=tdvp.typ) for t in xrange(self.q): for u in xrange(self.q): for k in xrange(self.q): C_AA_Vrh[k, u, t] = AA_[t, u].dot(Vr_[k].conj().T) C_AA_Vrh = sp.tensordot(ham_, C_AA_Vrh, ((4, 5, 2), (2, 1, 0))).copy(order='C') C_ = sp.tensordot(ham_, AAA_, ((3, 4, 5), (0, 1, 2))).copy(order='C') rhs10 = tm.eps_r_op_3s_C123_AAA456(r_, AAA_, C_Vri_AA_) #NOTE: These C's are good as C12 or C34, but only because h is Hermitian! #TODO: Make this consistent with the updated 2-site case above. return V_, Vr_, Vri_, Vri_A_, C_, C_Vri_AA_, C_AAA_r_Ah_Vrih, C_AhAhlAA, C_AA_r_Ah_Vrih_, C_AAA_Vrh_, C_Vri_A_r_Ah_, C_AhlAA, C_AhlAA_conj, C_AA_Vrh, rhs10,
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: V_, Vr_, Vri_, Vri_A_, C_, C_Vri_AA_, C_AAA_r_Ah_Vrih, \ C_AhAhlAA, C_AA_r_Ah_Vrih_, C_AAA_Vrh_, C_Vri_A_r_Ah_, \ C_AhlAA, C_AhlAA_conj, C_AA_Vrh, rhs10 = prereq else: C_, C_conj, V_, Vr_, Vri_, C_Vri_A_conj, C_AhlA, C_A_Vrh_, rhs10 = prereq A = tdvp.A[0] A_ = tdvp2.A[0] AA = tdvp.AA[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(Vri_[s])) if la.norm(B - B2) / la.norm(B) > self.sanity_tol: log.warning("Sanity Fail in calc_BHB! Bad Vri!") BA_ = tm.calc_AA(B, A_) AB = tm.calc_AA(A, B) if self.ham_sites == 3: BAA_ = tm.calc_AAA_AA(BA_, A_) ABA_ = tm.calc_AAA_AA(AB, A_) AAB = tm.calc_AAA_AA(AA, B) 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') if self.ham_sites == 3: tmp = BAA_ + sp.exp(+1.j * p) * ABA_ + sp.exp(+2.j * p) * AAB res = l_sqrt.dot(tm.eps_r_op_3s_C123_AAA456(r_, tmp, C_Vri_AA_)) #1 1D, #3, #3c else: tmp = BA_ + sp.exp(+1.j * p) * AB res = l_sqrt.dot(tm.eps_r_op_2s_AA12_C34(r_, tmp, C_Vri_A_conj)) #1, #3 OK res += sp.exp(-1.j * p) * l_sqrt_i.dot(Mh.dot(rhs10)) #10 exp = sp.exp subres = sp.zeros_like(res) eye = m.eyemat(C_.shape[2], dtype=tdvp.typ) eye2 = m.eyemat(A.shape[2], dtype=tdvp.typ) if self.ham_sites == 3: subres += exp(-2.j * p) * tm.eps_l_noop(Mh, A, C_AAA_r_Ah_Vrih) #12 subres += exp(-3.j * p) * tm.eps_l_op_2s_AA12_C34(Mh, AA, C_AAA_Vrh_) #12b for s in xrange(self.q): #subres += exp(-2.j * p) * A[s].conj().T.dot(Mh.dot(C_AAA_r_Ah_Vrih[s])) #12 subres += tm.eps_r_noop(B[s], C_AhAhlAA[s, :], Vr_) #2b subres += exp(-1.j * p) * tm.eps_l_noop(l.dot(B[s]), A, C_AA_r_Ah_Vrih_[s, :]) #4 subres += A[s].conj().T.dot(l.dot(tm.eps_r_op_2s_AA12_C34(eye2, AB, C_Vri_A_r_Ah_[s, :, :]))) #2 -ive of that it should be.... subres += exp(-1.j * p) * tm.eps_l_op_2s_AA12_C34(eye2, C_AhlAA_conj[s, :, :], BA_).dot(Vr_[s].conj().T) #4b subres += exp(-2.j * p) * tm.eps_l_op_2s_AA12_C34(l.dot(B[s]), AA, C_AA_Vrh[s, :, :]) #4c subres += exp(+1.j * p) * tm.eps_r_op_2s_AA12_C34(r_.dot_left(B[s]), C_AhlAA[s, :, :], Vri_A_) #3b #for t in xrange(self.q): #subres += (C_AhAhlAA[t, s].dot(B[s]).dot(Vr_[t].conj().T)) #2b #subres += (exp(-1.j * p) * A[s].conj().T.dot(l.dot(B[t])).dot(C_AA_r_Ah_Vrih_[s, t])) #4 #subres += (exp(-3.j * p) * AA[t, s].conj().T.dot(Mh).dot(C_AAA_Vrh_[t, s])) #12b #for u in xrange(self.q): #subres += A[s].conj().T.dot(l.dot(AB[t, u]).dot(C_A_r_Ah_Vrih[s, t, u])) #2 -ive of that it should be.... #subres += (exp(+1.j * p) * C_AhlAA[t, s, s].dot(B[u]).dot(r_.dot(A_[u].conj().T)).dot(Vri_[t].conj().T)) #3b #subres += (exp(-1.j * p) * C_AhAhlA[s, t, u].dot(BA_[t, u]).dot(Vr_[s].conj().T)) #4b #subres += (exp(-2.j * p) * AA[t, s].conj().T.dot(l.dot(B[u])).dot(C_AA_Vrh[t, s, u])) #4c else: for s in xrange(self.q): #subres += C_AhlA[s, t].dot(B[s]).dot(Vr_[t].conj().T) #2 OK subres += tm.eps_r_noop(B[s], C_AhlA[s, :], Vr_) #2 #+ exp(-1.j * p) * A[t].conj().T.dot(l.dot(B[s])).dot(C_A_Vrh_[t, s]) #4 OK with 3 subres += exp(-1.j * p) * tm.eps_l_noop(l.dot(B[s]), A, C_A_Vrh_[s, :]) #4 #+ exp(-2.j * p) * A[s].conj().T.dot(Mh.dot(C_[s, t])).dot(Vr_[t].conj().T)) #12 subres += exp(-2.j * p) * A[s].conj().T.dot(Mh).dot(tm.eps_r_noop(eye, C_[s, :], Vr_)) #12 res += l_sqrt_i.dot(subres) res += l_sqrt.dot(tm.eps_r_noop(K__r, B, Vri_)) #5 res += l_sqrt_i.dot(K_l.dot(tm.eps_r_noop(r__sqrt, B, V_))) #6 res += sp.exp(-1.j * p) * l_sqrt_i.dot(Mh.dot(tm.eps_r_noop(K__r, A_, Vri_))) #8 y1 = sp.exp(+1.j * p) * tm.eps_r_noop(K__r, B, A_) #7 if self.ham_sites == 3: tmp = sp.exp(+1.j * p) * BAA_ + sp.exp(+2.j * p) * ABA_ + sp.exp(+3.j * p) * AAB #9, #11, #11b y = y1 + tm.eps_r_op_3s_C123_AAA456(r_, tmp, C_) elif self.ham_sites == 2: tmp = sp.exp(+1.j * p) * BA_ + sp.exp(+2.j * p) * AB #9, #11 y = y1 + tm.eps_r_op_2s_AA12_C34(r_, tmp, C_conj) 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 += l_sqrt.dot(tm.eps_r_noop(y_pi, A, Vri_)) 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
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] AA = tdvp.AA[0] AA_ = tdvp2.AA[0] AAA_ = tdvp2.AAA[0] eyed = np.eye(self.q**self.ham_sites) eyed = eyed.reshape(tuple([self.q] * self.ham_sites * 2)) ham_ = self.ham - tdvp.h_expect.real * eyed V_ = sp.transpose(tdvp2.Vsh[0], axes=(0, 2, 1)).conj().copy(order='C') Vri_ = sp.zeros_like(V_) try: for s in xrange(self.q): Vri_[s] = r__sqrt_i.dot_left(V_[s]) except AttributeError: for s in xrange(self.q): Vri_[s] = V_[s].dot(r__sqrt_i) Vr_ = sp.zeros_like(V_) try: for s in xrange(self.q): Vr_[s] = r__sqrt.dot_left(V_[s]) except AttributeError: for s in xrange(self.q): Vr_[s] = V_[s].dot(r__sqrt) Vri_A_ = tm.calc_AA(Vri_, A_) if self.ham_sites == 2: _C_AhlA = np.empty((self.q, self.q, A.shape[2], A.shape[2]), dtype=tdvp.typ) for u in xrange(self.q): for s in xrange(self.q): _C_AhlA[u, s] = A[u].conj().T.dot(l.dot(A[s])) C_AhlA = sp.tensordot(ham_, _C_AhlA, ((0, 2), (0, 1))) C_AhlA = sp.transpose(C_AhlA, axes=(1, 0, 2, 3)).copy(order='C') _C_A_Vrh_ = tm.calc_AA(A_, sp.transpose(Vr_, axes=(0, 2, 1)).conj()) C_A_Vrh_ = sp.tensordot(ham_, _C_A_Vrh_, ((3, 1), (0, 1))) C_A_Vrh_ = sp.transpose(C_A_Vrh_, axes=(1, 0, 2, 3)).copy(order='C') C_Vri_A_conj = tm.calc_C_conj_mat_op_AA(ham_, Vri_A_).copy(order='C') C_ = tm.calc_C_mat_op_AA(ham_, AA_).copy(order='C') C_conj = tm.calc_C_conj_mat_op_AA(ham_, AA_).copy(order='C') rhs10 = tm.eps_r_op_2s_AA12_C34(r_, AA_, C_Vri_A_conj) return C_, C_conj, V_, Vr_, Vri_, C_Vri_A_conj, C_AhlA, C_A_Vrh_, rhs10 elif self.ham_sites == 3: C_Vri_AA_ = np.empty( (self.q, self.q, self.q, Vri_.shape[1], A_.shape[2]), dtype=tdvp.typ) for s in xrange(self.q): for t in xrange(self.q): for u in xrange(self.q): C_Vri_AA_[s, t, u] = Vri_[s].dot(AA_[t, u]) C_Vri_AA_ = sp.tensordot(ham_, C_Vri_AA_, ((3, 4, 5), (0, 1, 2))).copy(order='C') C_AAA_r_Ah_Vrih = np.empty( ( self.q, self.q, self.q, self.q, self.q, #FIXME: could be too memory-intensive A_.shape[1], Vri_.shape[1]), dtype=tdvp.typ) for s in xrange(self.q): for t in xrange(self.q): for u in xrange(self.q): for k in xrange(self.q): for j in xrange(self.q): C_AAA_r_Ah_Vrih[s, t, u, k, j] = AAA_[s, t, u].dot( r_.dot( A_[k].conj().T)).dot( Vri_[j].conj().T) C_AAA_r_Ah_Vrih = sp.tensordot(ham_, C_AAA_r_Ah_Vrih, ((3, 4, 5, 2, 1), (0, 1, 2, 3, 4))).copy(order='C') C_AhAhlAA = np.empty( (self.q, self.q, self.q, self.q, A_.shape[2], A.shape[2]), dtype=tdvp.typ) for t in xrange(self.q): for j in xrange(self.q): for i in xrange(self.q): for s in xrange(self.q): C_AhAhlAA[j, t, i, s] = AA[i, j].conj().T.dot(l.dot(AA[s, t])) C_AhAhlAA = sp.tensordot(ham_, C_AhAhlAA, ((4, 1, 0, 3), (1, 0, 2, 3))).copy(order='C') C_AA_r_Ah_Vrih_ = np.empty( (self.q, self.q, self.q, self.q, A_.shape[1], Vri_.shape[1]), dtype=tdvp.typ) for t in xrange(self.q): for u in xrange(self.q): for k in xrange(self.q): for j in xrange(self.q): C_AA_r_Ah_Vrih_[u, t, k, j] = AA_[t, u].dot( r_.dot(A_[k].conj().T)).dot(Vri_[j].conj().T) C_AA_r_Ah_Vrih_ = sp.tensordot(ham_, C_AA_r_Ah_Vrih_, ((4, 5, 2, 1), (1, 0, 2, 3))).copy(order='C') C_AAA_Vrh_ = np.empty( (self.q, self.q, self.q, self.q, A_.shape[1], Vri_.shape[1]), dtype=tdvp.typ) for s in xrange(self.q): for t in xrange(self.q): for u in xrange(self.q): for k in xrange(self.q): C_AAA_Vrh_[s, t, u, k] = AAA_[s, t, u].dot(Vr_[k].conj().T) C_AAA_Vrh_ = sp.tensordot(ham_, C_AAA_Vrh_, ((3, 4, 5, 2), (0, 1, 2, 3))).copy(order='C') C_Vri_A_r_Ah_ = np.empty( (self.q, self.q, self.q, A_.shape[2], Vri_.shape[1]), dtype=tdvp.typ) for u in xrange(self.q): for k in xrange(self.q): for j in xrange(self.q): C_Vri_A_r_Ah_[u, k, j] = Vri_[j].dot(A_[k]).dot( r_.dot(A_[u].conj().T)) C_Vri_A_r_Ah_ = sp.tensordot(ham_.conj(), C_Vri_A_r_Ah_, ((5, 2, 1), (0, 1, 2))).copy(order='C') C_AhlAA = np.empty( (self.q, self.q, self.q, A_.shape[2], A.shape[2]), dtype=tdvp.typ) for j in xrange(self.q): for i in xrange(self.q): for s in xrange(self.q): C_AhlAA[j, i, s] = A[s].conj().T.dot(l.dot(AA[i, j])) C_AhlAA_conj = sp.tensordot(ham_.conj(), C_AhlAA, ((1, 0, 3), (0, 1, 2))).copy(order='C') C_AhlAA = sp.tensordot(ham_, C_AhlAA, ((4, 3, 0), (0, 1, 2))) C_AhlAA = sp.transpose(C_AhlAA, axes=(2, 0, 1, 3, 4)).copy(order='C') C_AA_Vrh = np.empty( (self.q, self.q, self.q, A_.shape[2], Vr_.shape[1]), dtype=tdvp.typ) for t in xrange(self.q): for u in xrange(self.q): for k in xrange(self.q): C_AA_Vrh[k, u, t] = AA_[t, u].dot(Vr_[k].conj().T) C_AA_Vrh = sp.tensordot(ham_, C_AA_Vrh, ((4, 5, 2), (2, 1, 0))).copy(order='C') C_ = sp.tensordot(ham_, AAA_, ((3, 4, 5), (0, 1, 2))).copy(order='C') rhs10 = tm.eps_r_op_3s_C123_AAA456(r_, AAA_, C_Vri_AA_) #NOTE: These C's are good as C12 or C34, but only because h is Hermitian! #TODO: Make this consistent with the updated 2-site case above. return V_, Vr_, Vri_, Vri_A_, C_, C_Vri_AA_, C_AAA_r_Ah_Vrih, C_AhAhlAA, C_AA_r_Ah_Vrih_, C_AAA_Vrh_, C_Vri_A_r_Ah_, C_AhlAA, C_AhlAA_conj, C_AA_Vrh, rhs10,