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_FlatHermitianOperator(n=30, k=5, tol=5.e-15): 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_sparse = sparse.FlatHermitianOperator.from_NpcArray(H, charge_sector=qtotal) psi_init = npc.Array.from_func(np.random.random, [leg], qtotal=qtotal) psi_init /= npc.norm(psi_init) psi_init_flat = H_sparse.npc_to_flat(psi_init) # check diagonalization E, psi = scipy.sparse.linalg.eigsh(H_sparse, k, v0=psi_init_flat, which='SA') E0, psi0 = E[0], psi[:, 0] 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 = np.inner(psi0.conj(), H_sparse.matvec(psi0)).item() print("<psi0|H|psi0> / E0 = 1. + ", psi0_H_psi0 / E0 - 1.) assert (abs(psi0_H_psi0 / E0 - 1.) < tol)
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_charge_detection(): chinfo = chinfo3 for qtotal in [[0], [1], None]: print("qtotal=", qtotal) shape = (8, 6, 5) A = random_Array(shape, chinfo3, qtotal=qtotal) Aflat = A.to_ndarray() legs = A.legs[:] print(A) if not np.any(Aflat > 1.e-8): print("skip test: no non-zero entry") continue qt = npc.detect_qtotal(Aflat, legs) npt.assert_equal(qt, chinfo.make_valid(qtotal)) for i in range(len(shape)): correct_leg = legs[i] legs[i] = None legs = npc.detect_legcharge(Aflat, chinfo, legs, A.qtotal, correct_leg.qconj) res_leg = legs[i] assert res_leg.qconj == correct_leg.qconj legs[i].bunch()[1].test_equal(correct_leg.bunch()[1])
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!" )
def test_FlatHermitianOperator(n=30, k=5, tol=1.e-14): leg = gen_random_legcharge(ch, n // 2) leg2 = gen_random_legcharge(ch, 2) pipe = npc.LegPipe([leg, leg2], qconj=+1) H = npc.Array.from_func_square(rmat.GUE, pipe) H.iset_leg_labels(["(a.b)", "(a*.b*)"]) 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, [pipe]) H_sparse = sparse.FlatHermitianOperator.from_NpcArray(H, charge_sector=qtotal) psi_init = npc.Array.from_func(np.random.random, [pipe], qtotal=qtotal) psi_init /= npc.norm(psi_init) psi_init.iset_leg_labels(["(a.b)"]) psi_init_flat = H_sparse.npc_to_flat(psi_init) # check diagonalization E, psi = scipy.sparse.linalg.eigsh(H_sparse, k, v0=psi_init_flat, which='SA') E0, psi0 = E[0], psi[:, 0] 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 = np.inner(psi0.conj(), H_sparse.matvec(psi0)).item() print("<psi0|H|psi0> / E0 = 1. + ", psi0_H_psi0 / E0 - 1.) assert (abs(psi0_H_psi0 / E0 - 1.) < tol) # split H to check `FlatHermitianOperator.from_guess_with_pipe`. print("=========") print("split legs and define separate matvec") assert psi_init.legs[0] is pipe psi_init_split = psi_init.split_legs([0]) H_split = H.split_legs() def H_split_matvec(vec): vec = npc.tensordot(H_split, vec, [["a*", "b*"], ["a", "b"]]) # TODO as additional challenge, transpose the resulting vector return vec H_sparse_split, psi_init_split_flat = sparse.FlatLinearOperator.from_guess_with_pipe( H_split_matvec, psi_init_split, dtype=H_split.dtype) # diagonalize E, psi = scipy.sparse.linalg.eigsh(H_sparse_split, k, v0=psi_init_split_flat, which='SA') E0, psi0 = E[0], psi[:, 0] 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 = np.inner(psi0.conj(), H_sparse.matvec(psi0)).item() print("<psi0|H|psi0> / E0 = 1. + ", psi0_H_psi0 / E0 - 1.) assert (abs(psi0_H_psi0 / E0 - 1.) < tol)