def _restore_CF_diag(self, dbg=False): nc = self.N_centre #Want: r[0 <= n < nc] diagonal Ui = sp.eye(self.D[nc], dtype=self.typ) for n in xrange(nc, 0, -1): self.r[n - 1], Um1, Um1_i = tm.restore_LCF_r(self.A[n], self.r[n], Ui, sanity_checks=self.sanity_checks) Ui = Um1_i #Now U is U_0 U = Um1 for s in xrange(self.q[0]): self.uni_l.A[0][s] = U.dot(self.uni_l.A[0][s]) self.uni_l.A[-1][s] = self.uni_l.A[-1][s].dot(Ui) self.uni_l.r[-1] = U.dot(self.uni_l.r[-1].dot(U.conj().T)) #And now: l[nc <= n <= N] diagonal if dbg: Um1 = sp.eye(self.D[nc - 1], dtype=self.typ) else: Um1 = mm.eyemat(self.D[nc - 1], dtype=self.typ) #FIXME: This only works if l[nc - 1] is a special matrix type for n in xrange(nc, self.N + 1): self.l[n], U, Ui = tm.restore_RCF_l(self.A[n], self.l[n - 1], Um1, sanity_checks=self.sanity_checks) Um1 = U #Now, Um1 = U_N Um1_i = Ui for s in xrange(self.q[0]): self.uni_r.A[0][s] = Um1.dot(self.uni_r.A[0][s]) self.uni_r.A[-1][s] = self.uni_r.A[-1][s].dot(Um1_i) self.uni_r.l[-1] = Um1_i.conj().T.dot(self.uni_r.l[-1].dot(Um1_i))
def _restore_CF_diag(self, dbg=False): nc = self.N_centre #Want: r[0 <= n < nc] diagonal Ui = sp.eye(self.D[nc], dtype=self.typ) for n in xrange(nc, 0, -1): self.r[n - 1], Um1, Um1_i = tm.restore_LCF_r( self.A[n], self.r[n], Ui, sanity_checks=self.sanity_checks) Ui = Um1_i #Now U is U_0 U = Um1 for s in xrange(self.q[0]): self.uni_l.A[0][s] = U.dot(self.uni_l.A[0][s]) self.uni_l.A[-1][s] = self.uni_l.A[-1][s].dot(Ui) self.uni_l.r[-1] = U.dot(self.uni_l.r[-1].dot(U.conj().T)) #And now: l[nc <= n <= N] diagonal if dbg: Um1 = sp.eye(self.D[nc - 1], dtype=self.typ) else: Um1 = mm.eyemat( self.D[nc - 1], dtype=self.typ ) #FIXME: This only works if l[nc - 1] is a special matrix type for n in xrange(nc, self.N + 1): self.l[n], U, Ui = tm.restore_RCF_l( self.A[n], self.l[n - 1], Um1, sanity_checks=self.sanity_checks) Um1 = U #Now, Um1 = U_N Um1_i = Ui for s in xrange(self.q[0]): self.uni_r.A[0][s] = Um1.dot(self.uni_r.A[0][s]) self.uni_r.A[-1][s] = self.uni_r.A[-1][s].dot(Um1_i) self.uni_r.l[-1] = Um1_i.conj().T.dot(self.uni_r.l[-1].dot(Um1_i))
def _restore_CF_diag(self): nc = self.N_centre self.S_hc = sp.zeros((self.N + 1), dtype=sp.complex128) #Want: r[0 <= n < nc] diagonal Ui = sp.eye(self.D[nc], dtype=self.typ) for n in xrange(nc, 0, -1): self.r[n - 1], Um1, Um1_i = tm.restore_LCF_r(self.A[n], self.r[n], Ui, sanity_checks=self.sanity_checks) self.S_hc[n - 1] = -sp.sum(self.r[n - 1].diag * sp.log2(self.r[n - 1].diag)) Ui = Um1_i #Now U is U_0 U = Um1 for s in xrange(self.q[0]): self.A[0][s] = U.dot(self.A[0][s]).dot(Ui) self.uni_l.r = U.dot(self.uni_l.r.dot(U.conj().T)) #And now: l[nc <= n <= N] diagonal Um1 = mm.eyemat(self.D[nc - 1], dtype=self.typ) for n in xrange(nc, self.N + 1): self.l[n], U, Ui = tm.restore_RCF_l(self.A[n], self.l[n - 1], Um1, sanity_checks=self.sanity_checks) self.S_hc[n] = -sp.sum(self.l[n].diag * sp.log2(self.l[n].diag)) Um1 = U #Now, Um1 = U_N Um1_i = Ui for s in xrange(self.q[0]): self.A[self.N + 1][s] = Um1.dot(self.A[self.N + 1][s]).dot(Um1_i) self.uni_r.l = Um1_i.conj().T.dot(self.uni_r.l.dot(Um1_i))
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()
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()