def restore_RCF(self, use_QR=True, update_l=True, diag_l=True): """Use a gauge-transformation to restore right canonical form. Implements the conditions for right canonical form from sub-section 3.1, theorem 1 of arXiv:quant-ph/0608197v2. It is possible to skip the diagonalization of the l's, such that only the right orthonormalization condition (r_n = eye) is met. By default, the l's are updated even if diag_l=False. Parameters ---------- update_l : bool Whether to call calc_l() after completion (defaults to True) diag_l : bool Whether to put l in diagonal form (defaults to True) """ if use_QR: tm.restore_RCF_r_seq(self.A, self.r, sanity_checks=self.sanity_checks, sc_data="restore_RCF_r") else: G_n_i = sp.eye(self.D[self.N], dtype=self.typ) #This is actually just the number 1 for n in xrange(self.N, 0, -1): self.r[n - 1], G_n, G_n_i = tm.restore_RCF_r(self.A[n], self.r[n], G_n_i, sc_data=('site', n), zero_tol=self.zero_tol, sanity_checks=self.sanity_checks) if self.sanity_checks: if not sp.allclose(self.r[0].A, 1, atol=1E-12, rtol=1E-12): log.warning("Sanity Fail in restore_RCF!: r_0 is bad / norm failure") if diag_l: tm.restore_RCF_l_seq(self.A, self.l, sanity_checks=self.sanity_checks, sc_data="restore_RCF_l") if self.sanity_checks: if not sp.allclose(self.l[self.N].A, 1, atol=1E-12, rtol=1E-12): log.warning("Sanity Fail in restore_RCF!: l_N is bad / norm failure") log.warning("l_N = %s", self.l[self.N].squeeze().real) for n in xrange(1, self.N + 1): r_nm1 = tm.eps_r_noop(self.r[n], self.A[n], self.A[n]) #r_nm1 = tm.eps_r_noop(m.eyemat(self.D[n], self.typ), self.A[n], self.A[n]) if not sp.allclose(r_nm1, self.r[n - 1], atol=1E-11, rtol=1E-11): log.warning("Sanity Fail in restore_RCF!: r_%u is bad (off by %g)", n, la.norm(r_nm1 - self.r[n - 1])) elif update_l: self.calc_l()
def _restore_CF_ONR(self): nc = self.N_centre #Want: r[n >= nc] = eye #First do uni_r uGs, uG_is = self.uni_r.restore_RCF( zero_tol=self.zero_tol, diag_l=False, ret_g=True) #FIXME: Don't always to all! Gi = uGs[-1] #Inverse is on the other side in the uniform code. self.r[self.N] = self.uni_r.r[-1] for n in xrange(self.N, nc, -1): self.r[n - 1], Gm1, Gm1_i = tm.restore_RCF_r( self.A[n], self.r[n], Gi, zero_tol=self.zero_tol, sanity_checks=self.sanity_checks) Gi = Gm1_i self.r[self.N + 1] = self.r[self.N] #G is now G_nc for s in xrange(self.q[nc]): self.A[nc][s] = self.A[nc][s].dot(Gi) #Now want: l[n < nc] = eye uGs, uG_is = self.uni_l.restore_LCF(zero_tol=self.zero_tol, diag_r=False, ret_g=True) Gm1 = uG_is[-1] self.l[0] = self.uni_l.l[-1] lm1 = self.l[0] for n in xrange(1, nc): self.l[n], G, Gi = tm.restore_LCF_l( self.A[n], lm1, Gm1, zero_tol=self.zero_tol, sanity_checks=self.sanity_checks) lm1 = self.l[n] Gm1 = G #Gm1 is now G_nc-1 for s in xrange(self.q[nc]): self.A[nc][s] = Gm1.dot(self.A[nc][s])
def _restore_CF_ONR(self): nc = self.N_centre #Want: r[n >= nc] = eye #First do uni_r uGs, uG_is = self.uni_r.restore_RCF(zero_tol=self.zero_tol, diag_l=False, ret_g=True) #FIXME: Don't always to all! Gi = uGs[-1] #Inverse is on the other side in the uniform code. self.r[self.N] = self.uni_r.r[-1] for n in xrange(self.N, nc, -1): self.r[n - 1], Gm1, Gm1_i = tm.restore_RCF_r(self.A[n], self.r[n], Gi, zero_tol=self.zero_tol, sanity_checks=self.sanity_checks) Gi = Gm1_i self.r[self.N + 1] = self.r[self.N] #G is now G_nc for s in xrange(self.q[nc]): self.A[nc][s] = self.A[nc][s].dot(Gi) #Now want: l[n < nc] = eye uGs, uG_is = self.uni_l.restore_LCF(zero_tol=self.zero_tol, diag_r=False, ret_g=True) Gm1 = uG_is[-1] self.l[0] = self.uni_l.l[-1] lm1 = self.l[0] for n in xrange(1, nc): self.l[n], G, Gi = tm.restore_LCF_l(self.A[n], lm1, Gm1, zero_tol=self.zero_tol, sanity_checks=self.sanity_checks) lm1 = self.l[n] Gm1 = G #Gm1 is now G_nc-1 for s in xrange(self.q[nc]): self.A[nc][s] = Gm1.dot(self.A[nc][s])
def _restore_CF_ONR(self): nc = self.N_centre #Want: r[n >= nc] = eye Gi = None for n in xrange(self.N + 1, nc, -1): self.r[n - 1], Gm1, Gm1_i = tm.restore_RCF_r(self.A[n], self.r[n], Gi, zero_tol=self.zero_tol, sanity_checks=self.sanity_checks) if n == self.N + 1: self.uni_r.l = Gm1_i.conj().T.dot(self.uni_r.l.dot(Gm1_i)) Gi = Gm1_i self.r[self.N + 1] = self.r[self.N] #G is now G_nc for s in xrange(self.q[nc]): self.A[nc][s] = self.A[nc][s].dot(Gi) #Now want: l[n < nc] = eye Gm1 = None lm1 = self.l[0] for n in xrange(nc): self.l[n], G, Gi = tm.restore_LCF_l(self.A[n], lm1, Gm1, zero_tol=self.zero_tol, sanity_checks=self.sanity_checks) if n == 0: self.uni_l.r = G.dot(self.uni_l.r.dot(G.conj().T)) lm1 = self.l[n] Gm1 = G #Gm1 is now G_nc-1 for s in xrange(self.q[nc]): self.A[nc][s] = Gm1.dot(self.A[nc][s])
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()