def test_dmrg_excited(eps=1.e-12): # checks ground state and 2 excited states (in same symmetry sector) for a small system # (without truncation) L, g = 8, 1.3 bc = 'finite' model_params = dict(L=L, J=1., g=g, bc_MPS=bc, conserve='parity', verbose=0) M = TFIChain(model_params) # compare to exact solution ED = ExactDiag(M) ED.build_full_H_from_mpo() ED.full_diagonalization() # Note: energies sorted by chargesector (first 0), then ascending -> perfect for comparison print("Exact diag: E[:5] = ", ED.E[:5]) print("Exact diag: (smalles E)[:10] = ", np.sort(ED.E)[:10]) psi_ED = [ED.V.take_slice(i, 'ps*') for i in range(5)] print("charges : ", [psi.qtotal for psi in psi_ED]) # first DMRG run psi0 = mps.MPS.from_product_state(M.lat.mps_sites(), [0] * L, bc=bc) dmrg_pars = { 'verbose': 0, 'N_sweeps_check': 1, 'lanczos_params': { 'reortho': False }, 'diag_method': 'lanczos', 'combine': True } eng0 = dmrg.TwoSiteDMRGEngine(psi0, M, dmrg_pars) E0, psi0 = eng0.run() assert abs((E0 - ED.E[0]) / ED.E[0]) < eps ov = npc.inner(psi_ED[0], ED.mps_to_full(psi0), 'range', do_conj=True) assert abs(abs(ov) - 1.) < eps # unique groundstate: finite size gap! # second DMRG run for first excited state dmrg_pars['verbose'] = 1. dmrg_pars['orthogonal_to'] = [psi0] psi1 = mps.MPS.from_product_state(M.lat.mps_sites(), [0] * L, bc=bc) eng1 = dmrg.TwoSiteDMRGEngine(psi1, M, dmrg_pars) E1, psi1 = eng1.run() assert abs((E1 - ED.E[1]) / ED.E[1]) < eps ov = npc.inner(psi_ED[1], ED.mps_to_full(psi1), 'range', do_conj=True) assert abs(abs(ov) - 1.) < eps # unique groundstate: finite size gap! # and a third one to check with 2 eigenstates dmrg_pars['orthogonal_to'] = [psi0, psi1] # note: different intitial state necessary, otherwise H is 0 psi2 = mps.MPS.from_singlets(psi0.sites[0], L, [(0, 1), (2, 3), (4, 5), (6, 7)], bc=bc) eng2 = dmrg.TwoSiteDMRGEngine(psi2, M, dmrg_pars) E2, psi2 = eng2.run() print(E2) assert abs((E2 - ED.E[2]) / ED.E[2]) < eps ov = npc.inner(psi_ED[2], ED.mps_to_full(psi2), 'range', do_conj=True) assert abs(abs(ov) - 1.) < eps # unique groundstate: finite size gap!
def test_MPO_var(L=8, tol=1.e-13): xxz_pars = dict(L=L, Jx=1., Jy=1., Jz=1.1, hz=0.1, bc_MPS='finite', conserve=None) M = SpinChain(xxz_pars) psi = random_MPS(L, 2, 10) exp_val = M.H_MPO.expectation_value(psi) ED = ExactDiag(M) ED.build_full_H_from_mpo() psi_full = ED.mps_to_full(psi) exp_val_full = npc.inner(psi_full, npc.tensordot(ED.full_H, psi_full, axes=1), axes='range', do_conj=True) assert abs(exp_val - exp_val_full) / abs(exp_val_full) < tol Hsquared = M.H_MPO.variance(psi, 0.) Hsquared_full = npc.inner(psi_full, npc.tensordot(ED.full_H, npc.tensordot(ED.full_H, psi_full, axes=1), axes=1), axes='range', do_conj=True) assert abs(Hsquared - Hsquared_full) / abs(Hsquared_full) < tol var = M.H_MPO.variance(psi) var_full = Hsquared_full - exp_val_full**2 assert abs(var - var_full) / abs(var_full) < tol
def test_lanczos(n=30, k=5, tol=5.e-15): # generate Hermitian test array leg = gen_random_legcharge(ch, n) H = npc.Array.from_func_square(rmat.GUE, leg) H_flat = H.to_ndarray() E_flat, psi_flat = np.linalg.eigh(H_flat) E0_flat, psi0_flat = E_flat[0], psi_flat[:, 0] qtotal = npc.detect_qtotal(psi0_flat, [leg]) H_Op = H # use `matvec` of the array psi_init = npc.Array.from_func(np.random.random, [leg], qtotal=qtotal) E0, psi0, N = lanczos.lanczos(H_Op, psi_init, {'verbose': 1}) print("full spectrum:", E_flat) print("E0 = {E0:.14f} vs exact {E0_flat:.14f}".format(E0=E0, E0_flat=E0_flat)) print("|E0-E0_flat| / |E0_flat| =", abs((E0 - E0_flat) / E0_flat)) psi0_H_psi0 = npc.inner(psi0, npc.tensordot(H, psi0, axes=[1, 0]), do_conj=True) print("<psi0|H|psi0> / E0 = 1. + ", psi0_H_psi0 / E0 - 1.) assert (abs(psi0_H_psi0 / E0 - 1.) < tol) print("<psi0_flat|H_flat|psi0_flat> / E0_flat = ", end=' ') print(np.inner(psi0_flat.conj(), np.dot(H_flat, psi0_flat)) / E0_flat) ov = np.inner(psi0.to_ndarray().conj(), psi0_flat) print("|<psi0|psi0_flat>|=", abs(ov)) assert (abs(1. - abs(ov)) < tol) # now repeat, but keep orthogonal to original ground state # -> should give second eigenvector psi1 in the same charge sector for i in range(1, len(E_flat)): E1_flat, psi1_flat = E_flat[i], psi_flat[:, i] qtotal = npc.detect_qtotal(psi1_flat, psi0.legs) if np.all(qtotal == psi0.qtotal): break # found psi1 in same charge sector else: print( "warning: test didn't find a second eigenvector in the same charge sector!" ) return # just ignore the rest.... E1, psi1, N = lanczos.lanczos(H_Op, psi_init, {'verbose': 1}, orthogonal_to=[psi0]) print("E1 = {E1:.14f} vs exact {E1_flat:.14f}".format(E1=E1, E1_flat=E1_flat)) print("|E1-E1_flat| / |E1_flat| =", abs((E1 - E1_flat) / E1_flat)) psi1_H_psi1 = npc.inner(psi1, npc.tensordot(H, psi1, axes=[1, 0]), do_conj=True) print("<psi1|H|psi1> / E1 = 1 + ", psi1_H_psi1 / E1 - 1.) assert (abs(psi1_H_psi1 / E1 - 1.) < tol) print("<psi1_flat|H_flat|psi1_flat> / E1_flat = ", end=' ') print(np.inner(psi1_flat.conj(), np.dot(H_flat, psi1_flat)) / E1_flat) ov = np.inner(psi1.to_ndarray().conj(), psi1_flat) print("|<psi1|psi1_flat>|=", abs(ov)) assert (abs(1. - abs(ov)) < tol) # and finnally check also orthogonality to psi0 ov = npc.inner(psi0, psi1, do_conj=True) print("|<psi0|psi1>| =", abs(ov)) assert (abs(ov) < tol**0.5)
def test_ED(): # just quickly check that it runs without errors for a small system xxz_pars = dict(L=4, Jxx=1., Jz=1., hz=0.0, bc_MPS='finite') M = XXZChain(xxz_pars) ED = ExactDiag(M) ED.build_full_H_from_mpo() H, ED.full_H = ED.full_H, None ED.build_full_H_from_bonds() H2 = ED.full_H assert (npc.norm(H - H2, np.inf) < 1.e-14) ED.full_diagonalization() psi = ED.groundstate() print("select charge_sector =", psi.qtotal) ED2 = ExactDiag(M, psi.qtotal) ED2.build_full_H_from_mpo() ED2.full_diagonalization() psi2 = ED2.groundstate() full_psi2 = psi.zeros_like() full_psi2[ED2._mask] = psi2 ov = npc.inner(psi, full_psi2, do_conj=True) print("overlab <psi | psi2> = 1. -", 1. - ov) assert (abs(abs(ov) - 1.) < 1.e-15) # starting from a random guess in the correct charge sector, # check if we can also do lanczos. np.random.seed(12345) psi3 = npc.Array.from_func(np.random.random, psi2.legs, qtotal=psi2.qtotal, shape_kw='size') E0, psi3, N = lanczos(ED2, psi3) print("Lanczos E0 =", E0) ov = npc.inner(psi3, psi2, do_conj=True) print("overlab <psi2 | psi3> = 1. -", 1. - ov) assert (abs(abs(ov) - 1.) < 1.e-15)
def test_dmrg_excited(eps=1.e-12): # checks ground state and 2 excited states (in same symmetry sector) for a small system # (without truncation) L, g = 8, 1.1 bc = 'finite' model_params = dict(L=L, J=1., g=g, bc_MPS=bc, conserve='parity', verbose=0) M = TFIChain(model_params) # compare to exact solution ED = ExactDiag(M) ED.build_full_H_from_mpo() ED.full_diagonalization() # Note: energies sorted by chargesector (first 0), then ascending -> perfect for comparison print("Exact diag: E[:5] = ", ED.E[:5]) # first DMRG run psi0 = mps.MPS.from_product_state(M.lat.mps_sites(), [0] * L, bc=bc) dmrg_pars = { 'verbose': 1, 'N_sweeps_check': 1, 'lanczos_params': { 'reortho': True } } eng0 = dmrg.EngineCombine(psi0, M, dmrg_pars) E0, psi0 = eng0.run() assert abs((E0 - ED.E[0]) / ED.E[0]) < eps ov = npc.inner(ED.V.take_slice(0, 'ps*'), ED.mps_to_full(psi0), do_conj=True) assert abs(abs(ov) - 1.) < eps # unique groundstate: finite size gap! # second DMRG run for first excited state dmrg_pars['orthogonal_to'] = [psi0] psi1 = mps.MPS.from_product_state(M.lat.mps_sites(), [0] * L, bc=bc) eng1 = dmrg.EngineCombine(psi1, M, dmrg_pars) E1, psi1 = eng1.run() assert abs((E1 - ED.E[1]) / ED.E[1]) < eps ov = npc.inner(ED.V.take_slice(1, 'ps*'), ED.mps_to_full(psi1), do_conj=True) assert abs(abs(ov) - 1.) < eps # unique groundstate: finite size gap! # and a third one to check with 2 eigenstates dmrg_pars['orthogonal_to'] = [psi0, psi1] # note: different intitial state necessary, otherwise H is 0 psi2 = mps.MPS.from_product_state(M.lat.mps_sites(), [0, 1] * (L // 2), bc=bc) eng2 = dmrg.EngineCombine(psi2, M, dmrg_pars) E2, psi2 = eng2.run() print(E2) assert abs((E2 - ED.E[2]) / ED.E[2]) < eps ov = npc.inner(ED.V.take_slice(2, 'ps*'), ED.mps_to_full(psi2), do_conj=True) assert abs(abs(ov) - 1.) < eps # unique groundstate: finite size gap!
def update_new_psi(self, theta): i0 = self.i0 new_psi = self.psi qtotal_i0 = new_psi.get_B(i0, form=None).qtotal U, S, VH, err, renormalize = svd_theta(theta, self.trunc_params, qtotal_LR=[qtotal_i0, None], inner_labels=['vR', 'vL']) self.renormalize.append(renormalize) # TODO: up to the `renormalize`, we could use `new_psi.set_svd_theta`. A0 = U.split_legs(['(vL.p0.q0)']) B1 = VH.split_legs(['(p1.q1.vR)']) # first compare to old best guess to check convergence of the sweeps if self._tol_theta_diff is not None and self.update_LP_RP[0] == False: theta_old = new_psi.get_theta(i0) theta_new_trunc = npc.tensordot(A0.scale_axis(S, 'vR'), B1, ['vR', 'vL']) ov = npc.inner(theta_new_trunc, theta_old, do_conj=True, axes='labels') theta_diff = 1. - abs(ov) self._theta_diff.append(theta_diff) A0.ireplace_labels(['p0', 'q0'], ['p', 'q']) B1.ireplace_labels(['p1', 'q1'], ['p', 'q']) new_psi.set_B(i0, A0, form='A') # left-canonical new_psi.set_B(i0 + 1, B1, form='B') # right-canonical new_psi.set_SR(i0, S) return {'U': U, 'VH': VH, 'err': err}
def example_exact_diagonalization(L, Jz): xxz_pars = dict(L=L, Jxx=1., Jz=Jz, hz=0.0, bc_MPS='finite') M = XXZChain(xxz_pars) product_state = ["up", "down"] * (xxz_pars['L'] // 2) # this selects a charge sector! psi_DMRG = MPS.from_product_state(M.lat.mps_sites(), product_state) charge_sector = psi_DMRG.get_total_charge(True) # ED charge sector should match ED = ExactDiag(M, charge_sector=charge_sector, max_size=2.e6) ED.build_full_H_from_mpo() # ED.build_full_H_from_bonds() # whatever you prefer print("start diagonalization") ED.full_diagonalization() # the expensive part for large L E0_ED, psi_ED = ED.groundstate() # return the ground state print("psi_ED =", psi_ED) print("run DMRG") dmrg.run(psi_DMRG, M, {'verbose': 0}) # modifies psi_DMRG in place! # first way to compare ED with DMRG: convert MPS to ED vector psi_DMRG_full = ED.mps_to_full(psi_DMRG) print("psi_DMRG_full =", psi_DMRG_full) ov = npc.inner(psi_ED, psi_DMRG_full, axes='range', do_conj=True) print("<psi_ED|psi_DMRG_full> =", ov) assert (abs(abs(ov) - 1.) < 1.e-13) # second way: convert ED vector to MPS psi_ED_mps = ED.full_to_mps(psi_ED) ov2 = psi_ED_mps.overlap(psi_DMRG) print("<psi_ED_mps|psi_DMRG> =", ov2) assert (abs(abs(ov2) - 1.) < 1.e-13) assert (abs(ov - ov2) < 1.e-13) # -> advantage: expectation_value etc. of MPS are available! print("<Sz> =", psi_ED_mps.expectation_value('Sz'))
def test_tebd(bc_MPS, g=0.5): L = 2 if bc_MPS == 'infinite' else 6 # xxz_pars = dict(L=L, Jxx=1., Jz=3., hz=0., bc_MPS=bc_MPS) # M = XXZChain(xxz_pars) # factor of 4 (2) for J (h) to change spin-1/2 to Pauli matrices model_pars = dict(L=L, Jx=0., Jy=0., Jz=-4., hx=2. * g, bc_MPS=bc_MPS, conserve=None) M = SpinChain(model_pars) state = ([[1, -1.], [1, -1.]] * L)[:L] # pointing in (-x)-direction psi = MPS.from_product_state(M.lat.mps_sites(), state, bc=bc_MPS) tebd_param = { 'verbose': 2, 'dt': 0.01, 'order': 2, 'delta_tau_list': [0.1, 1.e-4, 1.e-8], 'max_error_E': 1.e-9, 'trunc_params': { 'chi_max': 50, 'trunc_cut': 1.e-13 } } engine = tebd.Engine(psi, M, tebd_param) engine.run_GS() print("norm_test", psi.norm_test()) if bc_MPS == 'finite': psi.canonical_form() ED = ExactDiag(M) ED.build_full_H_from_mpo() ED.full_diagonalization() psi_ED = ED.groundstate() Etebd = np.sum(M.bond_energies(psi)) Eexact = np.min(ED.E) print("E_TEBD={Etebd:.14f} vs E_exact={Eex:.14f}".format(Etebd=Etebd, Eex=Eexact)) assert (abs((Etebd - Eexact) / Eexact) < 1.e-7) ov = npc.inner(psi_ED, ED.mps_to_full(psi), do_conj=True) print("compare with ED: overlap = ", abs(ov)**2) assert (abs(abs(ov) - 1.) < 1.e-7) # Test real time TEBD: should change on an eigenstate Sold = np.average(psi.entanglement_entropy()) for i in range(3): engine.run() Enew = np.sum(M.bond_energies(psi)) Snew = np.average(psi.entanglement_entropy()) assert (abs(Enew - Etebd) < 1.e-8) assert (abs(Sold - Snew) < 1.e-5) # somehow we need larger tolerance here.... if bc_MPS == 'infinite': Etebd = np.average(M.bond_energies(psi)) Eexact = e0_tranverse_ising(g) print("E_TEBD={Etebd:.14f} vs E_exact={Eex:.14f}".format(Etebd=Etebd, Eex=Eexact)) Sold = np.average(psi.entanglement_entropy()) for i in range(2): engine.run() Enew = np.average(M.bond_energies(psi)) Snew = np.average(psi.entanglement_entropy()) assert (abs(Etebd - Enew) < 1.e-7) assert (abs(Sold - Snew) < 1.e-5) # somehow we need larger tolerance here....
def test_dmrg_diag_method(engine, diag_method, tol=1.e-6): bc_MPS = 'finite' model_params = dict(L=6, S=0.5, bc_MPS=bc_MPS, conserve='Sz', verbose=0) M = SpinChain(model_params) # chose total Sz= 4, not 3=6/2, i.e. not the sector with lowest energy! # make sure below that we stay in that sector, if we're supposed to. init_Sz_4 = ['up', 'down', 'up', 'up', 'up', 'down'] psi_Sz_4 = mps.MPS.from_product_state(M.lat.mps_sites(), init_Sz_4, bc=bc_MPS) dmrg_pars = { 'verbose': 1, 'N_sweeps_check': 1, 'combine': True, 'max_sweeps': 5, 'diag_method': diag_method, 'mixer': True, } ED = ExactDiag(M) ED.build_full_H_from_mpo() ED.full_diagonalization() if diag_method == "ED_all": charge_sector = None # allow to change the sector else: charge_sector = [2] # don't allow to change the sector E_ED, psi_ED = ED.groundstate(charge_sector=charge_sector) DMRGEng = dmrg.__dict__.get(engine) print("DMRGEng = ", DMRGEng) print("setting diag_method = ", dmrg_pars['diag_method']) eng = DMRGEng(psi_Sz_4.copy(), M, dmrg_pars) E0, psi0 = eng.run() print("E0 = {0:.15f}".format(E0)) assert abs(E_ED - E0) < tol ov = npc.inner(psi_ED, ED.mps_to_full(psi0), 'range', do_conj=True) assert abs(abs(ov) - 1) < tol
def test_dmrg(bc_MPS, combine, mixer, n, g=1.2): L = 2 if bc_MPS == 'infinite' else 8 model_params = dict(L=L, J=1., g=g, bc_MPS=bc_MPS, conserve=None, verbose=0) M = TFIChain(model_params) state = [0] * L # Ferromagnetic Ising psi = mps.MPS.from_product_state(M.lat.mps_sites(), state, bc=bc_MPS) dmrg_pars = { 'verbose': 5, 'combine': combine, 'mixer': mixer, 'chi_list': { 0: 10, 5: 30 }, 'max_E_err': 1.e-12, 'max_S_err': 1.e-8, 'N_sweeps_check': 4, 'mixer_params': { 'disable_after': 15, 'amplitude': 1.e-5 }, 'trunc_params': { 'svd_min': 1.e-10, }, 'max_N_for_ED': 20, # small enough that we test both diag_method=lanczos and ED_block! 'max_sweeps': 40, 'active_sites': n, } if not mixer: del dmrg_pars['mixer_params'] # avoid warning of unused parameter if bc_MPS == 'infinite': # if mixer is not None: # dmrg_pars['mixer_params']['amplitude'] = 1.e-12 # don't actually contribute... dmrg_pars['start_env'] = 1 res = dmrg.run(psi, M, dmrg_pars) if bc_MPS == 'finite': ED = ExactDiag(M) ED.build_full_H_from_mpo() ED.full_diagonalization() E_ED, psi_ED = ED.groundstate() ov = npc.inner(psi_ED, ED.mps_to_full(psi), 'range', do_conj=True) print("E_DMRG={Edmrg:.14f} vs E_exact={Eex:.14f}".format(Edmrg=res['E'], Eex=E_ED)) print("compare with ED: overlap = ", abs(ov)**2) assert abs(abs(ov) - 1.) < 1.e-10 # unique groundstate: finite size gap! var = M.H_MPO.variance(psi) assert var < 1.e-10 else: # compare exact solution for transverse field Ising model Edmrg = res['E'] Eexact = e0_tranverse_ising(g) print("E_DMRG={Edmrg:.12f} vs E_exact={Eex:.12f}".format(Edmrg=Edmrg, Eex=Eexact)) print("relative energy error: {err:.2e}".format(err=abs((Edmrg - Eexact) / Eexact))) print("norm err:", psi.norm_test()) Edmrg2 = np.mean(psi.expectation_value(M.H_bond)) Edmrg3 = M.H_MPO.expectation_value(psi) assert abs((Edmrg - Eexact) / Eexact) < 1.e-10 assert abs((Edmrg - Edmrg2) / Edmrg2) < max(1.e-10, np.max(psi.norm_test())) assert abs((Edmrg - Edmrg3) / Edmrg3) < max(1.e-10, np.max(psi.norm_test()))
def test_ExpMPOEvolution(bc_MPS, approximation, compression, g=1.5): # Test a time evolution against exact diagonalization for finite bc dt = 0.01 if bc_MPS == 'finite': L = 6 else: L = 2 model_pars = dict(L=L, Jx=0., Jy=0., Jz=-4., hx=2. * g, bc_MPS=bc_MPS, conserve=None) M = SpinChain(model_pars) state = ([[1 / np.sqrt(2), -1 / np.sqrt(2)]] * L ) # pointing in (-x)-direction state = ['up'] * L # pointing in (-z)-direction psi = MPS.from_product_state(M.lat.mps_sites(), state, bc=bc_MPS) options = { 'dt': dt, 'N_steps': 1, 'order': 1, 'approximation': approximation, 'compression_method': compression, 'trunc_params': { 'chi_max': 30 } } eng = mpo_evolution.ExpMPOEvolution(psi, M, options) if bc_MPS == 'finite': ED = exact_diag.ExactDiag(M) ED.build_full_H_from_mpo() ED.full_diagonalization() psiED = ED.mps_to_full(psi) psiED /= psiED.norm() UED = ED.exp_H(dt) for i in range(30): psi = eng.run() psiED = npc.tensordot(UED, psiED, ('ps*', [0])) psi_full = ED.mps_to_full(psi) assert (abs(abs(npc.inner(psiED, psi_full, [0, 0], True)) - 1) < dt) if bc_MPS == 'infinite': psiTEBD = psi.copy() TEBD_params = {'dt': dt, 'N_steps': 1} EngTEBD = tebd.Engine(psiTEBD, M, TEBD_params) for i in range(30): EngTEBD.run() psi = eng.run() print(psi.norm) print(np.abs(psi.overlap(psiTEBD) - 1)) #This test fails assert (abs(abs(psi.overlap(psiTEBD)) - 1) < 1e-2)
def check_dmrg(L=4, bc_MPS='finite', engine='EngineCombine', mixer=None, g=1.5): model_params = dict(L=L, J=1., g=g, bc_MPS=bc_MPS, conserve=None, verbose=0) M = TFIChain(model_params) state = [0] * L # Ferromagnetic Ising psi = mps.MPS.from_product_state(M.lat.mps_sites(), state, bc=bc_MPS) dmrg_pars = { 'verbose': 5, 'engine': engine, 'mixer': mixer, 'chi_list': { 0: 10, 5: 30 }, 'max_E_err': 1.e-12, 'max_S_err': 1.e-8, 'N_sweeps_check': 4, 'mixer_params': { 'disable_after': 6, 'amplitude': 1.e-5 }, 'trunc_params': { 'svd_min': 1.e-10, }, 'lanczos_params': { 'reortho': True, 'N_cache': 20 }, 'max_sweeps': 40, } if mixer is None: del dmrg_pars['mixer_params'] # avoid warning of unused parameter if bc_MPS == 'infinite': if mixer is not None: dmrg_pars['mixer_params']['amplitude'] = 1.e-12 # don't actually contribute... dmrg_pars['start_env'] = 1 res = dmrg.run(psi, M, dmrg_pars) if bc_MPS == 'finite': ED = ExactDiag(M) ED.build_full_H_from_mpo() ED.full_diagonalization() psi_ED = ED.groundstate() ov = npc.inner(psi_ED, ED.mps_to_full(psi), do_conj=True) print("E_DMRG={Edmrg:.14f} vs E_exact={Eex:.14f}".format(Edmrg=res['E'], Eex=np.min(ED.E))) print("compare with ED: overlap = ", abs(ov)**2) assert abs(abs(ov) - 1.) < 1.e-10 # unique groundstate: finite size gap! else: # compare exact solution for transverse field Ising model Edmrg = res['E'] Eexact = e0_tranverse_ising(g) print("E_DMRG={Edmrg:.12f} vs E_exact={Eex:.12f}".format(Edmrg=Edmrg, Eex=Eexact)) print("relative energy error: {err:.2e}".format(err=abs((Edmrg - Eexact) / Eexact))) print("norm err:", psi.norm_test()) Edmrg2 = np.mean(psi.expectation_value(M.H_bond)) assert abs((Edmrg - Eexact) / Eexact) < 1.e-10 assert abs((Edmrg - Edmrg2) / Edmrg2) < max(1.e-10, np.max(psi.norm_test()))
def test_npc_inner(tol=1.e-13): for sort in [True, False]: print("sort =", sort) a = random_Array((10, 7, 5), chinfo3, sort=sort) a.iset_leg_labels(['x', 'y', 'z']) aflat = a.to_ndarray() b = npc.Array.from_func(np.random.random, a.legs, qtotal=a.qtotal, shape_kw='size') b.iset_leg_labels(['x', 'y', 'z']) b_conj = b.conj() b_conj_flat = b.to_ndarray() cflat = np.tensordot(aflat, b_conj_flat, axes=[[0, 1, 2], [0, 1, 2]]) c = npc.inner(a, b_conj, axes='range') # no transpose assert type(c) == np.dtype(float) assert (abs(c - cflat) < tol) c = npc.inner(a, b_conj, axes=[[0, 1, 2], [0, 1, 2]]) assert (abs(c - cflat) < tol) c = npc.inner(a, b, axes='range', do_conj=True) assert (abs(c - cflat) < tol) # now transpose b.itranspose([2, 1, 0]) b_conj.itranspose([2, 1, 0]) c = npc.inner(a, b_conj, axes=[[2, 0, 1], [0, 2, 1]]) # unordered axes! assert (abs(c - cflat) < tol) c = npc.inner(a, b_conj, axes='labels') assert (abs(c - cflat) < tol) c = npc.inner(a, b, axes='labels', do_conj=True) assert (abs(c - cflat) < tol) print("for trivial charge") a = npc.Array.from_func(np.random.random, [lcTr, lcTr.conj()], shape_kw='size') aflat = a.to_ndarray() b = npc.tensordot(a, a, axes=2) bflat = np.tensordot(aflat, aflat, axes=2) npt.assert_array_almost_equal_nulp(b, bflat, sum(a.shape))
def test_U_I(bc_MPS, method, g=0.5): # Test a time evolution against exact diagonalization for finite bc L = 10 dt = 0.01 if bc_MPS == 'finite': L = 6 model_pars = dict(L=L, Jx=0., Jy=0., Jz=-4., hx=2. * g, bc_MPS=bc_MPS, conserve=None) M = SpinChain(model_pars) state = ([[1 / np.sqrt(2), -1 / np.sqrt(2)]] * L ) # pointing in (-x)-direction psi = tenpy.networks.mps.MPS.from_product_state(M.lat.mps_sites(), state, bc=bc_MPS) psi.test_sanity() U = make_U(M.H_MPO, dt * 1j, which=method) if bc_MPS == 'finite': ED = tenpy.algorithms.exact_diag.ExactDiag(M) ED.build_full_H_from_mpo() ED.full_diagonalization() psiED = ED.mps_to_full(psi) psiED /= psiED.norm() UED = ED.exp_H(dt) for i in range(30): psi = apply_mpo(U, psi, {}) psiED = npc.tensordot(UED, psiED, ('ps*', [0])) psi_full = ED.mps_to_full(psi) assert (abs(abs(npc.inner(psiED, psi_full, [0, 0], True)) - 1) < 1e-2) if bc_MPS == 'infinite': psiTEBD = psi.copy() TEBD_params = {'dt': dt, 'N_steps': 1} EngTEBD = tenpy.algorithms.tebd.Engine(psiTEBD, M, TEBD_params) for i in range(30): EngTEBD.run() psi = apply_mpo(U, psi, {}) print(np.abs(psi.overlap(psiTEBD) - 1)) print(psi.norm) #This test fails assert (abs(abs(psi.overlap(psiTEBD)) - 1) < 1e-2)
def test_arnoldi(n, which): tol = 5.e-14 if n <= 20 else 1.e-10 # generate Hermitian test array leg = gen_random_legcharge(ch, n) # if looking for small/large real part, ensure hermitian H func = rmat.GUE if which[-1] == 'R' else rmat.standard_normal_complex H = npc.Array.from_func_square(func, leg) H_flat = H.to_ndarray() E_flat, psi_flat = np.linalg.eig(H_flat) if which == 'LM': i = np.argmax(np.abs(E_flat)) elif which == 'LR': i = np.argmax(np.real(E_flat)) elif which == 'SR': i = np.argmin(np.real(E_flat)) E0_flat, psi0_flat = E_flat[i], psi_flat[:, i] qtotal = npc.detect_qtotal(psi0_flat, [leg]) H_Op = H # use `matvec` of the array psi_init = npc.Array.from_func(np.random.random, [leg], qtotal=qtotal) eng = lanczos.Arnoldi(H_Op, psi_init, { 'which': which, 'num_ev': 1, 'N_max': 20 }) (E0, ), (psi0, ), N = eng.run() print("full spectrum:", E_flat) print("E0 = {E0:.14f} vs exact {E0_flat:.14f}".format(E0=E0, E0_flat=E0_flat)) print("|E0-E0_flat| / |E0_flat| =", abs((E0 - E0_flat) / E0_flat)) assert abs((E0 - E0_flat) / E0_flat) < tol psi0_H_psi0 = npc.inner(psi0, npc.tensordot(H, psi0, axes=[1, 0]), 'range', do_conj=True) print("<psi0|H|psi0> / E0 = 1. + ", psi0_H_psi0 / E0 - 1.) assert (abs(psi0_H_psi0 / E0 - 1.) < tol) print("<psi0_flat|H_flat|psi0_flat> / E0_flat = ", end=' ') print(np.inner(psi0_flat.conj(), np.dot(H_flat, psi0_flat)) / E0_flat) ov = np.inner(psi0.to_ndarray().conj(), psi0_flat) print("|<psi0|psi0_flat>|=", abs(ov)) assert (abs(1. - abs(ov)) < tol)
def test_npc_inner(): for sort in [True, False]: print("sort =", sort) a = random_Array((10, 7, 5), chinfo3, sort=sort) aflat = a.to_ndarray() legs_b = [l.conj() for l in a.legs[::-1]] b = npc.Array.from_func(np.random.random, legs_b, qtotal=-a.qtotal, shape_kw='size') bflat = b.to_ndarray() c = npc.inner(a, b, axes=[[2, 0, 1], [0, 2, 1]]) assert type(c) == np.dtype(float) cflat = np.tensordot(aflat, bflat, axes=[[2, 0, 1], [0, 2, 1]]) npt.assert_array_almost_equal_nulp(c, cflat, max(a.size, b.size)) print("for trivial charge") a = npc.Array.from_func(np.random.random, [lcTr, lcTr.conj()], shape_kw='size') aflat = a.to_ndarray() b = npc.tensordot(a, a, axes=2) bflat = np.tensordot(aflat, aflat, axes=2) npt.assert_array_almost_equal_nulp(b, bflat, sum(a.shape))
def pollmann_turner_inversion(results, psi, simulation, tol=0.01): """Measurement function for equation 15 of :arxiv:`1204.0704`. See :func:`~tenpy.simulations.measurement.measurement_index` for the call structure. """ psi2 = psi.copy() psi2.spatial_inversion() mixed_TM = TransferMatrix(psi, psi2, transpose=False, charge_sector=0, form='B') evals, evecs = mixed_TM.eigenvectors(which='LM', num_ev=1) results['pollmann_turner_inversion_eta'] = eta = evals[0] U_I = evecs[0].split_legs().transpose().conj() U_I *= np.sqrt( U_I.shape[0] ) # previously normalized to npc.norm(U_I) = 1, need unitary results['pollmann_turner_inversion_U_I'] = U_I if abs(eta) < 1. - tol: O_I = 0. else: O_I = npc.inner(U_I, U_I.conj(), axes=[[0, 1], [1, 0]]) / U_I.shape[0] results['pollmann_turner_inversion'] = O_I
print("4) contract MPS and MPO to calculate the energy <psi|H|psi>") contr = envL for i in range(L): # contr labels: wR, vR, vR* contr = npc.tensordot(contr, Bs[i], axes=('vR', 'vL')) # wR, vR*, vR, p contr = npc.tensordot(contr, Ws[i], axes=(['p', 'wR'], ['p*', 'wL'])) # vR*, vR, wR, p contr = npc.tensordot(contr, Bs[i].conj(), axes=(['p', 'vR*'], ['p*', 'vL*'])) # vR, wR, vR* # note that the order of the legs changed, but that's no problem with labels: # the arrays are automatically transposed as necessary E = npc.inner(contr, envR, axes=(['vR', 'wR', 'vR*'], ['vL', 'wL', 'vL*'])) print("E =", E) print("5) calculate two-site hamiltonian ``H2`` from the MPO") # label left, right physical legs with p, q W0 = W.replace_labels(['p', 'p*'], ['p0', 'p0*']) W1 = W.replace_labels(['p', 'p*'], ['p1', 'p1*']) H2 = npc.tensordot(W0, W1, axes=('wR', 'wL')).itranspose( ['wL', 'wR', 'p0', 'p1', 'p0*', 'p1*']) H2 = H2[0, -1] # (If H has single-site terms, it's not that simple anymore) print("H2 labels:", H2.get_leg_labels()) print("6) calculate exp(H2) by diagonalization of H2") # diagonalization requires to view H2 as a matrix H2 = H2.combine_legs([('p0', 'p1'), ('p0*', 'p1*')], qconj=[+1, -1]) print("labels after combine_legs:", H2.get_leg_labels())
xxz_pars = dict(L=4, Jxx=1., Jz=1., hz=0.0, bc_MPS='finite') M = XXZChain(xxz_pars) ED = ExactDiag(M, [0]) ED.build_full_H_from_mpo() # ED.build_full_H_from_bonds() # whatever you prefer print("start diagonalization") ED.full_diagonalization() psi_ED = ED.groundstate() print("psi_ED =", psi_ED) print("start DMRG") product_state = [0, 1] * (xxz_pars['L'] // 2) # this selects a charge sector! psi_DMRG = MPS.from_product_state(M.lat.mps_sites(), product_state) res = run_DMRG(psi_DMRG, M, {'verbose': 0}) # first way to compare ED with DMRG: convert MPS to ED vector psi_DMRG_full = ED.mps_to_full(psi_DMRG) print("psi_DMRG_full =", psi_DMRG_full) ov = abs(npc.inner(psi_ED, psi_DMRG_full, do_conj=True)) print("|<psi_ED|psi_DMRG>| =", ov) assert (abs(ov - 1.) < 1.e-13) # second way: convert ED vector to MPS psi_ED_mps = ED.full_to_mps(psi_ED) ov = psi_ED_mps.overlap(psi_DMRG) print("|<psi_ED_mps|psi_DMRG>| =", abs(ov)) assert (abs(abs(ov) - 1.) < 1.e-13) # -> advantange: expectation_value etc. of MPS are available! print("<Sz> =", psi_ED_mps.expectation_value('Sz'))
"""Basic use of the `Array` class with trivial arrays.""" # Copyright 2019 TeNPy Developers, GNU GPLv3 import tenpy.linalg.np_conserved as npc M = npc.Array.from_ndarray_trivial([[0., 1.], [1., 0.]]) v = npc.Array.from_ndarray_trivial([2., 4. + 1.j]) v[0] = 3. # set indiviual entries like in numpy print("|v> =", v.to_ndarray()) # |v> = [ 3.+0.j 4.+1.j] M_v = npc.tensordot(M, v, axes=[1, 0]) print("M|v> =", M_v.to_ndarray()) # M|v> = [ 4.+1.j 3.+0.j] print("<v|M|v> =", npc.inner(v.conj(), M_v)) # <v|M|v> = (24+0j)
"""Basic use of the `Array` class with trivial arrays.""" # Copyright 2019-2020 TeNPy Developers, GNU GPLv3 import tenpy.linalg.np_conserved as npc M = npc.Array.from_ndarray_trivial([[0., 1.], [1., 0.]]) v = npc.Array.from_ndarray_trivial([2., 4. + 1.j]) v[0] = 3. # set indiviual entries like in numpy print("|v> =", v.to_ndarray()) # |v> = [ 3.+0.j 4.+1.j] M_v = npc.tensordot(M, v, axes=[1, 0]) print("M|v> =", M_v.to_ndarray()) # M|v> = [ 4.+1.j 3.+0.j] print("<v|M|v> =", npc.inner(v.conj(), M_v, axes='range')) # <v|M|v> = (24+0j)
def test_lanczos_gs(n, N_cache, tol=5.e-14): # generate Hermitian test array leg = gen_random_legcharge(ch, n) H = npc.Array.from_func_square(rmat.GUE, leg) H_flat = H.to_ndarray() E_flat, psi_flat = np.linalg.eigh(H_flat) E0_flat, psi0_flat = E_flat[0], psi_flat[:, 0] qtotal = npc.detect_qtotal(psi0_flat, [leg]) H_Op = H # use `matvec` of the array psi_init = npc.Array.from_func(np.random.random, [leg], qtotal=qtotal) E0, psi0, N = lanczos.lanczos(H_Op, psi_init, { 'verbose': 1, 'N_cache': N_cache }) print("full spectrum:", E_flat) print("E0 = {E0:.14f} vs exact {E0_flat:.14f}".format(E0=E0, E0_flat=E0_flat)) print("|E0-E0_flat| / |E0_flat| =", abs((E0 - E0_flat) / E0_flat)) psi0_H_psi0 = npc.inner(psi0, npc.tensordot(H, psi0, axes=[1, 0]), 'range', do_conj=True) print("<psi0|H|psi0> / E0 = 1. + ", psi0_H_psi0 / E0 - 1.) assert (abs(psi0_H_psi0 / E0 - 1.) < tol) print("<psi0_flat|H_flat|psi0_flat> / E0_flat = ", end=' ') print(np.inner(psi0_flat.conj(), np.dot(H_flat, psi0_flat)) / E0_flat) ov = np.inner(psi0.to_ndarray().conj(), psi0_flat) print("|<psi0|psi0_flat>|=", abs(ov)) assert (abs(1. - abs(ov)) < tol) print("version with arpack") E0a, psi0a = lanczos.lanczos_arpack(H_Op, psi_init, {'verbose': 1}) print("E0a = {E0a:.14f} vs exact {E0_flat:.14f}".format(E0a=E0a, E0_flat=E0_flat)) print("|E0a-E0_flat| / |E0_flat| =", abs((E0a - E0_flat) / E0_flat)) psi0a_H_psi0a = npc.inner(psi0a, npc.tensordot(H, psi0a, axes=[1, 0]), 'range', do_conj=True) print("<psi0a|H|psi0a> / E0a = 1. + ", psi0a_H_psi0a / E0a - 1.) assert (abs(psi0a_H_psi0a / E0a - 1.) < tol) ov = np.inner(psi0a.to_ndarray().conj(), psi0_flat) print("|<psi0a|psi0_flat>|=", abs(ov)) assert (abs(1. - abs(ov)) < tol) # now repeat, but keep orthogonal to original ground state orthogonal_to = [psi0] # -> should give second eigenvector psi1 in the same charge sector for i in range(1, len(E_flat)): E1_flat, psi1_flat = E_flat[i], psi_flat[:, i] qtotal = npc.detect_qtotal(psi1_flat, psi0.legs, cutoff=1.e-10) if np.any(qtotal != psi0.qtotal): continue # not in same charge sector print("--- excited state #", len(orthogonal_to)) ortho_to = [psi.copy() for psi in orthogonal_to] # (gets modified inplace) lanczos_params = {'verbose': 1, 'reortho': True} if E1_flat > -0.01: lanczos_params['E_shift'] = -2. * E1_flat - 0.2 E1, psi1, N = lanczos.lanczos( sparse.OrthogonalNpcLinearOperator(H_Op, ortho_to), psi_init, lanczos_params) print("E1 = {E1:.14f} vs exact {E1_flat:.14f}".format(E1=E1, E1_flat=E1_flat)) print("|E1-E1_flat| / |E1_flat| =", abs((E1 - E1_flat) / E1_flat)) psi1_H_psi1 = npc.inner(psi1, npc.tensordot(H, psi1, axes=[1, 0]), 'range', do_conj=True) print("<psi1|H|psi1> / E1 = 1 + ", psi1_H_psi1 / E1 - 1.) assert (abs(psi1_H_psi1 / E1 - 1.) < tol) print("<psi1_flat|H_flat|psi1_flat> / E1_flat = ", end='') print(np.inner(psi1_flat.conj(), np.dot(H_flat, psi1_flat)) / E1_flat) ov = np.inner(psi1.to_ndarray().conj(), psi1_flat) print("|<psi1|psi1_flat>|=", abs(ov)) assert (abs(1. - abs(ov)) < tol) # and finnally check also orthogonality to previous states for psi_prev in orthogonal_to: ov = npc.inner(psi_prev, psi1, 'range', do_conj=True) assert (abs(ov) < tol) orthogonal_to.append(psi1) if len(orthogonal_to) == 1: print( "warning: test didn't find a second eigenvector in the same charge sector!" )